Skip to content

Reduce stream trim scan cost - #1137

Merged
bootjp merged 6 commits into
mainfrom
feature/redis-stream-trim-cursor
Jul 22, 2026
Merged

Reduce stream trim scan cost#1137
bootjp merged 6 commits into
mainfrom
feature/redis-stream-trim-cursor

Conversation

@bootjp

@bootjp bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Author: bootjp

Summary

  • persist a stream head-trim cursor in StreamMeta so XADD/XTRIM/Lua stream trims skip already trimmed MVCC history
  • use the cursor for stream materialization, XREAD, XRANGE, and XREVRANGE scans
  • keep legacy 24/32 byte stream metadata readable and omit the runtime cursor from logical backups

Risk

  • StreamMeta encoding now writes 48 bytes, but readers still accept the previous 24 and 32 byte formats.
  • The trim cursor is internal only and does not change Redis-visible stream IDs, lengths, TTL, or backup JSONL output.

Tests

  • go test ./store ./internal/backup ./adapter -run 'Test(StreamMeta|StreamEntryScanStart|LuaStreamXAddMaxLenUsesTrimCursor|LuaStreamXAddMaxLenUsesBoundedDeltaTrim|LuaStreamXAddMaxLenZeroKeepsLastID|LuaStreamXAddDeltaCommitAvoidsFullScan|RedisDB_StreamAcceptsTrimCursorMetaValue|RedisDB_StreamRejectsMalformedMetaValueLength|RedisDB_StreamRoundTripBasic|RedisCollectionExpireWritesInlineMetaTTL|RedisTTLAtInlineNoTTLCollectionMetaSkipsLegacyFallback)'
  • go test ./adapter -run 'TestRedis.*Stream|TestLua.*Stream|TestTrimLuaStream'
  • go test ./proxy ./cmd/redis-proxy
  • golangci-lint run ./store ./adapter ./internal/backup ./proxy ./cmd/redis-proxy --timeout=5m
  • git diff --check HEAD~2..HEAD

Summary by CodeRabbit

  • 改善
    • ストリームのトリム位置を保持・復元できるようになり、続きの読み取りを適切な位置から再開できるようになりました。
    • XADD、XTRIM、範囲検索などで、トリム後のデータ処理とメタデータ更新の精度を向上しました。
    • 既存および新しい形式のストリームメタデータに対応しました。
    • リーダー切り替え時のLua操作の安定性を改善しました。

@bootjp

bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@bootjp, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bec7a1af-bb7c-4e4b-a597-85394b98bc69

📥 Commits

Reviewing files that changed from the base of the PR and between ac00ea5 and 5ef05d4.

📒 Files selected for processing (9)
  • adapter/redis_collection_ttl.go
  • adapter/redis_delta_compactor_test.go
  • adapter/redis_lua_collection_fastpath_test.go
  • adapter/redis_ttl_inline_migrator.go
  • internal/backup/redis_stream.go
  • internal/backup/redis_stream_test.go
  • kv/keyviz_label.go
  • kv/sharded_coordinator.go
  • store/stream_helpers.go
📝 Walkthrough

Walkthrough

ストリームメタにトリムカーソルを追加し、XADD/XTRIMによる更新、読み取り時のスキャン開始位置、TTL・バックアップの48バイト形式を対応しました。Lua fast-pathテストはリーダー切替時の再試行を検証します。

Changes

ストリームトリムカーソル

Layer / File(s) Summary
メタデータ形式とスキャン開始位置
store/stream_helpers.go, store/stream_helpers_test.go
StreamMetaTrimmedMs/TrimmedSeqを追加し、48バイト形式のシリアライズ、復元、カーソルに基づくスキャン開始キーを実装・検証します。
XADD/XTRIMのカーソル更新
adapter/redis_stream_cmds.go, adapter/redis_lua_context.go, adapter/redis_lua_stream_delta_test.go
XADD/XTRIMとLua差分コミットが、削除済みエントリーのカーソルをメタデータへ反映します。
読み取りスキャンへのメタデータ適用
adapter/redis_compat_helpers.go, adapter/redis_stream_cmds.go, adapter/redis_lua_stream_delta_test.go
読み取り・範囲検索・互換性スキャンがStreamMetaを使い、トリムカーソル以降を開始位置として扱います。
TTL・バックアップのメタ形式対応
adapter/redis_collection_ttl.go, adapter/redis_delta_compactor_test.go, internal/backup/redis_stream.go, internal/backup/redis_stream_test.go, internal/backup/encode_redis_coll.go
TTL判定とバックアップが48バイトのトリムカーソル付きメタ値を受理し、論理JSONLにはトリムカーソルを出力しません。

Lua fast-pathリーダー検証

Layer / File(s) Summary
リーダー切替時のSISMEMBER検証
adapter/redis_lua_collection_fastpath_test.go
SISMEMBER Lua fast-pathテストが現在のリーダー向けクライアントとretryNotLeaderを使用します。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant prepareXAdd
  participant buildXTrimHeadElems
  participant StreamMeta
  participant scanStreamRangeKVs

  Client->>prepareXAdd: XADD with MAXLEN
  prepareXAdd->>buildXTrimHeadElems: trim entries using StreamMeta
  buildXTrimHeadElems-->>prepareXAdd: trim elements and trimmedThrough
  prepareXAdd->>StreamMeta: update length and trim cursor
  Client->>scanStreamRangeKVs: XRANGE/XREVRANGE
  scanStreamRangeKVs->>StreamMeta: obtain cursor-based scan start
  StreamMeta-->>scanStreamRangeKVs: adjusted scan start
Loading

Possibly related PRs

  • bootjp/elastickv#620: 同じストリームメタとキー配置を基盤として、トリムカーソルを追加しています。
  • bootjp/elastickv#997: 同じTTL処理でストリームのインラインメタ長判定を扱っています。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed ストリームのトリム済み履歴スキャンを減らしてコストを下げるという変更の主旨を正しく表しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@bootjp

bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed the CI failure in TestDeltaCompactor_TTLInlineMigratesLegacyStreamTTL by updating the test to expect the new 48-byte StreamMeta shape and assert the trim cursor fields remain zero for legacy TTL migration.

Validation:

  • go test -race ./adapter -run TestDeltaCompactor_TTLInlineMigratesLegacyStreamTTL -count=1
  • go test ./store ./internal/backup ./adapter -run 'Test(StreamMeta|StreamEntryScanStart|LuaStreamXAddMaxLenUsesTrimCursor|LuaStreamXAddMaxLenUsesBoundedDeltaTrim|LuaStreamXAddMaxLenZeroKeepsLastID|LuaStreamXAddDeltaCommitAvoidsFullScan|RedisDB_StreamAcceptsTrimCursorMetaValue|RedisDB_StreamRejectsMalformedMetaValueLength|RedisDB_StreamRoundTripBasic|RedisCollectionExpireWritesInlineMetaTTL|RedisTTLAtInlineNoTTLCollectionMetaSkipsLegacyFallback|DeltaCompactor_TTLInlineMigratesLegacyStreamTTL)'
  • golangci-lint run ./store ./adapter ./internal/backup --timeout=5m
  • git diff --check

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 0eec41ca21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bootjp

bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed the CI flake in TestLua_SISMEMBER_FastPathHit. The test now retries only transient NOTLEADER/leader-not-found errors and selects the currently reported leader Redis endpoint for the EVAL call, while preserving non-leader errors as normal failures.

Validation:

  • go test -race ./adapter -run TestLua_SISMEMBER_FastPathHit -count=1
  • go test ./adapter -run 'TestLua_(SISMEMBER|HGET|HEXISTS).*FastPath|TestLua_SISMEMBER_FastPathHit' -count=1
  • golangci-lint run ./adapter --timeout=5m
  • git diff --check

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
store/stream_helpers.go (1)

18-20: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

48バイト(トリムカーソル付きストリームメタ)のサイズが3パッケージで独立に重複定義されています。 store.MarshalStreamMeta/UnmarshalStreamMeta が定義する48バイトのバイナリ形式に対して、adapterinternal/backup がそれぞれ独自の同値定数を保持しており、単一のソース・オブ・トゥルースがありません。現時点では値は一致していますが、StreamMeta にフィールドが追加された場合に他2箇所が追従せず、TTL判定やバックアップの長さ検証が静かに不整合になるリスクがあります。

  • store/stream_helpers.go#L18-L20: このファイルが正典なので、streamMetaTrimBinarySize を(必要なら他の定数と共に)エクスポートし、他パッケージから参照させる。
  • adapter/redis_collection_ttl.go#L18-L19: redisStreamMetaTrimSizeBytes を削除し、エクスポートされた store.StreamMetaTrimBinarySize(相当)を参照するように変更する。
  • internal/backup/redis_stream.go#L58-L61: redisStreamMetaTrimCursorSize を同様にエクスポート定数へ差し替える(バックアップ側は独立バイナリ仕様のドキュメントも兼ねているため、コメントで store 側の定義との対応関係を明記する)。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@store/stream_helpers.go` around lines 18 - 20, Export the canonical 48-byte
stream metadata size from store/stream_helpers.go alongside
streamMetaTrimBinarySize, then remove redisStreamMetaTrimSizeBytes in
adapter/redis_collection_ttl.go and use the exported store constant. In
internal/backup/redis_stream.go, replace redisStreamMetaTrimCursorSize with the
same exported constant and document its correspondence to the store definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapter/redis_lua_collection_fastpath_test.go`:
- Around line 239-240: Update the leader-resolution helper around the
redis.NewClient return so it does not fall back to nodes[0] when no leader
information or matching raftAddress is found. Return a “leader not found” error
instead, allowing retryNotLeader to retry; preserve the existing client creation
for successfully resolved leaders.

---

Nitpick comments:
In `@store/stream_helpers.go`:
- Around line 18-20: Export the canonical 48-byte stream metadata size from
store/stream_helpers.go alongside streamMetaTrimBinarySize, then remove
redisStreamMetaTrimSizeBytes in adapter/redis_collection_ttl.go and use the
exported store constant. In internal/backup/redis_stream.go, replace
redisStreamMetaTrimCursorSize with the same exported constant and document its
correspondence to the store definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d23fb7cb-a3e0-424c-b9cf-6141337c24d5

📥 Commits

Reviewing files that changed from the base of the PR and between 52953be and ac00ea5.

📒 Files selected for processing (12)
  • adapter/redis_collection_ttl.go
  • adapter/redis_compat_helpers.go
  • adapter/redis_delta_compactor_test.go
  • adapter/redis_lua_collection_fastpath_test.go
  • adapter/redis_lua_context.go
  • adapter/redis_lua_stream_delta_test.go
  • adapter/redis_stream_cmds.go
  • internal/backup/encode_redis_coll.go
  • internal/backup/redis_stream.go
  • internal/backup/redis_stream_test.go
  • store/stream_helpers.go
  • store/stream_helpers_test.go

Comment thread adapter/redis_lua_collection_fastpath_test.go Outdated
@bootjp

bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Added a production performance fix for the TTL inline migrator observed during Redis proxy monitoring. In sharded deployments, the migrator now scans only local leader groups with ScanGroupAt instead of letting every node issue whole-keyspace scans that proxy back to hot leaders. The keyviz coordinator wrapper now preserves that local-leader-group capability.

Validation:

  • go test ./adapter -run 'TestDeltaCompactor_TTLInline(UsesLocalLeaderGroupScan|MigratesOnNonDefaultShardLeader|StopsAfterTimeout|MigratesLegacyHashMeta|MigratesLegacyStreamTTL)' -count=1
  • go test ./kv -run 'TestShardedCoordinator|TestShardStoreScanGroupAt|TestKey' -count=1
  • go test ./store ./internal/backup ./adapter ./kv -run 'Test(StreamMeta|StreamEntryScanStart|LuaStreamXAddMaxLenUsesTrimCursor|LuaStreamXAddMaxLenUsesBoundedDeltaTrim|LuaStreamXAddMaxLenZeroKeepsLastID|LuaStreamXAddDeltaCommitAvoidsFullScan|RedisDB_StreamAcceptsTrimCursorMetaValue|RedisDB_StreamRejectsMalformedMetaValueLength|RedisDB_StreamRoundTripBasic|RedisCollectionExpireWritesInlineMetaTTL|RedisTTLAtInlineNoTTLCollectionMetaSkipsLegacyFallback|DeltaCompactor_TTLInline|ShardedCoordinator|ShardStoreScanGroupAt)' -count=1
  • golangci-lint run ./adapter ./kv ./store ./internal/backup --timeout=5m
  • git diff --check

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (a36dac7):

  • kv/sharded_coordinator.go

What to check, by subsystem:

  • kv/hlc*.goNext() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.goRunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@github-actions

Copy link
Copy Markdown
Contributor

TLA+ spec divergence review (auto-triggered)

This PR touches files that the TLA+ safety spec has an anchor on (per
docs/design/2026_05_28_implemented_tla_safety_spec.md §3),
so an AI review is requested below to verify the implementation has not drifted
from the model.

Anchored files changed in this PR head (5ef05d4):

  • kv/sharded_coordinator.go

What to check, by subsystem:

  • kv/hlc*.goNext() must respect the HLC-4 preconditions (i)/(ii)/(iii) from the design doc: bounded skew, logical-counter handoff on leader change (strategy (c) Observe(MaxAppliedHLC)), and the commit-time ceiling fence (fail-closed when wall_now >= physicalCeiling). Any change to the bit layout (48/16), the CAS loop, or the ceiling getter/setter is in scope.
  • kv/coordinator.go, kv/sharded_coordinator.goRunHLCLeaseRenewal, hlcRenewalInterval, hlcPhysicalWindowMs constants, and the new-term detection that calls Observe(fsm.MaxAppliedHLC()) (strategy (c)). Any change to renewal cadence, group selection, or fail-closed behaviour is in scope.
  • kv/transaction.go, kv/lock_resolver.go — OCC commit-ts assignment, lock-map encoding (key, lock_ts) -> start_ts, and the LockResolver action OCC-3 depends on. (M2 spec will land OCC-1..OCC-5; until then the spec doc §5.2 is the contract.)
  • kv/fsm.go — FSM apply of HLC lease entries (SetPhysicalCeiling), and any future MaxAppliedHLC() accessor that strategy (c) needs.
  • store/mvcc_store.go — version visibility, snapshot install, and the MVCC-1..MVCC-4 invariants (M3 scope).
  • distribution/** — route catalog versioning, SplitRange atomicity, and CatalogWatcher async fan-out (M4 scope).

If the change is correct but requires a spec update, edit tla/hlc/HLC.tla (or the corresponding M2..M5 module once landed) and the design doc in the same PR. The tla-check workflow runs the TLC model check on the same paths.


@claude review please verify TLA+ spec divergence per the checklist above.

@codex review please verify TLA+ spec divergence per the checklist above.

@bootjp

bootjp commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Addressed the current review feedback. The SISMEMBER fast-path test now returns a retryable leader-not-found error instead of falling back to an arbitrary node, and the 48-byte stream metadata size now uses the exported store constant from adapter and backup code.

Validation:

  • go test ./adapter -run 'TestLua_SISMEMBER_FastPathHit|TestDeltaCompactor_TTLInlineMigratesLegacyStreamTTL' -count=1
  • go test ./store ./internal/backup -run 'Test(StreamMeta|RedisDB_Stream)' -count=1
  • go test ./store ./internal/backup ./adapter ./kv -run 'Test(StreamMeta|StreamEntryScanStart|LuaStreamXAddMaxLenUsesTrimCursor|LuaStreamXAddMaxLenUsesBoundedDeltaTrim|LuaStreamXAddMaxLenZeroKeepsLastID|LuaStreamXAddDeltaCommitAvoidsFullScan|Lua_SISMEMBER_FastPathHit|RedisDB_StreamAcceptsTrimCursorMetaValue|RedisDB_StreamRejectsMalformedMetaValueLength|RedisDB_StreamRoundTripBasic|RedisCollectionExpireWritesInlineMetaTTL|RedisTTLAtInlineNoTTLCollectionMetaSkipsLegacyFallback|DeltaCompactor_TTLInline|ShardedCoordinator|ShardStoreScanGroupAt)' -count=1
  • golangci-lint run ./adapter ./kv ./store ./internal/backup --timeout=5m
  • git diff --check

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 5ef05d4d04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bootjp
bootjp merged commit 653e847 into main Jul 22, 2026
11 checks passed
@bootjp
bootjp deleted the feature/redis-stream-trim-cursor branch July 22, 2026 09:21
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.

1 participant