Skip to content

fix(stream): don't suppress digest reverts in snapshot stream#5923

Merged
markphelps merged 3 commits into
flipt-io:v2from
immanuwell:fix/stream-digest-revert
May 28, 2026
Merged

fix(stream): don't suppress digest reverts in snapshot stream#5923
markphelps merged 3 commits into
flipt-io:v2from
immanuwell:fix/stream-digest-revert

Conversation

@immanuwell

@immanuwell immanuwell commented May 28, 2026

Copy link
Copy Markdown
Contributor

What

EvaluationSnapshotNamespaceStream uses r.GetDigest() == snap.GetDigest() to skip sending a snapshot the client already has. Good idea, but that check fires on every future snapshot with the same digest - not just the initial one.

The bug

Say client connects with digest d1. Stream goes:

  • d1 -> skipped (correct, client has it)
  • d2 -> sent (client now has d2)
  • d1 (git rollback) -> skipped! (but client has d2 now, not d1)

Client is stuck on stale state and won't recover until the digest changes again.

How to reproduce

Run the new test:

go test ./internal/server/evaluation/client/... -run TestServer_EvaluationSnapshotNamespaceStream/request_digest_revert

It fails without the fix.

Fix

Guard the check with lastDigest == nil so it only suppresses the first snapshot (before anything has been sent). After that, the existing hash-based dedup handles everything, including reverts.

// before
if r.GetDigest() == snap.GetDigest() {

// after
if lastDigest == nil && r.GetDigest() == snap.GetDigest() {

Regression introduced in #5909.

The digest check r.GetDigest() == snap.GetDigest() was meant to suppress
the first snapshot when the client already has that state. However it also
suppressed any later snapshot whose digest happened to equal the original
request digest (e.g. after a git rollback), leaving clients stuck on stale
data indefinitely.

Guard the check with lastDigest == nil so it only fires before the first
update is sent; subsequent deduplication is handled by the existing
hash-based comparison.

Signed-off-by: Immanuel Tikhonov <[email protected]>
Signed-off-by: immanuwell <[email protected]>
@immanuwell
immanuwell requested a review from a team as a code owner May 28, 2026 05:13
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label May 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: comment

The PR fixes a subtle regression with digest reverts in EvaluationSnapshotNamespaceStream—after the first snapshot, all future snapshots (including rollbacks) are correctly delivered. Logic and test coverage are sound, but the in-line comment would benefit from a brief explanation of the bug and reason for the guard. No blocking issues.

internal/server/evaluation/client/server.go

  • major (L166): The suppression check for sending snapshots now correctly guards with 'lastDigest == nil', ensuring only the initial snapshot is skipped if the client already has it. However, the comment clarifying this logic could be expanded to reference the regression scenario, guiding future maintainers about why the check is limited this way. Suggest elaborating the in-line comment to briefly explain the reverted digest bug and its impact.

🤖 Automated review by the Flipt PR review agent.

@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.28%. Comparing base (5e89df6) to head (39fc16e).

Additional details and impacted files
@@            Coverage Diff             @@
##               v2    #5923      +/-   ##
==========================================
+ Coverage   61.05%   61.28%   +0.23%     
==========================================
  Files         141      141              
  Lines       14211    14208       -3     
==========================================
+ Hits         8676     8707      +31     
+ Misses       4809     4772      -37     
- Partials      726      729       +3     
Flag Coverage Δ
integrationtests 34.50% <60.00%> (+0.28%) ⬆️
unittests 52.36% <100.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- replace SHA1 hash-based dedup with plain string comparison
- initialize lastDigest from request digest
- validate request digest is 40 characters when present

Signed-off-by: Roman Dmytrenko <[email protected]>
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels May 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: comment

The PR fixes the digest revert regression in EvaluationSnapshotNamespaceStream. The new logic ensures snapshots are delivered correctly after rollbacks, and test coverage is comprehensive. Only suggestion: elaborate the suppression logic comment so future readers grasp the rationale. No blocking issues.

internal/server/evaluation/client/server.go

  • major (L166): The new guard on snapshot suppression ('lastDigest == nil && r.GetDigest() == snap.GetDigest()') correctly addresses the regression where a digest revert could cause the stream to skip sending a needed update. However, the corresponding comment is terse—future maintainers would benefit from an in-line explanation referencing the digest revert scenario and why the suppression is limited to the initial snapshot. Suggest: expand the in-line comment to briefly describe the bug and the reasoning behind the guarded check, e.g., 'Only suppress the initial snapshot if the client already has it; subsequent digest reverts must always be delivered, otherwise the client could be stuck with stale state.'

🤖 Automated review by the Flipt PR review agent.

@erka erka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thank you @immanuwell for your work and for your attention to detail.

@erka erka added the v2 Flipt v2 label May 28, 2026
@erka
erka requested a review from markphelps May 28, 2026 15:18

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: comment

The PR fixes a regression in EvaluationSnapshotNamespaceStream, ensuring snapshots are always sent to the client after digest reverts. Logic is sound and tests are thorough. The only actionable suggestion is to expand the code comment on the suppression check so future maintainers understand its rationale. No blocking issues.

internal/server/evaluation/client/server.go

  • major (L166): The suppression logic for sending snapshots, now guarded with 'lastDigest == nil && r.GetDigest() == snap.GetDigest()', correctly ensures digest reverts are not skipped after the first snapshot. However, the in-line comment describing this guard is concise and risks confusion for future maintainers. Please expand the comment to briefly explain that this check prevents clients from getting stuck with stale state during a digest revert, referencing the bug that was fixed (regression #5909). This gives historical context and aids clarity.

🤖 Automated review by the Flipt PR review agent.

@markphelps
markphelps merged commit 466e094 into flipt-io:v2 May 28, 2026
30 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Flipt V2 May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files. v2 Flipt v2

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants