diff --git a/PLAN.md b/PLAN.md index 2bb0ec1..2a4b88c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -189,9 +189,9 @@ Owner-only storage + remote Get would scale memory with N but would **break** th | Operation | Contract | |-----------|----------| -| **Get** | Returns a local copy if present. On CacheOnly miss, **forwards to the owner** (replica stores the result; non-replica does not). May lag other replicas or the source-of-truth. **Invalid** on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. | +| **Get** | Returns a local copy if present. On CacheOnly miss, **forwards to the owner** (replica stores the result; non-replica does not). May lag other replicas or the source-of-truth. **Invalid** on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream. | | **Put / PutMany** | Returns once the key's **owner** has accepted the write (assigned version, local apply). Value is **async fan-out** to the other **R−1 replicas** on the ring (`ReplicationFactor`, default 3; negative = all peers). Non-replica peer failures are not contacted. Replica failures: **log + metric only** (not in Put error). **Invalid** on structured modes. | -| **Delete / DeleteMany** | Owner installs a tombstone and fans it through the **same replica apply+hint pool as Put** (`Fanout.Apply`, sync first attempt). Failed RPCs are hinted and replayed (LWW so a later delete supersedes a queued put). Returns **structured multi-error** if any replica is unreachable on the first attempt. Topology handoff uses the same pool. Applies to KV keys **and** named Bloom / set / zset / geo / list / hash / counter / JSON / bitmap / HLL / TopK / CMS / vector-set entries. | +| **Delete / DeleteMany** | Owner installs a tombstone and fans it through the **same replica apply+hint pool as Put** (`Fanout.Apply`, sync first attempt). Failed RPCs are hinted and replayed (LWW so a later delete supersedes a queued put). Returns **structured multi-error** if any replica is unreachable on the first attempt. Topology handoff uses the same pool. Applies to KV keys **and** named Bloom / set / zset / geo / list / hash / counter / JSON / bitmap / HLL / TopK / CMS / vector-set / stream entries. | | **BloomAdd / BloomTest** | `ModeBloom` only. Add ORs bits on owner + replicas (not LWW of the bitset). Test: local if replica has the filter, else owner-forward. Missing filter → test false. No per-item delete. | | **SetAdd / SetRemove / SetContains / SetCard / SetMembers** | `ModeSet` only. Owner serializes mutations; item-level fan-out (`FlagSetAdd` / `FlagSetRemove`). Contains/card/members: local on replica, owner-forward otherwise. Missing set → contains false, card 0, empty members. | | **ZAdd / ZRem / ZScore / ZCard / ZRange / ZRangeByScore** | `ModeZSet` only. Same ownership pattern as ModeSet; item-level `FlagZSetAdd` / `FlagZSetRem`; score `float64` (NaN rejected). Equal scores order by member bytes. Range by Redis-style rank or inclusive score window. | @@ -205,13 +205,14 @@ Owner-only storage + remote Get would scale memory with N but would **break** th | **TopKAdd / TopKList** | `ModeTopK` only. Approximate heavy-hitters; Space-Saving, +1 observations. Owner applies the op then fans out a **full `FlagTopK` snapshot**. Replica `TopKList` may lag. Missing name → `ok=false`. Empty-until-delete. Get/Put **invalid**. `TopKAdd` is ACK-only. Estimates do **not** match Redis `TOPK`. | | **CMSIncr / CMSQuery** | `ModeCMS` only. Approximate frequency of a named item; Count-Min, d=4 w=2048 dense 64 KiB. Owner applies the op then fans out a **full `FlagCMS` snapshot**. Replica `CMSQuery` may lag. Missing name → `ok=false`. Empty-until-delete. Get/Put **invalid**. `CMSIncr` is ACK-only (`n==0` means 1; no returned estimate). Estimates do **not** match Redis `CMS.QUERY`. | | **VAdd / VRem / VSim / VCard / VDim / VEmb** | `ModeVectorSet` only. Named embedding set; brute-force K-NN (`VectorMetric` cosine/L2/IP). Owner applies then fans a **full `FlagVectorSet` snapshot** (inbox `A`/`R` owner-only). Replica `VSim` may lag. Missing name → `VSim` empty, `VCard`/`VDim` `present=false`. Last rem keeps empty set (dim locked). Get/Put **invalid**. | +| **XAdd / XRange / XRevRange / XLen / XDel / XTrim** | `ModeStream` only. Named append-only log; owner-minted `millis-seq` ids; opaque `[]byte` payload. Owner `XAdd` returns the id (peer `StreamAdd` from non-owner). Replicas install a **full `FlagStream` snapshot** (`S`; inbox `A`/`D`/`T` owner-only). Replica `XRange` may lag. Missing name → `XRange` empty, `XLen` `present=false`. Empty after last del/trim stays live until `Delete(name)`. Get/Put **invalid**. | | **UpdateKeySpace / DeleteKeySpace** | **Local to the calling node.** Re-issue on every node for cluster-wide rollout. Drift is unsupported in v1; expose config generation on `/peers` for detection. | ### Read-your-writes (normative) | Where | Guarantee | |-------|-----------| -| **Owner node** after successful Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / Incr / JsonSet / JsonDel / BitSet / HLLAdd / TopKAdd / CMSIncr / BloomAdd | Local read verbs see the update immediately. | +| **Owner node** after successful Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / Incr / JsonSet / JsonDel / BitSet / HLLAdd / TopKAdd / CMSIncr / BloomAdd / XAdd | Local read verbs see the update immediately. | | **Initiating client** (via gRPC) | **No** automatic same-socket RYOW unless the client dials the owner and reads there, **or** the client enables optional **client-side sticky buffer** (out of scope for v1 server). Document: *Write success means owner has the value; other nodes may lag until fan-out.* | | **Optional v1.1** | Client library may cache last write locally for RYOW within process — not server contract. | @@ -318,6 +319,7 @@ Each keyspace has a mode. Opaque KV modes use Get/Put/Delete. Structured modes r | `ModeTopK` | table `name` | TopKAdd, TopKList; Delete(name) | `FlagTopK` snapshot; owner-inbox `FlagTopKAdd` | | `ModeCMS` | sketch `name` | CMSIncr, CMSQuery; Delete(name) | `FlagCMS` snapshot; owner-inbox `FlagCMSIncr` | | `ModeVectorSet` | set `name` | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | `FlagVectorSet` snapshot; owner-inbox same flag + A/R prefix | +| `ModeStream` | stream `name` | XAdd, XRange, XRevRange, XLen, XDel, XTrim; Delete(name) | `FlagStream` snapshot; owner-inbox same flag + A/D/T prefix | Public reference: [docs/API.md](./docs/API.md), OpenAPI `api/openapi/cache.openapi.yaml`, proto `api/proto/cache.proto`. @@ -441,6 +443,17 @@ Public reference: [docs/API.md](./docs/API.md), OpenAPI `api/openapi/cache.opena - Package: `pkg/cmsx`; CLI: `sc cmsincr|cmsquery`; billboard complement: [`examples/billboard`](./examples/billboard/) - Design: [docs/design/2026-09-01-mode-cms.md](./docs/design/2026-09-01-mode-cms.md) +### `ModeStream` (append-only log) + +- Named log; `XAdd` / `XRange` / `XRevRange` / `XLen` / `XDel` / `XTrim`; `Delete(name)` tombstone +- Owner-minted `millis-seq` ids; one opaque `[]byte` payload (client encodes). Exclusive start is a leading `(` on `start` +- Owner `XAdd` returns the id (peer `StreamAdd` from a non-owner). `XDel` / `XTrim` are ACK-only inbox `ApplyPut` +- Replicas install a **full `FlagStream` snapshot** (`S`; inbox `A`/`D`/`T` owner-only). Replica `XRange` may lag +- Missing name → `XRange` empty, `XLen` `present=false`. Empty after last del/trim stays live until `Delete(name)` +- `StreamMaxLen` 0 = no auto-trim; hard cap **4096**. Get/Put invalid. Not consumer groups / not blocking `XREAD` +- Package: `pkg/stream`; CLI: `sc xadd|xlen|xrange|xrevrange|xdel|xtrim` +- Design: [docs/design/2026-09-08-mode-stream.md](./docs/design/2026-09-08-mode-stream.md) + ### Precedence matrix | Incoming | vs local | Result | @@ -453,7 +466,7 @@ Public reference: [docs/API.md](./docs/API.md), OpenAPI `api/openapi/cache.opena | Negative entry | higher version / miss path | store negative; **Put always overrides** negative with higher version | | ApplyDelete adequate version | present or missing | install tombstone | | FlagSetAdd / FlagZSetAdd / FlagHashSet / Bloom item-add | structure present | mutate under mutex; version gate | -| FlagSet / FlagZSet / FlagBloom / FlagHash / FlagCounter / FlagJSON / FlagBitmap / FlagHLL / FlagTopK / FlagCMS snapshot | structure or tombstone | install if version **>** local (equal ignore; tombstone blocks stale) | +| FlagSet / FlagZSet / FlagBloom / FlagHash / FlagCounter / FlagJSON / FlagBitmap / FlagHLL / FlagTopK / FlagCMS / FlagVectorSet / FlagStream snapshot | structure or tombstone | install if version **>** local (equal ignore; tombstone blocks stale) | --- @@ -464,7 +477,7 @@ Public reference: [docs/API.md](./docs/API.md), OpenAPI `api/openapi/cache.opena pkg/client · cmd/sc (not in ring) │ gRPC Cache :client - (KV · Bloom · Set · ZSet · Geo · List · Hash · Counter · JSON · Bitmap · HLL · TopK) + (KV · Bloom · Set · ZSet · Geo · List · Hash · Counter · JSON · Bitmap · HLL · TopK · CMS · VectorSet · Stream) ▼ ┌─────────────────────┐ │ supercache-node │ @@ -619,17 +632,23 @@ CMSIncr (ModeCMS; List-class snapshot, not step-3 item flag): 2. else: CMSIncr under store mutex (stored = local+1); fan-out FlagCMS snapshot at PeekVersion 3. replica ApplyPut of inbox flags is ignored -Read (SetContains / ZScore / GeoPos / HGet / HExists / HLen / HGetAll / BloomTest / CounterGet / JsonGet / BitGet / BitCount / BitPos / HLLCount / TopKList / CMSQuery): +XAdd / XDel / XTrim (ModeStream; List-class snapshot, Value[0] inbox): + 1. if self != owner → peer StreamAdd (XAdd, returns id) or ApplyPut inbox A/D/T (XDel / XTrim, ACK-only) + 2. else: mutate under store mutex (stored = local+1); mint millis-seq on XAdd; fan-out FlagStream snapshot + 3. replica ApplyPut of inbox A/D/T is ignored; S snapshot installs LWW + +Read (SetContains / ZScore / GeoPos / HGet / HExists / HLen / HGetAll / BloomTest / CounterGet / JsonGet / BitGet / BitCount / BitPos / HLLCount / TopKList / CMSQuery / VSim / XRange / XLen): 1. if local live structure → answer from store cache (field miss stays local) BitGet / BitCount / BitPos: local extract if HasBitmap (replica may lag) HLLCount: local extract if HasHLL (replica may lag) TopKList: local extract if HasTopK (replica may lag) CMSQuery: local extract if HasCMS (replica may lag) + XRange / XLen: local extract if HasStream (replica may lag) 2. else if owner self → missing → empty/false 3. else → peer.GetOrLoad(owner) structure snapshot; optional install if holdsReplica 4. decode / answer -Handoff: include structure entries in LocalEntries; ApplyPut FlagSet|FlagZSet|FlagGeo|FlagHash|FlagBloom|FlagCounter|FlagJSON|FlagBitmap|FlagHLL|FlagTopK|FlagCMS +Handoff: include structure entries in LocalEntries; ApplyPut FlagSet|FlagZSet|FlagGeo|FlagHash|FlagBloom|FlagCounter|FlagJSON|FlagBitmap|FlagHLL|FlagTopK|FlagCMS|FlagVectorSet|FlagStream if incoming version > local (tombstone still blocks stale). ``` @@ -643,7 +662,7 @@ Bloom **add** ORs bits (no full-blob LWW on item add). Set/ZSet **item** ops app | Need | Approach | |------|----------| -| MaxBytes | cost-based eviction; tombstones / Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap / HLL / TopK / CMS protected while live | +| MaxBytes | cost-based eviction; tombstones / Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap / HLL / TopK / CMS / VectorSet / Stream protected while live | | TTL | `expire_at` on entry; lazy expire | | Set / Delete | First-class AcceptIfNewer / DeleteIfVersion | | Negative | Envelope flag | @@ -655,6 +674,8 @@ Bloom **add** ORs bits (no full-blob LWW on item add). Set/ZSet **item** ops app | ModeHLL | dense register `Value` (no dirty cache); protect `FlagHLL` | | ModeTopK | encoded table `Value` (no dirty cache); protect `FlagTopK` | | ModeCMS | dense counter `Value` (no dirty cache); protect `FlagCMS` | +| ModeVectorSet | encoded member table `Value` (no dirty cache); protect `FlagVectorSet` | +| ModeStream | encoded log `Value` (no dirty cache); protect `FlagStream` | | ModeBloom | Bitset in entry value; in-place OR under mutex | **Not using stock golang/groupcache** for distribution or primary API (get-only, HTTP peers, no Put/Delete/TTL). @@ -755,6 +776,22 @@ type Engine interface { CMSIncr(ctx context.Context, keyspace, name string, item []byte, n uint64) error CMSQuery(ctx context.Context, keyspace, name string, item []byte) (n uint64, ok bool, err error) + // ModeVectorSet + VAdd(ctx context.Context, keyspace, name string, member []byte, vec []float32) error + VRem(ctx context.Context, keyspace, name string, member []byte) error + VSim(ctx context.Context, keyspace, name string, vec []float32, k int) ([]VSimHit, error) + VCard(ctx context.Context, keyspace, name string) (n int, present bool, err error) + VDim(ctx context.Context, keyspace, name string) (dim int, present bool, err error) + VEmb(ctx context.Context, keyspace, name string, member []byte) (vec []float32, found bool, err error) + + // ModeStream + XAdd(ctx context.Context, keyspace, name string, payload []byte) (id string, err error) + XRange(ctx context.Context, keyspace, name, start, end string, count int) ([]StreamEntry, error) + XRevRange(ctx context.Context, keyspace, name, start, end string, count int) ([]StreamEntry, error) + XLen(ctx context.Context, keyspace, name string) (n int, present bool, err error) + XDel(ctx context.Context, keyspace, name, id string) error + XTrim(ctx context.Context, keyspace, name string, maxLen int) error + UpdateKeySpace(cfg KeySpaceConfig) error DeleteKeySpace(name string) error Events() <-chan ClusterEvent @@ -794,6 +831,8 @@ const ( ModeHLL ModeTopK ModeCMS + ModeVectorSet + ModeStream ) type KeySpaceConfig struct { @@ -814,6 +853,9 @@ type KeySpaceConfig struct { BloomBits int // ModeBloom; 0 = default BloomHashes int // ModeBloom; 0 = default TopKSize int // ModeTopK; 0 = default 100 + VectorDim int // ModeVectorSet; 0 = first add locks + VectorMetric VectorMetric // ModeVectorSet; cosine / l2 / ip + StreamMaxLen int // ModeStream; 0 = no auto-trim; cap 4096 } ``` @@ -827,7 +869,7 @@ type KeySpaceConfig struct { | Multi-node availability + read scale | Replicated mesh §2 | | Reduced backend load | Local hits, owner singleflight, negative TTL, protect | | TTL + LRU + MaxBytes + negative TTL | Store envelope + cost eviction | -| ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS | §7, §9.6; `pkg/bloom`, `pkg/set`, `pkg/zset`, `pkg/geo`, `pkg/listx`, `pkg/hashx`, `pkg/counter`, `pkg/jsonx`, `pkg/bitmapx`, `pkg/hllx`, `pkg/topkx`, `pkg/cmsx` | +| ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream | §7, §9.6; `pkg/bloom`, `pkg/set`, `pkg/zset`, `pkg/geo`, `pkg/listx`, `pkg/hashx`, `pkg/counter`, `pkg/jsonx`, `pkg/bitmapx`, `pkg/hllx`, `pkg/topkx`, `pkg/cmsx`, `pkg/vecset`, `pkg/stream` | | Node discovery | memberlist among supercache-nodes | | KeySpace overrides | KeySpaceConfig | | Dynamic keyspace updates | Local UpdateKeySpace + config hash | @@ -837,7 +879,7 @@ type KeySpaceConfig struct { | Admin diagnostics + OpenAPI docs | admin HTTP `/docs`; GitHub Pages | | OTel | telemetry pkg | | TLS + gossip auth | transport + memberlist secret | -| CLI | `cmd/sc` get/put/del, bloom, sadd*, z*, geo*, l*, h*, incr/cget, json*, bitset/bitget/bitcount/bitpos, hlladd/hllcount, topkadd/topklist, cmsincr/cmsquery | +| CLI | `cmd/sc` get/put/del, bloom, sadd*, z*, geo*, l*, h*, incr/cget, json*, bitset/bitget/bitcount/bitpos, hlladd/hllcount, topkadd/topklist, cmsincr/cmsquery, vadd*, xadd* | --- @@ -939,6 +981,20 @@ service Cache { // ModeCMS rpc CMSIncr(CMSIncrRequest) returns (CMSIncrResponse); rpc CMSQuery(CMSQueryRequest) returns (CMSQueryResponse); + // ModeVectorSet + rpc VAdd(VAddRequest) returns (VAddResponse); + rpc VRem(VRemRequest) returns (VRemResponse); + rpc VSim(VSimRequest) returns (VSimResponse); + rpc VCard(VCardRequest) returns (VCardResponse); + rpc VDim(VDimRequest) returns (VDimResponse); + rpc VEmb(VEmbRequest) returns (VEmbResponse); + // ModeStream + rpc XAdd(XAddRequest) returns (XAddResponse); + rpc XRange(XRangeRequest) returns (XRangeResponse); + rpc XRevRange(XRevRangeRequest) returns (XRangeResponse); + rpc XLen(XLenRequest) returns (XLenResponse); + rpc XDel(XDelRequest) returns (XDelResponse); + rpc XTrim(XTrimRequest) returns (XTrimResponse); } // Messages carry: keyspace, key/name, value/item/member, score, start/stop, @@ -960,6 +1016,7 @@ service Peer { rpc Prefetch(PrefetchRequest) returns (PrefetchResponse); rpc ListPop(ListPopRequest) returns (ListPopResponse); // ModeList non-owner pop rpc CounterIncr(CounterIncrRequest) returns (CounterIncrResponse); // ModeCounter non-owner incr + rpc StreamAdd(StreamAddRequest) returns (StreamAddResponse); // ModeStream non-owner XAdd } ``` @@ -1138,7 +1195,7 @@ Do **not** use SuperCache for: | No versioning | §6 owner versions + LWW apply rules | | groupcache unfit | §10 custom LRU Store interface | | DataSource vs Put | §7 LoadThrough vs CacheOnly + precedence | -| Structured types | §7 ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS; §9.6; §11 Engine API; §14 Cache RPCs | +| Structured types | §7 ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream; §9.6; §11 Engine API; §14 Cache RPCs | | Library vs peers | §4 nodes-only ring; pkg/client for apps | | Multi-error / PutMany | §11 MultiError; §9.2–9.3 batch rules | | Get miss underspecified | §9.1 normative algorithm | diff --git a/README.md b/README.md index d8561db..debb4ef 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Eventually consistent, read-heavy distributed cache for shared runtime storage (Go). -In-process Engine or dedicated nodes. Owner writes with async fan-out, local reads, gossip membership, load-through keyspaces, structured types (Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap / HLL / TopK / CMS / VectorSet), and a bounded per-node LRU. +In-process Engine or dedicated nodes. Owner writes with async fan-out, local reads, gossip membership, load-through keyspaces, structured types (Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap / HLL / TopK / CMS / VectorSet / Stream), and a bounded per-node LRU. ```text github.com/Code0987/supercache @@ -89,6 +89,9 @@ go run ./cmd/sc -keyspace vectorset vadd products north 0,1 go run ./cmd/sc -keyspace vectorset vadd products ne 0.7,0.7 go run ./cmd/sc -keyspace vectorset vcard products go run ./cmd/sc -keyspace vectorset vsim products 1,0.05 3 +# go run ./cmd/sc -keyspace events xadd logs * '{"msg":"hello"}' +# go run ./cmd/sc -keyspace events xlen logs +# go run ./cmd/sc -keyspace events xrange logs - + 10 go run ./cmd/sc -keyspace seen bloom add users alice # ModeBloom keyspace # ModeTopK (register a ModeTopK keyspace, or use examples/billboard plays/hot): # go run ./cmd/sc -keyspace plays topkadd hot t001 @@ -200,9 +203,9 @@ Apps: `client.DialTLS` with `pkg/tlsconfig.ClientFiles`. See [docs/OPERATIONS.md | Package | Role | |---------|------| -| `pkg/engine` | Core Get/Put/Delete, Bloom/Set/ZSet/Geo/List/Hash/Counter/JSON/Bitmap/HLL/TopK, keyspaces, cluster routing | +| `pkg/engine` | Core Get/Put/Delete, Bloom/Set/ZSet/Geo/List/Hash/Counter/JSON/Bitmap/HLL/TopK/CMS/VectorSet/Stream, keyspaces, cluster routing | | `pkg/store` | Versioned LRU memory store (immediate Set / RYOW; structure caches) | -| `pkg/keyspace` | Config: `LoadThrough` / `CacheOnly` / `Bloom` / `Set` / `ZSet` / `Geo` / `List` / `Hash` / `Counter` / `JSON` / `Bitmap` / `HLL` / `TopK` | +| `pkg/keyspace` | Config: `LoadThrough` / `CacheOnly` / `Bloom` / `Set` / `ZSet` / `Geo` / `List` / `Hash` / `Counter` / `JSON` / `Bitmap` / `HLL` / `TopK` / `CMS` / `VectorSet` / `Stream` | | `pkg/bloom` | Bitset Bloom filter used by `ModeBloom` | | `pkg/set` | Exact set encode/decode for `ModeSet` | | `pkg/zset` | Sorted-set encode/decode for `ModeZSet` | @@ -215,6 +218,8 @@ Apps: `client.DialTLS` with `pkg/tlsconfig.ClientFiles`. See [docs/OPERATIONS.md | `pkg/hllx` | Dense HyperLogLog sketch for `ModeHLL` | | `pkg/topkx` | Space-Saving top-K table for `ModeTopK` | | `pkg/cmsx` | Count-Min Sketch for `ModeCMS` | +| `pkg/vecset` | Embedding set + K-NN for `ModeVectorSet` | +| `pkg/stream` | Append-only log codec for `ModeStream` | | `pkg/datasource` | Backend loader interface | | `pkg/protect` | Rate limit + circuit breaker | | `pkg/admin` | `/healthz` `/readyz` `/peers` `/keyspaces` `/metrics` + `/docs` (Swagger) | @@ -222,10 +227,10 @@ Apps: `client.DialTLS` with `pkg/tlsconfig.ClientFiles`. See [docs/OPERATIONS.md | `pkg/telemetry` | Counters + OpenTelemetry | | `pkg/membership` | Gossip + ring rebuild | | `pkg/warmup` | Hot keys, topology handoff (hot then rest), refresh-ahead | -| `pkg/client` | Application gRPC client (KV + Bloom + Set + ZSet + Geo + List + Hash + Counter + JSON + Bitmap + HLL + TopK) | +| `pkg/client` | Application gRPC client (KV + Bloom + Set + ZSet + Geo + List + Hash + Counter + JSON + Bitmap + HLL + TopK + CMS + VectorSet + Stream) | | `pkg/tlsconfig` | TLS/mTLS config from PEM files | | `cmd/supercache-node` | Node binary (`-demo-keyspace`: cacheonly / set / zset / hash / json / bitmap / vectorset) | -| `cmd/sc` | CLI: get/put/del, bloom, sadd*, z*, geo*, l*, h*, incr/cget, json*, bit*, hlladd/hllcount, topkadd/topklist, admin diagnostics | +| `cmd/sc` | CLI: get/put/del, bloom, sadd*, z*, geo*, l*, h*, incr/cget, json*, bit*, hlladd/hllcount, topkadd/topklist, cmsincr/cmsquery, vadd*, xadd* | | `cmd/scbench` | SuperCache vs Redis load harness + in-process matrix | ## Consistency @@ -237,7 +242,7 @@ SuperCache is **eventually consistent**. Writes ACK on the owner; fan-out is asy - `UpdateKeySpace` is **local** — re-issue on every node; compare `keyspace_hashes` on `/peers`. - Topology change: existing nodes async-push inventory to peers (hot keys first, then rest). See [docs/CLUSTER_FLOWS.md](./docs/CLUSTER_FLOWS.md). - Delete installs a versioned tombstone for `TombstoneTTL` (default 5m) so a delayed ApplyPut cannot resurrect the key. -- Keyspace modes: **CacheOnly** / **LoadThrough** (KV), **ModeBloom**, **ModeSet**, **ModeZSet**, **ModeGeo**, **ModeList**, **ModeHash**, **ModeCounter**, **ModeJSON**, **ModeBitmap**, **ModeHLL**, **ModeTopK**, **ModeCMS**. Wrong verb → invalid argument. API summary: [docs/API.md](./docs/API.md). Designs: [Bloom](./docs/design/2026-08-11-bloom-filter.md), [Set](./docs/design/2026-08-13-mode-set.md), [ZSet](./docs/design/2026-08-13-mode-zset.md), [Geo](./docs/design/2026-08-19-mode-geo.md), [List](./docs/design/2026-08-19-mode-list.md), [Hash](./docs/design/2026-08-20-mode-hash.md), [Counter](./docs/design/2026-08-20-mode-counter.md), [JSON](./docs/design/2026-08-21-mode-json.md), [Bitmap](./docs/design/2026-08-21-mode-bitmap.md), [HLL](./docs/design/2026-08-25-mode-hll.md), [TopK](./docs/design/2026-08-31-mode-topk.md), [CMS](./docs/design/2026-09-01-mode-cms.md). +- Keyspace modes: **CacheOnly** / **LoadThrough** (KV), **ModeBloom**, **ModeSet**, **ModeZSet**, **ModeGeo**, **ModeList**, **ModeHash**, **ModeCounter**, **ModeJSON**, **ModeBitmap**, **ModeHLL**, **ModeTopK**, **ModeCMS**, **ModeVectorSet**, **ModeStream**. Wrong verb → invalid argument. API summary: [docs/API.md](./docs/API.md). Designs: [Bloom](./docs/design/2026-08-11-bloom-filter.md), [Set](./docs/design/2026-08-13-mode-set.md), [ZSet](./docs/design/2026-08-13-mode-zset.md), [Geo](./docs/design/2026-08-19-mode-geo.md), [List](./docs/design/2026-08-19-mode-list.md), [Hash](./docs/design/2026-08-20-mode-hash.md), [Counter](./docs/design/2026-08-20-mode-counter.md), [JSON](./docs/design/2026-08-21-mode-json.md), [Bitmap](./docs/design/2026-08-21-mode-bitmap.md), [HLL](./docs/design/2026-08-25-mode-hll.md), [TopK](./docs/design/2026-08-31-mode-topk.md), [CMS](./docs/design/2026-09-01-mode-cms.md), [VectorSet](./docs/design/2026-09-04-mode-vector-set.md), [Stream](./docs/design/2026-09-08-mode-stream.md). Details: [PLAN.md](./PLAN.md) §3 / §7 and [docs/OPERATIONS.md](./docs/OPERATIONS.md). diff --git a/api/gen/cache/v1/cache.pb.go b/api/gen/cache/v1/cache.pb.go index bd97138..bed081b 100644 --- a/api/gen/cache/v1/cache.pb.go +++ b/api/gen/cache/v1/cache.pb.go @@ -6481,6 +6481,654 @@ func (x *VEmbResponse) GetFound() bool { return false } +type StreamEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamEntry) Reset() { + *x = StreamEntry{} + mi := &file_cache_proto_msgTypes[123] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamEntry) ProtoMessage() {} + +func (x *StreamEntry) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[123] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamEntry.ProtoReflect.Descriptor instead. +func (*StreamEntry) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{123} +} + +func (x *StreamEntry) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *StreamEntry) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type XAddRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XAddRequest) Reset() { + *x = XAddRequest{} + mi := &file_cache_proto_msgTypes[124] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XAddRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XAddRequest) ProtoMessage() {} + +func (x *XAddRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[124] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XAddRequest.ProtoReflect.Descriptor instead. +func (*XAddRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{124} +} + +func (x *XAddRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XAddRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *XAddRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type XAddResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XAddResponse) Reset() { + *x = XAddResponse{} + mi := &file_cache_proto_msgTypes[125] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XAddResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XAddResponse) ProtoMessage() {} + +func (x *XAddResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[125] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XAddResponse.ProtoReflect.Descriptor instead. +func (*XAddResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{125} +} + +func (x *XAddResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type XRangeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Start string `protobuf:"bytes,3,opt,name=start,proto3" json:"start,omitempty"` + End string `protobuf:"bytes,4,opt,name=end,proto3" json:"end,omitempty"` + Count int32 `protobuf:"varint,5,opt,name=count,proto3" json:"count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XRangeRequest) Reset() { + *x = XRangeRequest{} + mi := &file_cache_proto_msgTypes[126] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XRangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XRangeRequest) ProtoMessage() {} + +func (x *XRangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[126] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XRangeRequest.ProtoReflect.Descriptor instead. +func (*XRangeRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{126} +} + +func (x *XRangeRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XRangeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *XRangeRequest) GetStart() string { + if x != nil { + return x.Start + } + return "" +} + +func (x *XRangeRequest) GetEnd() string { + if x != nil { + return x.End + } + return "" +} + +func (x *XRangeRequest) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +type XRevRangeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Start string `protobuf:"bytes,3,opt,name=start,proto3" json:"start,omitempty"` + End string `protobuf:"bytes,4,opt,name=end,proto3" json:"end,omitempty"` + Count int32 `protobuf:"varint,5,opt,name=count,proto3" json:"count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XRevRangeRequest) Reset() { + *x = XRevRangeRequest{} + mi := &file_cache_proto_msgTypes[127] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XRevRangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XRevRangeRequest) ProtoMessage() {} + +func (x *XRevRangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[127] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XRevRangeRequest.ProtoReflect.Descriptor instead. +func (*XRevRangeRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{127} +} + +func (x *XRevRangeRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XRevRangeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *XRevRangeRequest) GetStart() string { + if x != nil { + return x.Start + } + return "" +} + +func (x *XRevRangeRequest) GetEnd() string { + if x != nil { + return x.End + } + return "" +} + +func (x *XRevRangeRequest) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 +} + +type XRangeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*StreamEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XRangeResponse) Reset() { + *x = XRangeResponse{} + mi := &file_cache_proto_msgTypes[128] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XRangeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XRangeResponse) ProtoMessage() {} + +func (x *XRangeResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[128] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XRangeResponse.ProtoReflect.Descriptor instead. +func (*XRangeResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{128} +} + +func (x *XRangeResponse) GetEntries() []*StreamEntry { + if x != nil { + return x.Entries + } + return nil +} + +type XLenRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XLenRequest) Reset() { + *x = XLenRequest{} + mi := &file_cache_proto_msgTypes[129] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XLenRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XLenRequest) ProtoMessage() {} + +func (x *XLenRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[129] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XLenRequest.ProtoReflect.Descriptor instead. +func (*XLenRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{129} +} + +func (x *XLenRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XLenRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type XLenResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + N int64 `protobuf:"varint,1,opt,name=n,proto3" json:"n,omitempty"` + Present bool `protobuf:"varint,2,opt,name=present,proto3" json:"present,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XLenResponse) Reset() { + *x = XLenResponse{} + mi := &file_cache_proto_msgTypes[130] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XLenResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XLenResponse) ProtoMessage() {} + +func (x *XLenResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[130] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XLenResponse.ProtoReflect.Descriptor instead. +func (*XLenResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{130} +} + +func (x *XLenResponse) GetN() int64 { + if x != nil { + return x.N + } + return 0 +} + +func (x *XLenResponse) GetPresent() bool { + if x != nil { + return x.Present + } + return false +} + +type XDelRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XDelRequest) Reset() { + *x = XDelRequest{} + mi := &file_cache_proto_msgTypes[131] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XDelRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XDelRequest) ProtoMessage() {} + +func (x *XDelRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[131] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XDelRequest.ProtoReflect.Descriptor instead. +func (*XDelRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{131} +} + +func (x *XDelRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XDelRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *XDelRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type XDelResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XDelResponse) Reset() { + *x = XDelResponse{} + mi := &file_cache_proto_msgTypes[132] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XDelResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XDelResponse) ProtoMessage() {} + +func (x *XDelResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[132] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XDelResponse.ProtoReflect.Descriptor instead. +func (*XDelResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{132} +} + +type XTrimRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + MaxLen int32 `protobuf:"varint,3,opt,name=max_len,json=maxLen,proto3" json:"max_len,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XTrimRequest) Reset() { + *x = XTrimRequest{} + mi := &file_cache_proto_msgTypes[133] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XTrimRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XTrimRequest) ProtoMessage() {} + +func (x *XTrimRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[133] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XTrimRequest.ProtoReflect.Descriptor instead. +func (*XTrimRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{133} +} + +func (x *XTrimRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *XTrimRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *XTrimRequest) GetMaxLen() int32 { + if x != nil { + return x.MaxLen + } + return 0 +} + +type XTrimResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *XTrimResponse) Reset() { + *x = XTrimResponse{} + mi := &file_cache_proto_msgTypes[134] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *XTrimResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*XTrimResponse) ProtoMessage() {} + +func (x *XTrimResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[134] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use XTrimResponse.ProtoReflect.Descriptor instead. +func (*XTrimResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{134} +} + var File_cache_proto protoreflect.FileDescriptor var file_cache_proto_rawDesc = []byte{ @@ -7003,315 +7651,397 @@ var file_cache_proto_rawDesc = []byte{ 0x65, 0x72, 0x22, 0x36, 0x0a, 0x0c, 0x56, 0x45, 0x6d, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x65, 0x63, 0x18, 0x01, 0x20, 0x03, 0x28, 0x02, 0x52, 0x03, 0x76, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x32, 0xf8, 0x25, 0x0a, 0x05, 0x43, - 0x61, 0x63, 0x68, 0x65, 0x12, 0x48, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x75, - 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, - 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x50, 0x75, 0x74, 0x4d, - 0x61, 0x6e, 0x79, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, - 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x5d, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x79, 0x12, - 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x57, 0x0a, 0x08, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x12, 0x24, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x41, 0x64, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x42, 0x6c, 0x6f, - 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, - 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, + 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x37, 0x0a, 0x0b, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x22, 0x57, 0x0a, 0x0b, 0x58, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x1e, 0x0a, 0x0c, + 0x58, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x7d, 0x0a, 0x0d, + 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x80, 0x01, 0x0a, 0x10, + 0x58, 0x52, 0x65, 0x76, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4c, + 0x0a, 0x0e, 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3a, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x3d, 0x0a, 0x0b, + 0x58, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x36, 0x0a, 0x0c, 0x58, + 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, + 0x73, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, + 0x65, 0x6e, 0x74, 0x22, 0x4d, 0x0a, 0x0b, 0x58, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x58, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x57, 0x0a, 0x0c, 0x58, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x22, 0x0f, 0x0a, 0x0d, 0x58, + 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xdb, 0x29, 0x0a, + 0x05, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x48, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x53, 0x65, 0x74, 0x41, 0x64, 0x64, 0x12, - 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x64, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x43, 0x61, 0x72, - 0x64, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, - 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0a, - 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x48, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x50, 0x75, + 0x74, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x4d, + 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x5a, - 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x41, 0x64, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x41, 0x64, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x5a, 0x52, 0x65, 0x6d, - 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x5a, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, - 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x53, 0x63, 0x6f, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x5a, 0x43, 0x61, 0x72, - 0x64, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x43, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x5a, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0d, 0x5a, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x29, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, - 0x47, 0x65, 0x6f, 0x41, 0x64, 0x64, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, - 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, + 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x51, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x6f, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x51, 0x0a, 0x06, 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x22, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x43, 0x61, 0x72, 0x64, - 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x47, - 0x65, 0x6f, 0x44, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, - 0x44, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, - 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x44, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x25, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, - 0x61, 0x64, 0x69, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, - 0x05, 0x4c, 0x50, 0x75, 0x73, 0x68, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x50, 0x75, - 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, + 0x79, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, - 0x05, 0x52, 0x50, 0x75, 0x73, 0x68, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x75, - 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, - 0x04, 0x4c, 0x50, 0x6f, 0x70, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x50, 0x6f, 0x70, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x12, 0x24, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, + 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x42, + 0x6c, 0x6f, 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x6c, 0x6f, 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x54, 0x65, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x53, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x43, + 0x61, 0x72, 0x64, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x74, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, + 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x26, 0x2e, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, + 0x04, 0x5a, 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x50, - 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x52, 0x50, - 0x6f, 0x70, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x6f, 0x70, 0x52, 0x65, 0x71, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x41, + 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x5a, 0x52, + 0x65, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x6f, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x4c, 0x4c, 0x65, 0x6e, 0x12, - 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x22, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x4c, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x65, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x5a, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x53, - 0x65, 0x74, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x47, 0x65, 0x74, 0x12, - 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x44, 0x65, 0x6c, 0x12, 0x20, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x53, 0x63, 0x6f, + 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x5a, 0x43, + 0x61, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x43, 0x61, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x43, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x5a, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, + 0x0d, 0x5a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x29, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x54, 0x0a, 0x07, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x73, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x5a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x79, 0x53, 0x63, 0x6f, + 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x5a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, + 0x0a, 0x06, 0x47, 0x65, 0x6f, 0x41, 0x64, 0x64, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x6f, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x4c, 0x65, 0x6e, 0x12, - 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x48, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x12, 0x22, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x41, - 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x49, 0x6e, - 0x63, 0x72, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x65, 0x72, 0x47, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x12, 0x22, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x50, 0x6f, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x43, 0x61, + 0x72, 0x64, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x61, 0x72, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x6f, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, + 0x07, 0x47, 0x65, 0x6f, 0x44, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x6f, 0x44, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x4a, 0x73, 0x6f, 0x6e, 0x53, 0x65, - 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x52, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x44, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, + 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x6f, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4e, 0x0a, 0x05, 0x4c, 0x50, 0x75, 0x73, 0x68, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4e, 0x0a, 0x05, 0x52, 0x50, 0x75, 0x73, 0x68, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4b, 0x0a, 0x04, 0x4c, 0x50, 0x6f, 0x70, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x50, + 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x50, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, + 0x52, 0x50, 0x6f, 0x70, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x6f, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x6f, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x4c, 0x4c, 0x65, + 0x6e, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x4c, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x4c, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, + 0x48, 0x53, 0x65, 0x74, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x47, 0x65, + 0x74, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x44, 0x65, 0x6c, 0x12, 0x20, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x23, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x4c, 0x65, + 0x6e, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x48, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, - 0x6e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, - 0x4a, 0x73, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, - 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x12, 0x23, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x42, 0x69, 0x74, 0x53, - 0x65, 0x74, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, - 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x42, - 0x69, 0x74, 0x47, 0x65, 0x74, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, + 0x49, 0x6e, 0x63, 0x72, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x63, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x63, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0a, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x47, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x4a, 0x73, 0x6f, 0x6e, + 0x53, 0x65, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, + 0x73, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, + 0x0a, 0x07, 0x4a, 0x73, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x69, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, - 0x0a, 0x08, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, + 0x4a, 0x73, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x12, + 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x44, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x42, 0x69, + 0x74, 0x53, 0x65, 0x74, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x69, 0x74, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, + 0x06, 0x42, 0x69, 0x74, 0x47, 0x65, 0x74, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, + 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x69, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x57, 0x0a, 0x08, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x42, 0x69, 0x74, + 0x50, 0x6f, 0x73, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x50, 0x6f, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, + 0x74, 0x50, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, + 0x48, 0x4c, 0x4c, 0x41, 0x64, 0x64, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x4c, + 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x48, 0x4c, 0x4c, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x57, 0x0a, 0x08, 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x54, 0x6f, 0x70, 0x4b, + 0x41, 0x64, 0x64, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x41, 0x64, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x6f, 0x70, 0x4b, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, + 0x0a, 0x08, 0x54, 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x42, 0x69, 0x74, 0x50, 0x6f, - 0x73, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x50, 0x6f, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x74, 0x50, - 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x48, 0x4c, - 0x4c, 0x41, 0x64, 0x64, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x4c, 0x41, 0x64, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, - 0x4c, 0x4c, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, - 0x08, 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x43, 0x4d, 0x53, 0x49, 0x6e, + 0x63, 0x72, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, 0x53, 0x49, 0x6e, 0x63, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, + 0x53, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, + 0x08, 0x43, 0x4d, 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x43, 0x4d, 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x4c, 0x4c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x54, 0x6f, 0x70, 0x4b, 0x41, 0x64, - 0x64, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x41, 0x64, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, - 0x4b, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x08, - 0x54, 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x4b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x43, 0x4d, 0x53, 0x49, 0x6e, 0x63, 0x72, - 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, 0x53, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, 0x53, 0x49, - 0x6e, 0x63, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x43, - 0x4d, 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, - 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x4d, 0x53, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x41, 0x64, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x52, 0x65, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x56, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x52, 0x65, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, - 0x0a, 0x04, 0x56, 0x53, 0x69, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x53, 0x69, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x31, 0x2e, 0x56, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x53, 0x69, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x53, 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x56, - 0x43, 0x61, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x43, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x43, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, - 0x44, 0x69, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x44, 0x69, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x53, 0x69, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x56, 0x53, 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, + 0x05, 0x56, 0x43, 0x61, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x43, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x56, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, + 0x04, 0x56, 0x44, 0x69, 0x6d, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x44, 0x69, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x45, 0x6d, 0x62, - 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x45, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x45, 0x6d, 0x62, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x64, 0x65, 0x30, 0x39, 0x38, 0x37, 0x2f, 0x73, 0x75, 0x70, - 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x61, 0x63, 0x68, 0x65, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x44, + 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x56, 0x45, + 0x6d, 0x62, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x45, 0x6d, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x45, 0x6d, 0x62, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x58, 0x41, 0x64, 0x64, 0x12, + 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x06, 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, + 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x58, 0x52, 0x65, 0x76, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x52, 0x65, 0x76, 0x52, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x58, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4b, 0x0a, 0x04, 0x58, 0x4c, 0x65, 0x6e, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, + 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x58, 0x4c, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, + 0x04, 0x58, 0x44, 0x65, 0x6c, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x44, 0x65, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x44, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x05, 0x58, 0x54, + 0x72, 0x69, 0x6d, 0x12, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x54, 0x72, 0x69, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x54, 0x72, + 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x64, 0x65, 0x30, 0x39, 0x38, + 0x37, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7326,7 +8056,7 @@ func file_cache_proto_rawDescGZIP() []byte { return file_cache_proto_rawDescData } -var file_cache_proto_msgTypes = make([]protoimpl.MessageInfo, 123) +var file_cache_proto_msgTypes = make([]protoimpl.MessageInfo, 135) var file_cache_proto_goTypes = []any{ (*GetRequest)(nil), // 0: supercache.cache.v1.GetRequest (*GetResponse)(nil), // 1: supercache.cache.v1.GetResponse @@ -7451,6 +8181,18 @@ var file_cache_proto_goTypes = []any{ (*VDimResponse)(nil), // 120: supercache.cache.v1.VDimResponse (*VEmbRequest)(nil), // 121: supercache.cache.v1.VEmbRequest (*VEmbResponse)(nil), // 122: supercache.cache.v1.VEmbResponse + (*StreamEntry)(nil), // 123: supercache.cache.v1.StreamEntry + (*XAddRequest)(nil), // 124: supercache.cache.v1.XAddRequest + (*XAddResponse)(nil), // 125: supercache.cache.v1.XAddResponse + (*XRangeRequest)(nil), // 126: supercache.cache.v1.XRangeRequest + (*XRevRangeRequest)(nil), // 127: supercache.cache.v1.XRevRangeRequest + (*XRangeResponse)(nil), // 128: supercache.cache.v1.XRangeResponse + (*XLenRequest)(nil), // 129: supercache.cache.v1.XLenRequest + (*XLenResponse)(nil), // 130: supercache.cache.v1.XLenResponse + (*XDelRequest)(nil), // 131: supercache.cache.v1.XDelRequest + (*XDelResponse)(nil), // 132: supercache.cache.v1.XDelResponse + (*XTrimRequest)(nil), // 133: supercache.cache.v1.XTrimRequest + (*XTrimResponse)(nil), // 134: supercache.cache.v1.XTrimResponse } var file_cache_proto_depIdxs = []int32{ 4, // 0: supercache.cache.v1.PutManyRequest.items:type_name -> supercache.cache.v1.KV @@ -7463,127 +8205,140 @@ var file_cache_proto_depIdxs = []int32{ 77, // 7: supercache.cache.v1.HGetAllResponse.fields:type_name -> supercache.cache.v1.HashField 104, // 8: supercache.cache.v1.TopKListResponse.entries:type_name -> supercache.cache.v1.TopKEntry 115, // 9: supercache.cache.v1.VSimResponse.hits:type_name -> supercache.cache.v1.VSimHit - 0, // 10: supercache.cache.v1.Cache.Get:input_type -> supercache.cache.v1.GetRequest - 2, // 11: supercache.cache.v1.Cache.Put:input_type -> supercache.cache.v1.PutRequest - 5, // 12: supercache.cache.v1.Cache.PutMany:input_type -> supercache.cache.v1.PutManyRequest - 8, // 13: supercache.cache.v1.Cache.Delete:input_type -> supercache.cache.v1.DeleteRequest - 11, // 14: supercache.cache.v1.Cache.DeleteMany:input_type -> supercache.cache.v1.DeleteManyRequest - 13, // 15: supercache.cache.v1.Cache.BloomAdd:input_type -> supercache.cache.v1.BloomAddRequest - 15, // 16: supercache.cache.v1.Cache.BloomTest:input_type -> supercache.cache.v1.BloomTestRequest - 17, // 17: supercache.cache.v1.Cache.SetAdd:input_type -> supercache.cache.v1.SetAddRequest - 19, // 18: supercache.cache.v1.Cache.SetRemove:input_type -> supercache.cache.v1.SetRemoveRequest - 21, // 19: supercache.cache.v1.Cache.SetContains:input_type -> supercache.cache.v1.SetContainsRequest - 23, // 20: supercache.cache.v1.Cache.SetCard:input_type -> supercache.cache.v1.SetCardRequest - 25, // 21: supercache.cache.v1.Cache.SetMembers:input_type -> supercache.cache.v1.SetMembersRequest - 28, // 22: supercache.cache.v1.Cache.ZAdd:input_type -> supercache.cache.v1.ZAddRequest - 30, // 23: supercache.cache.v1.Cache.ZRem:input_type -> supercache.cache.v1.ZRemRequest - 32, // 24: supercache.cache.v1.Cache.ZScore:input_type -> supercache.cache.v1.ZScoreRequest - 34, // 25: supercache.cache.v1.Cache.ZCard:input_type -> supercache.cache.v1.ZCardRequest - 36, // 26: supercache.cache.v1.Cache.ZRange:input_type -> supercache.cache.v1.ZRangeRequest - 37, // 27: supercache.cache.v1.Cache.ZRangeByScore:input_type -> supercache.cache.v1.ZRangeByScoreRequest - 40, // 28: supercache.cache.v1.Cache.GeoAdd:input_type -> supercache.cache.v1.GeoAddRequest - 42, // 29: supercache.cache.v1.Cache.GeoRem:input_type -> supercache.cache.v1.GeoRemRequest - 44, // 30: supercache.cache.v1.Cache.GeoPos:input_type -> supercache.cache.v1.GeoPosRequest - 46, // 31: supercache.cache.v1.Cache.GeoCard:input_type -> supercache.cache.v1.GeoCardRequest - 48, // 32: supercache.cache.v1.Cache.GeoDist:input_type -> supercache.cache.v1.GeoDistRequest - 50, // 33: supercache.cache.v1.Cache.GeoRadius:input_type -> supercache.cache.v1.GeoRadiusRequest - 52, // 34: supercache.cache.v1.Cache.LPush:input_type -> supercache.cache.v1.LPushRequest - 54, // 35: supercache.cache.v1.Cache.RPush:input_type -> supercache.cache.v1.RPushRequest - 56, // 36: supercache.cache.v1.Cache.LPop:input_type -> supercache.cache.v1.LPopRequest - 58, // 37: supercache.cache.v1.Cache.RPop:input_type -> supercache.cache.v1.RPopRequest - 60, // 38: supercache.cache.v1.Cache.LLen:input_type -> supercache.cache.v1.LLenRequest - 62, // 39: supercache.cache.v1.Cache.LIndex:input_type -> supercache.cache.v1.LIndexRequest - 64, // 40: supercache.cache.v1.Cache.LRange:input_type -> supercache.cache.v1.LRangeRequest - 66, // 41: supercache.cache.v1.Cache.HSet:input_type -> supercache.cache.v1.HSetRequest - 68, // 42: supercache.cache.v1.Cache.HGet:input_type -> supercache.cache.v1.HGetRequest - 70, // 43: supercache.cache.v1.Cache.HDel:input_type -> supercache.cache.v1.HDelRequest - 72, // 44: supercache.cache.v1.Cache.HExists:input_type -> supercache.cache.v1.HExistsRequest - 74, // 45: supercache.cache.v1.Cache.HLen:input_type -> supercache.cache.v1.HLenRequest - 76, // 46: supercache.cache.v1.Cache.HGetAll:input_type -> supercache.cache.v1.HGetAllRequest - 79, // 47: supercache.cache.v1.Cache.Incr:input_type -> supercache.cache.v1.IncrRequest - 81, // 48: supercache.cache.v1.Cache.CounterGet:input_type -> supercache.cache.v1.CounterGetRequest - 83, // 49: supercache.cache.v1.Cache.JsonSet:input_type -> supercache.cache.v1.JsonSetRequest - 85, // 50: supercache.cache.v1.Cache.JsonGet:input_type -> supercache.cache.v1.JsonGetRequest - 87, // 51: supercache.cache.v1.Cache.JsonDel:input_type -> supercache.cache.v1.JsonDelRequest - 89, // 52: supercache.cache.v1.Cache.BitSet:input_type -> supercache.cache.v1.BitSetRequest - 91, // 53: supercache.cache.v1.Cache.BitGet:input_type -> supercache.cache.v1.BitGetRequest - 93, // 54: supercache.cache.v1.Cache.BitCount:input_type -> supercache.cache.v1.BitCountRequest - 95, // 55: supercache.cache.v1.Cache.BitPos:input_type -> supercache.cache.v1.BitPosRequest - 97, // 56: supercache.cache.v1.Cache.HLLAdd:input_type -> supercache.cache.v1.HLLAddRequest - 99, // 57: supercache.cache.v1.Cache.HLLCount:input_type -> supercache.cache.v1.HLLCountRequest - 101, // 58: supercache.cache.v1.Cache.TopKAdd:input_type -> supercache.cache.v1.TopKAddRequest - 103, // 59: supercache.cache.v1.Cache.TopKList:input_type -> supercache.cache.v1.TopKListRequest - 106, // 60: supercache.cache.v1.Cache.CMSIncr:input_type -> supercache.cache.v1.CMSIncrRequest - 108, // 61: supercache.cache.v1.Cache.CMSQuery:input_type -> supercache.cache.v1.CMSQueryRequest - 110, // 62: supercache.cache.v1.Cache.VAdd:input_type -> supercache.cache.v1.VAddRequest - 112, // 63: supercache.cache.v1.Cache.VRem:input_type -> supercache.cache.v1.VRemRequest - 114, // 64: supercache.cache.v1.Cache.VSim:input_type -> supercache.cache.v1.VSimRequest - 117, // 65: supercache.cache.v1.Cache.VCard:input_type -> supercache.cache.v1.VCardRequest - 119, // 66: supercache.cache.v1.Cache.VDim:input_type -> supercache.cache.v1.VDimRequest - 121, // 67: supercache.cache.v1.Cache.VEmb:input_type -> supercache.cache.v1.VEmbRequest - 1, // 68: supercache.cache.v1.Cache.Get:output_type -> supercache.cache.v1.GetResponse - 3, // 69: supercache.cache.v1.Cache.Put:output_type -> supercache.cache.v1.PutResponse - 7, // 70: supercache.cache.v1.Cache.PutMany:output_type -> supercache.cache.v1.PutManyResponse - 10, // 71: supercache.cache.v1.Cache.Delete:output_type -> supercache.cache.v1.DeleteResponse - 12, // 72: supercache.cache.v1.Cache.DeleteMany:output_type -> supercache.cache.v1.DeleteManyResponse - 14, // 73: supercache.cache.v1.Cache.BloomAdd:output_type -> supercache.cache.v1.BloomAddResponse - 16, // 74: supercache.cache.v1.Cache.BloomTest:output_type -> supercache.cache.v1.BloomTestResponse - 18, // 75: supercache.cache.v1.Cache.SetAdd:output_type -> supercache.cache.v1.SetAddResponse - 20, // 76: supercache.cache.v1.Cache.SetRemove:output_type -> supercache.cache.v1.SetRemoveResponse - 22, // 77: supercache.cache.v1.Cache.SetContains:output_type -> supercache.cache.v1.SetContainsResponse - 24, // 78: supercache.cache.v1.Cache.SetCard:output_type -> supercache.cache.v1.SetCardResponse - 26, // 79: supercache.cache.v1.Cache.SetMembers:output_type -> supercache.cache.v1.SetMembersResponse - 29, // 80: supercache.cache.v1.Cache.ZAdd:output_type -> supercache.cache.v1.ZAddResponse - 31, // 81: supercache.cache.v1.Cache.ZRem:output_type -> supercache.cache.v1.ZRemResponse - 33, // 82: supercache.cache.v1.Cache.ZScore:output_type -> supercache.cache.v1.ZScoreResponse - 35, // 83: supercache.cache.v1.Cache.ZCard:output_type -> supercache.cache.v1.ZCardResponse - 38, // 84: supercache.cache.v1.Cache.ZRange:output_type -> supercache.cache.v1.ZRangeResponse - 38, // 85: supercache.cache.v1.Cache.ZRangeByScore:output_type -> supercache.cache.v1.ZRangeResponse - 41, // 86: supercache.cache.v1.Cache.GeoAdd:output_type -> supercache.cache.v1.GeoAddResponse - 43, // 87: supercache.cache.v1.Cache.GeoRem:output_type -> supercache.cache.v1.GeoRemResponse - 45, // 88: supercache.cache.v1.Cache.GeoPos:output_type -> supercache.cache.v1.GeoPosResponse - 47, // 89: supercache.cache.v1.Cache.GeoCard:output_type -> supercache.cache.v1.GeoCardResponse - 49, // 90: supercache.cache.v1.Cache.GeoDist:output_type -> supercache.cache.v1.GeoDistResponse - 51, // 91: supercache.cache.v1.Cache.GeoRadius:output_type -> supercache.cache.v1.GeoRadiusResponse - 53, // 92: supercache.cache.v1.Cache.LPush:output_type -> supercache.cache.v1.LPushResponse - 55, // 93: supercache.cache.v1.Cache.RPush:output_type -> supercache.cache.v1.RPushResponse - 57, // 94: supercache.cache.v1.Cache.LPop:output_type -> supercache.cache.v1.LPopResponse - 59, // 95: supercache.cache.v1.Cache.RPop:output_type -> supercache.cache.v1.RPopResponse - 61, // 96: supercache.cache.v1.Cache.LLen:output_type -> supercache.cache.v1.LLenResponse - 63, // 97: supercache.cache.v1.Cache.LIndex:output_type -> supercache.cache.v1.LIndexResponse - 65, // 98: supercache.cache.v1.Cache.LRange:output_type -> supercache.cache.v1.LRangeResponse - 67, // 99: supercache.cache.v1.Cache.HSet:output_type -> supercache.cache.v1.HSetResponse - 69, // 100: supercache.cache.v1.Cache.HGet:output_type -> supercache.cache.v1.HGetResponse - 71, // 101: supercache.cache.v1.Cache.HDel:output_type -> supercache.cache.v1.HDelResponse - 73, // 102: supercache.cache.v1.Cache.HExists:output_type -> supercache.cache.v1.HExistsResponse - 75, // 103: supercache.cache.v1.Cache.HLen:output_type -> supercache.cache.v1.HLenResponse - 78, // 104: supercache.cache.v1.Cache.HGetAll:output_type -> supercache.cache.v1.HGetAllResponse - 80, // 105: supercache.cache.v1.Cache.Incr:output_type -> supercache.cache.v1.IncrResponse - 82, // 106: supercache.cache.v1.Cache.CounterGet:output_type -> supercache.cache.v1.CounterGetResponse - 84, // 107: supercache.cache.v1.Cache.JsonSet:output_type -> supercache.cache.v1.JsonSetResponse - 86, // 108: supercache.cache.v1.Cache.JsonGet:output_type -> supercache.cache.v1.JsonGetResponse - 88, // 109: supercache.cache.v1.Cache.JsonDel:output_type -> supercache.cache.v1.JsonDelResponse - 90, // 110: supercache.cache.v1.Cache.BitSet:output_type -> supercache.cache.v1.BitSetResponse - 92, // 111: supercache.cache.v1.Cache.BitGet:output_type -> supercache.cache.v1.BitGetResponse - 94, // 112: supercache.cache.v1.Cache.BitCount:output_type -> supercache.cache.v1.BitCountResponse - 96, // 113: supercache.cache.v1.Cache.BitPos:output_type -> supercache.cache.v1.BitPosResponse - 98, // 114: supercache.cache.v1.Cache.HLLAdd:output_type -> supercache.cache.v1.HLLAddResponse - 100, // 115: supercache.cache.v1.Cache.HLLCount:output_type -> supercache.cache.v1.HLLCountResponse - 102, // 116: supercache.cache.v1.Cache.TopKAdd:output_type -> supercache.cache.v1.TopKAddResponse - 105, // 117: supercache.cache.v1.Cache.TopKList:output_type -> supercache.cache.v1.TopKListResponse - 107, // 118: supercache.cache.v1.Cache.CMSIncr:output_type -> supercache.cache.v1.CMSIncrResponse - 109, // 119: supercache.cache.v1.Cache.CMSQuery:output_type -> supercache.cache.v1.CMSQueryResponse - 111, // 120: supercache.cache.v1.Cache.VAdd:output_type -> supercache.cache.v1.VAddResponse - 113, // 121: supercache.cache.v1.Cache.VRem:output_type -> supercache.cache.v1.VRemResponse - 116, // 122: supercache.cache.v1.Cache.VSim:output_type -> supercache.cache.v1.VSimResponse - 118, // 123: supercache.cache.v1.Cache.VCard:output_type -> supercache.cache.v1.VCardResponse - 120, // 124: supercache.cache.v1.Cache.VDim:output_type -> supercache.cache.v1.VDimResponse - 122, // 125: supercache.cache.v1.Cache.VEmb:output_type -> supercache.cache.v1.VEmbResponse - 68, // [68:126] is the sub-list for method output_type - 10, // [10:68] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 123, // 10: supercache.cache.v1.XRangeResponse.entries:type_name -> supercache.cache.v1.StreamEntry + 0, // 11: supercache.cache.v1.Cache.Get:input_type -> supercache.cache.v1.GetRequest + 2, // 12: supercache.cache.v1.Cache.Put:input_type -> supercache.cache.v1.PutRequest + 5, // 13: supercache.cache.v1.Cache.PutMany:input_type -> supercache.cache.v1.PutManyRequest + 8, // 14: supercache.cache.v1.Cache.Delete:input_type -> supercache.cache.v1.DeleteRequest + 11, // 15: supercache.cache.v1.Cache.DeleteMany:input_type -> supercache.cache.v1.DeleteManyRequest + 13, // 16: supercache.cache.v1.Cache.BloomAdd:input_type -> supercache.cache.v1.BloomAddRequest + 15, // 17: supercache.cache.v1.Cache.BloomTest:input_type -> supercache.cache.v1.BloomTestRequest + 17, // 18: supercache.cache.v1.Cache.SetAdd:input_type -> supercache.cache.v1.SetAddRequest + 19, // 19: supercache.cache.v1.Cache.SetRemove:input_type -> supercache.cache.v1.SetRemoveRequest + 21, // 20: supercache.cache.v1.Cache.SetContains:input_type -> supercache.cache.v1.SetContainsRequest + 23, // 21: supercache.cache.v1.Cache.SetCard:input_type -> supercache.cache.v1.SetCardRequest + 25, // 22: supercache.cache.v1.Cache.SetMembers:input_type -> supercache.cache.v1.SetMembersRequest + 28, // 23: supercache.cache.v1.Cache.ZAdd:input_type -> supercache.cache.v1.ZAddRequest + 30, // 24: supercache.cache.v1.Cache.ZRem:input_type -> supercache.cache.v1.ZRemRequest + 32, // 25: supercache.cache.v1.Cache.ZScore:input_type -> supercache.cache.v1.ZScoreRequest + 34, // 26: supercache.cache.v1.Cache.ZCard:input_type -> supercache.cache.v1.ZCardRequest + 36, // 27: supercache.cache.v1.Cache.ZRange:input_type -> supercache.cache.v1.ZRangeRequest + 37, // 28: supercache.cache.v1.Cache.ZRangeByScore:input_type -> supercache.cache.v1.ZRangeByScoreRequest + 40, // 29: supercache.cache.v1.Cache.GeoAdd:input_type -> supercache.cache.v1.GeoAddRequest + 42, // 30: supercache.cache.v1.Cache.GeoRem:input_type -> supercache.cache.v1.GeoRemRequest + 44, // 31: supercache.cache.v1.Cache.GeoPos:input_type -> supercache.cache.v1.GeoPosRequest + 46, // 32: supercache.cache.v1.Cache.GeoCard:input_type -> supercache.cache.v1.GeoCardRequest + 48, // 33: supercache.cache.v1.Cache.GeoDist:input_type -> supercache.cache.v1.GeoDistRequest + 50, // 34: supercache.cache.v1.Cache.GeoRadius:input_type -> supercache.cache.v1.GeoRadiusRequest + 52, // 35: supercache.cache.v1.Cache.LPush:input_type -> supercache.cache.v1.LPushRequest + 54, // 36: supercache.cache.v1.Cache.RPush:input_type -> supercache.cache.v1.RPushRequest + 56, // 37: supercache.cache.v1.Cache.LPop:input_type -> supercache.cache.v1.LPopRequest + 58, // 38: supercache.cache.v1.Cache.RPop:input_type -> supercache.cache.v1.RPopRequest + 60, // 39: supercache.cache.v1.Cache.LLen:input_type -> supercache.cache.v1.LLenRequest + 62, // 40: supercache.cache.v1.Cache.LIndex:input_type -> supercache.cache.v1.LIndexRequest + 64, // 41: supercache.cache.v1.Cache.LRange:input_type -> supercache.cache.v1.LRangeRequest + 66, // 42: supercache.cache.v1.Cache.HSet:input_type -> supercache.cache.v1.HSetRequest + 68, // 43: supercache.cache.v1.Cache.HGet:input_type -> supercache.cache.v1.HGetRequest + 70, // 44: supercache.cache.v1.Cache.HDel:input_type -> supercache.cache.v1.HDelRequest + 72, // 45: supercache.cache.v1.Cache.HExists:input_type -> supercache.cache.v1.HExistsRequest + 74, // 46: supercache.cache.v1.Cache.HLen:input_type -> supercache.cache.v1.HLenRequest + 76, // 47: supercache.cache.v1.Cache.HGetAll:input_type -> supercache.cache.v1.HGetAllRequest + 79, // 48: supercache.cache.v1.Cache.Incr:input_type -> supercache.cache.v1.IncrRequest + 81, // 49: supercache.cache.v1.Cache.CounterGet:input_type -> supercache.cache.v1.CounterGetRequest + 83, // 50: supercache.cache.v1.Cache.JsonSet:input_type -> supercache.cache.v1.JsonSetRequest + 85, // 51: supercache.cache.v1.Cache.JsonGet:input_type -> supercache.cache.v1.JsonGetRequest + 87, // 52: supercache.cache.v1.Cache.JsonDel:input_type -> supercache.cache.v1.JsonDelRequest + 89, // 53: supercache.cache.v1.Cache.BitSet:input_type -> supercache.cache.v1.BitSetRequest + 91, // 54: supercache.cache.v1.Cache.BitGet:input_type -> supercache.cache.v1.BitGetRequest + 93, // 55: supercache.cache.v1.Cache.BitCount:input_type -> supercache.cache.v1.BitCountRequest + 95, // 56: supercache.cache.v1.Cache.BitPos:input_type -> supercache.cache.v1.BitPosRequest + 97, // 57: supercache.cache.v1.Cache.HLLAdd:input_type -> supercache.cache.v1.HLLAddRequest + 99, // 58: supercache.cache.v1.Cache.HLLCount:input_type -> supercache.cache.v1.HLLCountRequest + 101, // 59: supercache.cache.v1.Cache.TopKAdd:input_type -> supercache.cache.v1.TopKAddRequest + 103, // 60: supercache.cache.v1.Cache.TopKList:input_type -> supercache.cache.v1.TopKListRequest + 106, // 61: supercache.cache.v1.Cache.CMSIncr:input_type -> supercache.cache.v1.CMSIncrRequest + 108, // 62: supercache.cache.v1.Cache.CMSQuery:input_type -> supercache.cache.v1.CMSQueryRequest + 110, // 63: supercache.cache.v1.Cache.VAdd:input_type -> supercache.cache.v1.VAddRequest + 112, // 64: supercache.cache.v1.Cache.VRem:input_type -> supercache.cache.v1.VRemRequest + 114, // 65: supercache.cache.v1.Cache.VSim:input_type -> supercache.cache.v1.VSimRequest + 117, // 66: supercache.cache.v1.Cache.VCard:input_type -> supercache.cache.v1.VCardRequest + 119, // 67: supercache.cache.v1.Cache.VDim:input_type -> supercache.cache.v1.VDimRequest + 121, // 68: supercache.cache.v1.Cache.VEmb:input_type -> supercache.cache.v1.VEmbRequest + 124, // 69: supercache.cache.v1.Cache.XAdd:input_type -> supercache.cache.v1.XAddRequest + 126, // 70: supercache.cache.v1.Cache.XRange:input_type -> supercache.cache.v1.XRangeRequest + 127, // 71: supercache.cache.v1.Cache.XRevRange:input_type -> supercache.cache.v1.XRevRangeRequest + 129, // 72: supercache.cache.v1.Cache.XLen:input_type -> supercache.cache.v1.XLenRequest + 131, // 73: supercache.cache.v1.Cache.XDel:input_type -> supercache.cache.v1.XDelRequest + 133, // 74: supercache.cache.v1.Cache.XTrim:input_type -> supercache.cache.v1.XTrimRequest + 1, // 75: supercache.cache.v1.Cache.Get:output_type -> supercache.cache.v1.GetResponse + 3, // 76: supercache.cache.v1.Cache.Put:output_type -> supercache.cache.v1.PutResponse + 7, // 77: supercache.cache.v1.Cache.PutMany:output_type -> supercache.cache.v1.PutManyResponse + 10, // 78: supercache.cache.v1.Cache.Delete:output_type -> supercache.cache.v1.DeleteResponse + 12, // 79: supercache.cache.v1.Cache.DeleteMany:output_type -> supercache.cache.v1.DeleteManyResponse + 14, // 80: supercache.cache.v1.Cache.BloomAdd:output_type -> supercache.cache.v1.BloomAddResponse + 16, // 81: supercache.cache.v1.Cache.BloomTest:output_type -> supercache.cache.v1.BloomTestResponse + 18, // 82: supercache.cache.v1.Cache.SetAdd:output_type -> supercache.cache.v1.SetAddResponse + 20, // 83: supercache.cache.v1.Cache.SetRemove:output_type -> supercache.cache.v1.SetRemoveResponse + 22, // 84: supercache.cache.v1.Cache.SetContains:output_type -> supercache.cache.v1.SetContainsResponse + 24, // 85: supercache.cache.v1.Cache.SetCard:output_type -> supercache.cache.v1.SetCardResponse + 26, // 86: supercache.cache.v1.Cache.SetMembers:output_type -> supercache.cache.v1.SetMembersResponse + 29, // 87: supercache.cache.v1.Cache.ZAdd:output_type -> supercache.cache.v1.ZAddResponse + 31, // 88: supercache.cache.v1.Cache.ZRem:output_type -> supercache.cache.v1.ZRemResponse + 33, // 89: supercache.cache.v1.Cache.ZScore:output_type -> supercache.cache.v1.ZScoreResponse + 35, // 90: supercache.cache.v1.Cache.ZCard:output_type -> supercache.cache.v1.ZCardResponse + 38, // 91: supercache.cache.v1.Cache.ZRange:output_type -> supercache.cache.v1.ZRangeResponse + 38, // 92: supercache.cache.v1.Cache.ZRangeByScore:output_type -> supercache.cache.v1.ZRangeResponse + 41, // 93: supercache.cache.v1.Cache.GeoAdd:output_type -> supercache.cache.v1.GeoAddResponse + 43, // 94: supercache.cache.v1.Cache.GeoRem:output_type -> supercache.cache.v1.GeoRemResponse + 45, // 95: supercache.cache.v1.Cache.GeoPos:output_type -> supercache.cache.v1.GeoPosResponse + 47, // 96: supercache.cache.v1.Cache.GeoCard:output_type -> supercache.cache.v1.GeoCardResponse + 49, // 97: supercache.cache.v1.Cache.GeoDist:output_type -> supercache.cache.v1.GeoDistResponse + 51, // 98: supercache.cache.v1.Cache.GeoRadius:output_type -> supercache.cache.v1.GeoRadiusResponse + 53, // 99: supercache.cache.v1.Cache.LPush:output_type -> supercache.cache.v1.LPushResponse + 55, // 100: supercache.cache.v1.Cache.RPush:output_type -> supercache.cache.v1.RPushResponse + 57, // 101: supercache.cache.v1.Cache.LPop:output_type -> supercache.cache.v1.LPopResponse + 59, // 102: supercache.cache.v1.Cache.RPop:output_type -> supercache.cache.v1.RPopResponse + 61, // 103: supercache.cache.v1.Cache.LLen:output_type -> supercache.cache.v1.LLenResponse + 63, // 104: supercache.cache.v1.Cache.LIndex:output_type -> supercache.cache.v1.LIndexResponse + 65, // 105: supercache.cache.v1.Cache.LRange:output_type -> supercache.cache.v1.LRangeResponse + 67, // 106: supercache.cache.v1.Cache.HSet:output_type -> supercache.cache.v1.HSetResponse + 69, // 107: supercache.cache.v1.Cache.HGet:output_type -> supercache.cache.v1.HGetResponse + 71, // 108: supercache.cache.v1.Cache.HDel:output_type -> supercache.cache.v1.HDelResponse + 73, // 109: supercache.cache.v1.Cache.HExists:output_type -> supercache.cache.v1.HExistsResponse + 75, // 110: supercache.cache.v1.Cache.HLen:output_type -> supercache.cache.v1.HLenResponse + 78, // 111: supercache.cache.v1.Cache.HGetAll:output_type -> supercache.cache.v1.HGetAllResponse + 80, // 112: supercache.cache.v1.Cache.Incr:output_type -> supercache.cache.v1.IncrResponse + 82, // 113: supercache.cache.v1.Cache.CounterGet:output_type -> supercache.cache.v1.CounterGetResponse + 84, // 114: supercache.cache.v1.Cache.JsonSet:output_type -> supercache.cache.v1.JsonSetResponse + 86, // 115: supercache.cache.v1.Cache.JsonGet:output_type -> supercache.cache.v1.JsonGetResponse + 88, // 116: supercache.cache.v1.Cache.JsonDel:output_type -> supercache.cache.v1.JsonDelResponse + 90, // 117: supercache.cache.v1.Cache.BitSet:output_type -> supercache.cache.v1.BitSetResponse + 92, // 118: supercache.cache.v1.Cache.BitGet:output_type -> supercache.cache.v1.BitGetResponse + 94, // 119: supercache.cache.v1.Cache.BitCount:output_type -> supercache.cache.v1.BitCountResponse + 96, // 120: supercache.cache.v1.Cache.BitPos:output_type -> supercache.cache.v1.BitPosResponse + 98, // 121: supercache.cache.v1.Cache.HLLAdd:output_type -> supercache.cache.v1.HLLAddResponse + 100, // 122: supercache.cache.v1.Cache.HLLCount:output_type -> supercache.cache.v1.HLLCountResponse + 102, // 123: supercache.cache.v1.Cache.TopKAdd:output_type -> supercache.cache.v1.TopKAddResponse + 105, // 124: supercache.cache.v1.Cache.TopKList:output_type -> supercache.cache.v1.TopKListResponse + 107, // 125: supercache.cache.v1.Cache.CMSIncr:output_type -> supercache.cache.v1.CMSIncrResponse + 109, // 126: supercache.cache.v1.Cache.CMSQuery:output_type -> supercache.cache.v1.CMSQueryResponse + 111, // 127: supercache.cache.v1.Cache.VAdd:output_type -> supercache.cache.v1.VAddResponse + 113, // 128: supercache.cache.v1.Cache.VRem:output_type -> supercache.cache.v1.VRemResponse + 116, // 129: supercache.cache.v1.Cache.VSim:output_type -> supercache.cache.v1.VSimResponse + 118, // 130: supercache.cache.v1.Cache.VCard:output_type -> supercache.cache.v1.VCardResponse + 120, // 131: supercache.cache.v1.Cache.VDim:output_type -> supercache.cache.v1.VDimResponse + 122, // 132: supercache.cache.v1.Cache.VEmb:output_type -> supercache.cache.v1.VEmbResponse + 125, // 133: supercache.cache.v1.Cache.XAdd:output_type -> supercache.cache.v1.XAddResponse + 128, // 134: supercache.cache.v1.Cache.XRange:output_type -> supercache.cache.v1.XRangeResponse + 128, // 135: supercache.cache.v1.Cache.XRevRange:output_type -> supercache.cache.v1.XRangeResponse + 130, // 136: supercache.cache.v1.Cache.XLen:output_type -> supercache.cache.v1.XLenResponse + 132, // 137: supercache.cache.v1.Cache.XDel:output_type -> supercache.cache.v1.XDelResponse + 134, // 138: supercache.cache.v1.Cache.XTrim:output_type -> supercache.cache.v1.XTrimResponse + 75, // [75:139] is the sub-list for method output_type + 11, // [11:75] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_cache_proto_init() } @@ -7597,7 +8352,7 @@ func file_cache_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cache_proto_rawDesc, NumEnums: 0, - NumMessages: 123, + NumMessages: 135, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/cache/v1/cache_grpc.pb.go b/api/gen/cache/v1/cache_grpc.pb.go index b8f7825..ddd0d00 100644 --- a/api/gen/cache/v1/cache_grpc.pb.go +++ b/api/gen/cache/v1/cache_grpc.pb.go @@ -77,6 +77,12 @@ const ( Cache_VCard_FullMethodName = "/supercache.cache.v1.Cache/VCard" Cache_VDim_FullMethodName = "/supercache.cache.v1.Cache/VDim" Cache_VEmb_FullMethodName = "/supercache.cache.v1.Cache/VEmb" + Cache_XAdd_FullMethodName = "/supercache.cache.v1.Cache/XAdd" + Cache_XRange_FullMethodName = "/supercache.cache.v1.Cache/XRange" + Cache_XRevRange_FullMethodName = "/supercache.cache.v1.Cache/XRevRange" + Cache_XLen_FullMethodName = "/supercache.cache.v1.Cache/XLen" + Cache_XDel_FullMethodName = "/supercache.cache.v1.Cache/XDel" + Cache_XTrim_FullMethodName = "/supercache.cache.v1.Cache/XTrim" ) // CacheClient is the client API for Cache service. @@ -143,6 +149,12 @@ type CacheClient interface { VCard(ctx context.Context, in *VCardRequest, opts ...grpc.CallOption) (*VCardResponse, error) VDim(ctx context.Context, in *VDimRequest, opts ...grpc.CallOption) (*VDimResponse, error) VEmb(ctx context.Context, in *VEmbRequest, opts ...grpc.CallOption) (*VEmbResponse, error) + XAdd(ctx context.Context, in *XAddRequest, opts ...grpc.CallOption) (*XAddResponse, error) + XRange(ctx context.Context, in *XRangeRequest, opts ...grpc.CallOption) (*XRangeResponse, error) + XRevRange(ctx context.Context, in *XRevRangeRequest, opts ...grpc.CallOption) (*XRangeResponse, error) + XLen(ctx context.Context, in *XLenRequest, opts ...grpc.CallOption) (*XLenResponse, error) + XDel(ctx context.Context, in *XDelRequest, opts ...grpc.CallOption) (*XDelResponse, error) + XTrim(ctx context.Context, in *XTrimRequest, opts ...grpc.CallOption) (*XTrimResponse, error) } type cacheClient struct { @@ -733,6 +745,66 @@ func (c *cacheClient) VEmb(ctx context.Context, in *VEmbRequest, opts ...grpc.Ca return out, nil } +func (c *cacheClient) XAdd(ctx context.Context, in *XAddRequest, opts ...grpc.CallOption) (*XAddResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XAddResponse) + err := c.cc.Invoke(ctx, Cache_XAdd_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) XRange(ctx context.Context, in *XRangeRequest, opts ...grpc.CallOption) (*XRangeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XRangeResponse) + err := c.cc.Invoke(ctx, Cache_XRange_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) XRevRange(ctx context.Context, in *XRevRangeRequest, opts ...grpc.CallOption) (*XRangeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XRangeResponse) + err := c.cc.Invoke(ctx, Cache_XRevRange_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) XLen(ctx context.Context, in *XLenRequest, opts ...grpc.CallOption) (*XLenResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XLenResponse) + err := c.cc.Invoke(ctx, Cache_XLen_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) XDel(ctx context.Context, in *XDelRequest, opts ...grpc.CallOption) (*XDelResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XDelResponse) + err := c.cc.Invoke(ctx, Cache_XDel_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) XTrim(ctx context.Context, in *XTrimRequest, opts ...grpc.CallOption) (*XTrimResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(XTrimResponse) + err := c.cc.Invoke(ctx, Cache_XTrim_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // CacheServer is the server API for Cache service. // All implementations must embed UnimplementedCacheServer // for forward compatibility. @@ -797,6 +869,12 @@ type CacheServer interface { VCard(context.Context, *VCardRequest) (*VCardResponse, error) VDim(context.Context, *VDimRequest) (*VDimResponse, error) VEmb(context.Context, *VEmbRequest) (*VEmbResponse, error) + XAdd(context.Context, *XAddRequest) (*XAddResponse, error) + XRange(context.Context, *XRangeRequest) (*XRangeResponse, error) + XRevRange(context.Context, *XRevRangeRequest) (*XRangeResponse, error) + XLen(context.Context, *XLenRequest) (*XLenResponse, error) + XDel(context.Context, *XDelRequest) (*XDelResponse, error) + XTrim(context.Context, *XTrimRequest) (*XTrimResponse, error) mustEmbedUnimplementedCacheServer() } @@ -981,6 +1059,24 @@ func (UnimplementedCacheServer) VDim(context.Context, *VDimRequest) (*VDimRespon func (UnimplementedCacheServer) VEmb(context.Context, *VEmbRequest) (*VEmbResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method VEmb not implemented") } +func (UnimplementedCacheServer) XAdd(context.Context, *XAddRequest) (*XAddResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XAdd not implemented") +} +func (UnimplementedCacheServer) XRange(context.Context, *XRangeRequest) (*XRangeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XRange not implemented") +} +func (UnimplementedCacheServer) XRevRange(context.Context, *XRevRangeRequest) (*XRangeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XRevRange not implemented") +} +func (UnimplementedCacheServer) XLen(context.Context, *XLenRequest) (*XLenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XLen not implemented") +} +func (UnimplementedCacheServer) XDel(context.Context, *XDelRequest) (*XDelResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XDel not implemented") +} +func (UnimplementedCacheServer) XTrim(context.Context, *XTrimRequest) (*XTrimResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XTrim not implemented") +} func (UnimplementedCacheServer) mustEmbedUnimplementedCacheServer() {} func (UnimplementedCacheServer) testEmbeddedByValue() {} @@ -2046,6 +2142,114 @@ func _Cache_VEmb_Handler(srv interface{}, ctx context.Context, dec func(interfac return interceptor(ctx, in, info, handler) } +func _Cache_XAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XAddRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XAdd(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XAdd_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XAdd(ctx, req.(*XAddRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_XRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XRangeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XRange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XRange_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XRange(ctx, req.(*XRangeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_XRevRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XRevRangeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XRevRange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XRevRange_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XRevRange(ctx, req.(*XRevRangeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_XLen_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XLenRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XLen(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XLen_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XLen(ctx, req.(*XLenRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_XDel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XDelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XDel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XDel_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XDel(ctx, req.(*XDelRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_XTrim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(XTrimRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).XTrim(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_XTrim_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).XTrim(ctx, req.(*XTrimRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Cache_ServiceDesc is the grpc.ServiceDesc for Cache service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -2285,6 +2489,30 @@ var Cache_ServiceDesc = grpc.ServiceDesc{ MethodName: "VEmb", Handler: _Cache_VEmb_Handler, }, + { + MethodName: "XAdd", + Handler: _Cache_XAdd_Handler, + }, + { + MethodName: "XRange", + Handler: _Cache_XRange_Handler, + }, + { + MethodName: "XRevRange", + Handler: _Cache_XRevRange_Handler, + }, + { + MethodName: "XLen", + Handler: _Cache_XLen_Handler, + }, + { + MethodName: "XDel", + Handler: _Cache_XDel_Handler, + }, + { + MethodName: "XTrim", + Handler: _Cache_XTrim_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cache.proto", diff --git a/api/gen/peer/v1/peer.pb.go b/api/gen/peer/v1/peer.pb.go index 7918ffc..1698a3b 100644 --- a/api/gen/peer/v1/peer.pb.go +++ b/api/gen/peer/v1/peer.pb.go @@ -25,7 +25,7 @@ type Entry struct { Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` ExpireAtUnixNano int64 `protobuf:"varint,3,opt,name=expire_at_unix_nano,json=expireAtUnixNano,proto3" json:"expire_at_unix_nano,omitempty"` - Flags uint32 `protobuf:"varint,4,opt,name=flags,proto3" json:"flags,omitempty"` + Flags uint64 `protobuf:"varint,4,opt,name=flags,proto3" json:"flags,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -81,7 +81,7 @@ func (x *Entry) GetExpireAtUnixNano() int64 { return 0 } -func (x *Entry) GetFlags() uint32 { +func (x *Entry) GetFlags() uint64 { if x != nil { return x.Flags } @@ -902,6 +902,110 @@ func (x *CounterIncrResponse) GetValue() int64 { return 0 } +type StreamAddRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamAddRequest) Reset() { + *x = StreamAddRequest{} + mi := &file_peer_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamAddRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamAddRequest) ProtoMessage() {} + +func (x *StreamAddRequest) ProtoReflect() protoreflect.Message { + mi := &file_peer_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamAddRequest.ProtoReflect.Descriptor instead. +func (*StreamAddRequest) Descriptor() ([]byte, []int) { + return file_peer_proto_rawDescGZIP(), []int{16} +} + +func (x *StreamAddRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *StreamAddRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StreamAddRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +type StreamAddResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamAddResponse) Reset() { + *x = StreamAddResponse{} + mi := &file_peer_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamAddResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamAddResponse) ProtoMessage() {} + +func (x *StreamAddResponse) ProtoReflect() protoreflect.Message { + mi := &file_peer_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamAddResponse.ProtoReflect.Descriptor instead. +func (*StreamAddResponse) Descriptor() ([]byte, []int) { + return file_peer_proto_rawDescGZIP(), []int{17} +} + +func (x *StreamAddResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_peer_proto protoreflect.FileDescriptor var file_peer_proto_rawDesc = []byte{ @@ -914,7 +1018,7 @@ var file_peer_proto_rawDesc = []byte{ 0x69, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x99, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, @@ -993,52 +1097,66 @@ var file_peer_proto_rawDesc = []byte{ 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x22, 0x2b, 0x0a, 0x13, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x8e, 0x05, 0x0a, 0x04, 0x50, 0x65, - 0x65, 0x72, 0x12, 0x55, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, 0x12, 0x23, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, - 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x0b, 0x41, 0x70, 0x70, - 0x6c, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x0a, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x0d, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, - 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x09, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x07, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, - 0x70, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, - 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, 0x72, 0x12, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, - 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, - 0x63, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x64, 0x65, 0x30, 0x39, 0x38, - 0x37, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x65, 0x65, - 0x72, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x5c, 0x0a, 0x10, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x23, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xe8, 0x05, 0x0a, + 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, + 0x74, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, + 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x75, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, + 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x0b, + 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x0a, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, + 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x50, 0x75, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x0d, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, + 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x4c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x07, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x6f, 0x70, 0x12, 0x22, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, + 0x0b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, 0x72, 0x12, 0x26, 0x2e, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x49, 0x6e, 0x63, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, + 0x09, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x64, 0x64, 0x12, 0x24, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x65, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x64, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x64, 0x65, 0x30, 0x39, 0x38, 0x37, 0x2f, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, + 0x6e, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x65, 0x65, 0x72, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1053,7 +1171,7 @@ func file_peer_proto_rawDescGZIP() []byte { return file_peer_proto_rawDescData } -var file_peer_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_peer_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_peer_proto_goTypes = []any{ (*Entry)(nil), // 0: supercache.peer.v1.Entry (*ApplyPutRequest)(nil), // 1: supercache.peer.v1.ApplyPutRequest @@ -1071,6 +1189,8 @@ var file_peer_proto_goTypes = []any{ (*ListPopResponse)(nil), // 13: supercache.peer.v1.ListPopResponse (*CounterIncrRequest)(nil), // 14: supercache.peer.v1.CounterIncrRequest (*CounterIncrResponse)(nil), // 15: supercache.peer.v1.CounterIncrResponse + (*StreamAddRequest)(nil), // 16: supercache.peer.v1.StreamAddRequest + (*StreamAddResponse)(nil), // 17: supercache.peer.v1.StreamAddResponse } var file_peer_proto_depIdxs = []int32{ 0, // 0: supercache.peer.v1.ApplyPutRequest.entry:type_name -> supercache.peer.v1.Entry @@ -1083,15 +1203,17 @@ var file_peer_proto_depIdxs = []int32{ 10, // 7: supercache.peer.v1.Peer.GetOrLoad:input_type -> supercache.peer.v1.GetOrLoadRequest 12, // 8: supercache.peer.v1.Peer.ListPop:input_type -> supercache.peer.v1.ListPopRequest 14, // 9: supercache.peer.v1.Peer.CounterIncr:input_type -> supercache.peer.v1.CounterIncrRequest - 2, // 10: supercache.peer.v1.Peer.ApplyPut:output_type -> supercache.peer.v1.ApplyPutResponse - 4, // 11: supercache.peer.v1.Peer.ApplyDelete:output_type -> supercache.peer.v1.ApplyDeleteResponse - 6, // 12: supercache.peer.v1.Peer.ForwardPut:output_type -> supercache.peer.v1.ForwardPutResponse - 9, // 13: supercache.peer.v1.Peer.ForwardDelete:output_type -> supercache.peer.v1.ForwardDeleteResponse - 11, // 14: supercache.peer.v1.Peer.GetOrLoad:output_type -> supercache.peer.v1.GetOrLoadResponse - 13, // 15: supercache.peer.v1.Peer.ListPop:output_type -> supercache.peer.v1.ListPopResponse - 15, // 16: supercache.peer.v1.Peer.CounterIncr:output_type -> supercache.peer.v1.CounterIncrResponse - 10, // [10:17] is the sub-list for method output_type - 3, // [3:10] is the sub-list for method input_type + 16, // 10: supercache.peer.v1.Peer.StreamAdd:input_type -> supercache.peer.v1.StreamAddRequest + 2, // 11: supercache.peer.v1.Peer.ApplyPut:output_type -> supercache.peer.v1.ApplyPutResponse + 4, // 12: supercache.peer.v1.Peer.ApplyDelete:output_type -> supercache.peer.v1.ApplyDeleteResponse + 6, // 13: supercache.peer.v1.Peer.ForwardPut:output_type -> supercache.peer.v1.ForwardPutResponse + 9, // 14: supercache.peer.v1.Peer.ForwardDelete:output_type -> supercache.peer.v1.ForwardDeleteResponse + 11, // 15: supercache.peer.v1.Peer.GetOrLoad:output_type -> supercache.peer.v1.GetOrLoadResponse + 13, // 16: supercache.peer.v1.Peer.ListPop:output_type -> supercache.peer.v1.ListPopResponse + 15, // 17: supercache.peer.v1.Peer.CounterIncr:output_type -> supercache.peer.v1.CounterIncrResponse + 17, // 18: supercache.peer.v1.Peer.StreamAdd:output_type -> supercache.peer.v1.StreamAddResponse + 11, // [11:19] is the sub-list for method output_type + 3, // [3:11] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name @@ -1108,7 +1230,7 @@ func file_peer_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_peer_proto_rawDesc, NumEnums: 0, - NumMessages: 16, + NumMessages: 18, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/peer/v1/peer_grpc.pb.go b/api/gen/peer/v1/peer_grpc.pb.go index 7fdec3c..264e219 100644 --- a/api/gen/peer/v1/peer_grpc.pb.go +++ b/api/gen/peer/v1/peer_grpc.pb.go @@ -26,6 +26,7 @@ const ( Peer_GetOrLoad_FullMethodName = "/supercache.peer.v1.Peer/GetOrLoad" Peer_ListPop_FullMethodName = "/supercache.peer.v1.Peer/ListPop" Peer_CounterIncr_FullMethodName = "/supercache.peer.v1.Peer/CounterIncr" + Peer_StreamAdd_FullMethodName = "/supercache.peer.v1.Peer/StreamAdd" ) // PeerClient is the client API for Peer service. @@ -39,6 +40,7 @@ type PeerClient interface { GetOrLoad(ctx context.Context, in *GetOrLoadRequest, opts ...grpc.CallOption) (*GetOrLoadResponse, error) ListPop(ctx context.Context, in *ListPopRequest, opts ...grpc.CallOption) (*ListPopResponse, error) CounterIncr(ctx context.Context, in *CounterIncrRequest, opts ...grpc.CallOption) (*CounterIncrResponse, error) + StreamAdd(ctx context.Context, in *StreamAddRequest, opts ...grpc.CallOption) (*StreamAddResponse, error) } type peerClient struct { @@ -119,6 +121,16 @@ func (c *peerClient) CounterIncr(ctx context.Context, in *CounterIncrRequest, op return out, nil } +func (c *peerClient) StreamAdd(ctx context.Context, in *StreamAddRequest, opts ...grpc.CallOption) (*StreamAddResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(StreamAddResponse) + err := c.cc.Invoke(ctx, Peer_StreamAdd_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // PeerServer is the server API for Peer service. // All implementations must embed UnimplementedPeerServer // for forward compatibility. @@ -130,6 +142,7 @@ type PeerServer interface { GetOrLoad(context.Context, *GetOrLoadRequest) (*GetOrLoadResponse, error) ListPop(context.Context, *ListPopRequest) (*ListPopResponse, error) CounterIncr(context.Context, *CounterIncrRequest) (*CounterIncrResponse, error) + StreamAdd(context.Context, *StreamAddRequest) (*StreamAddResponse, error) mustEmbedUnimplementedPeerServer() } @@ -161,6 +174,9 @@ func (UnimplementedPeerServer) ListPop(context.Context, *ListPopRequest) (*ListP func (UnimplementedPeerServer) CounterIncr(context.Context, *CounterIncrRequest) (*CounterIncrResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CounterIncr not implemented") } +func (UnimplementedPeerServer) StreamAdd(context.Context, *StreamAddRequest) (*StreamAddResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StreamAdd not implemented") +} func (UnimplementedPeerServer) mustEmbedUnimplementedPeerServer() {} func (UnimplementedPeerServer) testEmbeddedByValue() {} @@ -308,6 +324,24 @@ func _Peer_CounterIncr_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Peer_StreamAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamAddRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PeerServer).StreamAdd(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Peer_StreamAdd_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PeerServer).StreamAdd(ctx, req.(*StreamAddRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Peer_ServiceDesc is the grpc.ServiceDesc for Peer service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -343,6 +377,10 @@ var Peer_ServiceDesc = grpc.ServiceDesc{ MethodName: "CounterIncr", Handler: _Peer_CounterIncr_Handler, }, + { + MethodName: "StreamAdd", + Handler: _Peer_StreamAdd_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "peer.proto", diff --git a/api/openapi/cache.openapi.yaml b/api/openapi/cache.openapi.yaml index 7a34ebd..827eede 100644 --- a/api/openapi/cache.openapi.yaml +++ b/api/openapi/cache.openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: SuperCache Cache API (gRPC reference) - version: 0.13.0 + version: 0.14.0 description: | **Application data plane** — gRPC service `supercache.cache.v1.Cache` on the node **`-cache`** port (default `9000`). @@ -14,7 +14,7 @@ info: | Client | Example | |--------|---------| | Go | `pkg/client` — `client.Dial` / `DialTLS` | - | CLI | `sc get` / `put` / `del` / `bloom` / `sadd` / `zadd` / `geoadd` / `lpush` / `hset` / `incr` / `jsonset` / `bitset` / `hlladd` / `topkadd` / `cmsincr` / `vadd` … | + | CLI | `sc get` / `put` / `del` / `bloom` / `sadd` / `zadd` / `geoadd` / `lpush` / `hset` / `incr` / `jsonset` / `bitset` / `hlladd` / `topkadd` / `cmsincr` / `vadd` / `xadd` … | | Proto | `api/proto/cache.proto` | Stock demo keyspaces are lowercase `Mode.String()`: `cacheonly`, `set`, `zset`, @@ -38,13 +38,14 @@ info: | ModeTopK | TopKAdd, TopKList; Delete(name) | | ModeCMS | CMSIncr, CMSQuery; Delete(name) | | ModeVectorSet | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | + | ModeStream | XAdd, XRange, XRevRange, XLen, XDel, XTrim; Delete(name) | Wrong verb for the mode → invalid argument (gRPC InvalidArgument). ### Consistency (short) - - **Get / SetContains / ZScore / GeoPos / LIndex / HGet / BloomTest / JsonGet / BitGet / HLLCount / TopKList / CMSQuery / VSim / VCard / VDim / VEmb** — observation on the **queried node** (may lag; non-replicas may owner-forward) - - **Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / BloomAdd / JsonSet / BitSet / HLLAdd / TopKAdd / CMSIncr / VAdd / VRem** — ACK after **ring owner** accepts; async fan-out to replicas + - **Get / SetContains / ZScore / GeoPos / LIndex / HGet / BloomTest / JsonGet / BitGet / HLLCount / TopKList / CMSQuery / VSim / VCard / VDim / VEmb / XRange / XRevRange / XLen** — observation on the **queried node** (may lag; non-replicas may owner-forward) + - **Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / BloomAdd / JsonSet / BitSet / HLLAdd / TopKAdd / CMSIncr / VAdd / VRem / XAdd** — ACK after **ring owner** accepts; async fan-out to replicas - **Delete** — owner tombstone + replica apply/hint; peer failures returned on first attempt Peer mesh (`api/proto/peer.proto`) is internal — do not expose to apps. @@ -96,6 +97,12 @@ tags: Dim 2–256 (first `VAdd` locks). Max 512 members, member id 1–255 bytes. Keyspace `VectorMetric`: cosine (default, high→low), l2 (low→high), ip (high→low). Cosine rejects the zero vector; L2/IP allow zeros. Not HNSW / not Redis VADD wire. + - name: Stream + description: | + Named append-only log (`ModeStream`). Owner-minted `millis-seq` ids; + one opaque `[]byte` payload. Exclusive start is a leading `(` on `start`. + `StreamMaxLen` 0 = no auto-trim; hard cap 4096. Not consumer groups / + not blocking XREAD. paths: /supercache.cache.v1.Cache/Get: @@ -107,7 +114,7 @@ paths: gRPC `Get(GetRequest) returns (GetResponse)`. Returns `found=false` when missing or negative-cached (client maps to not found). - Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream. CLI: `sc get greeting` (miss → `(nil)`). requestBody: @@ -137,7 +144,7 @@ paths: `ttl_set=true` applies `ttl_nanos` for this write (0 = no expiry). When `ttl_set=false`, the keyspace default TTL is used. - Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream. CLI: `sc put greeting "hello"` → `OK put greeting (5 bytes)`. requestBody: @@ -1428,6 +1435,180 @@ paths: schema: $ref: "#/components/schemas/VEmbResponse" + /supercache.cache.v1.Cache/XAdd: + post: + tags: [Stream] + operationId: Cache_XAdd + summary: Append an entry + description: | + ModeStream only. Creates the stream if missing. Returns the minted + `millis-seq` id. Payload is opaque `[]byte` (empty allowed). + Non-owner calls use peer `StreamAdd`. Replicas install a full + `FlagStream` snapshot. After add, `StreamMaxLen` (if >0) drops oldest + until `len <= StreamMaxLen`. Hard cap 4096. + + CLI: `sc -keyspace events xadd logs * hello` → prints id + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XAddRequest" + example: + keyspace: events + name: logs + payload: aGVsbG8= + responses: + "200": + description: Minted id + content: + application/json: + schema: + $ref: "#/components/schemas/XAddResponse" + + /supercache.cache.v1.Cache/XRange: + post: + tags: [Stream] + operationId: Cache_XRange + summary: Inclusive id window, oldest first + description: | + ModeStream only. `start`/`end` are Redis-ish `-` / `+` or `millis-seq`. + A leading `(` on `start` is exclusive (resume after a cursor). + `count<=0` means no cap; `count>512` is clamped to 512. + Missing name → empty `entries` (not an error). Replica may lag. + + CLI: `sc -keyspace events xrange logs - + 10` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeRequest" + example: + keyspace: events + name: logs + start: "-" + end: "+" + count: 10 + responses: + "200": + description: Entries, oldest first + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeResponse" + + /supercache.cache.v1.Cache/XRevRange: + post: + tags: [Stream] + operationId: Cache_XRevRange + summary: Inclusive id window, newest first + description: | + ModeStream only. Same `start`/`end` window as `XRange` (`-` min, `+` max; + exclusive `(` on `start` only). Returns newest first. + Missing name → empty `entries`. Replica may lag. + + CLI: `sc -keyspace events xrevrange logs - + 10` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XRevRangeRequest" + example: + keyspace: events + name: logs + start: "-" + end: "+" + count: 10 + responses: + "200": + description: Entries, newest first + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeResponse" + + /supercache.cache.v1.Cache/XLen: + post: + tags: [Stream] + operationId: Cache_XLen + summary: Entry count + description: | + ModeStream only. Missing name → `present=false`, `n=0`. + Empty live stream (after last XDel / XTrim 0) → `present=true`, `n=0`. + + CLI: `sc -keyspace events xlen logs` (miss → `(nil)`) + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XLenRequest" + responses: + "200": + description: Result + content: + application/json: + schema: + $ref: "#/components/schemas/XLenResponse" + + /supercache.cache.v1.Cache/XDel: + post: + tags: [Stream] + operationId: Cache_XDel + summary: Remove one id + description: | + ModeStream only. ACK-only. Missing name or id is a no-op. + Last delete leaves a live empty stream until `Delete(name)`. + + CLI: `sc -keyspace events xdel logs 1710000000000-0` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XDelRequest" + example: + keyspace: events + name: logs + id: "1710000000000-0" + responses: + "200": + description: Accepted by owner (empty body) + content: + application/json: + schema: + $ref: "#/components/schemas/Empty" + + /supercache.cache.v1.Cache/XTrim: + post: + tags: [Stream] + operationId: Cache_XTrim + summary: Keep newest N entries + description: | + ModeStream only. ACK-only. `max_len < 0` is invalid. `max_len=0` + empties the stream but keeps it live until `Delete(name)`. + + CLI: `sc -keyspace events xtrim logs 100` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XTrimRequest" + example: + keyspace: events + name: logs + max_len: 100 + responses: + "200": + description: Accepted by owner (empty body) + content: + application/json: + schema: + $ref: "#/components/schemas/Empty" + components: schemas: Empty: @@ -2286,3 +2467,111 @@ components: format: float found: type: boolean + StreamEntry: + type: object + properties: + id: + type: string + description: "millis-seq, e.g. 1710000000000-0" + payload: + type: string + format: byte + description: Opaque client-encoded payload + XAddRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + payload: + type: string + format: byte + XAddResponse: + type: object + properties: + id: + type: string + description: Minted millis-seq id + XRangeRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + start: + type: string + description: "-" / millis-seq / exclusive "(id" + end: + type: string + description: "+" / millis-seq (always inclusive) + count: + type: integer + format: int32 + description: "<=0 no cap; >512 clamped to 512" + XRevRangeRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + start: + type: string + description: "-" / millis-seq / exclusive "(id" (same as XRange) + end: + type: string + description: "+" / millis-seq (always inclusive) + count: + type: integer + format: int32 + XRangeResponse: + type: object + properties: + entries: + type: array + items: + $ref: "#/components/schemas/StreamEntry" + XLenRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + XLenResponse: + type: object + properties: + n: + type: integer + format: int64 + present: + type: boolean + description: false if the name is missing / expired / tombstoned + XDelRequest: + type: object + required: [keyspace, name, id] + properties: + keyspace: + type: string + name: + type: string + id: + type: string + XTrimRequest: + type: object + required: [keyspace, name, max_len] + properties: + keyspace: + type: string + name: + type: string + max_len: + type: integer + format: int32 + description: Keep newest N; 0 empties the live stream diff --git a/api/proto/cache.proto b/api/proto/cache.proto index 5e6b395..29186b5 100644 --- a/api/proto/cache.proto +++ b/api/proto/cache.proto @@ -64,6 +64,12 @@ service Cache { rpc VCard(VCardRequest) returns (VCardResponse); rpc VDim(VDimRequest) returns (VDimResponse); rpc VEmb(VEmbRequest) returns (VEmbResponse); + rpc XAdd(XAddRequest) returns (XAddResponse); + rpc XRange(XRangeRequest) returns (XRangeResponse); + rpc XRevRange(XRevRangeRequest) returns (XRangeResponse); + rpc XLen(XLenRequest) returns (XLenResponse); + rpc XDel(XDelRequest) returns (XDelResponse); + rpc XTrim(XTrimRequest) returns (XTrimResponse); } message GetRequest { @@ -636,3 +642,58 @@ message VEmbResponse { repeated float vec = 1; bool found = 2; } + +message StreamEntry { + string id = 1; + bytes payload = 2; +} + +message XAddRequest { + string keyspace = 1; + string name = 2; + bytes payload = 3; +} +message XAddResponse { + string id = 1; +} + +message XRangeRequest { + string keyspace = 1; + string name = 2; + string start = 3; + string end = 4; + int32 count = 5; +} +message XRevRangeRequest { + string keyspace = 1; + string name = 2; + string start = 3; + string end = 4; + int32 count = 5; +} +message XRangeResponse { + repeated StreamEntry entries = 1; +} + +message XLenRequest { + string keyspace = 1; + string name = 2; +} +message XLenResponse { + int64 n = 1; + bool present = 2; +} + +message XDelRequest { + string keyspace = 1; + string name = 2; + string id = 3; +} +message XDelResponse {} + +message XTrimRequest { + string keyspace = 1; + string name = 2; + int32 max_len = 3; +} +message XTrimResponse {} diff --git a/api/proto/peer.proto b/api/proto/peer.proto index 0261518..345807a 100644 --- a/api/proto/peer.proto +++ b/api/proto/peer.proto @@ -12,13 +12,14 @@ service Peer { rpc GetOrLoad(GetOrLoadRequest) returns (GetOrLoadResponse); rpc ListPop(ListPopRequest) returns (ListPopResponse); rpc CounterIncr(CounterIncrRequest) returns (CounterIncrResponse); + rpc StreamAdd(StreamAddRequest) returns (StreamAddResponse); } message Entry { bytes value = 1; uint64 version = 2; int64 expire_at_unix_nano = 3; - uint32 flags = 4; + uint64 flags = 4; } message ApplyPutRequest { @@ -99,3 +100,12 @@ message CounterIncrRequest { message CounterIncrResponse { int64 value = 1; } + +message StreamAddRequest { + string keyspace = 1; + string name = 2; + bytes payload = 3; +} +message StreamAddResponse { + string id = 1; +} diff --git a/cmd/sc/README.md b/cmd/sc/README.md index e40f964..557746d 100644 --- a/cmd/sc/README.md +++ b/cmd/sc/README.md @@ -1,6 +1,6 @@ # sc — SuperCache CLI -Talk to SuperCache without writing Go: **get/put/del**, **bloom**, **z\***, **geo\***, **l\***, **h\***, **incr** / **cget**, **jsonset** / **jsonget** / **jsondel**, **bitset** / **bitget** / **bitcount** / **bitpos**, **hlladd** / **hllcount**, **topkadd** / **topklist**, **cmsincr** / **cmsquery** over Cache gRPC, **admin** diagnostics over HTTP, **multi-seed** failover, and an interactive **REPL**. +Talk to SuperCache without writing Go: **get/put/del**, **bloom**, **z\***, **geo\***, **l\***, **h\***, **incr** / **cget**, **jsonset** / **jsonget** / **jsondel**, **bitset** / **bitget** / **bitcount** / **bitpos**, **hlladd** / **hllcount**, **topkadd** / **topklist**, **cmsincr** / **cmsquery**, **vadd**…, **xadd** / **xlen** / **xrange**… over Cache gRPC, **admin** diagnostics over HTTP, **multi-seed** failover, and an interactive **REPL**. ## Quick start @@ -52,7 +52,7 @@ This is **not** client-side sharding. Any healthy cache node is a valid front do |---------|------|----------------| | `get [key...]` | Cache gRPC | Fetch value(s); miss = `(nil)`; exit `1` if any missing | | `put` / `set` | Cache gRPC | Store a value (string, `-file`, or stdin) — **KV modes only** (`set` is put, not ModeSet) | -| `del` / `delete` | Cache gRPC | Cluster invalidate (peer warnings on stderr); also wipes named Bloom/set/zset/geo/list/hash/counter/json/bitmap | +| `del` / `delete` | Cache gRPC | Cluster invalidate (peer warnings on stderr); also wipes named Bloom/set/zset/geo/list/hash/counter/json/bitmap/hll/topk/cms/vectorset/stream | | `bloom add\|test ` | Cache gRPC | `ModeBloom` membership (`test` prints `true`/`false`) | | `sadd ` | Cache gRPC | `ModeSet` add | | `srem ` | Cache gRPC | `ModeSet` remove | @@ -98,6 +98,7 @@ This is **not** client-side sharding. Any healthy cache node is a valid front do | `cmsincr [n]` | Cache gRPC | `ModeCMS` increment (`n` optional, default 1; 0 means 1) | | `cmsquery ` | Cache gRPC | decimal estimate, or `(nil)` + exit 1 if name missing | | `vadd` / `vrem` / `vsim` / `vcard` / `vdim` / `vemb` | Cache gRPC | `ModeVectorSet`; miss = `(nil)` | +| `xadd` / `xlen` / `xrange` / `xrevrange` / `xdel` / `xtrim` | Cache gRPC | `ModeStream`; `xadd * ` prints id | | `ping` | both | Dial cache seeds + admin `/healthz` | | `peers` / `keyspaces` / `metrics` | Admin HTTP | Diagnostics | | `health` / `ready` | Admin HTTP | Probes | diff --git a/cmd/sc/cmds.go b/cmd/sc/cmds.go index bddabfb..5297fa8 100644 --- a/cmd/sc/cmds.go +++ b/cmd/sc/cmds.go @@ -138,6 +138,18 @@ func dispatch(ctx context.Context, sess *session, cmd string, args []string) int return cmdVCard(ctx, sess, args) case "vdim": return cmdVDim(ctx, sess, args) + case "xadd": + return cmdXAdd(ctx, sess, args) + case "xlen": + return cmdXLen(ctx, sess, args) + case "xrange": + return cmdXRange(ctx, sess, args) + case "xrevrange": + return cmdXRevRange(ctx, sess, args) + case "xdel": + return cmdXDel(ctx, sess, args) + case "xtrim": + return cmdXTrim(ctx, sess, args) case "vemb": return cmdVEmb(ctx, sess, args) default: diff --git a/cmd/sc/cmds_stream.go b/cmd/sc/cmds_stream.go new file mode 100644 index 0000000..f3359cc --- /dev/null +++ b/cmd/sc/cmds_stream.go @@ -0,0 +1,143 @@ +package main + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/Code0987/supercache/pkg/client" + "github.com/Code0987/supercache/pkg/engine" +) + +func cmdXAdd(ctx context.Context, sess *session, args []string) int { + if len(args) < 3 || args[1] != "*" { + printUsageLine("usage: xadd * ") + return 2 + } + var id string + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + id, e = cli.XAdd(ctx, sess.cfg.keyspace, args[0], []byte(strings.Join(args[2:], " "))) + return e + }) + if err != nil { + printCmdErr("xadd", err) + return 1 + } + if !sess.cfg.quiet { + fmt.Println(id) + } + return 0 +} + +func cmdXLen(ctx context.Context, sess *session, args []string) int { + if len(args) != 1 { + printUsageLine("usage: xlen ") + return 2 + } + var n int + var ok bool + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + n, ok, e = cli.XLen(ctx, sess.cfg.keyspace, args[0]) + return e + }) + if err != nil { + printCmdErr("xlen", err) + return 1 + } + if !ok { + printNil() + return 1 + } + fmt.Println(n) + return 0 +} + +func cmdXRange(ctx context.Context, sess *session, args []string) int { + return cmdXRangeDir(ctx, sess, args, false) +} + +func cmdXRevRange(ctx context.Context, sess *session, args []string) int { + return cmdXRangeDir(ctx, sess, args, true) +} + +func cmdXRangeDir(ctx context.Context, sess *session, args []string, rev bool) int { + verb := "xrange" + if rev { + verb = "xrevrange" + } + if len(args) < 3 || len(args) > 4 { + printUsageLine("usage: " + verb + " [count]") + return 2 + } + count := 0 + if len(args) == 4 { + n, err := strconv.Atoi(args[3]) + if err != nil { + printUsageLine("usage: " + verb + " [count]") + return 2 + } + count = n + } + var rows []engine.StreamEntry + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + if rev { + rows, e = cli.XRevRange(ctx, sess.cfg.keyspace, args[0], args[1], args[2], count) + } else { + rows, e = cli.XRange(ctx, sess.cfg.keyspace, args[0], args[1], args[2], count) + } + return e + }) + if err != nil { + printCmdErr(verb, err) + return 1 + } + for _, r := range rows { + fmt.Printf("%s %s\n", r.ID, r.Payload) + } + return 0 +} + +func cmdXDel(ctx context.Context, sess *session, args []string) int { + if len(args) != 2 { + printUsageLine("usage: xdel ") + return 2 + } + err := sess.withClient(func(cli *client.Client, _ string) error { + return cli.XDel(ctx, sess.cfg.keyspace, args[0], args[1]) + }) + if err != nil { + printCmdErr("xdel", err) + return 1 + } + if !sess.cfg.quiet { + printOK(fmt.Sprintf("xdel %s %s", args[0], args[1])) + } + return 0 +} + +func cmdXTrim(ctx context.Context, sess *session, args []string) int { + if len(args) != 2 { + printUsageLine("usage: xtrim ") + return 2 + } + n, err := strconv.Atoi(args[1]) + if err != nil { + printUsageLine("usage: xtrim ") + return 2 + } + err = sess.withClient(func(cli *client.Client, _ string) error { + return cli.XTrim(ctx, sess.cfg.keyspace, args[0], n) + }) + if err != nil { + printCmdErr("xtrim", err) + return 1 + } + if !sess.cfg.quiet { + printOK(fmt.Sprintf("xtrim %s %s", args[0], args[1])) + } + return 0 +} diff --git a/cmd/sc/help.go b/cmd/sc/help.go index c48e38d..27ba474 100644 --- a/cmd/sc/help.go +++ b/cmd/sc/help.go @@ -25,6 +25,7 @@ var cacheHelpGroups = []helpGroup{ {"topk", "topkadd topklist"}, {"cms", "cmsincr cmsquery"}, {"vector", "vadd vrem vsim vcard vdim vemb"}, + {"stream", "xadd xlen xrange xrevrange xdel xtrim"}, {"admin", "ping peers keyspaces metrics health ready"}, } diff --git a/cmd/sc/main.go b/cmd/sc/main.go index 72d2e11..80fa3a1 100644 --- a/cmd/sc/main.go +++ b/cmd/sc/main.go @@ -113,7 +113,8 @@ func run(args []string) int { "hlladd", "hllcount", "topkadd", "topklist", "cmsincr", "cmsquery", - "vadd", "vrem", "vsim", "vcard", "vdim", "vemb": + "vadd", "vrem", "vsim", "vcard", "vdim", "vemb", + "xadd", "xlen", "xrange", "xrevrange", "xdel", "xtrim": sess := newSession(cfg) defer sess.Close() ctx, cancel := context.WithTimeout(context.Background(), cfg.timeout) diff --git a/cmd/sc/stream_cli_test.go b/cmd/sc/stream_cli_test.go new file mode 100644 index 0000000..06dff19 --- /dev/null +++ b/cmd/sc/stream_cli_test.go @@ -0,0 +1,61 @@ +package main + +import ( + "strings" + "testing" + "time" + + "github.com/Code0987/supercache/internal/cacheserver" + "github.com/Code0987/supercache/pkg/engine" + "github.com/Code0987/supercache/pkg/keyspace" +) + +func TestXAddUsage(t *testing.T) { + if code := run([]string{"xadd"}); code != 2 { + t.Fatalf("xadd: %d", code) + } + if code := run([]string{"xadd", "logs", "notstar", "hi"}); code != 2 { + t.Fatalf("xadd no star: %d", code) + } + if code := run([]string{"xlen"}); code != 2 { + t.Fatalf("xlen: %d", code) + } + if code := run([]string{"xrange", "logs"}); code != 2 { + t.Fatalf("xrange: %d", code) + } +} + +func TestStreamCLIRoundTrip(t *testing.T) { + eng := engine.New() + defer eng.Close() + if err := eng.UpdateKeySpace(keyspace.Config{ + Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1 << 20, TTL: time.Hour, + }); err != nil { + t.Fatal(err) + } + gs, lis, err := cacheserver.ListenAndServe("127.0.0.1:0", eng) + if err != nil { + t.Fatal(err) + } + defer gs.Stop() + addr := lis.Addr().String() + + out, code := captureOut(func() int { + return run([]string{"-addr", addr, "-keyspace", "events", "xadd", "logs", "*", "hello"}) + }) + if code != 0 || !strings.Contains(out, "-") { + t.Fatalf("xadd: exit %d out=%q", code, out) + } + out, code = captureOut(func() int { + return run([]string{"-addr", addr, "-keyspace", "events", "xlen", "logs"}) + }) + if code != 0 || strings.TrimSpace(out) != "1" { + t.Fatalf("xlen: exit %d out=%q", code, out) + } + out, code = captureOut(func() int { + return run([]string{"-addr", addr, "-keyspace", "events", "xrange", "logs", "-", "+"}) + }) + if code != 0 || !strings.Contains(out, "hello") { + t.Fatalf("xrange: exit %d out=%q", code, out) + } +} diff --git a/cmd/sc/style_test.go b/cmd/sc/style_test.go index b66ee4a..ed80ef0 100644 --- a/cmd/sc/style_test.go +++ b/cmd/sc/style_test.go @@ -111,6 +111,7 @@ func TestCompactHelp(t *testing.T) { "Commands", "get put del", "vadd vrem vsim", + "xadd xlen xrange", "Flags", "Examples", } { diff --git a/docs/API.md b/docs/API.md index 6173371..cdbb59b 100644 --- a/docs/API.md +++ b/docs/API.md @@ -25,7 +25,7 @@ go run ./cmd/supercache-node \ ### Clients - **Go:** `pkg/client` -- **CLI:** `cmd/sc` (`sc get` / `put` / `del`, `bloom`, `sadd`…, `zadd`…, `geoadd`…, `lpush`…, `hset`…, `incr` / `cget`, `jsonset`…, `bitset`…, `vadd`…, or REPL) +- **CLI:** `cmd/sc` (`sc get` / `put` / `del`, `bloom`, `sadd`…, `zadd`…, `geoadd`…, `lpush`…, `hset`…, `incr` / `cget`, `jsonset`…, `bitset`…, `vadd`…, `xadd`…, or REPL) - **Protos:** `api/proto/cache.proto`, `api/proto/peer.proto` (peer is mesh-internal) ## Keyspace modes @@ -49,8 +49,9 @@ Each keyspace has exactly one mode. Verbs that do not match the mode return inva | `ModeTopK` | Named Space-Saving heavy-hitters | `TopKAdd`, `TopKList`; `Delete(name)` | | `ModeCMS` | Named Count-Min frequency sketch | `CMSIncr`, `CMSQuery`; `Delete(name)` | | `ModeVectorSet` | Named embedding set + K-NN | `VAdd`, `VRem`, `VSim`, `VCard`, `VDim`, `VEmb`; `Delete(name)` | +| `ModeStream` | Named append-only log | `XAdd`, `XRange`, `XRevRange`, `XLen`, `XDel`, `XTrim`; `Delete(name)` | -Config: `pkg/keyspace.Config` (`Name`, `Mode`, `MaxBytes`, `TTL`, `ReplicationFactor`, …). Bloom also uses `BloomBits` / `BloomHashes`. ModeTopK uses `TopKSize` (0 → 100). ModeVectorSet uses `VectorDim` (0 = first add locks) and `VectorMetric` (cosine / l2 / ip). +Config: `pkg/keyspace.Config` (`Name`, `Mode`, `MaxBytes`, `TTL`, `ReplicationFactor`, …). Bloom also uses `BloomBits` / `BloomHashes`. ModeTopK uses `TopKSize` (0 → 100). ModeVectorSet uses `VectorDim` (0 = first add locks) and `VectorMetric` (cosine / l2 / ip). ModeStream uses `StreamMaxLen` (0 = no auto-trim; hard cap 4096). ## Cache gRPC RPCs @@ -215,6 +216,20 @@ Hosted shapes: Cache gRPC tab on [`/docs`](https://code0987.github.io/supercache Named embedding set. Dim **2–256**, max **512** members, member id **1–255** bytes. Keyspace `VectorMetric`: **cosine** (default, high→low; rejects zero), **l2** (low→high), **ip** (high→low). Replicas install a **full `FlagVectorSet` snapshot**. Replica `VSim` may lag. Get/Put on `ModeVectorSet` are invalid. Not HNSW / not Redis `VADD` wire. Walkthrough: [`examples/vecset`](../examples/vecset/). CLI: `sc -keyspace vectorset vadd items a 1,0`. +### Stream (`ModeStream`) + +| RPC | Notes | +|-----|--------| +| `XAdd` | Append opaque `[]byte`. Creates the stream if missing. Returns minted id `millis-seq` | +| `XRange` | Inclusive id window, oldest first. `-` / `+`. Start `(`+id is exclusive. Missing → empty | +| `XRevRange` | Same `start`/`end` window as `XRange`, newest first | +| `XLen` | Count + present-bit. Missing ⇒ `present=false` | +| `XDel` | Remove one id. ACK-only. Missing id is a no-op | +| `XTrim` | Keep newest `max_len`. ACK-only | +| `Delete(name)` | Tombstone whole stream | + +Named append-only log. Payload is opaque (client encodes). Owner mints ids; `XAdd` from a non-owner uses peer `StreamAdd`. Replicas install a **full `FlagStream` snapshot**. `StreamMaxLen` 0 = no auto-trim; hard cap 4096. Empty after last delete/trim stays live until `Delete(name)`. Get/Put invalid. Not consumer groups / not blocking `XREAD`. CLI: `sc -keyspace events xadd logs * hello`. + ## Enabling GitHub Pages 1. Repo **Settings → Pages → Build and deployment → GitHub Actions** diff --git a/docs/CLUSTER_FLOWS.md b/docs/CLUSTER_FLOWS.md index d713c15..3a7d873 100644 --- a/docs/CLUSTER_FLOWS.md +++ b/docs/CLUSTER_FLOWS.md @@ -321,7 +321,7 @@ flowchart TD --- -## Structured types (Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap) +## Structured types (Bloom / Set / ZSet / Geo / List / Hash / Counter / JSON / Bitmap / HLL / TopK / CMS / VectorSet / Stream) KV Get/Put diagrams above apply only to `ModeCacheOnly` / `ModeLoadThrough`. @@ -340,6 +340,7 @@ KV Get/Put diagrams above apply only to `ModeCacheOnly` / `ModeLoadThrough`. | ModeTopK | owner `TopKAdd` then **`FlagTopK` snapshot** | `TopKList` | Space-Saving table (≤ K slots) | | ModeCMS | owner `CMSIncr` then **`FlagCMS` snapshot** | `CMSQuery` | dense 64 KiB Count-Min | | ModeVectorSet | owner `VAdd`/`VRem` then **`FlagVectorSet` snapshot** | `VSim` / `VEmb` / `VCard` / `VDim` | brute-force cosine/L2/IP | +| ModeStream | owner `XAdd`/`XDel`/`XTrim` then **`FlagStream` snapshot** | `XRange` / `XRevRange` / `XLen` | append-only log; peer `StreamAdd` returns id | `Delete(name)` uses the same tombstone path as KV Delete. Owner serializes writes; replicas apply under version gates. Non-replicas forward reads to the owner (and may install a replica copy when in RF). @@ -352,7 +353,7 @@ See [API.md](./API.md) and design docs under `docs/design/`. ```mermaid flowchart TB subgraph Client - OPS["Get / Put / PutMany
Delete / DeleteMany
Bloom* / Set* / Z* / Geo* / L* / H* / Incr / CounterGet / Json* / BitSet / BitGet / BitCount / BitPos / HLLAdd / HLLCount / TopKAdd / TopKList / CMSIncr / CMSQuery"] + OPS["Get / Put / PutMany
Delete / DeleteMany
Bloom* / Set* / Z* / Geo* / L* / H* / Incr / CounterGet / Json* / BitSet / BitGet / BitCount / BitPos / HLLAdd / HLLCount / TopKAdd / TopKList / CMSIncr / CMSQuery / VAdd / VSim / XAdd / XRange"] end subgraph Node["Any of N nodes"] diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 251d7ce..c82186a 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -75,6 +75,7 @@ Peer mesh with mTLS: every node uses the same CA; each node presents a cert sign | `ModeTopK` | TopKAdd / TopKList / Delete(name) | Space-Saving table; snapshot fan-out + owner-inbox; replica TopKList may lag | | `ModeCMS` | CMSIncr / CMSQuery / Delete(name) | Count-Min 64 KiB; snapshot fan-out + owner-inbox; replica CMSQuery may lag | | `ModeVectorSet` | VAdd / VRem / VSim / VCard / VDim / VEmb / Delete(name) | embeddings; cosine/L2/IP; snapshot fan-out + A/R inbox; replica VSim may lag | +| `ModeStream` | XAdd / XRange / XRevRange / XLen / XDel / XTrim / Delete(name) | append-only log; opaque payload; snapshot fan-out; XAdd returns id | Wrong verb for the mode → invalid argument. Configure the same modes on every node (see rollout above). @@ -86,7 +87,7 @@ Demo node (`-demo-keyspace`): one keyspace per shipped demo mode, **name = lower |----|-----------| | Get | Local observation on the queried node | | Put | ACK after **owner** accept; async fan-out to **R−1 replicas** (`ReplicationFactor`, default 3). Failed `ApplyPut`s are hinted per replica and replayed when that peer is reachable again (bounded; oldest dropped). | -| Delete | Owner tombstone, then the **same replica apply+hint pool as Put** (sync first attempt). Failed peers are hinted and replayed. `MultiError` if any replica fails on that first attempt. Tombstones expire after `TombstoneTTL` (default 5m; negative = never). Join handoff uses the same pool. Applies to KV keys **and** named Bloom/Set/ZSet/Geo/List/Hash/Counter/JSON/Bitmap/HLL/TopK entries. | +| Delete | Owner tombstone, then the **same replica apply+hint pool as Put** (sync first attempt). Failed peers are hinted and replayed. `MultiError` if any replica fails on that first attempt. Tombstones expire after `TombstoneTTL` (default 5m; negative = never). Join handoff uses the same pool. Applies to KV keys **and** named Bloom/Set/ZSet/Geo/List/Hash/Counter/JSON/Bitmap/HLL/TopK/CMS/VectorSet/Stream entries. | | BloomAdd / BloomTest | `ModeBloom` only. Add ORs bits on the owner and replicas (not LWW of the bitset). Test is local on a replica, owner-forward otherwise. There is no per-item delete. | | SetAdd / SetRemove / SetContains | `ModeSet` only. Owner serializes mutations; item-level fan-out (`FlagSetAdd` / `FlagSetRemove`). Contains is local on a replica, owner-forward otherwise. | | ZAdd / ZRem / ZScore / ZRange* | `ModeZSet` only. Same ownership pattern as ModeSet; item-level `FlagZSetAdd` / `FlagZSetRem`; handoff ships full encoded zset (`FlagZSet`). | @@ -100,6 +101,7 @@ Demo node (`-demo-keyspace`): one keyspace per shipped demo mode, **name = lower | TopKAdd / TopKList | `ModeTopK` only. Owner applies the op then fans out a **full `FlagTopK` snapshot** (inbox `FlagTopKAdd` stays owner-only). Replica `TopKList` may lag. Missing name → `ok=false`. `TopKAdd` is ACK-only (+1). | | CMSIncr / CMSQuery | `ModeCMS` only. Owner applies the op then fans out a **full `FlagCMS` snapshot** (inbox `FlagCMSIncr` stays owner-only). Replica `CMSQuery` may lag. Missing name → `ok=false`. `CMSIncr` is ACK-only (`n==0` means 1). See [`examples/billboard`](../examples/billboard/) for the ModeCMS complement. | | VAdd / VSim / VRem | `ModeVectorSet` only. Owner applies then fans a **full `FlagVectorSet` snapshot** (`A`/`R` inbox owner-only). Metric is keyspace `VectorMetric`. Replica `VSim` may lag. See [`examples/vecset`](../examples/vecset/). | +| XAdd / XRange / XLen | `ModeStream` only. Owner mints `millis-seq` and fans a **full `FlagStream` snapshot**. Non-owner `XAdd` uses peer `StreamAdd`. Replica `XRange` may lag. | | Failures | Fan-out errors are metrics-only on Put (and analogous async structure fan-out) | Set TTLs to your max acceptable staleness (TTL applies to the **whole** named structure, not per member). diff --git a/docs/api/cache.openapi.yaml b/docs/api/cache.openapi.yaml index 7a34ebd..827eede 100644 --- a/docs/api/cache.openapi.yaml +++ b/docs/api/cache.openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: SuperCache Cache API (gRPC reference) - version: 0.13.0 + version: 0.14.0 description: | **Application data plane** — gRPC service `supercache.cache.v1.Cache` on the node **`-cache`** port (default `9000`). @@ -14,7 +14,7 @@ info: | Client | Example | |--------|---------| | Go | `pkg/client` — `client.Dial` / `DialTLS` | - | CLI | `sc get` / `put` / `del` / `bloom` / `sadd` / `zadd` / `geoadd` / `lpush` / `hset` / `incr` / `jsonset` / `bitset` / `hlladd` / `topkadd` / `cmsincr` / `vadd` … | + | CLI | `sc get` / `put` / `del` / `bloom` / `sadd` / `zadd` / `geoadd` / `lpush` / `hset` / `incr` / `jsonset` / `bitset` / `hlladd` / `topkadd` / `cmsincr` / `vadd` / `xadd` … | | Proto | `api/proto/cache.proto` | Stock demo keyspaces are lowercase `Mode.String()`: `cacheonly`, `set`, `zset`, @@ -38,13 +38,14 @@ info: | ModeTopK | TopKAdd, TopKList; Delete(name) | | ModeCMS | CMSIncr, CMSQuery; Delete(name) | | ModeVectorSet | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | + | ModeStream | XAdd, XRange, XRevRange, XLen, XDel, XTrim; Delete(name) | Wrong verb for the mode → invalid argument (gRPC InvalidArgument). ### Consistency (short) - - **Get / SetContains / ZScore / GeoPos / LIndex / HGet / BloomTest / JsonGet / BitGet / HLLCount / TopKList / CMSQuery / VSim / VCard / VDim / VEmb** — observation on the **queried node** (may lag; non-replicas may owner-forward) - - **Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / BloomAdd / JsonSet / BitSet / HLLAdd / TopKAdd / CMSIncr / VAdd / VRem** — ACK after **ring owner** accepts; async fan-out to replicas + - **Get / SetContains / ZScore / GeoPos / LIndex / HGet / BloomTest / JsonGet / BitGet / HLLCount / TopKList / CMSQuery / VSim / VCard / VDim / VEmb / XRange / XRevRange / XLen** — observation on the **queried node** (may lag; non-replicas may owner-forward) + - **Put / SetAdd / ZAdd / GeoAdd / LPush / HSet / BloomAdd / JsonSet / BitSet / HLLAdd / TopKAdd / CMSIncr / VAdd / VRem / XAdd** — ACK after **ring owner** accepts; async fan-out to replicas - **Delete** — owner tombstone + replica apply/hint; peer failures returned on first attempt Peer mesh (`api/proto/peer.proto`) is internal — do not expose to apps. @@ -96,6 +97,12 @@ tags: Dim 2–256 (first `VAdd` locks). Max 512 members, member id 1–255 bytes. Keyspace `VectorMetric`: cosine (default, high→low), l2 (low→high), ip (high→low). Cosine rejects the zero vector; L2/IP allow zeros. Not HNSW / not Redis VADD wire. + - name: Stream + description: | + Named append-only log (`ModeStream`). Owner-minted `millis-seq` ids; + one opaque `[]byte` payload. Exclusive start is a leading `(` on `start`. + `StreamMaxLen` 0 = no auto-trim; hard cap 4096. Not consumer groups / + not blocking XREAD. paths: /supercache.cache.v1.Cache/Get: @@ -107,7 +114,7 @@ paths: gRPC `Get(GetRequest) returns (GetResponse)`. Returns `found=false` when missing or negative-cached (client maps to not found). - Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream. CLI: `sc get greeting` (miss → `(nil)`). requestBody: @@ -137,7 +144,7 @@ paths: `ttl_set=true` applies `ttl_nanos` for this write (0 = no expiry). When `ttl_set=false`, the keyspace default TTL is used. - Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet / ModeStream. CLI: `sc put greeting "hello"` → `OK put greeting (5 bytes)`. requestBody: @@ -1428,6 +1435,180 @@ paths: schema: $ref: "#/components/schemas/VEmbResponse" + /supercache.cache.v1.Cache/XAdd: + post: + tags: [Stream] + operationId: Cache_XAdd + summary: Append an entry + description: | + ModeStream only. Creates the stream if missing. Returns the minted + `millis-seq` id. Payload is opaque `[]byte` (empty allowed). + Non-owner calls use peer `StreamAdd`. Replicas install a full + `FlagStream` snapshot. After add, `StreamMaxLen` (if >0) drops oldest + until `len <= StreamMaxLen`. Hard cap 4096. + + CLI: `sc -keyspace events xadd logs * hello` → prints id + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XAddRequest" + example: + keyspace: events + name: logs + payload: aGVsbG8= + responses: + "200": + description: Minted id + content: + application/json: + schema: + $ref: "#/components/schemas/XAddResponse" + + /supercache.cache.v1.Cache/XRange: + post: + tags: [Stream] + operationId: Cache_XRange + summary: Inclusive id window, oldest first + description: | + ModeStream only. `start`/`end` are Redis-ish `-` / `+` or `millis-seq`. + A leading `(` on `start` is exclusive (resume after a cursor). + `count<=0` means no cap; `count>512` is clamped to 512. + Missing name → empty `entries` (not an error). Replica may lag. + + CLI: `sc -keyspace events xrange logs - + 10` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeRequest" + example: + keyspace: events + name: logs + start: "-" + end: "+" + count: 10 + responses: + "200": + description: Entries, oldest first + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeResponse" + + /supercache.cache.v1.Cache/XRevRange: + post: + tags: [Stream] + operationId: Cache_XRevRange + summary: Inclusive id window, newest first + description: | + ModeStream only. Same `start`/`end` window as `XRange` (`-` min, `+` max; + exclusive `(` on `start` only). Returns newest first. + Missing name → empty `entries`. Replica may lag. + + CLI: `sc -keyspace events xrevrange logs - + 10` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XRevRangeRequest" + example: + keyspace: events + name: logs + start: "-" + end: "+" + count: 10 + responses: + "200": + description: Entries, newest first + content: + application/json: + schema: + $ref: "#/components/schemas/XRangeResponse" + + /supercache.cache.v1.Cache/XLen: + post: + tags: [Stream] + operationId: Cache_XLen + summary: Entry count + description: | + ModeStream only. Missing name → `present=false`, `n=0`. + Empty live stream (after last XDel / XTrim 0) → `present=true`, `n=0`. + + CLI: `sc -keyspace events xlen logs` (miss → `(nil)`) + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XLenRequest" + responses: + "200": + description: Result + content: + application/json: + schema: + $ref: "#/components/schemas/XLenResponse" + + /supercache.cache.v1.Cache/XDel: + post: + tags: [Stream] + operationId: Cache_XDel + summary: Remove one id + description: | + ModeStream only. ACK-only. Missing name or id is a no-op. + Last delete leaves a live empty stream until `Delete(name)`. + + CLI: `sc -keyspace events xdel logs 1710000000000-0` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XDelRequest" + example: + keyspace: events + name: logs + id: "1710000000000-0" + responses: + "200": + description: Accepted by owner (empty body) + content: + application/json: + schema: + $ref: "#/components/schemas/Empty" + + /supercache.cache.v1.Cache/XTrim: + post: + tags: [Stream] + operationId: Cache_XTrim + summary: Keep newest N entries + description: | + ModeStream only. ACK-only. `max_len < 0` is invalid. `max_len=0` + empties the stream but keeps it live until `Delete(name)`. + + CLI: `sc -keyspace events xtrim logs 100` + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/XTrimRequest" + example: + keyspace: events + name: logs + max_len: 100 + responses: + "200": + description: Accepted by owner (empty body) + content: + application/json: + schema: + $ref: "#/components/schemas/Empty" + components: schemas: Empty: @@ -2286,3 +2467,111 @@ components: format: float found: type: boolean + StreamEntry: + type: object + properties: + id: + type: string + description: "millis-seq, e.g. 1710000000000-0" + payload: + type: string + format: byte + description: Opaque client-encoded payload + XAddRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + payload: + type: string + format: byte + XAddResponse: + type: object + properties: + id: + type: string + description: Minted millis-seq id + XRangeRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + start: + type: string + description: "-" / millis-seq / exclusive "(id" + end: + type: string + description: "+" / millis-seq (always inclusive) + count: + type: integer + format: int32 + description: "<=0 no cap; >512 clamped to 512" + XRevRangeRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + start: + type: string + description: "-" / millis-seq / exclusive "(id" (same as XRange) + end: + type: string + description: "+" / millis-seq (always inclusive) + count: + type: integer + format: int32 + XRangeResponse: + type: object + properties: + entries: + type: array + items: + $ref: "#/components/schemas/StreamEntry" + XLenRequest: + type: object + required: [keyspace, name] + properties: + keyspace: + type: string + name: + type: string + XLenResponse: + type: object + properties: + n: + type: integer + format: int64 + present: + type: boolean + description: false if the name is missing / expired / tombstoned + XDelRequest: + type: object + required: [keyspace, name, id] + properties: + keyspace: + type: string + name: + type: string + id: + type: string + XTrimRequest: + type: object + required: [keyspace, name, max_len] + properties: + keyspace: + type: string + name: + type: string + max_len: + type: integer + format: int32 + description: Keep newest N; 0 empties the live stream diff --git a/docs/design/2026-09-08-mode-stream.md b/docs/design/2026-09-08-mode-stream.md new file mode 100644 index 0000000..ffbd900 --- /dev/null +++ b/docs/design/2026-09-08-mode-stream.md @@ -0,0 +1,245 @@ +# Stream type (`ModeStream`) + +**Author:** SuperCache +**Status:** approved (chat: continue with coding now) +**Branch (later):** `feat/mode-stream` +**Date:** 2026-09-08 + +## Overview + +SuperCache has an ordered **list** (`ModeList`: push/pop at the ends, no stable ids). It cannot store an **append-only log** you address by id: “add this event, then read everything after `1710000000000-0`.” + +`ModeStream` is a Redis-Streams-like **named append-only log**. Each entry has a monotonic id (`millis-seq`) and a single `[]byte` payload (the client owns the encoding). v1 is owner-serialized, **snapshot fan-out**, feature-folder layout (`pkg/stream` + `pkg/stream/eng`). It is **not** consumer groups, not blocking `XREAD`, and not RESP wire parity. + +This is new product behavior on the data path. Full path in [WORKFLOW.md](../WORKFLOW.md). **No tests or implementation until this draft is approved** (`looks good` / `do it` / `implement`). + +## Problem + +Closest existing types fail the job: + +| Mode | Why it is not a stream | +|------|------------------------| +| `ModeList` | No ids. `LPop` **removes**. `LRange` is by index, which shifts. Two writers `RPush` is fine, but you cannot resume from a cursor. | +| `ModeZSet` | Score is a caller-written float. Not an append log; `ZRem` deletes; ties by member bytes. | +| `ModeJSON` / CacheOnly `Put` | Whole-blob LWW. Concurrent appends clobber. | +| `ModeHash` | Field map, no order, no range-after-id. | + +There is no verb that appends an event and returns a stable id, then ranges by id. Callers today either misuse List as a queue or keep Redis Streams outside SuperCache. + +Dedicated use: small in-process event / audit / “last N messages” cache — RAM-only, owner-serialized, RF copies of one blob. Not a Kafka. + +## Non-goals + +- Consumer groups (`XGROUP`, `XREADGROUP`, `XACK`, PEL, `XCLAIM`). +- Blocking `XREAD` / `XREAD BLOCK`. +- `XADD` with a **caller-chosen** id (v1 is `*` only). No `MINID`, no `NOMKSTREAM`. +- Multi-stream `XREAD`, `XINFO`, `XREVRANGE` extras beyond the table, `XAUTOCLAIM`. +- Per-entry TTL, persistence / WAL, Redis dump import. +- Changing RF, tombstones, hint identity `(ks, name)`, or any existing mode contract. +- New scbench YAML cells. +- A Lab widget or `examples/stream` walkthrough in this PR (follow-up). `cmd/sc` stream verbs **are** in this PR. +- Mixed-version mesh (same as CMS / VectorSet). + +## Contract + +### API (new; existing verbs unchanged) + +On `pkg/engine.Engine`, `pkg/client.Client`, Cache proto, and `cmd/sc` (same PR): + +| Call | Meaning | +|------|---------| +| `XAdd(ks, name, payload []byte) (id string, err error)` | Append. Creates the stream if missing. Returns the minted id. | +| `XRange(ks, name, start, end string, count int) ([]StreamEntry, error)` | Inclusive id window, oldest first. Missing → empty, nil error. | +| `XRevRange(ks, name, start, end string, count int) ([]StreamEntry, error)` | Inclusive, newest first. | +| `XLen(ks, name) (n int, present bool, err error)` | Entry count. Missing → `present=false`, `n=0`. | +| `XDel(ks, name, id string) error` | Remove one id if present. No-op if missing. ACK-only. | +| `XTrim(ks, name, maxLen int) error` | Keep the **newest** `maxLen` entries. `maxLen < 0` invalid. ACK-only. | +| `Delete(ks, name)` | Existing Delete: whole-stream tombstone. | + +```go +type StreamEntry struct { + ID string // "millis-seq", e.g. "1710000000000-0" + Payload []byte // opaque; client encodes JSON / proto / raw +} +``` + +- `name` is the stream identity; ring owner = `Owner(name)`. +- `payload` is opaque `[]byte`. Empty payload is **allowed** (present empty). Size checked against `MaxValueSize`. The engine does not parse it. +- `start` / `end`: Redis-ish `-` (min) and `+` (max). Bare millis (`1710000000000`) means that ms, seq 0 (start) or max seq (end). Invalid id syntax → `ErrInvalidArgument`. +- **Exclusive start:** a leading `(` on `start` (e.g. `(1710000000000-0`) skips that id — “everything after this cursor.” No separate `XRead` verb. `end` is always inclusive. `(` on `end` is invalid. +- `count <= 0` means **no cap**. `count > 512` is clamped to **512**. +- Get/Put on `ModeStream` → `ErrInvalidArgument`. Stream verbs on any other mode → `ErrInvalidArgument`. +- **Empty after last `XDel` / `XTrim 0`:** live empty stream (`XLen` 0, `present=true`) until `Delete(name)`. +- **XAdd returns the id** (unlike ACK-only `BitSet` / `HLLAdd`). Needed so a reader can resume. + +### IDs + +Owner-only mint under the store mutex (Redis `*`): + +- `id = fmt.Sprintf("%d-%d", ms, seq)` +- `ms` = `time.Now().UnixMilli()`. If `ms < lastMs`, keep `lastMs` and bump `seq` (clock went backward). +- If `ms == lastMs`, `seq = lastSeq+1`. Else `seq = 0`. + +v1 does **not** accept a client-supplied id. Resume: `XRange(ks, name, "("+lastID, "+", n)`. Inclusive start is the default (no `(`). + +### Keyspace + +New mode `keyspace.ModeStream`. + +| Field | Role | +|-------|------| +| `MaxBytes` | LRU store budget. Live `FlagStream` is **not** evicted (`lruVictim` skip, same as HLL/CMS/VectorSet). | +| `TTL` | Whole stream | +| `NegativeTTL` | Ignored | +| `TombstoneTTL` | On `Delete(name)` | +| `ReplicationFactor` | Same as other modes | +| `StreamMaxLen` | **0** = no auto-trim. `>0` = after each `XAdd`, drop oldest until `len <= StreamMaxLen`. Cap **4096**. | + +Hard cap **4096** entries even if `StreamMaxLen` is 0 (MaxBytes still applies). Over cap → `XAdd` is `ErrInvalidArgument` unless auto-trim can make room. + +### Replication + +Same story as List: append/trim/del **do not commute**. Item-level fan-out under `(ks, name)` hint coalesce would drop an earlier `XAdd`. + +| Hop | Payload | +|-----|---------| +| Client → any node → **owner** | inbox (`A` add / `D` del-id / `T` trim) | +| Owner → RF−1 | **full snapshot** (`S`) | +| Handoff | snapshot if version ≥ local | + +Reads: replica with a local stream answers `XRange` / `XLen` **locally** (may lag). Missing name on a replica → owner `GetOrLoad` snapshot; install only if this node `holdsReplica`. Non-replica does not install. + +**XAdd from a non-owner** must return the id. `ApplyPut` is ACK-only. Locked: new Peer RPC `StreamAdd` (same job as `CounterIncr` / `ListPop`). `XDel` / `XTrim` stay inbox `ApplyPut` (ACK-only). + +### Flags — `uint32` is full + +`FlagVectorSet` is `1<<31`. There is **no** free bit on `store.Entry.Flags uint32`. + +Locked: + +- `Entry.Flags` becomes **`uint64`**. +- Peer proto `Entry.flags` field 4 becomes **`uint64`** (proto3 varint; values `< 2^32` still decode on old binaries, but mixed-version mesh is **not** supported). +- `FlagStream uint64 = 1 << 32` +- Inbox vs snapshot: **`Value[0]`** like VectorSet — do **not** add `FlagStreamAdd`. + +| `Value[0]` | Meaning | Replicas | +|------------|---------|----------| +| `S` | full snapshot | install LWW | +| `A` | owner-inbox XAdd (raw payload) | **ignore** | +| `D` | owner-inbox XDel (id bytes) | **ignore** | +| `T` | owner-inbox XTrim (uvarint maxLen) | **ignore** | + +`IsStream() bool` = `Flags&FlagStream != 0`. + +This is the one-time Flags widening. Future modes use `1<<33` etc. + +### Present-bit + +Missing name ≠ empty range that looks like “no events, but the stream exists.” `XLen` carries `present`. `XRange` on a missing name is empty + nil error (same as `VSim`). Callers that need present use `XLen`. + +## Approach + +Feature folders (same as VectorSet / List): + +``` +pkg/stream/ # codec + in-memory log + codec.go + codec_test.go + eng/ # owner write, GetOrLoad, StreamAdd + host.go + local.go + cluster.go +pkg/store/stream.go # Memory XAdd / XRange / … under mutex +pkg/engine/stream.go # thin public verbs +``` + +Codec (snapshot `S`): + +``` +'S' | uvarint lastMs | uvarint lastSeq | uvarint n + | repeated { uvarint ms | uvarint seq | uvarint plen | payload } +``` + +In-place mutate under the store mutex (HLL/CMS/VectorSet class): decode → append/trim → encode → `Flags=FlagStream`, bump version. **No** dirty cache. + +Owner `XAdd`: mint id → append → optional `StreamMaxLen` trim → snapshot fan-out → return id. + +`sc`: + +```text +sc -keyspace events xadd logs * '{"msg":"hello"}' +sc -keyspace events xlen logs +sc -keyspace events xrange logs - + 10 +sc -keyspace events xrange logs '(1710000000000-0' + 10 +sc -keyspace events xrevrange logs - + 10 +sc -keyspace events xdel logs 1710000000000-0 +sc -keyspace events xtrim logs 100 +``` + +`*` is required in the `xadd` argv (documents “auto id”). No `examples/stream` in this PR. + +Docs in the same PR: `docs/API.md`, PLAN §7 row, `docs/design/README.md` shipped table, OpenAPI cache ref. + +### Rejected alternatives + +| Idea | Why not | +|------|---------| +| Reuse `ModeList` + document “don’t pop” | No ids, index shifts, pop is in the API. | +| Item-level `FlagStreamAdd` fan-out | `hintID` is `(ks, name)` — second add drops the first. Same as List. | +| New `FlagStreamAdd` bits on uint32 | **No bits left.** | +| Consumer groups in v1 | Separate product (PEL, ack, claim). Not a cache-shaped first cut. | +| ACK-only `XAdd` (no returned id) | Callers cannot resume. Redis `XADD` returns the id. | +| Caller-chosen ids | Ordering / clock fights; owner mint is enough. | +| Lab widget / `examples/stream` in this PR | Second feature. | +| Redis field/value pairs per entry | Client has less control; they encode JSON/proto themselves. | + +## Tests (write these first, after approval) + +| Test | Package | Asserts | +|------|---------|---------| +| `TestStreamXAddRangeLen` | `pkg/stream` | XAdd two entries; XLen 2; XRange `-` `+` both, order oldest-first; ids `ms-seq` | +| `TestStreamXAddSameMilliSeq` | `pkg/stream` | Two adds in the same ms → `…-0` then `…-1` | +| `TestStreamXRevRangeAndCount` | `pkg/stream` | Newest first; `count=1` is the newest | +| `TestStreamXDelAndEmptyUntilDelete` | `pkg/stream` | XDel one; XLen 0 + present after last del; Delete → present=false | +| `TestStreamXTrimMaxLen` | `pkg/stream` | Trim 1 keeps newest | +| `TestStreamAutoTrim` | `pkg/stream` | `StreamMaxLen=2`; third XAdd drops oldest | +| `TestStreamWrongMode` | `pkg/stream` | Get/Put on ModeStream and XAdd on CacheOnly → invalid argument | +| `TestStreamExclusiveStart` | `pkg/stream` | `XRange` start `(id` skips that id | +| `TestStreamClusterRF` | `pkg/stream` | RF=2; every node XRange same order; non-replica does not `HasLocal` | +| `TestStreamAddReturnsIdFromNonOwner` | `pkg/stream` | XAdd via non-owner returns the same id the owner would mint; replica fills | +| `TestFlagStreamUint64Wire` | `pkg/store` / peer | ApplyPut with `FlagStream` (bit 32) installs; `IsStream` true | +| `TestStreamNotEvicted` | `pkg/store` | MaxBytes=1 still keeps live FlagStream | + +`cmd/sc` stream CLI tests like `hash_cli_test.go`. + +## Bench risk + +- **Hot path?** Get-hit / StoreGetHit: **no** if Stream is not on those benches. +- Flags `uint32`→`uint64` widens `store.Entry` by 4 bytes. **Could** move StoreGetHit ns/op; **must not** raise Get-hit / StoreGetHit **allocs/op**. +- No new scbench cells. +- Gate: shared smoke ±10%; Get-hit allocs flat. + +## Key Decisions + +1. **Append-only log with Redis-like ids and one `[]byte` payload** — client owns the encoding. +2. **Snapshot fan-out** — appends do not commute under hint coalesce. +3. **`XAdd` returns the id** — new Peer `StreamAdd`. +4. **`Flags uint64` + `FlagStream = 1<<32` + `Value[0]` inbox** — uint32 is full. +5. **No consumer groups / blocking read in v1.** +6. **Feature folders + `sc` in one PR.** No example binary. + +## Open Questions + +None. Closed in chat: exclusive start is leading `(`; payload is a single `[]byte`. + +## PR Plan + +One design → one PR. + +### PR 1 — `feat: ModeStream append-only log` + +- **Title:** feat: ModeStream (Redis-like append-only log) +- **Branch:** `feat/mode-stream` +- **Files:** `pkg/store` Flags uint64 + stream methods; `api/proto` cache + peer; `pkg/stream` + `eng`; `pkg/engine/stream.go`; `pkg/client`; `cmd/sc`; API/PLAN/OpenAPI; this design status → approved. +- **Dependencies:** none +- **Description:** New mode only. No Get/Put/RF default change beyond Flags width. diff --git a/docs/design/README.md b/docs/design/README.md index a1f6b8f..ea40206 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -34,6 +34,7 @@ Do not implement from a draft. | [2026-09-02-refactor-geo.md](./2026-09-02-refactor-geo.md) | ModeGeo file layout (no contract) | | [2026-09-02-feature-folders.md](./2026-09-02-feature-folders.md) | Feature folders (List first) | | [2026-09-04-mode-vector-set.md](./2026-09-04-mode-vector-set.md) | `ModeVectorSet` | +| [2026-09-08-mode-stream.md](./2026-09-08-mode-stream.md) | `ModeStream` | | [2026-08-25-list-counter-version.md](./2026-08-25-list-counter-version.md) | List/Counter snapshot version | | [2026-08-13-unify-grpc-error-map.md](./2026-08-13-unify-grpc-error-map.md) | grpcmap | diff --git a/examples/lab/http.go b/examples/lab/http.go index 0a69698..8a32f37 100644 --- a/examples/lab/http.go +++ b/examples/lab/http.go @@ -286,7 +286,7 @@ func (l *Lab) snapshot(ks, key string) map[string]any { } out := make([]map[string]any, 0, len(nodes)) for _, n := range nodes { - kind, ver, flags, bytes := "missing", uint64(0), uint32(0), 0 + kind, ver, flags, bytes := "missing", uint64(0), uint64(0), 0 role := "other" if n.Engine != nil { v := n.Engine.LocalView(ks, key) diff --git a/internal/cacheserver/stream.go b/internal/cacheserver/stream.go new file mode 100644 index 0000000..cb2186a --- /dev/null +++ b/internal/cacheserver/stream.go @@ -0,0 +1,63 @@ +package cacheserver + +import ( + "context" + + cachev1 "github.com/Code0987/supercache/api/gen/cache/v1" + "github.com/Code0987/supercache/internal/grpcmap" + "github.com/Code0987/supercache/pkg/engine" +) + +func (s *Server) XAdd(ctx context.Context, req *cachev1.XAddRequest) (*cachev1.XAddResponse, error) { + id, err := s.eng.XAdd(ctx, req.GetKeyspace(), req.GetName(), req.GetPayload()) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XAddResponse{Id: id}, nil +} + +func (s *Server) XRange(ctx context.Context, req *cachev1.XRangeRequest) (*cachev1.XRangeResponse, error) { + rows, err := s.eng.XRange(ctx, req.GetKeyspace(), req.GetName(), req.GetStart(), req.GetEnd(), int(req.GetCount())) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XRangeResponse{Entries: streamEntries(rows)}, nil +} + +func (s *Server) XRevRange(ctx context.Context, req *cachev1.XRevRangeRequest) (*cachev1.XRangeResponse, error) { + rows, err := s.eng.XRevRange(ctx, req.GetKeyspace(), req.GetName(), req.GetStart(), req.GetEnd(), int(req.GetCount())) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XRangeResponse{Entries: streamEntries(rows)}, nil +} + +func (s *Server) XLen(ctx context.Context, req *cachev1.XLenRequest) (*cachev1.XLenResponse, error) { + n, ok, err := s.eng.XLen(ctx, req.GetKeyspace(), req.GetName()) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XLenResponse{N: int64(n), Present: ok}, nil +} + +func (s *Server) XDel(ctx context.Context, req *cachev1.XDelRequest) (*cachev1.XDelResponse, error) { + if err := s.eng.XDel(ctx, req.GetKeyspace(), req.GetName(), req.GetId()); err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XDelResponse{}, nil +} + +func (s *Server) XTrim(ctx context.Context, req *cachev1.XTrimRequest) (*cachev1.XTrimResponse, error) { + if err := s.eng.XTrim(ctx, req.GetKeyspace(), req.GetName(), int(req.GetMaxLen())); err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.XTrimResponse{}, nil +} + +func streamEntries(rows []engine.StreamEntry) []*cachev1.StreamEntry { + out := make([]*cachev1.StreamEntry, len(rows)) + for i, r := range rows { + out[i] = &cachev1.StreamEntry{Id: r.ID, Payload: r.Payload} + } + return out +} diff --git a/internal/peer/transport.go b/internal/peer/transport.go index 94498bb..a09b340 100644 --- a/internal/peer/transport.go +++ b/internal/peer/transport.go @@ -195,6 +195,21 @@ func (t *Transport) CounterIncr(ctx context.Context, addr, keyspace, name string return resp.GetValue(), nil } +// StreamAdd asks the owner to XAdd and returns the minted id. +func (t *Transport) StreamAdd(ctx context.Context, addr, keyspace, name string, payload []byte) (string, error) { + cli, err := t.client(addr) + if err != nil { + return "", err + } + ctx, cancel := t.rpcContext(ctx) + defer cancel() + resp, err := cli.StreamAdd(ctx, &peerv1.StreamAddRequest{Keyspace: keyspace, Name: name, Payload: payload}) + if err != nil { + return "", err + } + return resp.GetId(), nil +} + // FanoutConfig controls async ApplyPut fan-out. type FanoutConfig struct { Workers int diff --git a/internal/peerserver/server.go b/internal/peerserver/server.go index ab8e28f..a2905e0 100644 --- a/internal/peerserver/server.go +++ b/internal/peerserver/server.go @@ -140,6 +140,14 @@ func (s *Server) CounterIncr(ctx context.Context, req *peerv1.CounterIncrRequest return &peerv1.CounterIncrResponse{Value: n}, nil } +func (s *Server) StreamAdd(ctx context.Context, req *peerv1.StreamAddRequest) (*peerv1.StreamAddResponse, error) { + id, err := s.eng.XAdd(ctx, req.Keyspace, req.Name, req.Payload) + if err != nil { + return nil, grpcmap.Status(err) + } + return &peerv1.StreamAddResponse{Id: id}, nil +} + // ListenAndServe starts a gRPC server on addr (e.g. ":9001"). // Pass grpc.Creds(credentials.NewTLS(cfg)) for TLS/mTLS; omit for plaintext (dev only). func ListenAndServe(addr string, eng *engine.Engine, opts ...grpc.ServerOption) (*grpc.Server, net.Listener, error) { diff --git a/internal/testcluster/cluster.go b/internal/testcluster/cluster.go index 1ba0bdf..3ea63b0 100644 --- a/internal/testcluster/cluster.go +++ b/internal/testcluster/cluster.go @@ -263,8 +263,13 @@ func (c *Cluster) ready() error { } // ModeVectorSet rejects Get; missing name is present=false, nil error. _, _, verr := cli.VCard(ctx, ks, "__testcluster_ready__") - _ = cli.Close() if verr == nil { + _ = cli.Close() + continue + } + _, _, serr2 := cli.XLen(ctx, ks, "__testcluster_ready__") + _ = cli.Close() + if serr2 == nil { continue } return fmt.Errorf("testcluster: ready get %s: %w", n.ID, err) diff --git a/pkg/client/stream.go b/pkg/client/stream.go new file mode 100644 index 0000000..8ea25fd --- /dev/null +++ b/pkg/client/stream.go @@ -0,0 +1,89 @@ +package client + +import ( + "context" + "fmt" + "math" + + cachev1 "github.com/Code0987/supercache/api/gen/cache/v1" + "github.com/Code0987/supercache/pkg/engine" +) + +func (c *Client) XAdd(ctx context.Context, keyspace, name string, payload []byte) (string, error) { + resp, err := c.api.XAdd(ctx, &cachev1.XAddRequest{Keyspace: keyspace, Name: name, Payload: payload}) + if err != nil { + return "", err + } + return resp.GetId(), nil +} + +func (c *Client) XRange(ctx context.Context, keyspace, name, start, end string, count int) ([]engine.StreamEntry, error) { + n, err := int32Arg(count, "count") + if err != nil { + return nil, err + } + resp, err := c.api.XRange(ctx, &cachev1.XRangeRequest{ + Keyspace: keyspace, Name: name, Start: start, End: end, Count: n, + }) + if err != nil { + return nil, err + } + return clientStream(resp.GetEntries()), nil +} + +func (c *Client) XRevRange(ctx context.Context, keyspace, name, start, end string, count int) ([]engine.StreamEntry, error) { + n, err := int32Arg(count, "count") + if err != nil { + return nil, err + } + resp, err := c.api.XRevRange(ctx, &cachev1.XRevRangeRequest{ + Keyspace: keyspace, Name: name, Start: start, End: end, Count: n, + }) + if err != nil { + return nil, err + } + return clientStream(resp.GetEntries()), nil +} + +func (c *Client) XLen(ctx context.Context, keyspace, name string) (int, bool, error) { + resp, err := c.api.XLen(ctx, &cachev1.XLenRequest{Keyspace: keyspace, Name: name}) + if err != nil { + return 0, false, err + } + if !resp.GetPresent() { + return 0, false, nil + } + return int(resp.GetN()), true, nil +} + +func (c *Client) XDel(ctx context.Context, keyspace, name, id string) error { + _, err := c.api.XDel(ctx, &cachev1.XDelRequest{Keyspace: keyspace, Name: name, Id: id}) + return err +} + +func (c *Client) XTrim(ctx context.Context, keyspace, name string, maxLen int) error { + if maxLen < 0 { + return fmt.Errorf("%w: xtrim max_len", engine.ErrInvalidArgument) + } + n, err := int32Arg(maxLen, "max_len") + if err != nil { + return err + } + _, err = c.api.XTrim(ctx, &cachev1.XTrimRequest{Keyspace: keyspace, Name: name, MaxLen: n}) + return err +} + +func int32Arg(n int, what string) (int32, error) { + if n > math.MaxInt32 { + return 0, fmt.Errorf("%w: %s", engine.ErrInvalidArgument, what) + } + return int32(n), nil +} + +func clientStream(in []*cachev1.StreamEntry) []engine.StreamEntry { + out := make([]engine.StreamEntry, len(in)) + for i, e := range in { + out[i] = engine.StreamEntry{ID: e.GetId(), Payload: e.GetPayload()} + } + return out +} diff --git a/pkg/client/stream_test.go b/pkg/client/stream_test.go new file mode 100644 index 0000000..e1e0162 --- /dev/null +++ b/pkg/client/stream_test.go @@ -0,0 +1,32 @@ +package client + +import ( + "context" + "errors" + "math" + "testing" + + "github.com/Code0987/supercache/pkg/engine" +) + +func TestXTrimRejectsOverflow(t *testing.T) { + c := &Client{} + ctx := context.Background() + if err := c.XTrim(ctx, "events", "logs", 1<<32); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("1<<32: %v", err) + } + if err := c.XTrim(ctx, "events", "logs", -1); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("neg: %v", err) + } +} + +func TestXRangeRejectsCountOverflow(t *testing.T) { + c := &Client{} + ctx := context.Background() + if _, err := c.XRange(ctx, "events", "logs", "-", "+", math.MaxInt32+1); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("xrange: %v", err) + } + if _, err := c.XRevRange(ctx, "events", "logs", "-", "+", math.MaxInt32+1); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("xrevrange: %v", err) + } +} diff --git a/pkg/engine/cluster.go b/pkg/engine/cluster.go index 6a5b7ad..afa5dfc 100644 --- a/pkg/engine/cluster.go +++ b/pkg/engine/cluster.go @@ -400,6 +400,13 @@ func (e *Engine) GetOrLoadLocal(ctx context.Context, keyspaceName, key string) ( } return ent, nil } + if ks.cfg.Mode == keyspace.ModeStream { + ent, ok := ks.store.Peek(key) + if !ok || ent.IsTombstone() || !ent.IsStream() { + return store.Entry{}, ErrNotFound + } + return ent, nil + } if ent, ok := ks.store.Get(key); ok { if ent.IsNegative() { diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 8e25128..9424228 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -284,6 +284,9 @@ func (e *Engine) Get(ctx context.Context, keyspaceName, key string) ([]byte, err if ks.cfg.Mode == keyspace.ModeVectorSet { return nil, fmt.Errorf("%w: use VEmb", ErrInvalidArgument) } + if ks.cfg.Mode == keyspace.ModeStream { + return nil, fmt.Errorf("%w: use XRange", ErrInvalidArgument) + } if ent, ok := ks.store.Get(key); ok { if ent.IsNegative() { @@ -509,6 +512,9 @@ func (e *Engine) Put(ctx context.Context, keyspaceName, key string, value []byte if ks.cfg.Mode == keyspace.ModeVectorSet { return fmt.Errorf("%w: use VAdd", ErrInvalidArgument) } + if ks.cfg.Mode == keyspace.ModeStream { + return fmt.Errorf("%w: use XAdd", ErrInvalidArgument) + } } return e.putViaCluster(ctx, keyspaceName, key, value, opts...) } @@ -773,6 +779,9 @@ func (e *Engine) ApplyPutWithRingGen(keyspaceName, key string, ent store.Entry, if ent.IsVectorSet() { return e.applyVectorSet(ks, key, ent), nil } + if ent.IsStream() { + return e.applyStream(ks, key, ent), nil + } if ent.IsNegative() { // Negatives must not clobber live positives (AcceptNegative). ok := ks.store.AcceptNegative(key, ent) diff --git a/pkg/engine/geo_cluster.go b/pkg/engine/geo_cluster.go index 54b0ff8..8537243 100644 --- a/pkg/engine/geo_cluster.go +++ b/pkg/engine/geo_cluster.go @@ -6,7 +6,7 @@ import ( "github.com/Code0987/supercache/pkg/store" ) -func (e *Engine) gMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint32) error { +func (e *Engine) gMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint64) error { c := e.clusterSnapshot() owner, _ := c.Ring.Owner(name) ent := store.Entry{Value: value, Flags: flag, Version: 1} diff --git a/pkg/engine/hash_cluster.go b/pkg/engine/hash_cluster.go index 0b60bb7..f785d81 100644 --- a/pkg/engine/hash_cluster.go +++ b/pkg/engine/hash_cluster.go @@ -6,7 +6,7 @@ import ( "github.com/Code0987/supercache/pkg/store" ) -func (e *Engine) hMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint32) error { +func (e *Engine) hMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint64) error { c := e.clusterSnapshot() owner, _ := c.Ring.Owner(name) ent := store.Entry{Value: value, Flags: flag, Version: 1} diff --git a/pkg/engine/json_cluster.go b/pkg/engine/json_cluster.go index 5fff06b..a37790a 100644 --- a/pkg/engine/json_cluster.go +++ b/pkg/engine/json_cluster.go @@ -8,7 +8,7 @@ import ( "github.com/Code0987/supercache/pkg/store" ) -func (e *Engine) jMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint32) error { +func (e *Engine) jMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint64) error { c := e.clusterSnapshot() owner, _ := c.Ring.Owner(name) ent := store.Entry{Value: value, Flags: flag, Version: 1} diff --git a/pkg/engine/set_cluster.go b/pkg/engine/set_cluster.go index ab5665e..bd196a6 100644 --- a/pkg/engine/set_cluster.go +++ b/pkg/engine/set_cluster.go @@ -6,7 +6,7 @@ import ( "github.com/Code0987/supercache/pkg/store" ) -func (e *Engine) setMutViaOwner(ctx context.Context, ks *ksRuntime, name string, item []byte, flag uint32) error { +func (e *Engine) setMutViaOwner(ctx context.Context, ks *ksRuntime, name string, item []byte, flag uint64) error { c := e.clusterSnapshot() owner, _ := c.Ring.Owner(name) ent := store.Entry{Value: append([]byte(nil), item...), Flags: flag, Version: 1} diff --git a/pkg/engine/status.go b/pkg/engine/status.go index 5750961..e0a547b 100644 --- a/pkg/engine/status.go +++ b/pkg/engine/status.go @@ -38,7 +38,7 @@ func (k LocalKind) MarshalJSON() ([]byte, error) { type LocalView struct { Kind LocalKind `json:"kind"` Version uint64 `json:"version"` - Flags uint32 `json:"flags"` + Flags uint64 `json:"flags"` Bytes int `json:"bytes"` } diff --git a/pkg/engine/stream.go b/pkg/engine/stream.go new file mode 100644 index 0000000..09786e6 --- /dev/null +++ b/pkg/engine/stream.go @@ -0,0 +1,272 @@ +package engine + +import ( + "context" + "fmt" + + "github.com/Code0987/supercache/pkg/keyspace" + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/stream" + streng "github.com/Code0987/supercache/pkg/stream/eng" +) + +// StreamEntry is one XRange row (clients must not import pkg/stream). +type StreamEntry struct { + ID string + Payload []byte +} + +func (e *Engine) sxNeed(ks *ksRuntime, name string) error { + if err := e.validateKey(ks.cfg.Name, name); err != nil { + return err + } + if ks.cfg.Mode != keyspace.ModeStream { + return fmt.Errorf("%w: stream op requires ModeStream", ErrInvalidArgument) + } + return e.validateKeyLen(ks, name) +} + +func (h modeHost) StreamMaxLen() int { return h.ks.cfg.EffectiveStreamMaxLen() } + +func (h modeHost) HasStream(name string) bool { return h.ks.store.HasStream(name) } + +func (h modeHost) StreamAddRemote(ctx context.Context, addr, keyspace, name string, payload []byte) (string, error) { + c := h.e.clusterSnapshot() + if c == nil || c.Transport == nil { + return "", fmt.Errorf("%w: no transport", ErrUnavailable) + } + return c.Transport.StreamAdd(ctx, addr, keyspace, name, payload) +} + +func (e *Engine) XAdd(ctx context.Context, keyspaceName, name string, payload []byte) (string, error) { + if err := ctx.Err(); err != nil { + return "", err + } + if err := e.validateKey(keyspaceName, name); err != nil { + return "", err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return "", err + } + if err := e.sxNeed(ks, name); err != nil { + return "", err + } + if err := e.validateValueSize(ks, payload); err != nil { + return "", err + } + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + if owner, ok := c.Ring.Owner(name); ok && owner.ID != "" && owner.ID != c.SelfID { + if c.Transport == nil || owner.Addr == "" { + return "", fmt.Errorf("%w: owner %s has no address", ErrUnavailable, owner.ID) + } + pctx, cancel := e.peerCtx(ctx, ks) + defer cancel() + return c.Transport.StreamAdd(pctx, owner.Addr, ks.cfg.Name, name, payload) + } + } + id, err := streng.AddLocal(e.modeHost(ks), name, payload) + if err == streng.ErrTooLarge { + return "", ErrValueTooLarge + } + if err == streng.ErrFull { + return "", fmt.Errorf("%w: stream full", ErrInvalidArgument) + } + if err != nil { + return "", fmt.Errorf("%w: xadd rejected", ErrInvalidArgument) + } + return id, nil +} + +func (e *Engine) XDel(ctx context.Context, keyspaceName, name, id string) error { + if err := ctx.Err(); err != nil { + return err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return err + } + if err := e.sxNeed(ks, name); err != nil { + return err + } + if _, _, perr := stream.ParseID(id); perr != nil { + return fmt.Errorf("%w: bad stream id", ErrInvalidArgument) + } + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + if owner, ok := c.Ring.Owner(name); ok && owner.ID != "" && owner.ID != c.SelfID { + if c.Transport == nil || owner.Addr == "" { + return fmt.Errorf("%w: owner %s has no address", ErrUnavailable, owner.ID) + } + return e.sxInboxViaOwner(ctx, ks, name, stream.EncodeInboxDel(id)) + } + } + if err := streng.DelLocal(e.modeHost(ks), name, id); err == streng.ErrBadID { + return fmt.Errorf("%w: bad stream id", ErrInvalidArgument) + } else if err != nil { + return fmt.Errorf("%w: xdel rejected", ErrInvalidArgument) + } + return nil +} + +func (e *Engine) XTrim(ctx context.Context, keyspaceName, name string, maxLen int) error { + if err := ctx.Err(); err != nil { + return err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return err + } + if err := e.sxNeed(ks, name); err != nil { + return err + } + if maxLen < 0 { + return fmt.Errorf("%w: stream trim", ErrInvalidArgument) + } + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + if owner, ok := c.Ring.Owner(name); ok && owner.ID != "" && owner.ID != c.SelfID { + if c.Transport == nil || owner.Addr == "" { + return fmt.Errorf("%w: owner %s has no address", ErrUnavailable, owner.ID) + } + return e.sxInboxViaOwner(ctx, ks, name, stream.EncodeInboxTrim(maxLen)) + } + } + if err := streng.TrimLocal(e.modeHost(ks), name, maxLen); err != nil { + return fmt.Errorf("%w: xtrim rejected", ErrInvalidArgument) + } + return nil +} + +func (e *Engine) sxInboxViaOwner(ctx context.Context, ks *ksRuntime, name string, blob []byte) error { + c := e.clusterSnapshot() + owner, _ := c.Ring.Owner(name) + pctx, cancel := e.peerCtx(ctx, ks) + defer cancel() + ent := store.Entry{Value: blob, Flags: store.FlagStream} + applied, err := c.Transport.ApplyPut(pctx, owner.Addr, ks.cfg.Name, name, ent, c.Ring.Generation()) + if err != nil { + return err + } + if !applied { + return fmt.Errorf("%w: stream mutate rejected", ErrInvalidArgument) + } + return nil +} + +func (e *Engine) XRange(ctx context.Context, keyspaceName, name, start, end string, count int) ([]StreamEntry, error) { + return e.sxRange(ctx, keyspaceName, name, start, end, count, false) +} + +func (e *Engine) XRevRange(ctx context.Context, keyspaceName, name, start, end string, count int) ([]StreamEntry, error) { + return e.sxRange(ctx, keyspaceName, name, start, end, count, true) +} + +func (e *Engine) sxRange(ctx context.Context, keyspaceName, name, start, end string, count int, rev bool) ([]StreamEntry, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return nil, err + } + if err := e.sxNeed(ks, name); err != nil { + return nil, err + } + if _, err := stream.ParseBound(start, true); err != nil { + return nil, fmt.Errorf("%w: bad stream id", ErrInvalidArgument) + } + if _, err := stream.ParseBound(end, false); err != nil { + return nil, fmt.Errorf("%w: bad stream id", ErrInvalidArgument) + } + rows, ok, err := ks.store.XRange(name, start, end, count, rev) + if err != nil { + return nil, fmt.Errorf("%w: bad stream id", ErrInvalidArgument) + } + if ok { + return copyStream(rows), nil + } + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + ent, found, ferr := streng.FetchOwner(ctx, e.modeHost(ks), name) + if ferr != nil { + return nil, ferr + } + if found { + lg, decErr := stream.DecodeSnapshot(ent.Value) + if decErr != nil { + return nil, nil + } + sb, _ := stream.ParseBound(start, true) + eb, _ := stream.ParseBound(end, false) + raw := lg.Range(sb, eb, count, rev) + out := make([]StreamEntry, len(raw)) + for i, r := range raw { + out[i] = StreamEntry{ID: r.ID(), Payload: r.Payload} + } + return out, nil + } + } + return nil, nil +} + +func (e *Engine) XLen(ctx context.Context, keyspaceName, name string) (int, bool, error) { + if err := ctx.Err(); err != nil { + return 0, false, err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return 0, false, err + } + if err := e.sxNeed(ks, name); err != nil { + return 0, false, err + } + n, ok := ks.store.XLen(name) + if ok { + return n, true, nil + } + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + ent, found, ferr := streng.FetchOwner(ctx, e.modeHost(ks), name) + if ferr != nil { + return 0, false, ferr + } + if found { + lg, decErr := stream.DecodeSnapshot(ent.Value) + if decErr != nil { + return 0, false, nil + } + return len(lg.Entries), true, nil + } + } + return 0, false, nil +} + +func (e *Engine) applyStream(ks *ksRuntime, name string, ent store.Entry) bool { + if len(ent.Value) == 0 { + return false + } + switch ent.Value[0] { + case stream.PrefixAdd, stream.PrefixDel, stream.PrefixTrim: + c := e.clusterSnapshot() + if c != nil && c.Ring != nil { + if owner, ok := c.Ring.Owner(name); ok && owner.ID != "" && owner.ID != c.SelfID { + return false + } + } + return streng.ApplyInbox(e.modeHost(ks), name, ent.Value, ent.ExpireAt) + case stream.PrefixSnapshot: + return streng.ApplyInstall(e.modeHost(ks), name, ent.Value, ent.Version, ent.ExpireAt) + default: + return false + } +} + +func copyStream(in []store.StreamEntry) []StreamEntry { + out := make([]StreamEntry, len(in)) + for i, e := range in { + out[i] = StreamEntry{ID: e.ID, Payload: e.Payload} + } + return out +} diff --git a/pkg/engine/zset_cluster.go b/pkg/engine/zset_cluster.go index 4fe8788..0e57780 100644 --- a/pkg/engine/zset_cluster.go +++ b/pkg/engine/zset_cluster.go @@ -6,7 +6,7 @@ import ( "github.com/Code0987/supercache/pkg/store" ) -func (e *Engine) zMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint32) error { +func (e *Engine) zMutViaOwner(ctx context.Context, ks *ksRuntime, name string, value []byte, flag uint64) error { c := e.clusterSnapshot() owner, _ := c.Ring.Owner(name) ent := store.Entry{Value: value, Flags: flag, Version: 1} diff --git a/pkg/keyspace/config.go b/pkg/keyspace/config.go index eb51d1d..82bea75 100644 --- a/pkg/keyspace/config.go +++ b/pkg/keyspace/config.go @@ -48,6 +48,8 @@ const ( ModeCMS // ModeVectorSet is a named embedding set (VAdd / VSim / VRem / …). ModeVectorSet + // ModeStream is a named append-only log (XAdd / XRange / …). + ModeStream ) func (m Mode) String() string { @@ -82,6 +84,8 @@ func (m Mode) String() string { return "CMS" case ModeVectorSet: return "VectorSet" + case ModeStream: + return "Stream" default: return fmt.Sprintf("Mode(%d)", int(m)) } @@ -107,6 +111,8 @@ const ( DefaultBloomHashes = 7 // DefaultTopKSize is K when Config.TopKSize is 0 (billboard-sized). DefaultTopKSize = 100 + // StreamHardCap is the max entries per name even when StreamMaxLen is 0. + StreamHardCap = 4096 ) // EffectiveReplication returns how many peers should store each key given @@ -182,6 +188,10 @@ type Config struct { VectorDim int // VectorMetric is cosine (0), L2, or IP for ModeVectorSet. VectorMetric VectorMetric + + // StreamMaxLen auto-trims oldest entries after XAdd (0 = no auto-trim). + // Capped at StreamHardCap (4096). + StreamMaxLen int } // VectorMetric is the keyspace K-NN formula. @@ -304,6 +314,17 @@ func (c Config) EffectiveTopKSize() int { return c.TopKSize } +// EffectiveStreamMaxLen is auto-trim after XAdd (0 = none). Values above StreamHardCap clamp. +func (c Config) EffectiveStreamMaxLen() int { + if c.StreamMaxLen <= 0 { + return 0 + } + if c.StreamMaxLen > StreamHardCap { + return StreamHardCap + } + return c.StreamMaxLen +} + // ConfigHash is a stable hash of non-function config fields for drift detection. func (c Config) ConfigHash() string { type wire struct { @@ -330,6 +351,7 @@ func (c Config) ConfigHash() string { TopKSize int VectorDim int VectorMetric int + StreamMaxLen int } b, _ := json.Marshal(wire{ Name: c.Name, @@ -355,6 +377,7 @@ func (c Config) ConfigHash() string { TopKSize: c.TopKSize, VectorDim: c.VectorDim, VectorMetric: int(c.VectorMetric), + StreamMaxLen: c.StreamMaxLen, }) sum := sha256.Sum256(b) return hex.EncodeToString(sum[:8]) diff --git a/pkg/keyspace/config_test.go b/pkg/keyspace/config_test.go index dcaf57c..b158db1 100644 --- a/pkg/keyspace/config_test.go +++ b/pkg/keyspace/config_test.go @@ -102,6 +102,9 @@ func TestModeString(t *testing.T) { if ModeVectorSet.String() != "VectorSet" { t.Fatal(ModeVectorSet.String()) } + if ModeStream.String() != "Stream" { + t.Fatal(ModeStream.String()) + } if Mode(99).String() != "Mode(99)" { t.Fatal(Mode(99).String()) } @@ -218,6 +221,15 @@ func TestConfigHashIncludesVectorFields(t *testing.T) { } } +func TestConfigHashIncludesStreamMaxLen(t *testing.T) { + a := Config{Name: "k", Mode: ModeStream, MaxBytes: 1 << 20, StreamMaxLen: 8} + b := a + b.StreamMaxLen = 16 + if a.ConfigHash() == b.ConfigHash() { + t.Fatal("StreamMaxLen must change hash") + } +} + func TestConfigHashIncludesBloom(t *testing.T) { a := Config{Name: "k", Mode: ModeBloom, MaxBytes: 1 << 20, BloomBits: 1024, BloomHashes: 3} b := a diff --git a/pkg/store/entry.go b/pkg/store/entry.go index 2d6b954..aa7b3c9 100644 --- a/pkg/store/entry.go +++ b/pkg/store/entry.go @@ -4,38 +4,39 @@ import "time" // Flag bits for Entry.Flags. const ( - FlagNegative uint32 = 1 << 0 - FlagTombstone uint32 = 1 << 1 // versioned delete marker (blocks stale ApplyPut) - FlagBloom uint32 = 1 << 2 // value is a Bloom bitset - FlagBloomAdd uint32 = 1 << 3 // fan-out only: value is an item to OR into the filter - FlagSet uint32 = 1 << 4 // value is encoded exact-set membership - FlagSetAdd uint32 = 1 << 5 // fan-out only: value is an item to insert - FlagSetRemove uint32 = 1 << 6 // fan-out only: value is an item to remove - FlagZSet uint32 = 1 << 7 // value is encoded sorted set - FlagZSetAdd uint32 = 1 << 8 // fan-out: single scored member - FlagZSetRem uint32 = 1 << 9 // fan-out: member to remove - FlagGeo uint32 = 1 << 10 // value is encoded geo index - FlagGeoAdd uint32 = 1 << 11 // fan-out: single lon/lat member - FlagGeoRem uint32 = 1 << 12 // fan-out: member to remove - FlagList uint32 = 1 << 13 // value is encoded list - FlagListLPush uint32 = 1 << 14 // owner-inbox: prepend item - FlagListRPush uint32 = 1 << 15 // owner-inbox: append item - FlagHash uint32 = 1 << 16 // value is encoded hash - FlagHashSet uint32 = 1 << 17 // fan-out: single field/value - FlagHashDel uint32 = 1 << 18 // fan-out: field to remove - FlagCounter uint32 = 1 << 19 // value is 8-byte LE int64 snapshot - FlagJSON uint32 = 1 << 20 // value is encoded JSON document snapshot - FlagJSONSet uint32 = 1 << 21 // owner-inbox: path + JSON value - FlagJSONDel uint32 = 1 << 22 // owner-inbox: path - FlagBitmap uint32 = 1 << 23 // value is packed Redis-order bit string snapshot - FlagBitmapSet uint32 = 1 << 24 // owner-inbox: uvarint(offset) + 0/1 - FlagHLL uint32 = 1 << 25 // value is dense 12 KiB register snapshot - FlagHLLAdd uint32 = 1 << 26 // owner-inbox: raw item bytes - FlagTopK uint32 = 1 << 27 // value is encoded Space-Saving snapshot - FlagTopKAdd uint32 = 1 << 28 // owner-inbox only: raw item to Add (replicas ignore) - FlagCMS uint32 = 1 << 29 // value is dense 64 KiB Count-Min snapshot - FlagCMSIncr uint32 = 1 << 30 // owner-inbox only: uvarint(n) || item (replicas ignore) - FlagVectorSet uint32 = 1 << 31 // snapshot or owner-inbox; discriminate by Value[0] + FlagNegative uint64 = 1 << 0 + FlagTombstone uint64 = 1 << 1 // versioned delete marker (blocks stale ApplyPut) + FlagBloom uint64 = 1 << 2 // value is a Bloom bitset + FlagBloomAdd uint64 = 1 << 3 // fan-out only: value is an item to OR into the filter + FlagSet uint64 = 1 << 4 // value is encoded exact-set membership + FlagSetAdd uint64 = 1 << 5 // fan-out only: value is an item to insert + FlagSetRemove uint64 = 1 << 6 // fan-out only: value is an item to remove + FlagZSet uint64 = 1 << 7 // value is encoded sorted set + FlagZSetAdd uint64 = 1 << 8 // fan-out: single scored member + FlagZSetRem uint64 = 1 << 9 // fan-out: member to remove + FlagGeo uint64 = 1 << 10 // value is encoded geo index + FlagGeoAdd uint64 = 1 << 11 // fan-out: single lon/lat member + FlagGeoRem uint64 = 1 << 12 // fan-out: member to remove + FlagList uint64 = 1 << 13 // value is encoded list + FlagListLPush uint64 = 1 << 14 // owner-inbox: prepend item + FlagListRPush uint64 = 1 << 15 // owner-inbox: append item + FlagHash uint64 = 1 << 16 // value is encoded hash + FlagHashSet uint64 = 1 << 17 // fan-out: single field/value + FlagHashDel uint64 = 1 << 18 // fan-out: field to remove + FlagCounter uint64 = 1 << 19 // value is 8-byte LE int64 snapshot + FlagJSON uint64 = 1 << 20 // value is encoded JSON document snapshot + FlagJSONSet uint64 = 1 << 21 // owner-inbox: path + JSON value + FlagJSONDel uint64 = 1 << 22 // owner-inbox: path + FlagBitmap uint64 = 1 << 23 // value is packed Redis-order bit string snapshot + FlagBitmapSet uint64 = 1 << 24 // owner-inbox: uvarint(offset) + 0/1 + FlagHLL uint64 = 1 << 25 // value is dense 12 KiB register snapshot + FlagHLLAdd uint64 = 1 << 26 // owner-inbox: raw item bytes + FlagTopK uint64 = 1 << 27 // value is encoded Space-Saving snapshot + FlagTopKAdd uint64 = 1 << 28 // owner-inbox only: raw item to Add (replicas ignore) + FlagCMS uint64 = 1 << 29 // value is dense 64 KiB Count-Min snapshot + FlagCMSIncr uint64 = 1 << 30 // owner-inbox only: uvarint(n) || item (replicas ignore) + FlagVectorSet uint64 = 1 << 31 // snapshot or owner-inbox; discriminate by Value[0] + FlagStream uint64 = 1 << 32 // snapshot or owner-inbox; discriminate by Value[0] ) // Entry is the on-node stored value envelope (versioned LWW + TTL). @@ -43,7 +44,7 @@ type Entry struct { Value []byte Version uint64 ExpireAt int64 // unix nano; 0 = no expiry - Flags uint32 + Flags uint64 } // IsNegative reports whether this is a negative-cache sentinel. @@ -187,6 +188,10 @@ func (e Entry) IsVectorSet() bool { return e.Flags&FlagVectorSet != 0 } +func (e Entry) IsStream() bool { + return e.Flags&FlagStream != 0 +} + // Expired reports whether the entry is past ExpireAt at time now. func (e Entry) Expired(now time.Time) bool { if e.ExpireAt == 0 { diff --git a/pkg/store/memory.go b/pkg/store/memory.go index efe6dfc..0cd626d 100644 --- a/pkg/store/memory.go +++ b/pkg/store/memory.go @@ -436,7 +436,7 @@ func (m *Memory) evictLocked() { func (m *Memory) lruVictim() *list.Element { for el := m.order.Back(); el != nil; el = el.Prev() { it := el.Value.(*lruItem) - if (it.entry.IsTombstone() || it.entry.IsBloom() || it.entry.IsSet() || it.entry.IsZSet() || it.entry.IsGeo() || it.entry.IsList() || it.entry.IsHash() || it.entry.IsCounter() || it.entry.IsJSON() || it.entry.IsBitmap() || it.entry.IsHLL() || it.entry.IsTopK() || it.entry.IsCMS() || it.entry.IsVectorSet()) && !it.entry.Expired(m.now()) { + if (it.entry.IsTombstone() || it.entry.IsBloom() || it.entry.IsSet() || it.entry.IsZSet() || it.entry.IsGeo() || it.entry.IsList() || it.entry.IsHash() || it.entry.IsCounter() || it.entry.IsJSON() || it.entry.IsBitmap() || it.entry.IsHLL() || it.entry.IsTopK() || it.entry.IsCMS() || it.entry.IsVectorSet() || it.entry.IsStream()) && !it.entry.Expired(m.now()) { continue } return el diff --git a/pkg/store/store.go b/pkg/store/store.go index 4ea0c2c..c614140 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -217,6 +217,14 @@ type Store interface { HasVectorSet(key string) bool VSInstall(key string, blob []byte, version uint64, expireAt int64) bool + XAdd(key string, payload []byte, version uint64, expireAt int64, maxValue, autoTrim int) (id string, applied, tooLarge, full bool) + XDel(key, id string, version uint64, expireAt int64) bool + XTrim(key string, maxLen int, version uint64, expireAt int64) bool + XRange(key, start, end string, count int, rev bool) ([]StreamEntry, bool, error) + XLen(key string) (n int, ok bool) + HasStream(key string) bool + SXInstall(key string, blob []byte, version uint64, expireAt int64) bool + // Close releases resources. Close() } diff --git a/pkg/store/stream.go b/pkg/store/stream.go new file mode 100644 index 0000000..340ee9a --- /dev/null +++ b/pkg/store/stream.go @@ -0,0 +1,299 @@ +package store + +import ( + "container/list" + "errors" + + "github.com/Code0987/supercache/pkg/stream" +) + +// StreamEntry is one XRange row. +type StreamEntry struct { + ID string + Payload []byte +} + +func (m *Memory) XAdd(key string, payload []byte, version uint64, expireAt int64, maxValue, autoTrim int) (id string, applied, tooLarge, full bool) { + m.mu.Lock() + defer m.mu.Unlock() + now := stream.NowMilli() + if el, ok := m.items[key]; ok { + it := el.Value.(*lruItem) + if !it.entry.Expired(m.now()) { + if it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return "", false, false, false + } + return m.sxCreateLocked(el, it, key, payload, it.entry.Version+1, expireAt, maxValue, autoTrim, now) + } + if !it.entry.IsStream() { + return "", false, false, false + } + return m.sxCommitLocked(el, it, key, payload, it.entry.Version+1, expireAt, maxValue, autoTrim, now) + } + m.removeElement(el) + } + return m.sxInsertLocked(key, payload, 1, expireAt, maxValue, autoTrim, now) +} + +func (m *Memory) sxCommitLocked(el *list.Element, it *lruItem, key string, payload []byte, stored uint64, expireAt int64, maxValue, autoTrim int, now uint64) (string, bool, bool, bool) { + lg, err := stream.DecodeSnapshot(it.entry.Value) + if err != nil { + return "", false, false, false + } + id, err := lg.Append(payload, now, autoTrim) + if errors.Is(err, stream.ErrFull) { + return "", false, false, true + } + if err != nil { + return "", false, false, false + } + blob := lg.Encode() + if maxValue > 0 && len(blob) > maxValue { + return "", false, true, false + } + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = stored + it.entry.Flags = FlagStream + if expireAt != 0 { + it.entry.ExpireAt = expireAt + } + it.cost = entryCost(key, it.entry) + m.bytes += it.cost - oldCost + if m.bytes < 0 { + m.bytes = 0 + } + m.order.MoveToFront(el) + m.evictLocked() + _, still := m.items[key] + return id, still, false, false +} + +func (m *Memory) sxCreateLocked(el *list.Element, it *lruItem, key string, payload []byte, stored uint64, expireAt int64, maxValue, autoTrim int, now uint64) (string, bool, bool, bool) { + lg := stream.New() + id, err := lg.Append(payload, now, autoTrim) + if errors.Is(err, stream.ErrFull) { + return "", false, false, true + } + if err != nil { + return "", false, false, false + } + blob := lg.Encode() + if maxValue > 0 && len(blob) > maxValue { + return "", false, true, false + } + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = stored + it.entry.Flags = FlagStream + if expireAt != 0 { + it.entry.ExpireAt = expireAt + } + it.cost = entryCost(key, it.entry) + m.bytes += it.cost - oldCost + if m.bytes < 0 { + m.bytes = 0 + } + m.order.MoveToFront(el) + m.evictLocked() + _, still := m.items[key] + return id, still, false, false +} + +func (m *Memory) sxInsertLocked(key string, payload []byte, stored uint64, expireAt int64, maxValue, autoTrim int, now uint64) (string, bool, bool, bool) { + lg := stream.New() + id, err := lg.Append(payload, now, autoTrim) + if errors.Is(err, stream.ErrFull) { + return "", false, false, true + } + if err != nil { + return "", false, false, false + } + blob := lg.Encode() + if maxValue > 0 && len(blob) > maxValue { + return "", false, true, false + } + ent := Entry{Value: blob, Version: stored, ExpireAt: expireAt, Flags: FlagStream} + cost := entryCost(key, ent) + it := &lruItem{key: key, entry: copyEntry(ent), cost: cost} + el := m.order.PushFront(it) + m.items[key] = el + m.bytes += cost + m.evictLocked() + _, ok := m.items[key] + return id, ok, false, false +} + +func (m *Memory) XDel(key, id string, version uint64, expireAt int64) bool { + ms, seq, err := stream.ParseID(id) + if err != nil { + return false + } + m.mu.Lock() + defer m.mu.Unlock() + el, ok := m.items[key] + if !ok { + return true + } + it := el.Value.(*lruItem) + if it.entry.Expired(m.now()) { + m.removeElement(el) + return true + } + if it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + return true + } + if !it.entry.IsStream() { + return false + } + lg, err := stream.DecodeSnapshot(it.entry.Value) + if err != nil { + return false + } + lg.Del(ms, seq) + return m.sxWriteLocked(el, it, key, lg, it.entry.Version+1, expireAt) +} + +func (m *Memory) XTrim(key string, maxLen int, version uint64, expireAt int64) bool { + if maxLen < 0 { + return false + } + m.mu.Lock() + defer m.mu.Unlock() + el, ok := m.items[key] + if !ok { + return true + } + it := el.Value.(*lruItem) + if it.entry.Expired(m.now()) { + m.removeElement(el) + return true + } + if it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + return true + } + if !it.entry.IsStream() { + return false + } + lg, err := stream.DecodeSnapshot(it.entry.Value) + if err != nil { + return false + } + lg.Trim(maxLen) + return m.sxWriteLocked(el, it, key, lg, it.entry.Version+1, expireAt) +} + +func (m *Memory) sxWriteLocked(el *list.Element, it *lruItem, key string, lg *stream.Log, stored uint64, expireAt int64) bool { + blob := lg.Encode() + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = stored + it.entry.Flags = FlagStream + if expireAt != 0 { + it.entry.ExpireAt = expireAt + } + it.cost = entryCost(key, it.entry) + m.bytes += it.cost - oldCost + if m.bytes < 0 { + m.bytes = 0 + } + m.order.MoveToFront(el) + return true +} + +func (m *Memory) liveStreamLocked(key string) (*stream.Log, bool) { + el, ok := m.items[key] + if !ok { + return nil, false + } + it := el.Value.(*lruItem) + if it.entry.Expired(m.now()) { + m.removeElement(el) + return nil, false + } + if it.entry.IsTombstone() || !it.entry.IsStream() { + return nil, false + } + lg, err := stream.DecodeSnapshot(it.entry.Value) + if err != nil { + return nil, false + } + return lg, true +} + +func (m *Memory) XRange(key, start, end string, count int, rev bool) ([]StreamEntry, bool, error) { + sb, err := stream.ParseBound(start, true) + if err != nil { + return nil, false, err + } + eb, err := stream.ParseBound(end, false) + if err != nil { + return nil, false, err + } + m.mu.Lock() + defer m.mu.Unlock() + lg, ok := m.liveStreamLocked(key) + if !ok { + return nil, false, nil + } + raw := lg.Range(sb, eb, count, rev) + out := make([]StreamEntry, len(raw)) + for i, e := range raw { + out[i] = StreamEntry{ID: e.ID(), Payload: e.Payload} + } + return out, true, nil +} + +func (m *Memory) XLen(key string) (int, bool) { + m.mu.Lock() + defer m.mu.Unlock() + lg, ok := m.liveStreamLocked(key) + if !ok { + return 0, false + } + return len(lg.Entries), true +} + +func (m *Memory) HasStream(key string) bool { + m.mu.Lock() + defer m.mu.Unlock() + _, ok := m.liveStreamLocked(key) + return ok +} + +func (m *Memory) SXInstall(key string, blob []byte, version uint64, expireAt int64) bool { + if _, err := stream.DecodeSnapshot(blob); err != nil { + return false + } + m.mu.Lock() + defer m.mu.Unlock() + if el, ok := m.items[key]; ok { + it := el.Value.(*lruItem) + if !it.entry.Expired(m.now()) { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + } + m.removeElement(el) + } + ent := Entry{Value: append([]byte(nil), blob...), Version: version, ExpireAt: expireAt, Flags: FlagStream} + cost := entryCost(key, ent) + it := &lruItem{key: key, entry: copyEntry(ent), cost: cost} + el := m.order.PushFront(it) + m.items[key] = el + m.bytes += cost + m.evictLocked() + _, ok := m.items[key] + return ok +} diff --git a/pkg/stream/codec.go b/pkg/stream/codec.go new file mode 100644 index 0000000..cba3700 --- /dev/null +++ b/pkg/stream/codec.go @@ -0,0 +1,379 @@ +// Package stream is the ModeStream codec: append-only log with millis-seq ids. +package stream + +import ( + "encoding/binary" + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +const ( + PrefixSnapshot byte = 'S' + PrefixAdd byte = 'A' + PrefixDel byte = 'D' + PrefixTrim byte = 'T' + + HardCap = 4096 + DefaultMax = 512 +) + +// NowMilli is the clock used to mint ids. Tests may replace it. +var NowMilli = func() uint64 { return uint64(time.Now().UnixMilli()) } + +// Entry is one log record. +type Entry struct { + Ms, Seq uint64 + Payload []byte +} + +// ID is "millis-seq". +func (e Entry) ID() string { return FormatID(e.Ms, e.Seq) } + +// Log is an in-memory stream (index 0 = oldest). +type Log struct { + LastMs, LastSeq uint64 + Entries []Entry +} + +func New() *Log { return &Log{} } + +func FormatID(ms, seq uint64) string { return fmt.Sprintf("%d-%d", ms, seq) } + +func ParseID(s string) (ms, seq uint64, err error) { + i := strings.IndexByte(s, '-') + if i <= 0 || i == len(s)-1 { + return 0, 0, errBadID + } + ms, err = strconv.ParseUint(s[:i], 10, 64) + if err != nil { + return 0, 0, errBadID + } + seq, err = strconv.ParseUint(s[i+1:], 10, 64) + if err != nil { + return 0, 0, errBadID + } + return ms, seq, nil +} + +var ( + errBadBlob = errors.New("stream: bad blob") + errBadID = errors.New("stream: bad id") + ErrFull = errors.New("stream: full") +) + +// Bound is an XRange endpoint. +type Bound struct { + Ms, Seq uint64 + Exclusive bool + Min, Max bool +} + +// ParseBound: "-" / "+" / millis[-seq]; "(" only on start. +func ParseBound(s string, isStart bool) (Bound, error) { + if s == "" || s == "-" { + return Bound{Min: true}, nil + } + if s == "+" { + return Bound{Max: true}, nil + } + ex := false + if strings.HasPrefix(s, "(") { + if !isStart { + return Bound{}, errBadID + } + ex = true + s = s[1:] + if s == "" { + return Bound{}, errBadID + } + } + if strings.IndexByte(s, '-') < 0 { + ms, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return Bound{}, errBadID + } + if isStart { + return Bound{Ms: ms, Seq: 0, Exclusive: ex}, nil + } + return Bound{Ms: ms, Seq: ^uint64(0), Exclusive: ex}, nil + } + ms, seq, err := ParseID(s) + if err != nil { + return Bound{}, err + } + return Bound{Ms: ms, Seq: seq, Exclusive: ex}, nil +} + +func cmpID(ms, seq, oms, oseq uint64) int { + if ms < oms { + return -1 + } + if ms > oms { + return 1 + } + if seq < oseq { + return -1 + } + if seq > oseq { + return 1 + } + return 0 +} + +func (b Bound) below(ms, seq uint64) bool { + if b.Min { + return false + } + if b.Max { + return true + } + c := cmpID(ms, seq, b.Ms, b.Seq) + if b.Exclusive { + return c <= 0 + } + return c < 0 +} + +func (b Bound) above(ms, seq uint64) bool { + if b.Max { + return false + } + if b.Min { + return true + } + return cmpID(ms, seq, b.Ms, b.Seq) > 0 +} + +func ClampCount(n int) int { + if n <= 0 { + return 0 + } + if n > DefaultMax { + return DefaultMax + } + return n +} + +func (l *Log) Mint(now uint64) (ms, seq uint64) { + if l == nil { + return now, 0 + } + if now < l.LastMs { + now = l.LastMs + } + if now == l.LastMs { + return now, l.LastSeq + 1 + } + return now, 0 +} + +func (l *Log) Append(payload []byte, now uint64, autoTrim int) (id string, err error) { + if l == nil { + return "", errBadBlob + } + if autoTrim <= 0 && len(l.Entries) >= HardCap { + return "", ErrFull + } + ms, seq := l.Mint(now) + cp := append([]byte(nil), payload...) + l.Entries = append(l.Entries, Entry{Ms: ms, Seq: seq, Payload: cp}) + l.LastMs, l.LastSeq = ms, seq + if autoTrim > 0 { + l.Trim(autoTrim) + } + if len(l.Entries) > HardCap { + l.Trim(HardCap) + } + return FormatID(ms, seq), nil +} + +func (l *Log) Trim(maxLen int) { + if l == nil || maxLen < 0 { + return + } + if maxLen == 0 { + l.Entries = l.Entries[:0] + return + } + if n := len(l.Entries); n > maxLen { + l.Entries = append([]Entry(nil), l.Entries[n-maxLen:]...) + } +} + +func (l *Log) Del(ms, seq uint64) { + if l == nil { + return + } + out := l.Entries[:0] + for _, e := range l.Entries { + if e.Ms == ms && e.Seq == seq { + continue + } + out = append(out, e) + } + l.Entries = out +} + +func (l *Log) Range(start, end Bound, count int, rev bool) []Entry { + if l == nil { + return nil + } + count = ClampCount(count) + var out []Entry + if !rev { + for _, e := range l.Entries { + if start.below(e.Ms, e.Seq) || end.above(e.Ms, e.Seq) { + continue + } + out = append(out, copyEnt(e)) + if count > 0 && len(out) >= count { + break + } + } + return out + } + for i := len(l.Entries) - 1; i >= 0; i-- { + e := l.Entries[i] + if start.below(e.Ms, e.Seq) || end.above(e.Ms, e.Seq) { + continue + } + out = append(out, copyEnt(e)) + if count > 0 && len(out) >= count { + break + } + } + return out +} + +func copyEnt(e Entry) Entry { + return Entry{Ms: e.Ms, Seq: e.Seq, Payload: append([]byte(nil), e.Payload...)} +} + +func (l *Log) Encode() []byte { + if l == nil { + l = New() + } + n := 1 + 10*3 + for _, e := range l.Entries { + n += 10*3 + len(e.Payload) + } + buf := make([]byte, 0, n) + buf = append(buf, PrefixSnapshot) + buf = binary.AppendUvarint(buf, l.LastMs) + buf = binary.AppendUvarint(buf, l.LastSeq) + buf = binary.AppendUvarint(buf, uint64(len(l.Entries))) + for _, e := range l.Entries { + buf = binary.AppendUvarint(buf, e.Ms) + buf = binary.AppendUvarint(buf, e.Seq) + buf = binary.AppendUvarint(buf, uint64(len(e.Payload))) + buf = append(buf, e.Payload...) + } + return buf +} + +func DecodeSnapshot(blob []byte) (*Log, error) { + if len(blob) < 1 || blob[0] != PrefixSnapshot { + return nil, errBadBlob + } + p := blob[1:] + var ok bool + l := New() + if l.LastMs, p, ok = uvarint(p); !ok { + return nil, errBadBlob + } + if l.LastSeq, p, ok = uvarint(p); !ok { + return nil, errBadBlob + } + var n uint64 + if n, p, ok = uvarint(p); !ok || n > HardCap { + return nil, errBadBlob + } + l.Entries = make([]Entry, 0, n) + for i := uint64(0); i < n; i++ { + var e Entry + var plen uint64 + if e.Ms, p, ok = uvarint(p); !ok { + return nil, errBadBlob + } + if e.Seq, p, ok = uvarint(p); !ok { + return nil, errBadBlob + } + if plen, p, ok = uvarint(p); !ok || uint64(len(p)) < plen { + return nil, errBadBlob + } + e.Payload = append([]byte(nil), p[:plen]...) + p = p[plen:] + l.Entries = append(l.Entries, e) + } + if len(p) != 0 { + return nil, errBadBlob + } + return l, nil +} + +func EncodeInboxAdd(payload []byte) []byte { + buf := make([]byte, 0, 1+10+len(payload)) + buf = append(buf, PrefixAdd) + buf = binary.AppendUvarint(buf, uint64(len(payload))) + return append(buf, payload...) +} + +func DecodeInboxAdd(blob []byte) ([]byte, error) { + if len(blob) < 1 || blob[0] != PrefixAdd { + return nil, errBadBlob + } + n, p, ok := uvarint(blob[1:]) + if !ok || uint64(len(p)) < n { + return nil, errBadBlob + } + return append([]byte(nil), p[:n]...), nil +} + +func EncodeInboxDel(id string) []byte { + b := []byte(id) + buf := make([]byte, 0, 1+10+len(b)) + buf = append(buf, PrefixDel) + buf = binary.AppendUvarint(buf, uint64(len(b))) + return append(buf, b...) +} + +func DecodeInboxDel(blob []byte) (string, error) { + if len(blob) < 1 || blob[0] != PrefixDel { + return "", errBadBlob + } + n, p, ok := uvarint(blob[1:]) + if !ok || uint64(len(p)) < n { + return "", errBadBlob + } + return string(p[:n]), nil +} + +func EncodeInboxTrim(maxLen int) []byte { + buf := []byte{PrefixTrim} + return binary.AppendUvarint(buf, uint64(maxLen)) +} + +func DecodeInboxTrim(blob []byte) (int, error) { + if len(blob) < 1 || blob[0] != PrefixTrim { + return 0, errBadBlob + } + n, p, ok := uvarint(blob[1:]) + if !ok || len(p) != 0 { + return 0, errBadBlob + } + if n > uint64(^uint(0)>>1) { + return 0, errBadBlob + } + return int(n), nil +} + +func uvarint(p []byte) (uint64, []byte, bool) { + v, n := binary.Uvarint(p) + if n <= 0 { + return 0, p, false + } + return v, p[n:], true +} diff --git a/pkg/stream/codec_test.go b/pkg/stream/codec_test.go new file mode 100644 index 0000000..34fca87 --- /dev/null +++ b/pkg/stream/codec_test.go @@ -0,0 +1,38 @@ +package stream + +import "testing" + +func TestCodecRoundTrip(t *testing.T) { + lg := New() + id, err := lg.Append([]byte("hi"), 100, 0) + if err != nil || id != "100-0" { + t.Fatal(id, err) + } + blob := lg.Encode() + got, err := DecodeSnapshot(blob) + if err != nil || len(got.Entries) != 1 || string(got.Entries[0].Payload) != "hi" { + t.Fatal(err, got) + } +} + +func TestAppendHardCap(t *testing.T) { + lg := New() + for i := 0; i < HardCap; i++ { + if _, err := lg.Append([]byte("x"), uint64(i+1), 0); err != nil { + t.Fatal(i, err) + } + } + if _, err := lg.Append([]byte("y"), 10000, 0); err != ErrFull { + t.Fatalf("got %v", err) + } +} + +func TestParseBoundExclusive(t *testing.T) { + b, err := ParseBound("(10-0", true) + if err != nil || !b.Exclusive || b.Ms != 10 { + t.Fatal(b, err) + } + if _, err := ParseBound("(10-0", false); err == nil { + t.Fatal("end exclusive should fail") + } +} diff --git a/pkg/stream/eng/cluster.go b/pkg/stream/eng/cluster.go new file mode 100644 index 0000000..1a5472d --- /dev/null +++ b/pkg/stream/eng/cluster.go @@ -0,0 +1,28 @@ +package eng + +import ( + "context" + + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/stream" +) + +func FetchOwner(ctx context.Context, h Host, name string) (store.Entry, bool, error) { + _, addr, isSelf, ok := h.Owner(name) + if !ok || isSelf || addr == "" { + return store.Entry{}, false, nil + } + pctx, cancel := h.PeerCtx(ctx) + defer cancel() + ent, found, err := h.GetOrLoad(pctx, addr, h.KeyspaceName(), name) + if err != nil || !found || !ent.IsStream() { + return store.Entry{}, false, nil + } + if _, decErr := stream.DecodeSnapshot(ent.Value); decErr != nil { + return store.Entry{}, false, nil + } + if h.HoldsReplica(name) { + _ = h.Store().SXInstall(name, ent.Value, ent.Version, ent.ExpireAt) + } + return ent, true, nil +} diff --git a/pkg/stream/eng/host.go b/pkg/stream/eng/host.go new file mode 100644 index 0000000..9d4ede9 --- /dev/null +++ b/pkg/stream/eng/host.go @@ -0,0 +1,23 @@ +package eng + +import ( + "context" + + "github.com/Code0987/supercache/pkg/store" +) + +// Host is the engine-owned wiring stream needs (no engine import). +type Host interface { + Store() store.Store + KeyspaceName() string + ExpireAt() int64 + MaxValue() int + StreamMaxLen() int + ObserveVersion(name string, ver uint64) + Replicate(name string, ent store.Entry) + PeerCtx(ctx context.Context) (context.Context, context.CancelFunc) + Owner(name string) (id, addr string, isSelf bool, ok bool) + GetOrLoad(ctx context.Context, addr, keyspace, name string) (store.Entry, bool, error) + HoldsReplica(name string) bool + StreamAddRemote(ctx context.Context, addr, keyspace, name string, payload []byte) (id string, err error) +} diff --git a/pkg/stream/eng/local.go b/pkg/stream/eng/local.go new file mode 100644 index 0000000..d45eaaa --- /dev/null +++ b/pkg/stream/eng/local.go @@ -0,0 +1,122 @@ +package eng + +import ( + "errors" + + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/stream" +) + +var ( + ErrRejected = errors.New("rejected") + ErrTooLarge = errors.New("too large") + ErrFull = errors.New("full") + ErrBadID = errors.New("bad id") +) + +func ReplicateSnapshot(h Host, name string, ver uint64, expire int64) { + ent, ok := h.Store().Peek(name) + if !ok || !ent.IsStream() || len(ent.Value) == 0 || ent.Value[0] != stream.PrefixSnapshot { + return + } + h.Replicate(name, store.Entry{ + Value: ent.Value, + Version: ver, + ExpireAt: expire, + Flags: store.FlagStream, + }) +} + +func AddLocal(h Host, name string, payload []byte) (string, error) { + expire := h.ExpireAt() + cur, _ := h.Store().PeekVersion(name) + id, applied, tooLarge, full := h.Store().XAdd(name, payload, cur+1, expire, h.MaxValue(), h.StreamMaxLen()) + if tooLarge { + return "", ErrTooLarge + } + if full { + return "", ErrFull + } + if !applied { + return "", ErrRejected + } + ver, _ := h.Store().PeekVersion(name) + h.ObserveVersion(name, ver) + ReplicateSnapshot(h, name, ver, expire) + return id, nil +} + +func DelLocal(h Host, name, id string) error { + if _, _, err := stream.ParseID(id); err != nil { + return ErrBadID + } + expire := h.ExpireAt() + cur, _ := h.Store().PeekVersion(name) + if !h.Store().XDel(name, id, cur+1, expire) { + return ErrRejected + } + if !h.Store().HasStream(name) { + return nil + } + ver, _ := h.Store().PeekVersion(name) + h.ObserveVersion(name, ver) + ReplicateSnapshot(h, name, ver, expire) + return nil +} + +func TrimLocal(h Host, name string, maxLen int) error { + if maxLen < 0 { + return ErrBadID + } + expire := h.ExpireAt() + cur, _ := h.Store().PeekVersion(name) + if !h.Store().XTrim(name, maxLen, cur+1, expire) { + return ErrRejected + } + if !h.Store().HasStream(name) { + return nil + } + ver, _ := h.Store().PeekVersion(name) + h.ObserveVersion(name, ver) + ReplicateSnapshot(h, name, ver, expire) + return nil +} + +func ApplyInbox(h Host, name string, blob []byte, expireAt int64) bool { + if len(blob) == 0 { + return false + } + if expireAt == 0 { + expireAt = h.ExpireAt() + } + switch blob[0] { + case stream.PrefixAdd: + payload, err := stream.DecodeInboxAdd(blob) + if err != nil { + return false + } + _, err = AddLocal(h, name, payload) + return err == nil + case stream.PrefixDel: + id, err := stream.DecodeInboxDel(blob) + if err != nil { + return false + } + return DelLocal(h, name, id) == nil + case stream.PrefixTrim: + n, err := stream.DecodeInboxTrim(blob) + if err != nil { + return false + } + return TrimLocal(h, name, n) == nil + default: + return false + } +} + +func ApplyInstall(h Host, name string, blob []byte, version uint64, expireAt int64) bool { + if expireAt == 0 { + expireAt = h.ExpireAt() + } + return h.Store().SXInstall(name, blob, version, expireAt) +} diff --git a/pkg/stream/engine_test.go b/pkg/stream/engine_test.go new file mode 100644 index 0000000..da93800 --- /dev/null +++ b/pkg/stream/engine_test.go @@ -0,0 +1,292 @@ +package stream_test + +import ( + "context" + "errors" + "strings" + "testing" + "time" + + "github.com/Code0987/supercache/internal/testcluster" + "github.com/Code0987/supercache/pkg/engine" + "github.com/Code0987/supercache/pkg/keyspace" + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/stream" +) + +func sxKS() keyspace.Config { + return keyspace.Config{Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1 << 20, TTL: time.Hour} +} + +func TestStreamXAddRangeLen(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + id1, err := e.XAdd(ctx, "events", "logs", []byte("a")) + if err != nil || !strings.Contains(id1, "-") { + t.Fatalf("id1 %q %v", id1, err) + } + id2, err := e.XAdd(ctx, "events", "logs", []byte("b")) + if err != nil { + t.Fatal(err) + } + n, ok, err := e.XLen(ctx, "events", "logs") + if err != nil || !ok || n != 2 { + t.Fatal(n, ok, err) + } + rows, err := e.XRange(ctx, "events", "logs", "-", "+", 0) + if err != nil || len(rows) != 2 || rows[0].ID != id1 || string(rows[1].Payload) != "b" { + t.Fatalf("%v %+v", err, rows) + } + _ = id2 +} + +func TestStreamXAddSameMilliSeq(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + stream.NowMilli = func() uint64 { return 1_700_000_000_000 } + t.Cleanup(func() { stream.NowMilli = func() uint64 { return uint64(time.Now().UnixMilli()) } }) + id1, err := e.XAdd(ctx, "events", "logs", []byte("a")) + if err != nil { + t.Fatal(err) + } + id2, err := e.XAdd(ctx, "events", "logs", []byte("b")) + if err != nil { + t.Fatal(err) + } + if id1 != "1700000000000-0" || id2 != "1700000000000-1" { + t.Fatalf("%s %s", id1, id2) + } +} + +func TestStreamXRevRangeAndCount(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + id1, _ := e.XAdd(ctx, "events", "logs", []byte("old")) + id2, _ := e.XAdd(ctx, "events", "logs", []byte("new")) + rows, err := e.XRevRange(ctx, "events", "logs", "-", "+", 1) + if err != nil || len(rows) != 1 || rows[0].ID != id2 || string(rows[0].Payload) != "new" { + t.Fatalf("%v %+v", err, rows) + } + rows, err = e.XRevRange(ctx, "events", "logs", "("+id1, "+", 0) + if err != nil || len(rows) != 1 || rows[0].ID != id2 { + t.Fatalf("exclusive %v %+v", err, rows) + } +} + +func TestStreamXDelAndEmptyUntilDelete(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + id, _ := e.XAdd(ctx, "events", "logs", []byte("only")) + if err := e.XDel(ctx, "events", "logs", id); err != nil { + t.Fatal(err) + } + n, ok, err := e.XLen(ctx, "events", "logs") + if err != nil || !ok || n != 0 { + t.Fatal(n, ok, err) + } + if err := e.Delete(ctx, "events", "logs"); err != nil { + t.Fatal(err) + } + n, ok, err = e.XLen(ctx, "events", "logs") + if err != nil || ok || n != 0 { + t.Fatal(n, ok, err) + } +} + +func TestStreamXTrimMaxLen(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + _, _ = e.XAdd(ctx, "events", "logs", []byte("old")) + id2, _ := e.XAdd(ctx, "events", "logs", []byte("new")) + if err := e.XTrim(ctx, "events", "logs", 1); err != nil { + t.Fatal(err) + } + rows, _ := e.XRange(ctx, "events", "logs", "-", "+", 0) + if len(rows) != 1 || rows[0].ID != id2 { + t.Fatalf("%+v", rows) + } +} + +func TestStreamAutoTrim(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(keyspace.Config{ + Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1 << 20, StreamMaxLen: 2, + }) + ctx := context.Background() + _, _ = e.XAdd(ctx, "events", "logs", []byte("1")) + _, _ = e.XAdd(ctx, "events", "logs", []byte("2")) + id3, _ := e.XAdd(ctx, "events", "logs", []byte("3")) + rows, _ := e.XRange(ctx, "events", "logs", "-", "+", 0) + if len(rows) != 2 || rows[1].ID != id3 || string(rows[0].Payload) != "2" { + t.Fatalf("%+v", rows) + } +} + +func TestStreamWrongMode(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + _ = e.UpdateKeySpace(keyspace.Config{Name: "kv", Mode: keyspace.ModeCacheOnly, MaxBytes: 1 << 20}) + ctx := context.Background() + if _, err := e.Get(ctx, "events", "logs"); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("get: %v", err) + } + if err := e.Put(ctx, "events", "logs", []byte("x")); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("put: %v", err) + } + if _, err := e.XAdd(ctx, "kv", "k", []byte("x")); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("xadd kv: %v", err) + } +} + +func TestStreamExclusiveStart(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + ctx := context.Background() + id1, _ := e.XAdd(ctx, "events", "logs", []byte("a")) + id2, _ := e.XAdd(ctx, "events", "logs", []byte("b")) + rows, err := e.XRange(ctx, "events", "logs", "("+id1, "+", 0) + if err != nil || len(rows) != 1 || rows[0].ID != id2 { + t.Fatalf("%v %+v", err, rows) + } +} + +func TestStreamClusterRF(t *testing.T) { + c, err := testcluster.Start(testcluster.Config{ + Nodes: 3, + Keyspaces: []keyspace.Config{{ + Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1 << 20, ReplicationFactor: 2, + }}, + }) + if err != nil { + t.Fatal(err) + } + defer c.Close() + ctx := context.Background() + nodes := c.Nodes() + id, err := nodes[0].Engine.XAdd(ctx, "events", "logs", []byte("hi")) + if err != nil { + t.Fatal(err) + } + deadline := time.Now().Add(2 * time.Second) + var live int + for time.Now().Before(deadline) { + live = 0 + for _, n := range nodes { + if n.Engine.HasLocal("events", "logs") { + live++ + } + } + if live == 2 { + break + } + time.Sleep(20 * time.Millisecond) + } + if live != 2 { + t.Fatalf("live copies %d", live) + } + for _, n := range nodes { + rows, err := n.Engine.XRange(ctx, "events", "logs", "-", "+", 0) + if err != nil || len(rows) != 1 || rows[0].ID != id { + t.Fatalf("%s %v %+v", n.ID, err, rows) + } + } +} + +func TestStreamAddReturnsIdFromNonOwner(t *testing.T) { + c, err := testcluster.Start(testcluster.Config{ + Nodes: 3, + Keyspaces: []keyspace.Config{{ + Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1 << 20, ReplicationFactor: 2, + }}, + }) + if err != nil { + t.Fatal(err) + } + defer c.Close() + ctx := context.Background() + nodes := c.Nodes() + var via *engine.Engine + for _, n := range nodes { + if o, ok := n.Engine.OwnerOf("logs"); ok && o.ID != n.ID { + via = n.Engine + break + } + } + if via == nil { + t.Fatal("no non-owner") + } + id, err := via.XAdd(ctx, "events", "logs", []byte("via")) + if err != nil || id == "" { + t.Fatal(id, err) + } + rows, err := nodes[0].Engine.XRange(ctx, "events", "logs", "-", "+", 0) + if err != nil || len(rows) != 1 || rows[0].ID != id { + t.Fatalf("%v %+v", err, rows) + } +} + +func TestFlagStreamUint64Wire(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + lg := stream.New() + _, _ = lg.Append([]byte("x"), 1, 0) + blob := lg.Encode() + ok, err := e.ApplyPut("events", "logs", store.Entry{Value: blob, Flags: store.FlagStream, Version: 1}) + if err != nil || !ok { + t.Fatal(ok, err) + } + if !e.HasLocal("events", "logs") { + t.Fatal("missing after apply") + } + v := e.LocalView("events", "logs") + if v.Flags&store.FlagStream == 0 { + t.Fatalf("flags %#x", v.Flags) + } +} + +func TestStreamHardCap(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(sxKS()) + lg := stream.New() + for i := 0; i < stream.HardCap; i++ { + if _, err := lg.Append([]byte("x"), uint64(i+1), 0); err != nil { + t.Fatal(i, err) + } + } + ok, err := e.ApplyPut("events", "logs", store.Entry{Value: lg.Encode(), Flags: store.FlagStream, Version: 1}) + if err != nil || !ok { + t.Fatal(ok, err) + } + _, err = e.XAdd(context.Background(), "events", "logs", []byte("y")) + if !errors.Is(err, engine.ErrInvalidArgument) || !strings.Contains(err.Error(), "stream full") { + t.Fatalf("got %v", err) + } +} + +func TestStreamNotEvicted(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(keyspace.Config{Name: "events", Mode: keyspace.ModeStream, MaxBytes: 1, TTL: time.Hour}) + ctx := context.Background() + if _, err := e.XAdd(ctx, "events", "logs", []byte("keep")); err != nil { + t.Fatal(err) + } + if !e.HasLocal("events", "logs") { + t.Fatal("evicted") + } +}