Skip to content

feat!: Require feature stores to report whether a write was applied - #554

Merged
keelerm84 merged 1 commit into
feat/10.xfrom
mk/SDK-62/store-write-results
Nov 2, 2026
Merged

keelerm84 merged 1 commit into
feat/10.xfrom
mk/SDK-62/store-write-results

Conversation

@keelerm84

@keelerm84 keelerm84 commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

fix: Prevent flag change listeners from firing for writes the store rejects

Summary

FeatureStore.upsert and FeatureStore.delete must now return a bool: True if the store applied the write, False if it rejected it because the stored version is equal or newer. This matches the async store contract.

  • The data source update sink now updates dependencies and sends flag change events only when the store applied the write. Before, a rejected write still fired change events.
  • InMemoryFeatureStore and CachingStoreWrapper return the result. CachingStoreWrapper derives it the same way the async wrapper does: upsert_internal must return the passed-in item on success. The FeatureStoreCore interface is unchanged; its docstring now states that requirement. The Redis, DynamoDB and Consul cores already meet it.
  • Resolves the old TODOs in test_data_sink.py; those tests now assert that a rejected write sends no event.

The Redis suites pass locally. The DynamoDB and Consul suites run in CI only.


Note

Overview
Breaking change: FeatureStore.upsert and FeatureStore.delete now return bool (True when the write is applied, False when it is rejected because the stored version is equal or newer), aligning the sync store contract with the async store.

DataSourceUpdateSinkImpl uses that result so dependency tracking and flag change listeners run only when the store actually changed. Stale or duplicate stream/poll updates no longer fire spurious change events.

Built-in paths implement the contract: InMemoryFeatureStore returns explicit success/failure; CachingStoreWrapper derives it from upsert_internal (success when the core returns the same object that was passed in). FDv1/FDv2 _FeatureStoreClientWrapper forwards the boolean. Interface docs clarify the core return-value rule and fix a typo in the delete docstring.

Tests assert return values on store operations and add coverage that a rejected upsert does not notify listeners; prior TODOs about duplicate deletes/upserts are resolved by expecting no second event when the store rejects the write.

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

fix: Flag change listeners no longer fire for writes the store rejects

FeatureStore.upsert and FeatureStore.delete must now return a bool. Return
True if the store applied the write, or False if it rejected the write
because the stored version is equal or newer. This matches the async
store contract. CachingStoreWrapper derives the result from
FeatureStoreCore.upsert_internal, so custom cores need no change if they
return the passed-in item on success.

The data source update sink now updates dependencies and sends flag
change events only when the store applied the write.
@keelerm84
keelerm84 requested a review from a team as a code owner November 2, 2026 16:51
@keelerm84
keelerm84 merged commit 7adcd08 into feat/10.x Nov 2, 2026
28 of 29 checks passed
@keelerm84
keelerm84 deleted the mk/SDK-62/store-write-results branch November 2, 2026 17:54
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