kevy4.0

CDC — 変更フィード(FEED.* / changes_since

kevyは、適用されたすべての書き込みを消費可能な変更ストリームとして公開します。AOFがログに記録し、レプリカが適用するのと同じ効果フレームです。典型的なコンシューマは、キャッシュの無効化、検索インデックスの更新、下流のミラー——RDSスタックで「outboxテーブル」がやっている仕事を、テーブルなしで、というものです。

configで有効にします。

[feed]
enabled = true
# feed_buffer_size = "64mb"   # per shard, caps at 1gb

組み込みならConfig::default().with_feed(0)です。

カーソル:(generation, offset)

すべてのストリーム位置は(generation, offset)の組です。

サーバー面

エラー

組み込み面

let store = kevy_embedded::Store::open(Config::default().with_feed(0))?;
let (gen, off) = store.changes_tail()?;             // start cursor
let batch = store.changes_since(gen, off, 256, &[b"user:"])?;
for change in &batch.changes { /* change.offset, change.argv */ }
let (gen, off) = batch.next;                        // resume here

feed_shards()は1を返します。組み込みの書き込みパスは全シャードを1本のストリームに直列化するため、同じコンシューマループが両方の面に対してそのまま動きます。FeedError::Resync { generation, tail }-FEEDRESYNCに対応します。

配送セマンティクス

at-least-once。 再同期による再構築のあと(そしてフェイルオーバーのいくつかの形において)、すでに適用したフレームをもう一度見ることがあります。コンシューマは冪等でなければなりません(キャッシュの無効化は自然に冪等です)。フレームは適用された効果を運ぶため(たとえばZINTERSTOREDEL + 素のZADDとして現れます)、送り元の状態に関係なく、適用は決定論的です。

保持期間はインメモリのバックログです(シャードごとのfeed_buffer_size)。この窓より遅れると-FEEDRESYNCを受け取ります。オンディスクのアーカイブは存在しません——耐久性のある話は、下のrecovery-point契約を参照してください。

Recovery-point契約(PITR)

フィードを有効にした状態で取ったスナップショットは、そのヘッダにストリームカーソルを記録します。スナップショットのデータと同じ「追記なし窓」の中で凍結された値です。

スナップショットS + Sのカーソル以降のフィードフレーム = 以降の任意のカーソル時点における、正確なその状態。

kevy_persist::read_snapshot_cursor(path)がそれを読み戻します(v2.3より前のスナップショットではNone)。リストアの訓練はこうです。まっさらなデータディレクトリにスナップショットをロードし、記録されたカーソルからFEED.READのフレームをリプレイする——バイト単位で正確な状態が復元され、PREFIX.STATSやキーのダンプで検証できます。訓練用のスクリプトはbench/diskgate.shのrestore行にあります。

プレフィックス単位の統計

PREFIX.STATS <prefix>(サーバー)/ Store::info_prefix(組み込み)は、あるバイトプレフィックスの下にある生きたキー数とTTL付きキー数を報告します。O(キー空間)の走査なので、運用ダッシュボード向けであり、ホットパス向けではありません。

これは何ではないか

シャードをまたぐグローバル順序はありません。サーバーサイドのコンシューマ位置もありません。コンシューマグループもありません。クエリ述語もありません(プレフィックスはバイト列のフィルタです)。それらが必要なら、あなたが説明しているのはメッセージブローカーかRDSです。kevyのフィードは意図的に事象の地平線の手前で止まります(リポジトリのドキュメントにある「3つの法」の設計ノートを参照)。