fix(kb): wait for the knowledge base to be ACTIVE before its data source - #889
Open
DerrickF wants to merge 1 commit into
Open
fix(kb): wait for the knowledge base to be ACTIVE before its data source#889DerrickF wants to merge 1 commit into
DerrickF wants to merge 1 commit into
Conversation
The first knowledge base that got past IAM was created and then abandoned: ConflictException: The Knowledge Base is not in a valid status. Wait for the knowledge base to reach a valid status and try again. `CreateKnowledgeBase` returns while the knowledge base is still `CREATING`. This module's own header records 47-124 s to `ACTIVE` (n=7) — the knowledge was there, just not in the code path, which called `CreateDataSource` immediately. `ConflictException` is deliberately absent from RETRYABLE_ERROR_CODES, because a genuine conflict should fail fast, and `_call`'s backoff tops out near 60 s anyway — short of the measured upper bound. Retrying the dependent call is the wrong shape regardless: it spends attempts on a precondition instead of waiting for it. So the wait is explicit and bounded at 300 s, roughly 2.5x the observed worst case and well inside the worker's 15-minute timeout. A terminal status (FAILED/DELETING) fails on the first poll rather than waiting out the budget. Consequence worth recording: the create succeeded, the data source did not, and `attach_aws_ids` never ran — so the knowledge base existed in AWS with no record pointing at it. Two pre-existing decisions contained it. The `clientToken` is derived from `app_kb_id`, so the next attempt *adopts* that knowledge base rather than creating a second one; and tags are written at create, so the reconciler can find it either way. The failure message now says so, because an operator reading it needs to know a retry is safe. WHY NO TEST CAUGHT THIS `FakeBedrockAgent.create_knowledge_base` returned `status: "ACTIVE"`, which the real API never does. Every provisioning test therefore skipped straight past the window where this breaks. The fake now returns `CREATING` and answers `get_knowledge_base` from a configurable status sequence, so the wait is exercised rather than assumed. Five new tests cover it, including that the data source is never attempted while CREATING and that the budget is read at call time. Mutation verified: deleting the wait fails the named tests. Tests: 2,321 passed across tests/shared, tests/lambdas and tests/property.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first knowledge base past IAM was created and then abandoned
CreateKnowledgeBasereturns while the knowledge base is stillCREATING. This module's own header records 47–124 s toACTIVE(n=7) — the knowledge was there, just not in the code path, which calledCreateDataSourceimmediately.Why not just widen the retry set
ConflictExceptionis deliberately absent fromRETRYABLE_ERROR_CODESso a genuine conflict fails fast, and_call's backoff tops out near 60 s anyway — short of the measured upper bound.More fundamentally, retrying the dependent call is the wrong shape: it spends attempts on a precondition instead of waiting for it. So the wait is explicit, bounded at 300 s (~2.5x the observed worst case, well inside the worker's 15-minute timeout). A terminal status —
FAILED,DELETING— fails on the first poll rather than waiting out the budget.The orphan, and what contained it
The create succeeded, the data source did not, and
attach_aws_idsnever ran — so the knowledge base existed in AWS with no record pointing at it. Two pre-existing decisions kept that from being serious:clientTokenis derived fromapp_kb_id, so the next attempt adopts that knowledge base rather than creating a second one.The failure message now says this, because an operator reading it needs to know a retry is safe rather than duplicating.
Why no test caught it
FakeBedrockAgent.create_knowledge_basereturnedstatus: "ACTIVE"— which the real API never does. Every provisioning test therefore skipped straight past the window where this breaks.The fake now returns
CREATINGand answersget_knowledge_basefrom a configurable status sequence, so the wait is exercised rather than assumed. That change alone made 11 existing tests fail until the wait was added, which is the behaviour you want from a test double.Five new tests: the data source is created only after
ACTIVE; it is never attempted whileCREATING; a terminal status fails on the first poll; the budget is read at call time rather than bound as a default argument; and the failure message tells the operator a retry is safe.Mutation verified: deleting the wait fails the named tests.
Verification
2,321 tests pass across
tests/shared,tests/lambdasandtests/property.ruffclean on both files.Observed in dev before the fix: the knowledge base reached
ACTIVE, typeMANAGED, correctly taggedManagedKbEnvironment=dev— so provisioning, the managed KB type, and the tag contract are all confirmed working. Only the data-source step was blocked.After merge
Backend-only, so
backend.ymlrebuilds the kb-migration image and updates the four Lambdas — no platform deploy needed. Then retrying adopts the existingACTIVEknowledge base, the wait returns immediately, and the data source is created.