Skip to content

feat: add IBigSegmentStore interface + Redis and DynamoDB stores#536

Open
beekld wants to merge 10 commits into
mainfrom
beeklimt/SDK-2363
Open

feat: add IBigSegmentStore interface + Redis and DynamoDB stores#536
beekld wants to merge 10 commits into
mainfrom
beeklimt/SDK-2363

Conversation

@beekld
Copy link
Copy Markdown
Contributor

@beekld beekld commented May 19, 2026

Ticket: SDK-2363 · Follows #534

Summary

Adds the public IBigSegmentStore interface, the Membership / StoreMetadata value types, and concrete DynamoDB + Redis implementations. Schema strings match what the Relay Proxy writes.

auto redis_store = RedisBigSegmentStore::Create("redis://localhost:6379", "prefix");
auto dynamo_store = DynamoDBBigSegmentStore::Create("my-table", "prefix", options);

Design notes

  • synchronizedOn parsing. Stored as DynamoDB N / Redis string. Both stores reject malformed values (non-numeric strings, wrong DynamoDB attribute type) rather than silently returning 0, matching the existing dynamodb_source.cpp row validation.
  • No hashing here. The interface contract says callers pass the already-hashed base64 SHA-256 context key; the SDK will hash in the wrapper, not the store implementations.

Not in scope

  • BigSegmentsBuilder config plumbing.
  • BigSegmentStoreWrapper (LRU cache + staleness polling) and the hashing path.
  • Evaluator wiring / replacing the rules.cpp big-segments TODO.

Test plan

  • 7 Membership unit tests pass.
  • 9 RedisBigSegmentStore integration tests pass against Redis 7 (docker).
  • 8 DynamoDBBigSegmentStore integration tests pass against DynamoDB Local.
  • Full server-sdk test suite (468 tests) still green.
  • Schema constants match what Relay writes (verified against the LocalStack/Redis fixture data).

Note

Medium Risk
New evaluation-adjacent persistence API with strict error handling on malformed store data; behavior is well-tested but not yet connected to flag evaluation, so misconfiguration would surface only after later wiring.

Overview
Introduces a Big Segments read path for the server SDK: public IBigSegmentStore plus Membership / StoreMetadata, with Redis and DynamoDB stores that match Relay’s key/schema layout (prefix-scoped, read-only).

GetMembership loads included/excluded segment refs per hashed context; GetMetadata reads the sync timestamp for staleness. DynamoDB uses consistent GetItem, validates String Set / Number attribute types, and errors on malformed rows instead of silent empty data; Redis uses set membership and string sync time with the same strict parsing. Membership::FromSegmentRefs applies spec rules (inclusion over exclusion). Integration tests cover empty stores, prefixes, and bad data; config, caching wrapper, hashing, and evaluator wiring are not in this PR.

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

@beekld beekld marked this pull request as ready for review May 20, 2026 18:50
@beekld beekld requested a review from a team as a code owner May 20, 2026 18:50
Comment thread libs/server-sdk-dynamodb-source/src/dynamodb_big_segment_store.cpp
Comment thread libs/server-sdk-dynamodb-source/src/dynamodb_big_segment_store.cpp
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 0082f6e. Configure here.

Comment thread libs/server-sdk-redis-source/src/redis_big_segment_store.cpp
return std::unique_ptr<RedisBigSegmentStore>(new RedisBigSegmentStore(
std::make_unique<sw::redis::Redis>(std::move(uri)),
std::move(prefix)));
} catch (sw::redis::Error const& e) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we catch std::exception as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

public:
DynamoDBBigSegmentTests()
: table_name_("ld-dynamodb-big-segments-test"),
prefix_("testprefix"),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we have a test for an empty prefix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

std::make_unique<sw::redis::Redis>(std::move(uri)),
std::move(prefix)));
} catch (sw::redis::Error const& e) {
return tl::make_unexpected(e.what());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need to sanitize this for the redis user/password?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Or maybe make this and the dynamo version into something vague? Main concern is just a URI coming out which includes user/password or something else the customer puts into the URI and considers secret.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I wanna push back on this one. I think making it vague would lose the diagnostic value of the original error messages. I would hope that redis already wouldn't be putting sensitive info into error messages. But also, we aren't logging this here, either. I think the "correct" solution would be to have a sanitizer wherever we are logging, but that's a much bigger task.

IBigSegmentStore::GetMembershipResult RedisBigSegmentStore::GetMembership(
std::string const& context_hash) const {
std::string const include_key =
prefix_ + ":" + kIncludeKeyNamespace + ":" + context_hash;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Would it be work it to cache prefix_ + ":" + kIncludeKeyNamespace and prefix_ + ":" + kExcludeKeyNamespace then just add on the context_hash?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

std::string prefix)
: redis_(std::move(redis)),
prefix_(std::move(prefix)),
sync_time_key_(prefix_ + ":" + kSyncTimeKey) {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we check for an empty prefix?

.Net and JS do, but Java doesn't.

public RedisStoreBuilder<T> Prefix(string prefix)
{
    _prefix = string.IsNullOrEmpty(prefix) ? Redis.DefaultPrefix : prefix;
    return this;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Yes, I think Java is probably wrong here


auto const it = item.find(kBigSegmentsSyncTimeAttribute);
if (it == item.end()) {
return tl::make_unexpected(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Other implementations seem more lenient here. Though I wonder if there was an evolution where maybe this field didn't exist originally.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The spec seems to say we should be more lenient too. Done.

@beekld beekld requested a review from kinyoklion May 27, 2026 23:53
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