Skip to content

fix: Report an incomplete DynamoDB auto-config cache write - #874

Merged
keelerm84 merged 2 commits into
v8from
mk/SDK-3102/dynamodb-incomplete-write
Sep 16, 2026
Merged

keelerm84 merged 2 commits into
v8from
mk/SDK-3102/dynamodb-incomplete-write

Conversation

@keelerm84

@keelerm84 keelerm84 commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Summary

dynamoDBStore.SetAll returned nil even when it had written nothing. A failed batch, an unprocessed item, or an environment over the size limit was logged and skipped, and the function reported success anyway.

The damage is not at write time. A later startup reads that partial snapshot and serves it as the whole configuration, marking Relay fully configured with a subset of its environments. SDKs for the missing ones then get a 401 rather than a 503 — and a LaunchDarkly SDK treats a 401 on its data source as terminal, so it stops retrying for good rather than backing off and recovering.

The fix counts everything that could not be written — build failures, size-limit drops, failed batches, unprocessed items — and returns an error naming the loss. The write still continues past a failed batch, because a partial cache is worth more than none, but the caller is told instead of reassured. persistPut already logs SetAll's error, so the problem surfaces when it happens rather than on some later startup.

Redis is unaffected: its SetAll is atomic via MULTI/EXEC.

Not fixed here

Reading a partial snapshot is still indistinguishable from reading a complete one, so a snapshot written before this change is still served as authoritative. Detecting that needs a completeness marker in the cache, which is a schema change and its own piece of work.

Origin

Found by a review of #866 and split out of it. It has nothing to do with retry behavior beyond having been noticed at the same time.


Note

Overview
dynamoDBStore.SetAll now fails when the DynamoDB snapshot is incomplete instead of always returning success after partial writes.

The store tracks every item that cannot be written—marshal/build errors, oversize drops, whole failed BatchWriteItem chunks, and unprocessed items—and returns an error like "N of M items could not be written". Batch writes still continue after a failed chunk so a partial cache can land, but callers are no longer told the write fully succeeded. batchWrite is changed to return a drop count to support this accounting without double-counting items in the error total.

New dynamodb_store_test.go exercises this with an httptest fake DynamoDB: success path, unprocessed items, failed batches, oversized envs (partial write still happens), and 25-item chunking.

Reviewed by Cursor Bugbot for commit 1a1002c. Bugbot is set up for automated code reviews on this repo. Configure here.

@keelerm84
keelerm84 marked this pull request as ready for review September 16, 2026 14:18
@keelerm84
keelerm84 requested a review from a team as a code owner September 16, 2026 14:18

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6b2f796. Configure here.

Comment thread internal/autoconfigcache/dynamodb_store.go Outdated
SetAll returned nil even when it had written nothing. A failed batch, an
unprocessed item, or an environment over the size limit was logged and
skipped, and the function reported success anyway.

The damage is not at write time. A later startup reads that partial
snapshot and serves it as the whole configuration, marking Relay fully
configured with a subset of its environments. SDKs for the missing ones
then get a 401 rather than a 503, and a LaunchDarkly SDK treats a 401 on
its data source as terminal, so it stops retrying for good.

Count everything that could not be written and return an error naming
the loss. The write still continues past a failed batch, because a
partial cache is worth more than none, but the caller is told instead of
reassured; persistPut already logs that error, so the problem surfaces
when it happens.

Redis is unaffected: its SetAll is atomic.

Reading a partial snapshot is still indistinguishable from reading a
complete one. That needs a completeness marker in the cache, which is a
schema change and its own piece of work.
@keelerm84
keelerm84 force-pushed the mk/SDK-3102/dynamodb-incomplete-write branch from 6b2f796 to d58ff62 Compare September 16, 2026 14:43
@keelerm84
keelerm84 merged commit 0964dc0 into v8 Sep 16, 2026
16 checks passed
@keelerm84
keelerm84 deleted the mk/SDK-3102/dynamodb-incomplete-write branch September 16, 2026 16:58
keelerm84 pushed a commit that referenced this pull request Sep 17, 2026
🤖 I have created a release *beep* *boop*
---


##
[8.22.0](v8.21.0...v8.22.0)
(2026-09-16)


### Features

* Adopt go-server-sdk v7.17.0 so an upstream 401 retries
([#856](#856))
([e152af7](e152af7))
* Back off big segment synchronization on a rejected SDK key
([#857](#857))
([6ede85b](6ede85b))
* Classify transport failures as normal, including certificate failures
([#873](#873))
([c3b6850](c3b6850))
* Keep retrying a rejected auto-configuration key
([#866](#866))
([4bf3bb9](4bf3bb9))
* Report the auto-configuration stream's health in the status resource
([#870](#870))
([5c1e3ee](5c1e3ee))


### Bug Fixes

* **autoconfig:** refresh stored environment defaults on update
(SEC-9484) ([#842](#842))
([e04e3e2](e04e3e2))
* **deps:** bump golang.org/x/crypto to v0.55.0 for CVE-2026-56854
([#848](#848))
([360d624](360d624))
* **deps:** bump golang.org/x/crypto to v0.56.0 for CVE-2026-78662 and
CVE-2026-56855
([#854](#854))
([6d5d346](6d5d346))
* **deps:** bump supported Go versions to 1.27.0 and 1.26.7
([#837](#837))
([9b4fb46](9b4fb46))
* **deps:** bump supported Go versions to 1.27.1 and 1.26.8
([#852](#852))
([4b06ed6](4b06ed6))
* emit Vary: Origin on CORS responses (SEC-9501)
([#844](#844))
([78a8f05](78a8f05))
* Report an incomplete DynamoDB auto-config cache write
([#874](#874))
([0964dc0](0964dc0))
* **security:** redact all credential-bearing URL components in status
dbServer ([#846](#846))
([20effc5](20effc5))
* **streams:** treat a nil replay result as no event instead of
panicking ([#845](#845))
([7d7eace](7d7eace))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants