From 8181ee8bb5e1342d54863409c6f7ed589c6f7a99 Mon Sep 17 00:00:00 2001 From: Code0987 <1825861+Code0987@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:27:53 +0000 Subject: [PATCH] feat: add ModeVectorSet (VAdd / VSim / VRem) Named embedding set with brute-force K-NN. Keyspace VectorMetric is cosine (default), L2, or IP. Snapshot fan-out on the last uint32 flag plus S/A/R payload prefix. Same Engine/client/sc/docs/example PR. --- PLAN.md | 6 +- README.md | 2 +- api/gen/cache/v1/cache.pb.go | 1511 ++++++++++++++++----- api/gen/cache/v1/cache_grpc.pb.go | 228 ++++ api/openapi/cache.openapi.yaml | 5 +- api/proto/cache.proto | 63 + cmd/sc/cmds.go | 12 + cmd/sc/cmds_vecset.go | 175 +++ cmd/sc/main.go | 9 +- cmd/sc/repl.go | 1 + cmd/sc/vecset_cli_test.go | 52 + docs/API.md | 3 +- docs/CLUSTER_FLOWS.md | 3 +- docs/OPERATIONS.md | 2 + docs/api/cache.openapi.yaml | 5 +- docs/design/2026-09-04-mode-vector-set.md | 439 ++++++ docs/design/README.md | 6 + examples/vecset/demo.go | 77 ++ examples/vecset/demo_test.go | 17 + examples/vecset/main.go | 16 + internal/cacheserver/vecset.go | 58 + internal/testcluster/cluster.go | 8 +- pkg/client/vecset.go | 69 + pkg/engine/cluster.go | 7 + pkg/engine/engine.go | 9 + pkg/engine/modehost.go | 27 +- pkg/engine/vecset.go | 288 ++++ pkg/keyspace/config.go | 63 + pkg/keyspace/config_test.go | 26 + pkg/store/entry.go | 5 + pkg/store/memory.go | 2 +- pkg/store/store.go | 16 + pkg/store/vecset.go | 272 ++++ pkg/store/vecset_bench_test.go | 37 + pkg/store/vecset_test.go | 144 ++ pkg/vecset/codec.go | 373 +++++ pkg/vecset/codec_test.go | 163 +++ pkg/vecset/eng/cluster.go | 25 + pkg/vecset/eng/host.go | 23 + pkg/vecset/eng/local.go | 100 ++ pkg/vecset/engine_cluster_test.go | 104 ++ pkg/vecset/engine_test.go | 143 ++ 42 files changed, 4210 insertions(+), 384 deletions(-) create mode 100644 cmd/sc/cmds_vecset.go create mode 100644 cmd/sc/vecset_cli_test.go create mode 100644 docs/design/2026-09-04-mode-vector-set.md create mode 100644 examples/vecset/demo.go create mode 100644 examples/vecset/demo_test.go create mode 100644 examples/vecset/main.go create mode 100644 internal/cacheserver/vecset.go create mode 100644 pkg/client/vecset.go create mode 100644 pkg/engine/vecset.go create mode 100644 pkg/store/vecset.go create mode 100644 pkg/store/vecset_bench_test.go create mode 100644 pkg/store/vecset_test.go create mode 100644 pkg/vecset/codec.go create mode 100644 pkg/vecset/codec_test.go create mode 100644 pkg/vecset/eng/cluster.go create mode 100644 pkg/vecset/eng/host.go create mode 100644 pkg/vecset/eng/local.go create mode 100644 pkg/vecset/engine_cluster_test.go create mode 100644 pkg/vecset/engine_test.go diff --git a/PLAN.md b/PLAN.md index 6dd3009..8889b57 100644 --- a/PLAN.md +++ b/PLAN.md @@ -188,9 +188,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. | +| **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. | | **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 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 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. | @@ -203,6 +203,7 @@ Owner-only storage + remote Get would scale memory with N but would **break** th | **HLLAdd / HLLCount** | `ModeHLL` only. Approximate distinct count of hashed items; FNV-1a 64, `p=14` dense 12 KiB. Owner applies the op then fans out a **full `FlagHLL` snapshot**. Replica `HLLCount` may lag. Missing name → `ok=false`. Empty-until-delete. Get/Put **invalid**. `HLLAdd` is ACK-only (no Redis changed-bool). Estimates do **not** match Redis `PFCOUNT`. | | **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**. | | **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) @@ -315,6 +316,7 @@ Each keyspace has a mode. Opaque KV modes use Get/Put/Delete. Structured modes r | `ModeHLL` | sketch `name` | HLLAdd, HLLCount; Delete(name) | `FlagHLL` snapshot; owner-inbox `FlagHLLAdd` | | `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 | Public reference: [docs/API.md](./docs/API.md), OpenAPI `api/openapi/cache.openapi.yaml`, proto `api/proto/cache.proto`. diff --git a/README.md b/README.md index f074280..66a6861 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), 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), and a bounded per-node LRU. ```text github.com/Code0987/supercache diff --git a/api/gen/cache/v1/cache.pb.go b/api/gen/cache/v1/cache.pb.go index 2208417..bd97138 100644 --- a/api/gen/cache/v1/cache.pb.go +++ b/api/gen/cache/v1/cache.pb.go @@ -5797,6 +5797,690 @@ func (x *CMSQueryResponse) GetCount() uint64 { return 0 } +type VAddRequest 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"` + Member []byte `protobuf:"bytes,3,opt,name=member,proto3" json:"member,omitempty"` + Vec []float32 `protobuf:"fixed32,4,rep,packed,name=vec,proto3" json:"vec,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VAddRequest) Reset() { + *x = VAddRequest{} + mi := &file_cache_proto_msgTypes[110] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VAddRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VAddRequest) ProtoMessage() {} + +func (x *VAddRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[110] + 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 VAddRequest.ProtoReflect.Descriptor instead. +func (*VAddRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{110} +} + +func (x *VAddRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VAddRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *VAddRequest) GetMember() []byte { + if x != nil { + return x.Member + } + return nil +} + +func (x *VAddRequest) GetVec() []float32 { + if x != nil { + return x.Vec + } + return nil +} + +type VAddResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VAddResponse) Reset() { + *x = VAddResponse{} + mi := &file_cache_proto_msgTypes[111] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VAddResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VAddResponse) ProtoMessage() {} + +func (x *VAddResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[111] + 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 VAddResponse.ProtoReflect.Descriptor instead. +func (*VAddResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{111} +} + +type VRemRequest 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"` + Member []byte `protobuf:"bytes,3,opt,name=member,proto3" json:"member,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VRemRequest) Reset() { + *x = VRemRequest{} + mi := &file_cache_proto_msgTypes[112] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VRemRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VRemRequest) ProtoMessage() {} + +func (x *VRemRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[112] + 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 VRemRequest.ProtoReflect.Descriptor instead. +func (*VRemRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{112} +} + +func (x *VRemRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VRemRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *VRemRequest) GetMember() []byte { + if x != nil { + return x.Member + } + return nil +} + +type VRemResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VRemResponse) Reset() { + *x = VRemResponse{} + mi := &file_cache_proto_msgTypes[113] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VRemResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VRemResponse) ProtoMessage() {} + +func (x *VRemResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[113] + 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 VRemResponse.ProtoReflect.Descriptor instead. +func (*VRemResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{113} +} + +type VSimRequest 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"` + Vec []float32 `protobuf:"fixed32,3,rep,packed,name=vec,proto3" json:"vec,omitempty"` + K int32 `protobuf:"varint,4,opt,name=k,proto3" json:"k,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VSimRequest) Reset() { + *x = VSimRequest{} + mi := &file_cache_proto_msgTypes[114] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VSimRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VSimRequest) ProtoMessage() {} + +func (x *VSimRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[114] + 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 VSimRequest.ProtoReflect.Descriptor instead. +func (*VSimRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{114} +} + +func (x *VSimRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VSimRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *VSimRequest) GetVec() []float32 { + if x != nil { + return x.Vec + } + return nil +} + +func (x *VSimRequest) GetK() int32 { + if x != nil { + return x.K + } + return 0 +} + +type VSimHit struct { + state protoimpl.MessageState `protogen:"open.v1"` + Member []byte `protobuf:"bytes,1,opt,name=member,proto3" json:"member,omitempty"` + Score float32 `protobuf:"fixed32,2,opt,name=score,proto3" json:"score,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VSimHit) Reset() { + *x = VSimHit{} + mi := &file_cache_proto_msgTypes[115] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VSimHit) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VSimHit) ProtoMessage() {} + +func (x *VSimHit) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[115] + 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 VSimHit.ProtoReflect.Descriptor instead. +func (*VSimHit) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{115} +} + +func (x *VSimHit) GetMember() []byte { + if x != nil { + return x.Member + } + return nil +} + +func (x *VSimHit) GetScore() float32 { + if x != nil { + return x.Score + } + return 0 +} + +type VSimResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Hits []*VSimHit `protobuf:"bytes,1,rep,name=hits,proto3" json:"hits,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VSimResponse) Reset() { + *x = VSimResponse{} + mi := &file_cache_proto_msgTypes[116] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VSimResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VSimResponse) ProtoMessage() {} + +func (x *VSimResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[116] + 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 VSimResponse.ProtoReflect.Descriptor instead. +func (*VSimResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{116} +} + +func (x *VSimResponse) GetHits() []*VSimHit { + if x != nil { + return x.Hits + } + return nil +} + +type VCardRequest 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 *VCardRequest) Reset() { + *x = VCardRequest{} + mi := &file_cache_proto_msgTypes[117] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VCardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VCardRequest) ProtoMessage() {} + +func (x *VCardRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[117] + 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 VCardRequest.ProtoReflect.Descriptor instead. +func (*VCardRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{117} +} + +func (x *VCardRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VCardRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type VCardResponse 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 *VCardResponse) Reset() { + *x = VCardResponse{} + mi := &file_cache_proto_msgTypes[118] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VCardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VCardResponse) ProtoMessage() {} + +func (x *VCardResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[118] + 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 VCardResponse.ProtoReflect.Descriptor instead. +func (*VCardResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{118} +} + +func (x *VCardResponse) GetN() int64 { + if x != nil { + return x.N + } + return 0 +} + +func (x *VCardResponse) GetPresent() bool { + if x != nil { + return x.Present + } + return false +} + +type VDimRequest 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 *VDimRequest) Reset() { + *x = VDimRequest{} + mi := &file_cache_proto_msgTypes[119] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VDimRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VDimRequest) ProtoMessage() {} + +func (x *VDimRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[119] + 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 VDimRequest.ProtoReflect.Descriptor instead. +func (*VDimRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{119} +} + +func (x *VDimRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VDimRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type VDimResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Dim int32 `protobuf:"varint,1,opt,name=dim,proto3" json:"dim,omitempty"` + Present bool `protobuf:"varint,2,opt,name=present,proto3" json:"present,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VDimResponse) Reset() { + *x = VDimResponse{} + mi := &file_cache_proto_msgTypes[120] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VDimResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VDimResponse) ProtoMessage() {} + +func (x *VDimResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[120] + 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 VDimResponse.ProtoReflect.Descriptor instead. +func (*VDimResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{120} +} + +func (x *VDimResponse) GetDim() int32 { + if x != nil { + return x.Dim + } + return 0 +} + +func (x *VDimResponse) GetPresent() bool { + if x != nil { + return x.Present + } + return false +} + +type VEmbRequest 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"` + Member []byte `protobuf:"bytes,3,opt,name=member,proto3" json:"member,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VEmbRequest) Reset() { + *x = VEmbRequest{} + mi := &file_cache_proto_msgTypes[121] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VEmbRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VEmbRequest) ProtoMessage() {} + +func (x *VEmbRequest) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[121] + 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 VEmbRequest.ProtoReflect.Descriptor instead. +func (*VEmbRequest) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{121} +} + +func (x *VEmbRequest) GetKeyspace() string { + if x != nil { + return x.Keyspace + } + return "" +} + +func (x *VEmbRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *VEmbRequest) GetMember() []byte { + if x != nil { + return x.Member + } + return nil +} + +type VEmbResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Vec []float32 `protobuf:"fixed32,1,rep,packed,name=vec,proto3" json:"vec,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VEmbResponse) Reset() { + *x = VEmbResponse{} + mi := &file_cache_proto_msgTypes[122] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VEmbResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VEmbResponse) ProtoMessage() {} + +func (x *VEmbResponse) ProtoReflect() protoreflect.Message { + mi := &file_cache_proto_msgTypes[122] + 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 VEmbResponse.ProtoReflect.Descriptor instead. +func (*VEmbResponse) Descriptor() ([]byte, []int) { + return file_cache_proto_rawDescGZIP(), []int{122} +} + +func (x *VEmbResponse) GetVec() []float32 { + if x != nil { + return x.Vec + } + return nil +} + +func (x *VEmbResponse) GetFound() bool { + if x != nil { + return x.Found + } + return false +} + var File_cache_proto protoreflect.FileDescriptor var file_cache_proto_rawDesc = []byte{ @@ -6267,286 +6951,367 @@ var file_cache_proto_rawDesc = []byte{ 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xa7, 0x22, - 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, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x67, 0x0a, + 0x0b, 0x56, 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, 0x16, 0x0a, 0x06, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x65, 0x63, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x02, 0x52, 0x03, 0x76, 0x65, 0x63, 0x22, 0x0e, 0x0a, 0x0c, 0x56, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x0a, 0x0b, 0x56, 0x52, 0x65, 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, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x0e, 0x0a, + 0x0c, 0x56, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5d, 0x0a, + 0x0b, 0x56, 0x53, 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, 0x10, 0x0a, 0x03, + 0x76, 0x65, 0x63, 0x18, 0x03, 0x20, 0x03, 0x28, 0x02, 0x52, 0x03, 0x76, 0x65, 0x63, 0x12, 0x0c, + 0x0a, 0x01, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, 0x6b, 0x22, 0x37, 0x0a, 0x07, + 0x56, 0x53, 0x69, 0x6d, 0x48, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x40, 0x0a, 0x0c, 0x56, 0x53, 0x69, 0x6d, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x68, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 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, 0x48, 0x69, + 0x74, 0x52, 0x04, 0x68, 0x69, 0x74, 0x73, 0x22, 0x3e, 0x0a, 0x0c, 0x56, 0x43, 0x61, 0x72, 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, 0x22, 0x37, 0x0a, 0x0d, 0x56, 0x43, 0x61, 0x72, 0x64, + 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, 0x3d, 0x0a, 0x0b, 0x56, 0x44, 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, 0x22, + 0x3a, 0x0a, 0x0c, 0x56, 0x44, 0x69, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x64, 0x69, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x64, 0x69, + 0x6d, 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, 0x55, 0x0a, 0x0b, 0x56, + 0x45, 0x6d, 0x62, 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, 0x16, 0x0a, 0x06, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, + 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, 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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, 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, + 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, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 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, 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, + 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, 0x52, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 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, 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, + 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, 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, 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, + 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, 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, + 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, 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, 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, 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, + 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, 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, + 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, 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, + 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, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, + 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, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x04, 0x48, 0x44, 0x65, 0x6c, 0x12, + 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, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 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, 0x44, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x07, 0x48, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, + 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, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, + 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, 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, + 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, 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, 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, + 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, 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, - 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, + 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, 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, + 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, 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, 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, 0x4a, 0x73, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x52, 0x65, + 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, 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, + 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, 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, 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, + 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, 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, + 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, - 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, + 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, 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, 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, + 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, + 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, + 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, } var ( @@ -6561,7 +7326,7 @@ func file_cache_proto_rawDescGZIP() []byte { return file_cache_proto_rawDescData } -var file_cache_proto_msgTypes = make([]protoimpl.MessageInfo, 110) +var file_cache_proto_msgTypes = make([]protoimpl.MessageInfo, 123) var file_cache_proto_goTypes = []any{ (*GetRequest)(nil), // 0: supercache.cache.v1.GetRequest (*GetResponse)(nil), // 1: supercache.cache.v1.GetResponse @@ -6673,6 +7438,19 @@ var file_cache_proto_goTypes = []any{ (*CMSIncrResponse)(nil), // 107: supercache.cache.v1.CMSIncrResponse (*CMSQueryRequest)(nil), // 108: supercache.cache.v1.CMSQueryRequest (*CMSQueryResponse)(nil), // 109: supercache.cache.v1.CMSQueryResponse + (*VAddRequest)(nil), // 110: supercache.cache.v1.VAddRequest + (*VAddResponse)(nil), // 111: supercache.cache.v1.VAddResponse + (*VRemRequest)(nil), // 112: supercache.cache.v1.VRemRequest + (*VRemResponse)(nil), // 113: supercache.cache.v1.VRemResponse + (*VSimRequest)(nil), // 114: supercache.cache.v1.VSimRequest + (*VSimHit)(nil), // 115: supercache.cache.v1.VSimHit + (*VSimResponse)(nil), // 116: supercache.cache.v1.VSimResponse + (*VCardRequest)(nil), // 117: supercache.cache.v1.VCardRequest + (*VCardResponse)(nil), // 118: supercache.cache.v1.VCardResponse + (*VDimRequest)(nil), // 119: supercache.cache.v1.VDimRequest + (*VDimResponse)(nil), // 120: supercache.cache.v1.VDimResponse + (*VEmbRequest)(nil), // 121: supercache.cache.v1.VEmbRequest + (*VEmbResponse)(nil), // 122: supercache.cache.v1.VEmbResponse } var file_cache_proto_depIdxs = []int32{ 4, // 0: supercache.cache.v1.PutManyRequest.items:type_name -> supercache.cache.v1.KV @@ -6684,115 +7462,128 @@ var file_cache_proto_depIdxs = []int32{ 39, // 6: supercache.cache.v1.GeoRadiusResponse.members:type_name -> supercache.cache.v1.GeoMember 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 - 0, // 9: supercache.cache.v1.Cache.Get:input_type -> supercache.cache.v1.GetRequest - 2, // 10: supercache.cache.v1.Cache.Put:input_type -> supercache.cache.v1.PutRequest - 5, // 11: supercache.cache.v1.Cache.PutMany:input_type -> supercache.cache.v1.PutManyRequest - 8, // 12: supercache.cache.v1.Cache.Delete:input_type -> supercache.cache.v1.DeleteRequest - 11, // 13: supercache.cache.v1.Cache.DeleteMany:input_type -> supercache.cache.v1.DeleteManyRequest - 13, // 14: supercache.cache.v1.Cache.BloomAdd:input_type -> supercache.cache.v1.BloomAddRequest - 15, // 15: supercache.cache.v1.Cache.BloomTest:input_type -> supercache.cache.v1.BloomTestRequest - 17, // 16: supercache.cache.v1.Cache.SetAdd:input_type -> supercache.cache.v1.SetAddRequest - 19, // 17: supercache.cache.v1.Cache.SetRemove:input_type -> supercache.cache.v1.SetRemoveRequest - 21, // 18: supercache.cache.v1.Cache.SetContains:input_type -> supercache.cache.v1.SetContainsRequest - 23, // 19: supercache.cache.v1.Cache.SetCard:input_type -> supercache.cache.v1.SetCardRequest - 25, // 20: supercache.cache.v1.Cache.SetMembers:input_type -> supercache.cache.v1.SetMembersRequest - 28, // 21: supercache.cache.v1.Cache.ZAdd:input_type -> supercache.cache.v1.ZAddRequest - 30, // 22: supercache.cache.v1.Cache.ZRem:input_type -> supercache.cache.v1.ZRemRequest - 32, // 23: supercache.cache.v1.Cache.ZScore:input_type -> supercache.cache.v1.ZScoreRequest - 34, // 24: supercache.cache.v1.Cache.ZCard:input_type -> supercache.cache.v1.ZCardRequest - 36, // 25: supercache.cache.v1.Cache.ZRange:input_type -> supercache.cache.v1.ZRangeRequest - 37, // 26: supercache.cache.v1.Cache.ZRangeByScore:input_type -> supercache.cache.v1.ZRangeByScoreRequest - 40, // 27: supercache.cache.v1.Cache.GeoAdd:input_type -> supercache.cache.v1.GeoAddRequest - 42, // 28: supercache.cache.v1.Cache.GeoRem:input_type -> supercache.cache.v1.GeoRemRequest - 44, // 29: supercache.cache.v1.Cache.GeoPos:input_type -> supercache.cache.v1.GeoPosRequest - 46, // 30: supercache.cache.v1.Cache.GeoCard:input_type -> supercache.cache.v1.GeoCardRequest - 48, // 31: supercache.cache.v1.Cache.GeoDist:input_type -> supercache.cache.v1.GeoDistRequest - 50, // 32: supercache.cache.v1.Cache.GeoRadius:input_type -> supercache.cache.v1.GeoRadiusRequest - 52, // 33: supercache.cache.v1.Cache.LPush:input_type -> supercache.cache.v1.LPushRequest - 54, // 34: supercache.cache.v1.Cache.RPush:input_type -> supercache.cache.v1.RPushRequest - 56, // 35: supercache.cache.v1.Cache.LPop:input_type -> supercache.cache.v1.LPopRequest - 58, // 36: supercache.cache.v1.Cache.RPop:input_type -> supercache.cache.v1.RPopRequest - 60, // 37: supercache.cache.v1.Cache.LLen:input_type -> supercache.cache.v1.LLenRequest - 62, // 38: supercache.cache.v1.Cache.LIndex:input_type -> supercache.cache.v1.LIndexRequest - 64, // 39: supercache.cache.v1.Cache.LRange:input_type -> supercache.cache.v1.LRangeRequest - 66, // 40: supercache.cache.v1.Cache.HSet:input_type -> supercache.cache.v1.HSetRequest - 68, // 41: supercache.cache.v1.Cache.HGet:input_type -> supercache.cache.v1.HGetRequest - 70, // 42: supercache.cache.v1.Cache.HDel:input_type -> supercache.cache.v1.HDelRequest - 72, // 43: supercache.cache.v1.Cache.HExists:input_type -> supercache.cache.v1.HExistsRequest - 74, // 44: supercache.cache.v1.Cache.HLen:input_type -> supercache.cache.v1.HLenRequest - 76, // 45: supercache.cache.v1.Cache.HGetAll:input_type -> supercache.cache.v1.HGetAllRequest - 79, // 46: supercache.cache.v1.Cache.Incr:input_type -> supercache.cache.v1.IncrRequest - 81, // 47: supercache.cache.v1.Cache.CounterGet:input_type -> supercache.cache.v1.CounterGetRequest - 83, // 48: supercache.cache.v1.Cache.JsonSet:input_type -> supercache.cache.v1.JsonSetRequest - 85, // 49: supercache.cache.v1.Cache.JsonGet:input_type -> supercache.cache.v1.JsonGetRequest - 87, // 50: supercache.cache.v1.Cache.JsonDel:input_type -> supercache.cache.v1.JsonDelRequest - 89, // 51: supercache.cache.v1.Cache.BitSet:input_type -> supercache.cache.v1.BitSetRequest - 91, // 52: supercache.cache.v1.Cache.BitGet:input_type -> supercache.cache.v1.BitGetRequest - 93, // 53: supercache.cache.v1.Cache.BitCount:input_type -> supercache.cache.v1.BitCountRequest - 95, // 54: supercache.cache.v1.Cache.BitPos:input_type -> supercache.cache.v1.BitPosRequest - 97, // 55: supercache.cache.v1.Cache.HLLAdd:input_type -> supercache.cache.v1.HLLAddRequest - 99, // 56: supercache.cache.v1.Cache.HLLCount:input_type -> supercache.cache.v1.HLLCountRequest - 101, // 57: supercache.cache.v1.Cache.TopKAdd:input_type -> supercache.cache.v1.TopKAddRequest - 103, // 58: supercache.cache.v1.Cache.TopKList:input_type -> supercache.cache.v1.TopKListRequest - 106, // 59: supercache.cache.v1.Cache.CMSIncr:input_type -> supercache.cache.v1.CMSIncrRequest - 108, // 60: supercache.cache.v1.Cache.CMSQuery:input_type -> supercache.cache.v1.CMSQueryRequest - 1, // 61: supercache.cache.v1.Cache.Get:output_type -> supercache.cache.v1.GetResponse - 3, // 62: supercache.cache.v1.Cache.Put:output_type -> supercache.cache.v1.PutResponse - 7, // 63: supercache.cache.v1.Cache.PutMany:output_type -> supercache.cache.v1.PutManyResponse - 10, // 64: supercache.cache.v1.Cache.Delete:output_type -> supercache.cache.v1.DeleteResponse - 12, // 65: supercache.cache.v1.Cache.DeleteMany:output_type -> supercache.cache.v1.DeleteManyResponse - 14, // 66: supercache.cache.v1.Cache.BloomAdd:output_type -> supercache.cache.v1.BloomAddResponse - 16, // 67: supercache.cache.v1.Cache.BloomTest:output_type -> supercache.cache.v1.BloomTestResponse - 18, // 68: supercache.cache.v1.Cache.SetAdd:output_type -> supercache.cache.v1.SetAddResponse - 20, // 69: supercache.cache.v1.Cache.SetRemove:output_type -> supercache.cache.v1.SetRemoveResponse - 22, // 70: supercache.cache.v1.Cache.SetContains:output_type -> supercache.cache.v1.SetContainsResponse - 24, // 71: supercache.cache.v1.Cache.SetCard:output_type -> supercache.cache.v1.SetCardResponse - 26, // 72: supercache.cache.v1.Cache.SetMembers:output_type -> supercache.cache.v1.SetMembersResponse - 29, // 73: supercache.cache.v1.Cache.ZAdd:output_type -> supercache.cache.v1.ZAddResponse - 31, // 74: supercache.cache.v1.Cache.ZRem:output_type -> supercache.cache.v1.ZRemResponse - 33, // 75: supercache.cache.v1.Cache.ZScore:output_type -> supercache.cache.v1.ZScoreResponse - 35, // 76: supercache.cache.v1.Cache.ZCard:output_type -> supercache.cache.v1.ZCardResponse - 38, // 77: supercache.cache.v1.Cache.ZRange:output_type -> supercache.cache.v1.ZRangeResponse - 38, // 78: supercache.cache.v1.Cache.ZRangeByScore:output_type -> supercache.cache.v1.ZRangeResponse - 41, // 79: supercache.cache.v1.Cache.GeoAdd:output_type -> supercache.cache.v1.GeoAddResponse - 43, // 80: supercache.cache.v1.Cache.GeoRem:output_type -> supercache.cache.v1.GeoRemResponse - 45, // 81: supercache.cache.v1.Cache.GeoPos:output_type -> supercache.cache.v1.GeoPosResponse - 47, // 82: supercache.cache.v1.Cache.GeoCard:output_type -> supercache.cache.v1.GeoCardResponse - 49, // 83: supercache.cache.v1.Cache.GeoDist:output_type -> supercache.cache.v1.GeoDistResponse - 51, // 84: supercache.cache.v1.Cache.GeoRadius:output_type -> supercache.cache.v1.GeoRadiusResponse - 53, // 85: supercache.cache.v1.Cache.LPush:output_type -> supercache.cache.v1.LPushResponse - 55, // 86: supercache.cache.v1.Cache.RPush:output_type -> supercache.cache.v1.RPushResponse - 57, // 87: supercache.cache.v1.Cache.LPop:output_type -> supercache.cache.v1.LPopResponse - 59, // 88: supercache.cache.v1.Cache.RPop:output_type -> supercache.cache.v1.RPopResponse - 61, // 89: supercache.cache.v1.Cache.LLen:output_type -> supercache.cache.v1.LLenResponse - 63, // 90: supercache.cache.v1.Cache.LIndex:output_type -> supercache.cache.v1.LIndexResponse - 65, // 91: supercache.cache.v1.Cache.LRange:output_type -> supercache.cache.v1.LRangeResponse - 67, // 92: supercache.cache.v1.Cache.HSet:output_type -> supercache.cache.v1.HSetResponse - 69, // 93: supercache.cache.v1.Cache.HGet:output_type -> supercache.cache.v1.HGetResponse - 71, // 94: supercache.cache.v1.Cache.HDel:output_type -> supercache.cache.v1.HDelResponse - 73, // 95: supercache.cache.v1.Cache.HExists:output_type -> supercache.cache.v1.HExistsResponse - 75, // 96: supercache.cache.v1.Cache.HLen:output_type -> supercache.cache.v1.HLenResponse - 78, // 97: supercache.cache.v1.Cache.HGetAll:output_type -> supercache.cache.v1.HGetAllResponse - 80, // 98: supercache.cache.v1.Cache.Incr:output_type -> supercache.cache.v1.IncrResponse - 82, // 99: supercache.cache.v1.Cache.CounterGet:output_type -> supercache.cache.v1.CounterGetResponse - 84, // 100: supercache.cache.v1.Cache.JsonSet:output_type -> supercache.cache.v1.JsonSetResponse - 86, // 101: supercache.cache.v1.Cache.JsonGet:output_type -> supercache.cache.v1.JsonGetResponse - 88, // 102: supercache.cache.v1.Cache.JsonDel:output_type -> supercache.cache.v1.JsonDelResponse - 90, // 103: supercache.cache.v1.Cache.BitSet:output_type -> supercache.cache.v1.BitSetResponse - 92, // 104: supercache.cache.v1.Cache.BitGet:output_type -> supercache.cache.v1.BitGetResponse - 94, // 105: supercache.cache.v1.Cache.BitCount:output_type -> supercache.cache.v1.BitCountResponse - 96, // 106: supercache.cache.v1.Cache.BitPos:output_type -> supercache.cache.v1.BitPosResponse - 98, // 107: supercache.cache.v1.Cache.HLLAdd:output_type -> supercache.cache.v1.HLLAddResponse - 100, // 108: supercache.cache.v1.Cache.HLLCount:output_type -> supercache.cache.v1.HLLCountResponse - 102, // 109: supercache.cache.v1.Cache.TopKAdd:output_type -> supercache.cache.v1.TopKAddResponse - 105, // 110: supercache.cache.v1.Cache.TopKList:output_type -> supercache.cache.v1.TopKListResponse - 107, // 111: supercache.cache.v1.Cache.CMSIncr:output_type -> supercache.cache.v1.CMSIncrResponse - 109, // 112: supercache.cache.v1.Cache.CMSQuery:output_type -> supercache.cache.v1.CMSQueryResponse - 61, // [61:113] is the sub-list for method output_type - 9, // [9:61] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 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 } func init() { file_cache_proto_init() } @@ -6806,7 +7597,7 @@ func file_cache_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cache_proto_rawDesc, NumEnums: 0, - NumMessages: 110, + NumMessages: 123, 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 af33b25..b8f7825 100644 --- a/api/gen/cache/v1/cache_grpc.pb.go +++ b/api/gen/cache/v1/cache_grpc.pb.go @@ -71,6 +71,12 @@ const ( Cache_TopKList_FullMethodName = "/supercache.cache.v1.Cache/TopKList" Cache_CMSIncr_FullMethodName = "/supercache.cache.v1.Cache/CMSIncr" Cache_CMSQuery_FullMethodName = "/supercache.cache.v1.Cache/CMSQuery" + Cache_VAdd_FullMethodName = "/supercache.cache.v1.Cache/VAdd" + Cache_VRem_FullMethodName = "/supercache.cache.v1.Cache/VRem" + Cache_VSim_FullMethodName = "/supercache.cache.v1.Cache/VSim" + Cache_VCard_FullMethodName = "/supercache.cache.v1.Cache/VCard" + Cache_VDim_FullMethodName = "/supercache.cache.v1.Cache/VDim" + Cache_VEmb_FullMethodName = "/supercache.cache.v1.Cache/VEmb" ) // CacheClient is the client API for Cache service. @@ -131,6 +137,12 @@ type CacheClient interface { TopKList(ctx context.Context, in *TopKListRequest, opts ...grpc.CallOption) (*TopKListResponse, error) CMSIncr(ctx context.Context, in *CMSIncrRequest, opts ...grpc.CallOption) (*CMSIncrResponse, error) CMSQuery(ctx context.Context, in *CMSQueryRequest, opts ...grpc.CallOption) (*CMSQueryResponse, error) + VAdd(ctx context.Context, in *VAddRequest, opts ...grpc.CallOption) (*VAddResponse, error) + VRem(ctx context.Context, in *VRemRequest, opts ...grpc.CallOption) (*VRemResponse, error) + VSim(ctx context.Context, in *VSimRequest, opts ...grpc.CallOption) (*VSimResponse, error) + 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) } type cacheClient struct { @@ -661,6 +673,66 @@ func (c *cacheClient) CMSQuery(ctx context.Context, in *CMSQueryRequest, opts .. return out, nil } +func (c *cacheClient) VAdd(ctx context.Context, in *VAddRequest, opts ...grpc.CallOption) (*VAddResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VAddResponse) + err := c.cc.Invoke(ctx, Cache_VAdd_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) VRem(ctx context.Context, in *VRemRequest, opts ...grpc.CallOption) (*VRemResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VRemResponse) + err := c.cc.Invoke(ctx, Cache_VRem_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) VSim(ctx context.Context, in *VSimRequest, opts ...grpc.CallOption) (*VSimResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VSimResponse) + err := c.cc.Invoke(ctx, Cache_VSim_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) VCard(ctx context.Context, in *VCardRequest, opts ...grpc.CallOption) (*VCardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VCardResponse) + err := c.cc.Invoke(ctx, Cache_VCard_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) VDim(ctx context.Context, in *VDimRequest, opts ...grpc.CallOption) (*VDimResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VDimResponse) + err := c.cc.Invoke(ctx, Cache_VDim_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheClient) VEmb(ctx context.Context, in *VEmbRequest, opts ...grpc.CallOption) (*VEmbResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VEmbResponse) + err := c.cc.Invoke(ctx, Cache_VEmb_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. @@ -719,6 +791,12 @@ type CacheServer interface { TopKList(context.Context, *TopKListRequest) (*TopKListResponse, error) CMSIncr(context.Context, *CMSIncrRequest) (*CMSIncrResponse, error) CMSQuery(context.Context, *CMSQueryRequest) (*CMSQueryResponse, error) + VAdd(context.Context, *VAddRequest) (*VAddResponse, error) + VRem(context.Context, *VRemRequest) (*VRemResponse, error) + VSim(context.Context, *VSimRequest) (*VSimResponse, error) + VCard(context.Context, *VCardRequest) (*VCardResponse, error) + VDim(context.Context, *VDimRequest) (*VDimResponse, error) + VEmb(context.Context, *VEmbRequest) (*VEmbResponse, error) mustEmbedUnimplementedCacheServer() } @@ -885,6 +963,24 @@ func (UnimplementedCacheServer) CMSIncr(context.Context, *CMSIncrRequest) (*CMSI func (UnimplementedCacheServer) CMSQuery(context.Context, *CMSQueryRequest) (*CMSQueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CMSQuery not implemented") } +func (UnimplementedCacheServer) VAdd(context.Context, *VAddRequest) (*VAddResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VAdd not implemented") +} +func (UnimplementedCacheServer) VRem(context.Context, *VRemRequest) (*VRemResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VRem not implemented") +} +func (UnimplementedCacheServer) VSim(context.Context, *VSimRequest) (*VSimResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VSim not implemented") +} +func (UnimplementedCacheServer) VCard(context.Context, *VCardRequest) (*VCardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VCard not implemented") +} +func (UnimplementedCacheServer) VDim(context.Context, *VDimRequest) (*VDimResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VDim not implemented") +} +func (UnimplementedCacheServer) VEmb(context.Context, *VEmbRequest) (*VEmbResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VEmb not implemented") +} func (UnimplementedCacheServer) mustEmbedUnimplementedCacheServer() {} func (UnimplementedCacheServer) testEmbeddedByValue() {} @@ -1842,6 +1938,114 @@ func _Cache_CMSQuery_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } +func _Cache_VAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VAddRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VAdd(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VAdd_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VAdd(ctx, req.(*VAddRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_VRem_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VRemRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VRem(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VRem_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VRem(ctx, req.(*VRemRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_VSim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VSimRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VSim(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VSim_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VSim(ctx, req.(*VSimRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_VCard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VCardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VCard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VCard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VCard(ctx, req.(*VCardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_VDim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VDimRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VDim(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VDim_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VDim(ctx, req.(*VDimRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cache_VEmb_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VEmbRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServer).VEmb(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cache_VEmb_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServer).VEmb(ctx, req.(*VEmbRequest)) + } + 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) @@ -2057,6 +2261,30 @@ var Cache_ServiceDesc = grpc.ServiceDesc{ MethodName: "CMSQuery", Handler: _Cache_CMSQuery_Handler, }, + { + MethodName: "VAdd", + Handler: _Cache_VAdd_Handler, + }, + { + MethodName: "VRem", + Handler: _Cache_VRem_Handler, + }, + { + MethodName: "VSim", + Handler: _Cache_VSim_Handler, + }, + { + MethodName: "VCard", + Handler: _Cache_VCard_Handler, + }, + { + MethodName: "VDim", + Handler: _Cache_VDim_Handler, + }, + { + MethodName: "VEmb", + Handler: _Cache_VEmb_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cache.proto", diff --git a/api/openapi/cache.openapi.yaml b/api/openapi/cache.openapi.yaml index 57cbeb0..364d396 100644 --- a/api/openapi/cache.openapi.yaml +++ b/api/openapi/cache.openapi.yaml @@ -34,6 +34,7 @@ info: | ModeHLL | HLLAdd, HLLCount; Delete(name) | | ModeTopK | TopKAdd, TopKList; Delete(name) | | ModeCMS | CMSIncr, CMSQuery; Delete(name) | + | ModeVectorSet | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | Wrong verb for the mode → invalid argument (gRPC InvalidArgument). @@ -97,7 +98,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. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. requestBody: required: true content: @@ -125,7 +126,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. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. requestBody: required: true content: diff --git a/api/proto/cache.proto b/api/proto/cache.proto index 77a2a76..5e6b395 100644 --- a/api/proto/cache.proto +++ b/api/proto/cache.proto @@ -58,6 +58,12 @@ service Cache { rpc TopKList(TopKListRequest) returns (TopKListResponse); rpc CMSIncr(CMSIncrRequest) returns (CMSIncrResponse); rpc CMSQuery(CMSQueryRequest) returns (CMSQueryResponse); + 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); } message GetRequest { @@ -573,3 +579,60 @@ message CMSQueryResponse { bool present = 1; uint64 count = 2; } + +message VAddRequest { + string keyspace = 1; + string name = 2; + bytes member = 3; + repeated float vec = 4; +} +message VAddResponse {} + +message VRemRequest { + string keyspace = 1; + string name = 2; + bytes member = 3; +} +message VRemResponse {} + +message VSimRequest { + string keyspace = 1; + string name = 2; + repeated float vec = 3; + int32 k = 4; +} +message VSimHit { + bytes member = 1; + float score = 2; +} +message VSimResponse { + repeated VSimHit hits = 1; +} + +message VCardRequest { + string keyspace = 1; + string name = 2; +} +message VCardResponse { + int64 n = 1; + bool present = 2; +} + +message VDimRequest { + string keyspace = 1; + string name = 2; +} +message VDimResponse { + int32 dim = 1; + bool present = 2; +} + +message VEmbRequest { + string keyspace = 1; + string name = 2; + bytes member = 3; +} +message VEmbResponse { + repeated float vec = 1; + bool found = 2; +} diff --git a/cmd/sc/cmds.go b/cmd/sc/cmds.go index 4d877a5..f2c918c 100644 --- a/cmd/sc/cmds.go +++ b/cmd/sc/cmds.go @@ -128,6 +128,18 @@ func dispatch(ctx context.Context, sess *session, cmd string, args []string) int return cmdCMSIncr(ctx, sess, args) case "cmsquery": return cmdCMSQuery(ctx, sess, args) + case "vadd": + return cmdVAdd(ctx, sess, args) + case "vrem": + return cmdVRem(ctx, sess, args) + case "vsim": + return cmdVSim(ctx, sess, args) + case "vcard": + return cmdVCard(ctx, sess, args) + case "vdim": + return cmdVDim(ctx, sess, args) + case "vemb": + return cmdVEmb(ctx, sess, args) default: fmt.Fprintf(os.Stderr, "unknown command %q\n", cmd) return 2 diff --git a/cmd/sc/cmds_vecset.go b/cmd/sc/cmds_vecset.go new file mode 100644 index 0000000..3aef211 --- /dev/null +++ b/cmd/sc/cmds_vecset.go @@ -0,0 +1,175 @@ +package main + +import ( + "context" + "fmt" + "os" + "strconv" + "strings" + + "github.com/Code0987/supercache/pkg/client" + "github.com/Code0987/supercache/pkg/engine" +) + +func parseVec(s string) ([]float32, error) { + parts := strings.Split(s, ",") + if len(parts) == 0 { + return nil, fmt.Errorf("empty vector") + } + out := make([]float32, len(parts)) + for i, p := range parts { + f, err := strconv.ParseFloat(strings.TrimSpace(p), 32) + if err != nil { + return nil, err + } + out[i] = float32(f) + } + return out, nil +} + +func cmdVAdd(ctx context.Context, sess *session, args []string) int { + if len(args) != 3 { + fmt.Fprintln(os.Stderr, "usage: vadd ") + return 2 + } + vec, err := parseVec(args[2]) + if err != nil { + fmt.Fprintf(os.Stderr, "vadd: bad vec %q\n", args[2]) + return 2 + } + err = sess.withClient(func(cli *client.Client, _ string) error { + return cli.VAdd(ctx, sess.cfg.keyspace, args[0], []byte(args[1]), vec) + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vadd: %v\n", err) + return 1 + } + if !sess.cfg.quiet { + fmt.Printf("OK vadd %s %s\n", args[0], args[1]) + } + return 0 +} + +func cmdVRem(ctx context.Context, sess *session, args []string) int { + if len(args) != 2 { + fmt.Fprintln(os.Stderr, "usage: vrem ") + return 2 + } + err := sess.withClient(func(cli *client.Client, _ string) error { + return cli.VRem(ctx, sess.cfg.keyspace, args[0], []byte(args[1])) + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vrem: %v\n", err) + return 1 + } + if !sess.cfg.quiet { + fmt.Printf("OK vrem %s %s\n", args[0], args[1]) + } + return 0 +} + +func cmdVSim(ctx context.Context, sess *session, args []string) int { + if len(args) < 2 || len(args) > 3 { + fmt.Fprintln(os.Stderr, "usage: vsim [k]") + return 2 + } + vec, err := parseVec(args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "vsim: bad vec %q\n", args[1]) + return 2 + } + k := 0 + if len(args) == 3 { + parsed, err := strconv.Atoi(args[2]) + if err != nil { + fmt.Fprintf(os.Stderr, "vsim: bad k %q\n", args[2]) + return 2 + } + k = parsed + } + var hits []engine.VSimHit + err = sess.withClient(func(cli *client.Client, _ string) error { + var e error + hits, e = cli.VSim(ctx, sess.cfg.keyspace, args[0], vec, k) + return e + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vsim: %v\n", err) + return 1 + } + for _, h := range hits { + fmt.Printf("%g %s\n", h.Score, h.Member) + } + return 0 +} + +func cmdVCard(ctx context.Context, sess *session, args []string) int { + if len(args) != 1 { + fmt.Fprintln(os.Stderr, "usage: vcard ") + return 2 + } + var n int + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + n, _, e = cli.VCard(ctx, sess.cfg.keyspace, args[0]) + return e + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vcard: %v\n", err) + return 1 + } + fmt.Println(n) + return 0 +} + +func cmdVDim(ctx context.Context, sess *session, args []string) int { + if len(args) != 1 { + fmt.Fprintln(os.Stderr, "usage: vdim ") + return 2 + } + var dim int + var ok bool + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + dim, ok, e = cli.VDim(ctx, sess.cfg.keyspace, args[0]) + return e + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vdim: %v\n", err) + return 1 + } + if !ok { + fmt.Println("missing") + return 1 + } + fmt.Println(dim) + return 0 +} + +func cmdVEmb(ctx context.Context, sess *session, args []string) int { + if len(args) != 2 { + fmt.Fprintln(os.Stderr, "usage: vemb ") + return 2 + } + var vec []float32 + var ok bool + err := sess.withClient(func(cli *client.Client, _ string) error { + var e error + vec, ok, e = cli.VEmb(ctx, sess.cfg.keyspace, args[0], []byte(args[1])) + return e + }) + if err != nil { + fmt.Fprintf(os.Stderr, "vemb: %v\n", err) + return 1 + } + if !ok { + fmt.Println("missing") + return 1 + } + parts := make([]string, len(vec)) + for i, x := range vec { + parts[i] = strconv.FormatFloat(float64(x), 'g', -1, 32) + } + fmt.Println(strings.Join(parts, ",")) + return 0 +} diff --git a/cmd/sc/main.go b/cmd/sc/main.go index 03c7843..1e9d347 100644 --- a/cmd/sc/main.go +++ b/cmd/sc/main.go @@ -112,7 +112,8 @@ func run(args []string) int { "bitset", "bitget", "bitcount", "bitpos", "hlladd", "hllcount", "topkadd", "topklist", - "cmsincr", "cmsquery": + "cmsincr", "cmsquery", + "vadd", "vrem", "vsim", "vcard", "vdim", "vemb": sess := newSession(cfg) defer sess.Close() ctx, cancel := context.WithTimeout(context.Background(), cfg.timeout) @@ -352,6 +353,12 @@ Cache commands (gRPC -addr seeds): topklist ModeTopK chart (item count lines, or (nil)) cmsincr [n] ModeCMS increment (n optional, default 1) cmsquery ModeCMS estimate (or (nil)) + vadd ModeVectorSet upsert + vrem ModeVectorSet remove + vsim [k] ModeVectorSet neighbors + vcard ModeVectorSet member count + vdim ModeVectorSet dim (or missing) + vemb ModeVectorSet vector (or missing) ping Dial cache seeds (+ admin /healthz) Admin commands (HTTP -admin seeds): diff --git a/cmd/sc/repl.go b/cmd/sc/repl.go index aa92072..a8650cf 100644 --- a/cmd/sc/repl.go +++ b/cmd/sc/repl.go @@ -224,6 +224,7 @@ func printREPLHelp() { hlladd|hllcount ModeHLL topkadd|topklist ModeTopK cmsincr|cmsquery ModeCMS + vadd|vrem|vsim|vcard|vdim|vemb ModeVectorSet ping Cache + admin health peers | keyspaces | metrics | health | ready diff --git a/cmd/sc/vecset_cli_test.go b/cmd/sc/vecset_cli_test.go new file mode 100644 index 0000000..14734cc --- /dev/null +++ b/cmd/sc/vecset_cli_test.go @@ -0,0 +1,52 @@ +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 TestVectorSetCLIUsage(t *testing.T) { + if code := run([]string{"vadd"}); code != 2 { + t.Fatalf("vadd: %d", code) + } + if code := run([]string{"vsim", "s"}); code != 2 { + t.Fatalf("vsim: %d", code) + } + if code := run([]string{"vcard"}); code != 2 { + t.Fatalf("vcard: %d", code) + } +} + +func TestVectorSetCLIRoundTrip(t *testing.T) { + eng := engine.New() + defer eng.Close() + if err := eng.UpdateKeySpace(keyspace.Config{ + Name: "items", Mode: keyspace.ModeVectorSet, 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", "items", "-q", "vadd", "set", "a", "1,0"}) + }) + if code != 0 { + t.Fatalf("vadd: exit %d out=%q", code, out) + } + out, code = captureOut(func() int { + return run([]string{"-addr", addr, "-keyspace", "items", "vsim", "set", "1,0", "1"}) + }) + if code != 0 || !strings.Contains(out, "a") { + t.Fatalf("vsim: exit %d out=%q", code, out) + } +} diff --git a/docs/API.md b/docs/API.md index 8aedf9b..31989fd 100644 --- a/docs/API.md +++ b/docs/API.md @@ -48,8 +48,9 @@ Each keyspace has exactly one mode. Verbs that do not match the mode return inva | `ModeHLL` | Named HyperLogLog sketch | `HLLAdd`, `HLLCount`; `Delete(name)` | | `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)` | -Config: `pkg/keyspace.Config` (`Name`, `Mode`, `MaxBytes`, `TTL`, `ReplicationFactor`, …). Bloom also uses `BloomBits` / `BloomHashes`. ModeTopK uses `TopKSize` (0 → 100). +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). ## Cache gRPC RPCs diff --git a/docs/CLUSTER_FLOWS.md b/docs/CLUSTER_FLOWS.md index 3e06ce7..d713c15 100644 --- a/docs/CLUSTER_FLOWS.md +++ b/docs/CLUSTER_FLOWS.md @@ -339,6 +339,7 @@ KV Get/Put diagrams above apply only to `ModeCacheOnly` / `ModeLoadThrough`. | ModeHLL | owner `HLLAdd` then **`FlagHLL` snapshot** | `HLLCount` | dense 12 KiB registers | | 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 | `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). @@ -413,7 +414,7 @@ flowchart TD E --> E8["Owner down on Get"] E --> E9["Owner down on ForwardPut"] E --> E10["SetAdd / ZAdd / BloomAdd / HSet"] - E --> E11["SetContains / ZScore / BloomTest / HGet / CounterGet / JsonGet / BitGet / HLLCount / TopKList / CMSQuery"] + E --> E11["SetContains / ZScore / BloomTest / HGet / CounterGet / JsonGet / BitGet / HLLCount / TopKList / CMSQuery / VSim"] E1 --> A1["Owner apply + async ApplyPut × R−1"] E2 --> A2["Local store only"] diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 667d6d0..3b0196e 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -74,6 +74,7 @@ Peer mesh with mTLS: every node uses the same CA; each node presents a cert sign | `ModeHLL` | HLLAdd / HLLCount / Delete(name) | dense 12 KiB sketch; snapshot fan-out + owner-inbox; replica HLLCount may lag | | `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 | Wrong verb for the mode → invalid argument. Configure the same modes on every node (see rollout above). @@ -98,6 +99,7 @@ Demo node (`-demo-keyspace`): registers `demo` (CacheOnly), `tags` (ModeSet), `b | HLLAdd / HLLCount | `ModeHLL` only. Owner applies the op then fans out a **full `FlagHLL` snapshot** (inbox `FlagHLLAdd` stays owner-only). Replica `HLLCount` may lag. Missing name → `ok=false`. `HLLAdd` is ACK-only (no changed-bool). | | 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/). | | 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 57cbeb0..364d396 100644 --- a/docs/api/cache.openapi.yaml +++ b/docs/api/cache.openapi.yaml @@ -34,6 +34,7 @@ info: | ModeHLL | HLLAdd, HLLCount; Delete(name) | | ModeTopK | TopKAdd, TopKList; Delete(name) | | ModeCMS | CMSIncr, CMSQuery; Delete(name) | + | ModeVectorSet | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | Wrong verb for the mode → invalid argument (gRPC InvalidArgument). @@ -97,7 +98,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. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. requestBody: required: true content: @@ -125,7 +126,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. + Invalid on ModeBloom / ModeSet / ModeZSet / ModeGeo / ModeList / ModeHash / ModeCounter / ModeJSON / ModeBitmap / ModeHLL / ModeTopK / ModeCMS / ModeVectorSet. requestBody: required: true content: diff --git a/docs/design/2026-09-04-mode-vector-set.md b/docs/design/2026-09-04-mode-vector-set.md new file mode 100644 index 0000000..858b9ae --- /dev/null +++ b/docs/design/2026-09-04-mode-vector-set.md @@ -0,0 +1,439 @@ +# Vector set type (`ModeVectorSet`) + +**Author:** SuperCache +**Status:** approved (chat: ok got it, lets continue) +**Branch (later):** `feat/mode-vector-set` +**Date:** 2026-09-04 + +## Overview + +SuperCache can store exact membership (`ModeSet`), scored members (`ModeZSet`), and lon/lat points (`ModeGeo`). It cannot store a **named set of embedding vectors** and answer “which members are closest to this query vector?” + +`ModeVectorSet` is a Redis-8-like **vector set**: one keyspace name holds many members, each member has a fixed-dimension `float32` vector. v1 is brute-force K-NN under the store mutex with a **keyspace metric** (`cosine` default, plus `l2` and `ip`), **snapshot fan-out**, feature-folder layout (`pkg/vecset` + `pkg/vecset/eng`). It is **not** an ANN service, not HNSW, not Faiss, and not RESP `VADD`/`VSIM` 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 vector set | +|------|----------------------------| +| `ModeZSet` | Score is a scalar the **caller writes**. No multi-dim vector, no cosine. | +| `ModeGeo` | 2-D lon/lat + haversine. Not an embedding. | +| `ModeHash` | Field → blob. No similarity. A client-side map in one KV value is a lost-update race under LWW `Put`. | +| `ModeJSON` | Path set/get. Same LWW-blob problem if the client stores `[{id, vec}]`. | +| CacheOnly `Put` | Whole-blob replace. Two clients adding different members clobber each other. | + +There is no verb that takes `(name, member, []float32)` and a query that returns top-K neighbors. `Get`/`Put` cannot express it without overloading Put and still having no `VSim`. + +Dedicated use: a small in-process “similar items” cache (tracks, products, docs) where dim is tens–hundreds and cardinality per name is hundreds — RAM-only, owner-serialized, RF copies of one blob. + +## Goals & Non-Goals + +### Goals + +- Named, keyspace-scoped vector set with v1 verbs: `VAdd`, `VRem`, `VSim`, `VCard`, `VDim`, `VEmb`, plus existing `Delete(name)`. +- Brute-force K-NN. Keyspace metric: **cosine** (default), **L2**, **inner product**. First successful `VAdd` locks dim on that name. +- Owner ACK + async `FlagVectorSet` **snapshot** to RF−1. No new Peer RPC. +- Present-bit on the **name**. Missing ≠ empty result that looks like “dim 0”. +- Feature folders: codec `pkg/vecset`, owner write / GetOrLoad `pkg/vecset/eng`, thin `pkg/engine/vecset.go`. +- Product docs + `cmd/sc` + `examples/vecset` in the **same** implementation PR. + +### Non-goals + +- Redis 8 Vector Sets numeric/wire parity (`VADD REDUCE`, `Q8`/`BIN`/`NOQUANT`, `EF`, `M`, `FILTER`, `SETATTR`, `VLINKS`, `VRANDMEMBER`, `TRUTH`). +- HNSW / IVF / PQ / Faiss / CGO / GPU / disk indexes. +- Per-**query** metric (would disagree across RF and need an sc flag). Metric is a keyspace knob, like `TopKSize`. +- Extra metrics beyond cosine / L2 / IP (L1, angular, Hamming, Mahalanobis). +- `VSim` by member id (caller does `VEmb` then `VSim`). +- Quantization, dim reduce, JSON attributes, CAS, per-member TTL. +- A default `-demo-keyspace` on `supercache-node` (optional follow-up, like HLL/CMS). +- Changing RF, tombstones, hint identity, or any existing mode contract. +- Persistence / WAL / Redis dump import. +- New scbench YAML cells. +- Expanding `Entry.Flags` to `uint64`. + +## Contract + +### API (new; existing verbs unchanged) + +On `pkg/engine.Engine`, `pkg/client.Client`, Cache proto, and `cmd/sc` (same PR): + +| Call | Meaning | +|------|---------| +| `VAdd(ks, name, member, vec []float32)` | Insert or **replace** `member`’s vector. Creates the set if missing. ACK-only. | +| `VRem(ks, name, member)` | Remove `member` if present. No-op if missing / set missing. | +| `VSim(ks, name, vec []float32, k int) ([]VSimHit, error)` | Top-`k` by the keyspace metric, **best first**. Missing name → empty, nil error. | +| `VCard(ks, name) (n int, present bool, err error)` | Member count. Missing → `present=false`, `n=0`. | +| `VDim(ks, name) (dim int, present bool, err error)` | Locked dim. Missing → `present=false`. Empty live set still reports dim. | +| `VEmb(ks, name, member) (vec []float32, ok bool, err error)` | Stored vector copy. Missing name/member → `ok=false`. | +| `Delete(ks, name)` | Existing Delete: whole-set tombstone. | + +```go +type VSimHit struct { + Member []byte + Score float32 // meaning depends on keyspace VectorMetric (below) +} + +// pkg/keyspace — next to Mode / TopKSize +type VectorMetric int + +const ( + VectorMetricCosine VectorMetric = iota // 0 = default + VectorMetricL2 + VectorMetricIP +) +``` + +| `VectorMetric` | `Score` | Best first | +|----------------|---------|------------| +| `cosine` (0) | `dot/(\|a\|\|b\|)` in **[-1, 1]** | high → low | +| `l2` | Euclidean distance **≥ 0** | low → high | +| `ip` | raw `dot(a,b)` (unbounded) | high → low | + +Do **not** negate L2 to fake a similarity. Callers that need “higher is better” pick cosine or IP. Equal scores still break ties by member bytes ascending. + +- `name` is the set identity; ring owner = `Owner(name)`. +- `member` is `[]byte`, **1..255 bytes** (tighter than ZSet/Geo: snapshot codec uses `u8 idLen`). Empty or `len>255` → `ErrInvalidArgument` even when `MaxKeyLen` is 512. Engine still rejects `len > e.maxKeyLen` first if someone raises the 255 cap later. +- `vec` is IEEE-754 `float32`. Length must match the locked dim (or keyspace `VectorDim` when set). NaN / ±Inf → `ErrInvalidArgument`. **Zero vector:** invalid on **cosine** (`VAdd` and `VSim`); **allowed** on L2 and IP. +- `k <= 0` means **10**. `k > 50` is clamped to **50**. Result length is `min(k, card)`. Ties by **member bytes ascending**. +- Get/Put on `ModeVectorSet` → `ErrInvalidArgument` (`use VEmb` / `use VAdd`), same as ModeCMS / ModeZSet. +- Vector verbs on any other mode → `ErrInvalidArgument`. +- Last `VRem` **keeps** an empty live set (dim stays locked, `VCard=0`, `VSim` empty). `Delete` is what drops the name. + +**Check order (locked, CMS-shaped):** validate **before** present-bit. Empty member, `len(member)>255`, NaN/Inf, cosine zero-vector, `len(vec)<2` or `>256`, `len(vec) ≠ VectorDim` when keyspace `VectorDim>0`, `len(vec) ≠` locked dim when the name is **live** → `ErrInvalidArgument` even if the name is missing. Then missing name / owner-down / GetOrLoad `!Found` / `!IsVectorSet` → `VSim` empty + nil error; `VCard`/`VDim` `present=false`; `VEmb` `ok=false`. First `VAdd` with `VectorDim=0` still rejects dim 1 (not a legal lock). + +### Keyspace + +New mode `keyspace.ModeVectorSet` (next iota after `ModeCMS` = 13, so **14**). + +| Field | Role | +|-------|------| +| `MaxValueSize` | Per-blob cap (default `1 MiB`). Validate rejects if `WorstEncodedSize` > this (TopK pattern). | +| `MaxBytes` | LRU **store** budget, not the per-blob cap. May be smaller than a live set; `lruVictim` **must not** evict live `FlagVectorSet` (CMS/HLL). | +| `TTL` | Expires the **whole** set | +| `TombstoneTTL` | On `Delete(name)` | +| `ReplicationFactor` | Same as KV | +| `VectorDim` | **0** = unlocked until first `VAdd`. **>0** = every `VAdd`/`VSim` vector must be this dim. Range **2..256**. Included in `Config.ConfigHash`. | +| `VectorMetric` | **0 / unset = cosine**. Else `l2` or `ip`. Included in `Config.ConfigHash`. Not stored in the blob (brute force has no index). `UpdateKeySpace` **may** change it; the next `VSim` uses the new metric. | + +No `VectorMaxMembers` knob. Cardinality is bounded by encoded size vs `MaxValueSize` (see encoding). Metric is **not** per `VSim` call and **not** per name. + +`Validate` (like ModeTopK): + +- `VectorDim < 0` or `VectorDim == 1` or `VectorDim > 256` → error. +- `VectorMetric` not in `{0, cosine, l2, ip}` → error. (`String()` / config parse: `"cosine"`, `"l2"`, `"ip"`; empty/`""` → cosine.) +- `WorstEncodedSize(dim, maxMembers, maxMemberLen) > MaxValueSize` → error, where `maxMembers` is the v1 cap **512** and `dim` is `VectorDim` if set, else **256**. + +### Caps (locked) + +| Cap | Value | Why | +|-----|------:|-----| +| Dim | 2..256 | Fits 512 members in 1 MiB even with 255-byte ids (`5 + 512*(1+255+1024) = 655365` ≈ 640 KiB). | +| Members / name | 512 | Brute-force 512×256 dots is fine under the store mutex; 4096×1024 is not a 1 MiB snapshot. | +| `k` | 1..50 (default 10) | Result set, not index size. | +| Member id | 1..255 bytes | `u8 idLen`. Tighter than ZSet/Geo (`MaxKeyLen` default is **512**, uvarint). Validate uses `min(MaxKeyLen, 255)`. | + +### Replication + +**Snapshot family** (HLL / CMS / TopK / List), **not** item fan-out. + +| Path | Flag / payload | Who applies | +|------|----------------|-------------| +| Non-owner → owner | `FlagVectorSet` + **inbox** prefix (`A` add / `R` rem) | Owner only. Replicas **ignore** inbox (same as `FlagTopKAdd` / `FlagCMSIncr`). | +| Owner → RF−1 | `FlagVectorSet` + **snapshot** prefix (`S`) | Replicas `VSInstall` LWW (`incoming > local`). | +| Join / GetOrLoad | snapshot | RF holders install; non-replicas do not keep a copy. | + +`hintID` stays `(ks, name)`. That is why item-level `VAdd` fan-out is **rejected**: two adds of different members coalesce to one hint; K-NN on the replica would miss a neighbor. Snapshot after owner mutate is complete. + +Hint-loss of a **snapshot** is the usual “replica lags one successful replay” story. Replica `VSim` may miss a just-added member for one fan-out RTT. + +### Who stores a copy + +- **Owner:** always, after ACK. +- **Replicas (RF−1):** snapshot copy. Local `VSim` / `VEmb` / `VCard` / `VDim` allowed (may lag). +- **Non-replicas:** no copy. Writes forward inbox to owner. Reads use CMS/HLL `FetchOwner`: `GetOrLoad` the snapshot, **compute `VSim`/`VEmb`/`VCard`/`VDim` on the caller**, `VSInstall` **only if** `holdsReplica`. Owner does **not** run K-NN for other nodes (would hold the owner store mutex on every remote query). Owner-down / `!Found` / `!IsVectorSet` → miss (empty / `present=false` / `ok=false`, **nil error** — not `ErrUnavailable`). + +### Failure / TTL + +- Owner down → **writes** return the existing owner-forward error (same as `HLLAdd`). **Reads** that need GetOrLoad treat owner-down as a miss (nil error), same as CMS/HLL. +- Wrong dim / zero / NaN / oversize / too many members → `ErrInvalidArgument` **before** mutate; no version bump. +- Full (512 members and `VAdd` of a **new** id) → `ErrInvalidArgument` (`vector set full`). Replace of an existing id is allowed. +- Encoded blob > `MaxValueSize` → reject (should not happen if Validate + caps hold). +- TTL expires the whole name. Per-member expire is not v1. + +### Existing clients + +Unchanged. New mode is opt-in via `UpdateKeySpace`. Old binaries without the RPCs cannot use the type (same as every new mode). + +## Approach + +### Flag bit (last `uint32` slot) + +`pkg/store/entry.go` uses bits 0..30. **One bit left:** + +```go +FlagVectorSet uint32 = 1 << 31 // snapshot *or* owner-inbox; discriminate by Value[0] + +func (e Entry) IsVectorSet() bool { return e.Flags&FlagVectorSet != 0 } +``` + +`1<<31` is `0x80000000` on unsigned `uint32` (same as `peer.proto` `uint32 flags`). Use the existing `IsCMS` bitwise helper style. Do **not** assign the shift to `int32` or compare Flags as signed. + +Do **not** add `FlagVectorSetAdd` / `FlagVectorSetRem` (would require `uint64` Flags). Payload byte 0: + +| `Value[0]` | Meaning | Replicas | +|------------|---------|----------| +| `'S'` | Snapshot | `VSInstall` | +| `'A'` | Inbox add: `A` + u8 idLen + member + dim×float32 LE | Ignore if not owner | +| `'R'` | Inbox rem: `R` + member (remainder of `Value` is the id; 1..255 bytes) | Ignore if not owner | + +### Snapshot encoding (`pkg/vecset`) + +Little-endian, no compression: + +``` +'S' | u16 dim | u16 count | record* +record = u8 idLen | id | dim × float32 +``` + +Header is **5** bytes. `WorstEncodedSize(dim, count, idLen) = 5 + count*(1 + idLen + 4*dim)`. `idLen` 1..255. Count 0..512. Dim 2..256. The 1 MiB proof is `5 + 512*(1+255+1024) = 655365` bytes (~640 KiB). + +`Decode` rejects truncated / leftover / dim 0 / count>512 / NaN/Inf. Empty set (`count=0`) is valid and **still carries dim**. + +Store the blob **value-in-place** in `Entry.Value` (HLL/CMS/TopK class). **No** `vCache` / dirty map. Under the store mutex: decode → mutate map → encode → write `Value`, `Flags=FlagVectorSet`, `Version=local+1`. + +`VSim` under the same mutex: score each member with the **keyspace** metric, partial sort top-`min(k,card)` (cosine/IP high→low, L2 low→high), ties by member bytes ascending, clone member bytes into the result (caller owns the slice). + +``` +cosine: score = dot(a,b) / (||a|| * ||b||) // [-1, 1] +l2: score = sqrt(sum (a_i-b_i)^2) // >= 0 +ip: score = dot(a,b) // unbounded +``` + +Vectors are stored **raw** (not pre-normalized) so `VEmb` round-trips and the same blob works if `VectorMetric` is changed. Cosine still rejects a zero stored vector (cannot have been added) and a zero query. L2/IP may store and query zeros. + +### Engine / feature folder + +``` +pkg/vecset/ # package vecset — codec only + codec.go + codec_test.go + engine_test.go # package vecset_test; engine.New() + engine_cluster_test.go + engine_bench_test.go + eng/ # package eng + host.go # Host interface (Store, Owner, Replicate, …) + local.go # owner VAdd/VRem/VSim/VCard/VDim/VEmb + GetOrLoad + cluster.go # FetchOwner via Host + +pkg/engine/vecset.go # public VAdd/… : validate, route, vecseteng.* +pkg/store/vecset.go # Memory.VAdd / VRem / VSim / HasVectorSet / VSInstall +``` + +`ApplyPut` (`pkg/engine/engine.go`) — **one** `if ent.IsVectorSet()` block (do not fall through to `AcceptIfNewer`): + +- empty `Value` or unknown prefix → `return false, nil` +- `'A'` / `'R'` → if not owner, `return false, nil`; else mutate. Wire `Version` is a tombstone **gate** only; stored version is `1` on create / `local+1` under the store mutex +- `'S'` → `VSInstall` (LWW replace, `incoming > local`) + +Owner write assigns version **under the store mutex** (`local.Version+1`). Do **not** pre-lock `nextVersion` as stored. + +### CMS-class wiring (required) + +Same plumbing CMS/TopK/HLL needed. An engineer who only adds `vecset.go` + two ApplyPut lines will ship a type whose cluster reads 500 and whose sets vanish under MaxBytes. + +| Hook | File | What | +|------|------|------| +| `GetOrLoadLocal` Peek + `IsVectorSet` | `pkg/engine/cluster.go` (CMS ~389) | Client `Get` rejects the mode; without this, owner `GetOrLoad` returns invalid-argument and replica/non-replica `VSim` cannot load a snapshot. | +| `lruVictim` skip live `FlagVectorSet` | `pkg/store/memory.go` | Else MaxBytes pressure evicts a live set. | +| `store.Store` methods | `pkg/store/store.go` | `VAdd` / `VRem` / `VSim(name, vec, k, metric)` / `VCard` / `VDim` / `VEmb` / `HasVectorSet` / `VSInstall` | +| `modeHost` | `pkg/engine/modehost.go` | `HasVectorSet()`, `VectorDim()`, `VectorMetric()` (like `TopKSize()`) | +| `Config.ConfigHash` | `pkg/keyspace` | **Must** include `VectorDim` **and** `VectorMetric` (BloomBits / TopKSize are hashed). | +| `Mode.String()` | `pkg/keyspace` | `"VectorSet"` (else admin prints `Mode(14)`). | + +### sc CLI + +| CLI | Maps to | Output | +|-----|---------|--------| +| `vadd ` | `VAdd` | `OK vadd ` | +| `vrem ` | `VRem` | `OK vrem …` | +| `vsim [k]` | `VSim` | `score member` per line | +| `vcard ` | `VCard` | integer; missing → `0`, exit 0. Present-bit is `vdim`. | +| `vdim ` | `VDim` | integer or `missing` + exit 1 | +| `vemb ` | `VEmb` | comma-separated floats or `missing` + exit 1 | + +Session `-keyspace` same as other verbs. Empty args → usage exit 2. **No** `vsim -metric` flag — metric is on the keyspace (`UpdateKeySpace` / node config), like `TopKSize`. + +### Example + +`examples/vecset`: three nodes, ModeVectorSet `items`, add ~8 members (dim 4 is enough), `VSim` a query, print neighbors. Not a billboard rewrite. + +### Proto / docs (same PR) + +`api/proto/cache.proto` (CMS-shaped; `repeated float` is packed on the wire): + +``` +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); + +message VAddRequest { string keyspace = 1; string name = 2; bytes member = 3; repeated float vec = 4; } +message VAddResponse {} +message VRemRequest { string keyspace = 1; string name = 2; bytes member = 3; } +message VRemResponse {} +message VSimRequest { string keyspace = 1; string name = 2; repeated float vec = 3; int32 k = 4; } +message VSimHit { bytes member = 1; float score = 2; } +message VSimResponse { repeated VSimHit hits = 1; } // no present; missing = empty +message VCardRequest { string keyspace = 1; string name = 2; } +message VCardResponse{ int64 n = 1; bool present = 2; } +message VDimRequest { string keyspace = 1; string name = 2; } +message VDimResponse { int32 dim = 1; bool present = 2; } +message VEmbRequest { string keyspace = 1; string name = 2; bytes member = 3; } +message VEmbResponse { repeated float vec = 1; bool found = 2; } +``` + +Also: regen `api/gen`; `pkg/client`; `internal/cacheserver`; `internal/grpcmap` (wrong-mode / invalid → InvalidArgument); `api/openapi/cache.openapi.yaml` + `docs/api/cache.openapi.yaml`; `docs/API.md`; `docs/OPERATIONS.md`; root `README.md` mode table. + +**`PLAN.md` (same PR)** — append ModeVectorSet next to ModeCMS everywhere that list ends today: + +- §5 Get/Put invalid list; Delete “named … CMS” sentence; verb table: `VAdd` / `VSim` / `VRem` / `VCard` / `VDim` / `VEmb` +- §7 mode table row: `| ModeVectorSet | set name | VAdd, VRem, VSim, VCard, VDim, VEmb; Delete(name) | FlagVectorSet snapshot; owner-inbox same flag + A/R prefix |` +- §7 short section (CMS-shaped): brute force; `VectorMetric` cosine/L2/IP; dim lock; snapshot fan-out; Get/Put invalid +- §9.6 `lruVictim` protect `FlagVectorSet`; Config snippet `VectorDim int`, `VectorMetric VectorMetric` +- §11 Engine API + §14 Cache RPCs +- Package map: `pkg/vecset` (not `listx` — that path is already `pkg/list` on `main`) + +**`docs/CLUSTER_FLOWS.md`:** structured-types table row `ModeVectorSet | owner VAdd/VRem then FlagVectorSet snapshot | VSim / VEmb / VCard / VDim | brute-force cosine/L2/IP`; E11 node adds `VSim`. + +## Tests (write these first, after approval) + +| Test | Package | Asserts | +|------|---------|---------| +| Codec round-trip / reject NaN / leftover | `pkg/vecset` | encode→decode equal; bad blobs error | +| `VAdd` locks dim; mismatch rejected | `pkg/store` | second dim ≠ first → error; version unchanged | +| Replace same member | `pkg/store` | `VCard` unchanged; `VEmb` new vec; version +1 | +| `VRem` last keeps empty + dim | `pkg/store` | `HasVectorSet`; `VDim` still dim; `VCard=0` | +| Full at 512 | `pkg/store` | 513th **new** id rejected; replace still works | +| Cosine order | `pkg/store` | query closer to A than B → A first; scores in [-1,1] | +| L2 order | `pkg/store` | nearer neighbor first; scores ≥ 0; smaller first | +| IP order | `pkg/store` | larger dot first | +| Cosine rejects zero; L2 accepts | `pkg/engine` | cosine `VAdd`/`VSim` zero → invalid; L2 keyspace accepts | +| Zero / Inf / dim-1 vector | `pkg/engine` | `ErrInvalidArgument` even if name missing | +| `VSim` missing vs bad vec | `pkg/engine` | NaN query → invalid; missing + legal vec → empty | +| 256-byte member | `pkg/store` | rejected (`u8` cap), version unchanged | +| Cosine ties | `pkg/store` | equal scores ordered by member bytes | +| `k > card` | `pkg/store` | returns `card` hits | +| Get/Put rejected | `pkg/engine` | `use VEmb` / `use VAdd` | +| Wrong mode | `pkg/engine` | `VAdd` on ModeSet / `ZAdd` on ModeVectorSet fail | +| `GetOrLoadLocal` Peek | `pkg/engine` | owner GetOrLoad returns snapshot (Get still rejected) | +| Live set not evicted | `pkg/store` | MaxBytes=1 still keeps FlagVectorSet | +| `ConfigHash` / `String` | `pkg/keyspace` | hash changes with VectorDim **and** VectorMetric; `String()=="VectorSet"` | +| Owner ACK + replica `VSim` | `pkg/vecset` cluster | after wait, replica sees member | +| Inbox on non-owner replica ignored | `pkg/engine` | `'A'` apply on non-owner → not applied | +| Snapshot LWW | `pkg/store` | lower version `VSInstall` dropped | +| Non-replica `VSim` | `pkg/vecset` cluster | result from owner; non-replica `HasVectorSet` false | +| `Delete` tombstone | `pkg/engine` | `VCard` missing; stale snapshot does not resurrect | +| Join handoff | `pkg/engine` | joiner RF holder gets snapshot; `VSim` works | +| sc usage | `cmd/sc` | missing args exit 2; `vadd`/`vsim` against in-process node | +| Example | `examples/vecset` | `VSim` returns the planted neighbor | + +## Bench risk + +- **Hot path?** No. Get-hit / Put do not decode vector sets. +- **Must stay flat:** `BenchmarkEngineGetHit` allocs **15**, `BenchmarkStoreGetHit` allocs **2**. +- New micros only (not smoke): `BenchmarkStoreVAdd`, `BenchmarkStoreVSim` (dim=64, n=128, k=10). Report ns/op; no CI gate on them in v1. +- `VSim` holds the store mutex for O(n·dim). Caps keep this bounded. Do not add a Get/Peek helper that decodes vector sets. + +## Alternatives Considered + +| Alternative | Why rejected | +|-------------|--------------| +| Item fan-out `FlagVectorSetAdd` like ZSet | `hintID` is `(ks, name)` — second add clobbers the first hint; K-NN would miss neighbors. Also **no flag bits left**. | +| HNSW in v1 | Graph + LWW snapshot is a second design (rebuild vs serialize layers). Brute force is honest at n≤512. | +| Store one KV per member (`vec:name:id`) | No atomic K-NN over the name; ring ownership splits the set; Get-hit path polluted. | +| Reuse ModeZSet scores | Scalar only. | +| Expand `Flags` to `uint64` | Touches every peer codec and every mode. Separate refactor, not this PR. | +| Per-query metric on `VSim` | RF nodes and `sc` would disagree; metric is a keyspace knob like `TopKSize`. | +| L1 / Hamming / more metrics | Cosine + L2 + IP is the usual trio; each extra metric is a sort-direction + zero-vector rule. | +| Pre-normalize on `VAdd` | `VEmb` would not round-trip; L2/IP would be wrong; cosine of raw vectors is enough. | +| Drop empty set on last `VRem` | Would unlock dim and surprise `VDim`. Keeping empty matches “name still exists”. | + +## Security & Privacy + +Embeddings can be PII-adjacent (text/image features). Same trust model as other values: mesh is internal, client gRPC auth is existing TLS/mTLS. No extra ACL. Vectors are not logged. `sc vemb` prints floats to stdout (operator already can `Get` other types). + +## Observability + +Reuse store hit/miss + existing fan-out / hint / `staleSkip` counters. No new required metrics in v1. Wrong-mode and validation map through `grpcmap` as InvalidArgument. + +## Rollout + +1. Merge one PR on `feat/mode-vector-set` after tests + local Get-hit bench + CI bench gate. +2. Callers opt in with `UpdateKeySpace{Mode: ModeVectorSet}`. +3. Rollback = don’t create the keyspace; binary rollback is the usual “old node doesn’t understand Mode 14 / `FlagVectorSet`” — mixed-version mesh is **not** supported for new flags (same as CMS). Roll the whole mesh. + +## Risks + +| Risk | Severity | Mitigation | +|------|----------|------------| +| Last `uint32` flag | high | One flag + payload prefix. Document that the **next** structured type needs `uint64` Flags or a packed op nibble. | +| `VSim` holds store mutex | medium | Cap n=512, dim=256; no Get-hit involvement. | +| Snapshot size | medium | Validate worst-case vs `MaxValueSize`; default 1 MiB. | +| Replica lag | low | Same as CMS snapshot; accepted. | +| Mixed-version peers | medium | Same as every new flag; roll mesh together. | + +## Open Questions + +None that block implementation. Defaults above are locked for v1 (including member ids 1..255, last-`VRem` keeps empty, dim cap 256, metrics cosine/L2/IP at keyspace scope). User can override before approval. + +## Key Decisions + +1. **New mode, not a ZSet hack** — need a vector + K-NN, not a caller-written scalar. +2. **Brute force + three metrics** — cosine (default), L2, IP. Keyspace `VectorMetric`, not per query. HNSW is a later design. +3. **Snapshot fan-out, not item hints** — K-NN cannot survive hint coalesce; also only one flag bit remains. +4. **One flag (`1<<31`) + `'S'`/`'A'`/`'R'` prefix** — avoid `uint64` Flags in this PR. +5. **Value-in-place, no dirty cache** — Get/Put never see this blob; decode only on vector verbs. +6. **Version under store mutex** — same as Hash/List/CMS so replica LWW cannot drop the later snapshot. +7. **Dim locked on first add** — optional `VectorDim` keyspace field for fail-fast. +8. **Caps 256 / 512 / 1 MiB** — keep one LRU entry and one mutex-held `VSim` bounded. +9. **Feature folders** — match post-#43 layout; public API stays `engine.Engine.VAdd`. +10. **One implementation PR** — SuperCache rule; demo node KS is the only optional follow-up. +11. **Non-replica reads are CMS `FetchOwner`** — GetOrLoad snapshot, compute on the **caller**, install only if `holdsReplica`. Owner does not run K-NN for others. Owner-down is a miss, not `ErrUnavailable`. +12. **Last rem keeps empty set** — dim stays locked until `Delete`. +13. **CMS-class wiring is in v1** — `GetOrLoadLocal`, `lruVictim`, `store.Store`, `modeHost.VectorDim`/`VectorMetric`/`HasVectorSet`, `ConfigHash`, `Mode.String()`. +18. **Zero vector is cosine-only invalid** — L2/IP may store and query zeros. +19. **VSim sort is metric-specific** — cosine/IP high→low; L2 low→high. Score is not rewritten into a fake similarity. +14. **Member ids 1..255** — `u8 idLen`; not `MaxKeyLen` (512). +15. **Validate before present-bit** — bad vec/member is invalid even on a missing name. +16. **ApplyPut is one `IsVectorSet` block** — unknown prefix never falls through to `AcceptIfNewer`. +17. **VSim ties** — `min(k,card)`; equal cosine by member bytes ascending. + +## PR Plan + +### PR 1 — `feat/mode-vector-set` (this design) + +- **Title:** `feat: add ModeVectorSet (VAdd / VSim / VRem)` +- **Depends on:** nothing (landed on current `main`) +- **Files:** `pkg/keyspace` (`Mode`, `String`, `Validate`, `ConfigHash`, `VectorDim`, `VectorMetric`), `pkg/store/entry.go` + `store.go` + `vecset.go` + `memory.go` `lruVictim`, `pkg/vecset/**`, `pkg/vecset/eng/**`, `pkg/engine/vecset.go` + `engine.go` ApplyPut/Get/Put + `cluster.go` GetOrLoadLocal + `modehost.go`, proto + gen, `pkg/client`, `internal/cacheserver`, `internal/grpcmap`, `cmd/sc`, OpenAPI, `docs/API.md`, `docs/OPERATIONS.md`, `PLAN.md`, `docs/CLUSTER_FLOWS.md`, README, `docs/design/README.md` shipped row, `examples/vecset` +- **Changes:** tests first from the table; minimum code to pass `go test ./...`; local Get-hit / StoreGetHit allocs unchanged; CI bench ±10% gate. + +### PR 2 — optional follow-up only + +- **Title:** `feat: demo keyspace embeddings=ModeVectorSet` +- **Depends on:** PR 1 merged +- **Files:** `cmd/supercache-node/main.go`, node README +- **Changes:** `-demo-keyspace` registers `embeddings` so `sc -keyspace embeddings vadd …` works on a stock node. + +## References + +- [WORKFLOW.md](../WORKFLOW.md) +- [2026-09-01-mode-cms.md](./2026-09-01-mode-cms.md) — snapshot + inbox-ignore-on-replica + last flag bits +- [2026-08-13-mode-zset.md](./2026-08-13-mode-zset.md) — named members + sc verbs +- [2026-09-02-feature-folders.md](./2026-09-02-feature-folders.md) — `pkg//eng` +- Redis 8 Vector Sets (`VADD`/`VSIM`) — product analog only, not a wire target +- `pkg/store/entry.go` flags 0..30; `pkg/keyspace/config.go` `ModeCMS` last iota; `DefaultMaxValueSize = 1<<20` diff --git a/docs/design/README.md b/docs/design/README.md index 9c8c463..a38b5be 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -6,6 +6,12 @@ Use the template in [WORKFLOW.md](../WORKFLOW.md). Status is `draft` until revie Do not implement from a draft. +## Drafts (not approved) + +| Design | Feature | +|--------|---------| +| [2026-09-04-mode-vector-set.md](./2026-09-04-mode-vector-set.md) | `ModeVectorSet` (approved in chat; implementing on `feat/mode-vector-set`) | + ## Shipped (reference) | Design | Feature | diff --git a/examples/vecset/demo.go b/examples/vecset/demo.go new file mode 100644 index 0000000..1a8825f --- /dev/null +++ b/examples/vecset/demo.go @@ -0,0 +1,77 @@ +package main + +import ( + "context" + "fmt" + "io" + "time" + + "github.com/Code0987/supercache/internal/testcluster" + "github.com/Code0987/supercache/pkg/client" + "github.com/Code0987/supercache/pkg/keyspace" +) + +const ks = "items" + +func runDemo(out io.Writer) error { + c, err := testcluster.Start(testcluster.Config{ + Nodes: 3, + Keyspaces: []keyspace.Config{{ + Name: ks, Mode: keyspace.ModeVectorSet, + MaxBytes: 4 << 20, TTL: time.Hour, ReplicationFactor: 2, + VectorDim: 2, VectorMetric: keyspace.VectorMetricCosine, + }}, + }) + if err != nil { + return err + } + defer c.Close() + + ctx := context.Background() + cli, err := client.Dial(ctx, c.Nodes()[0].CacheAddr) + if err != nil { + return err + } + defer cli.Close() + + p := func(format string, args ...any) { fmt.Fprintf(out, format+"\n", args...) } + p("ModeVectorSet example — cosine neighbors (not ZSet scores)") + if err := cli.VAdd(ctx, ks, "set", []byte("east"), []float32{1, 0}); err != nil { + return err + } + if err := cli.VAdd(ctx, ks, "set", []byte("north"), []float32{0, 1}); err != nil { + return err + } + if err := cli.VAdd(ctx, ks, "set", []byte("ne"), []float32{0.7, 0.7}); err != nil { + return err + } + var hits []struct { + Member []byte + Score float32 + } + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + got, err := cli.VSim(ctx, ks, "set", []float32{1, 0.05}, 2) + if err != nil { + return err + } + if len(got) >= 1 && string(got[0].Member) == "east" { + hits = make([]struct { + Member []byte + Score float32 + }, len(got)) + for i, h := range got { + hits[i].Member = h.Member + hits[i].Score = h.Score + } + break + } + time.Sleep(20 * time.Millisecond) + } + if len(hits) == 0 || string(hits[0].Member) != "east" { + return fmt.Errorf("expected east first, got %+v", hits) + } + p(" VSim ~east → %s (score=%g)", hits[0].Member, hits[0].Score) + p("OK: ModeVectorSet walkthrough passed") + return nil +} diff --git a/examples/vecset/demo_test.go b/examples/vecset/demo_test.go new file mode 100644 index 0000000..0024db0 --- /dev/null +++ b/examples/vecset/demo_test.go @@ -0,0 +1,17 @@ +package main + +import ( + "bytes" + "strings" + "testing" +) + +func TestExampleVecSet(t *testing.T) { + var buf bytes.Buffer + if err := runDemo(&buf); err != nil { + t.Fatalf("%v\n%s", err, buf.String()) + } + if !strings.Contains(buf.String(), "OK: ModeVectorSet walkthrough passed") { + t.Fatalf("missing OK line:\n%s", buf.String()) + } +} diff --git a/examples/vecset/main.go b/examples/vecset/main.go new file mode 100644 index 0000000..e1b293e --- /dev/null +++ b/examples/vecset/main.go @@ -0,0 +1,16 @@ +// Example: ModeVectorSet cosine K-NN on a 3-node in-process cluster. +// +// go run ./examples/vecset +package main + +import ( + "fmt" + "os" +) + +func main() { + if err := runDemo(os.Stdout); err != nil { + fmt.Fprintf(os.Stderr, "FAIL: %v\n", err) + os.Exit(1) + } +} diff --git a/internal/cacheserver/vecset.go b/internal/cacheserver/vecset.go new file mode 100644 index 0000000..25613a3 --- /dev/null +++ b/internal/cacheserver/vecset.go @@ -0,0 +1,58 @@ +package cacheserver + +import ( + "context" + + cachev1 "github.com/Code0987/supercache/api/gen/cache/v1" + "github.com/Code0987/supercache/internal/grpcmap" +) + +func (s *Server) VAdd(ctx context.Context, req *cachev1.VAddRequest) (*cachev1.VAddResponse, error) { + if err := s.eng.VAdd(ctx, req.GetKeyspace(), req.GetName(), req.GetMember(), req.GetVec()); err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.VAddResponse{}, nil +} + +func (s *Server) VRem(ctx context.Context, req *cachev1.VRemRequest) (*cachev1.VRemResponse, error) { + if err := s.eng.VRem(ctx, req.GetKeyspace(), req.GetName(), req.GetMember()); err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.VRemResponse{}, nil +} + +func (s *Server) VSim(ctx context.Context, req *cachev1.VSimRequest) (*cachev1.VSimResponse, error) { + hits, err := s.eng.VSim(ctx, req.GetKeyspace(), req.GetName(), req.GetVec(), int(req.GetK())) + if err != nil { + return nil, grpcmap.Status(err) + } + out := make([]*cachev1.VSimHit, len(hits)) + for i, h := range hits { + out[i] = &cachev1.VSimHit{Member: h.Member, Score: h.Score} + } + return &cachev1.VSimResponse{Hits: out}, nil +} + +func (s *Server) VCard(ctx context.Context, req *cachev1.VCardRequest) (*cachev1.VCardResponse, error) { + n, ok, err := s.eng.VCard(ctx, req.GetKeyspace(), req.GetName()) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.VCardResponse{N: int64(n), Present: ok}, nil +} + +func (s *Server) VDim(ctx context.Context, req *cachev1.VDimRequest) (*cachev1.VDimResponse, error) { + dim, ok, err := s.eng.VDim(ctx, req.GetKeyspace(), req.GetName()) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.VDimResponse{Dim: int32(dim), Present: ok}, nil +} + +func (s *Server) VEmb(ctx context.Context, req *cachev1.VEmbRequest) (*cachev1.VEmbResponse, error) { + vec, ok, err := s.eng.VEmb(ctx, req.GetKeyspace(), req.GetName(), req.GetMember()) + if err != nil { + return nil, grpcmap.Status(err) + } + return &cachev1.VEmbResponse{Vec: vec, Found: ok}, nil +} diff --git a/internal/testcluster/cluster.go b/internal/testcluster/cluster.go index 943bc59..1ba0bdf 100644 --- a/internal/testcluster/cluster.go +++ b/internal/testcluster/cluster.go @@ -257,8 +257,14 @@ func (c *Cluster) ready() error { } // ModeCMS rejects Get; missing name is ok=false, nil error. _, _, cerr2 := cli.CMSQuery(ctx, ks, "__testcluster_ready__", []byte("x")) - _ = cli.Close() if cerr2 == nil { + _ = cli.Close() + continue + } + // ModeVectorSet rejects Get; missing name is present=false, nil error. + _, _, verr := cli.VCard(ctx, ks, "__testcluster_ready__") + _ = cli.Close() + if verr == nil { continue } return fmt.Errorf("testcluster: ready get %s: %w", n.ID, err) diff --git a/pkg/client/vecset.go b/pkg/client/vecset.go new file mode 100644 index 0000000..9e562dc --- /dev/null +++ b/pkg/client/vecset.go @@ -0,0 +1,69 @@ +package client + +import ( + "context" + + cachev1 "github.com/Code0987/supercache/api/gen/cache/v1" + "github.com/Code0987/supercache/pkg/engine" +) + +// VAdd inserts or replaces a member vector. +func (c *Client) VAdd(ctx context.Context, keyspace, name string, member []byte, vec []float32) error { + _, err := c.api.VAdd(ctx, &cachev1.VAddRequest{Keyspace: keyspace, Name: name, Member: member, Vec: vec}) + return err +} + +// VRem removes a member. +func (c *Client) VRem(ctx context.Context, keyspace, name string, member []byte) error { + _, err := c.api.VRem(ctx, &cachev1.VRemRequest{Keyspace: keyspace, Name: name, Member: member}) + return err +} + +// VSim is top-k neighbors. Missing name → empty, nil error. +func (c *Client) VSim(ctx context.Context, keyspace, name string, vec []float32, k int) ([]engine.VSimHit, error) { + resp, err := c.api.VSim(ctx, &cachev1.VSimRequest{Keyspace: keyspace, Name: name, Vec: vec, K: int32(k)}) + if err != nil { + return nil, err + } + hits := make([]engine.VSimHit, 0, len(resp.GetHits())) + for _, h := range resp.GetHits() { + hits = append(hits, engine.VSimHit{Member: h.GetMember(), Score: h.GetScore()}) + } + return hits, nil +} + +// VCard is member count. Missing → present=false. +func (c *Client) VCard(ctx context.Context, keyspace, name string) (int, bool, error) { + resp, err := c.api.VCard(ctx, &cachev1.VCardRequest{Keyspace: keyspace, Name: name}) + if err != nil { + return 0, false, err + } + if !resp.GetPresent() { + return 0, false, nil + } + return int(resp.GetN()), true, nil +} + +// VDim is locked dim. Missing → present=false. +func (c *Client) VDim(ctx context.Context, keyspace, name string) (int, bool, error) { + resp, err := c.api.VDim(ctx, &cachev1.VDimRequest{Keyspace: keyspace, Name: name}) + if err != nil { + return 0, false, err + } + if !resp.GetPresent() { + return 0, false, nil + } + return int(resp.GetDim()), true, nil +} + +// VEmb returns the stored vector. Missing → ok=false. +func (c *Client) VEmb(ctx context.Context, keyspace, name string, member []byte) ([]float32, bool, error) { + resp, err := c.api.VEmb(ctx, &cachev1.VEmbRequest{Keyspace: keyspace, Name: name, Member: member}) + if err != nil { + return nil, false, err + } + if !resp.GetFound() { + return nil, false, nil + } + return resp.GetVec(), true, nil +} diff --git a/pkg/engine/cluster.go b/pkg/engine/cluster.go index 833d8cd..6a5b7ad 100644 --- a/pkg/engine/cluster.go +++ b/pkg/engine/cluster.go @@ -393,6 +393,13 @@ func (e *Engine) GetOrLoadLocal(ctx context.Context, keyspaceName, key string) ( } return ent, nil } + if ks.cfg.Mode == keyspace.ModeVectorSet { + ent, ok := ks.store.Peek(key) + if !ok || ent.IsTombstone() || !ent.IsVectorSet() { + 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 4419eb9..8e25128 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -281,6 +281,9 @@ func (e *Engine) Get(ctx context.Context, keyspaceName, key string) ([]byte, err if ks.cfg.Mode == keyspace.ModeCMS { return nil, fmt.Errorf("%w: use CMSQuery", ErrInvalidArgument) } + if ks.cfg.Mode == keyspace.ModeVectorSet { + return nil, fmt.Errorf("%w: use VEmb", ErrInvalidArgument) + } if ent, ok := ks.store.Get(key); ok { if ent.IsNegative() { @@ -503,6 +506,9 @@ func (e *Engine) Put(ctx context.Context, keyspaceName, key string, value []byte if ks.cfg.Mode == keyspace.ModeCMS { return fmt.Errorf("%w: use CMSIncr", ErrInvalidArgument) } + if ks.cfg.Mode == keyspace.ModeVectorSet { + return fmt.Errorf("%w: use VAdd", ErrInvalidArgument) + } } return e.putViaCluster(ctx, keyspaceName, key, value, opts...) } @@ -764,6 +770,9 @@ func (e *Engine) ApplyPutWithRingGen(keyspaceName, key string, ent store.Entry, if ent.IsCMS() { return e.applyCMSInstall(ks, key, ent.Value, ent.Version, ent.ExpireAt), nil } + if ent.IsVectorSet() { + return e.applyVectorSet(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/modehost.go b/pkg/engine/modehost.go index be41e2d..e72234b 100644 --- a/pkg/engine/modehost.go +++ b/pkg/engine/modehost.go @@ -73,7 +73,9 @@ func (h modeHost) MaxValue() int { func (h modeHost) BloomMK() (mBits, k int) { return h.ks.cfg.EffectiveBloomBits(), h.ks.cfg.EffectiveBloomHashes() } -func (h modeHost) TopKSize() int { return h.ks.cfg.EffectiveTopKSize() } +func (h modeHost) TopKSize() int { return h.ks.cfg.EffectiveTopKSize() } +func (h modeHost) VectorDim() int { return h.ks.cfg.VectorDim } +func (h modeHost) VectorMetric() int { return int(h.ks.cfg.VectorMetric) } func (h modeHost) ItemMax() int { max := h.e.maxKeyLen if h.ks.cfg.MaxKeyLen > 0 { @@ -81,17 +83,18 @@ func (h modeHost) ItemMax() int { } return max } -func (h modeHost) HasList(name string) bool { return h.ks.store.HasList(name) } -func (h modeHost) HasHash(name string) bool { return h.ks.store.HasHash(name) } -func (h modeHost) HasSet(name string) bool { return h.ks.store.HasSet(name) } -func (h modeHost) HasZSet(name string) bool { return h.ks.store.HasZSet(name) } -func (h modeHost) HasGeo(name string) bool { return h.ks.store.HasGeo(name) } -func (h modeHost) HasCounter(name string) bool { return h.ks.store.HasCounter(name) } -func (h modeHost) HasJSON(name string) bool { return h.ks.store.HasJSON(name) } -func (h modeHost) HasBitmap(name string) bool { return h.ks.store.HasBitmap(name) } -func (h modeHost) HasHLL(name string) bool { return h.ks.store.HasHLL(name) } -func (h modeHost) HasTopK(name string) bool { return h.ks.store.HasTopK(name) } -func (h modeHost) HasCMS(name string) bool { return h.ks.store.HasCMS(name) } +func (h modeHost) HasList(name string) bool { return h.ks.store.HasList(name) } +func (h modeHost) HasHash(name string) bool { return h.ks.store.HasHash(name) } +func (h modeHost) HasSet(name string) bool { return h.ks.store.HasSet(name) } +func (h modeHost) HasZSet(name string) bool { return h.ks.store.HasZSet(name) } +func (h modeHost) HasGeo(name string) bool { return h.ks.store.HasGeo(name) } +func (h modeHost) HasCounter(name string) bool { return h.ks.store.HasCounter(name) } +func (h modeHost) HasJSON(name string) bool { return h.ks.store.HasJSON(name) } +func (h modeHost) HasBitmap(name string) bool { return h.ks.store.HasBitmap(name) } +func (h modeHost) HasHLL(name string) bool { return h.ks.store.HasHLL(name) } +func (h modeHost) HasTopK(name string) bool { return h.ks.store.HasTopK(name) } +func (h modeHost) HasCMS(name string) bool { return h.ks.store.HasCMS(name) } +func (h modeHost) HasVectorSet(name string) bool { return h.ks.store.HasVectorSet(name) } func (h modeHost) HasBloom(name string) bool { ent, ok := h.ks.store.Peek(name) return ok && ent.IsBloom() && !ent.IsTombstone() diff --git a/pkg/engine/vecset.go b/pkg/engine/vecset.go new file mode 100644 index 0000000..b831421 --- /dev/null +++ b/pkg/engine/vecset.go @@ -0,0 +1,288 @@ +package engine + +import ( + "context" + "fmt" + + "github.com/Code0987/supercache/pkg/keyspace" + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/vecset" + veceng "github.com/Code0987/supercache/pkg/vecset/eng" +) + +// VSimHit is one neighbor (clients must not import pkg/vecset). +type VSimHit struct { + Member []byte + Score float32 +} + +func (e *Engine) vsNeed(ks *ksRuntime, name string) error { + if err := e.validateKey(ks.cfg.Name, name); err != nil { + return err + } + if ks.cfg.Mode != keyspace.ModeVectorSet { + return fmt.Errorf("%w: vector op requires ModeVectorSet", ErrInvalidArgument) + } + return e.validateKeyLen(ks, name) +} + +func (e *Engine) vsCheckMember(member []byte) error { + if len(member) < 1 || len(member) > vecset.MaxMemberLen { + return fmt.Errorf("%w: bad vector member", ErrInvalidArgument) + } + if len(member) > e.maxKeyLen { + return ErrKeyTooLarge + } + return nil +} + +func (e *Engine) vsCheckVec(ks *ksRuntime, name string, vec []float32) error { + if !vecset.ValidDim(len(vec)) || !vecset.Finite(vec) { + return fmt.Errorf("%w: bad vector", ErrInvalidArgument) + } + if ks.cfg.VectorDim > 0 && len(vec) != ks.cfg.VectorDim { + return fmt.Errorf("%w: vector dim", ErrInvalidArgument) + } + if dim, ok := ks.store.VDim(name); ok && dim != len(vec) { + return fmt.Errorf("%w: vector dim", ErrInvalidArgument) + } + if ks.cfg.VectorMetric == keyspace.VectorMetricCosine && vecset.IsZero(vec) { + return fmt.Errorf("%w: zero vector", ErrInvalidArgument) + } + return nil +} + +// VAdd inserts or replaces member's vector. ACK-only. +func (e *Engine) VAdd(ctx context.Context, keyspaceName, name string, member []byte, vec []float32) 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.vsNeed(ks, name); err != nil { + return err + } + if err := e.vsCheckMember(member); err != nil { + return err + } + if err := e.vsCheckVec(ks, name, vec); err != nil { + return err + } + if n, ok := ks.store.VCard(name); ok { + if _, exists := ks.store.VEmb(name, member); !exists && n >= vecset.MaxMembers { + return fmt.Errorf("%w: vector set full", 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.vsMutViaOwner(ctx, ks, name, member, vec, true) + } + } + if err := veceng.AddLocal(e.modeHost(ks), name, member, vec); err == veceng.ErrTooLarge { + return ErrValueTooLarge + } else if err != nil { + return fmt.Errorf("%w: vadd rejected", ErrInvalidArgument) + } + return nil +} + +// VRem removes member. Missing is a no-op. +func (e *Engine) VRem(ctx context.Context, keyspaceName, name string, member []byte) error { + if err := ctx.Err(); err != nil { + return err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return err + } + if err := e.vsNeed(ks, name); err != nil { + return err + } + if err := e.vsCheckMember(member); 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) + } + return e.vsMutViaOwner(ctx, ks, name, member, nil, false) + } + } + if err := veceng.RemLocal(e.modeHost(ks), name, member); err != nil { + return fmt.Errorf("%w: vrem rejected", ErrInvalidArgument) + } + return nil +} + +// VSim returns top-k neighbors, best first. Missing name → empty, nil error. +func (e *Engine) VSim(ctx context.Context, keyspaceName, name string, vec []float32, k int) ([]VSimHit, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return nil, err + } + if err := e.vsNeed(ks, name); err != nil { + return nil, err + } + if err := e.vsCheckVec(ks, name, vec); err != nil { + return nil, err + } + metric := int(ks.cfg.VectorMetric) + if hits, ok := ks.store.VSim(name, vec, k, metric); ok { + return copyHits(hits), nil + } + ent, found, err := veceng.FetchOwner(ctx, e.modeHost(ks), name) + if err != nil || !found { + return nil, err + } + raw, err := vecset.Sim(ent.Value, vec, k, vecset.Metric(metric)) + if err != nil { + return nil, nil + } + out := make([]VSimHit, len(raw)) + for i, h := range raw { + out[i] = VSimHit{Member: h.Member, Score: h.Score} + } + return out, nil +} + +// VCard is member count. Missing → present=false. +func (e *Engine) VCard(ctx context.Context, keyspaceName, name string) (int, bool, error) { + return e.vsReadInt(ctx, keyspaceName, name, func(s store.Store) (int, bool) { return s.VCard(name) }, func(ent store.Entry) (int, bool) { + set, err := vecset.DecodeSnapshot(ent.Value) + if err != nil { + return 0, false + } + return set.Card(), true + }) +} + +// VDim is the locked dim. Missing → present=false. +func (e *Engine) VDim(ctx context.Context, keyspaceName, name string) (int, bool, error) { + return e.vsReadInt(ctx, keyspaceName, name, func(s store.Store) (int, bool) { return s.VDim(name) }, func(ent store.Entry) (int, bool) { + set, err := vecset.DecodeSnapshot(ent.Value) + if err != nil { + return 0, false + } + return set.Dim, true + }) +} + +func (e *Engine) vsReadInt(ctx context.Context, keyspaceName, name string, local func(store.Store) (int, bool), fromEnt func(store.Entry) (int, bool)) (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.vsNeed(ks, name); err != nil { + return 0, false, err + } + if n, ok := local(ks.store); ok { + return n, true, nil + } + ent, found, err := veceng.FetchOwner(ctx, e.modeHost(ks), name) + if err != nil || !found { + return 0, false, err + } + n, ok := fromEnt(ent) + return n, ok, nil +} + +// VEmb returns a copy of the stored vector. Missing → ok=false. +func (e *Engine) VEmb(ctx context.Context, keyspaceName, name string, member []byte) ([]float32, bool, error) { + if err := ctx.Err(); err != nil { + return nil, false, err + } + ks, err := e.getKS(keyspaceName) + if err != nil { + return nil, false, err + } + if err := e.vsNeed(ks, name); err != nil { + return nil, false, err + } + if err := e.vsCheckMember(member); err != nil { + return nil, false, err + } + if vec, ok := ks.store.VEmb(name, member); ok { + return vec, true, nil + } + ent, found, err := veceng.FetchOwner(ctx, e.modeHost(ks), name) + if err != nil || !found { + return nil, false, err + } + set, err := vecset.DecodeSnapshot(ent.Value) + if err != nil { + return nil, false, nil + } + vec, ok := set.Emb(member) + return vec, ok, nil +} + +func (e *Engine) vsMutViaOwner(ctx context.Context, ks *ksRuntime, name string, member []byte, vec []float32, add bool) error { + c := e.clusterSnapshot() + owner, _ := c.Ring.Owner(name) + var inbox []byte + var err error + if add { + inbox, err = vecset.EncodeInboxAdd(member, vec) + } else { + inbox, err = vecset.EncodeInboxRem(member) + } + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidArgument, err) + } + ent := store.Entry{Value: inbox, Flags: store.FlagVectorSet, Version: 1} + pctx, cancel := e.peerCtx(ctx, ks) + defer cancel() + 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: vector mutate rejected", ErrInvalidArgument) + } + return nil +} + +func (e *Engine) applyVectorSet(ks *ksRuntime, name string, ent store.Entry) bool { + if len(ent.Value) == 0 { + return false + } + switch ent.Value[0] { + case vecset.PrefixAdd, vecset.PrefixRem: + 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 veceng.ApplyInbox(e.modeHost(ks), name, ent.Value, ent.ExpireAt) + case vecset.PrefixSnapshot: + return veceng.ApplyInstall(e.modeHost(ks), name, ent.Value, ent.Version, ent.ExpireAt) + default: + return false + } +} + +func copyHits(in []store.VSimHit) []VSimHit { + out := make([]VSimHit, len(in)) + for i, h := range in { + out[i] = VSimHit{Member: h.Member, Score: h.Score} + } + return out +} diff --git a/pkg/keyspace/config.go b/pkg/keyspace/config.go index 92599e1..eb51d1d 100644 --- a/pkg/keyspace/config.go +++ b/pkg/keyspace/config.go @@ -11,6 +11,7 @@ import ( "github.com/Code0987/supercache/pkg/datasource" "github.com/Code0987/supercache/pkg/protect" "github.com/Code0987/supercache/pkg/topkx" + "github.com/Code0987/supercache/pkg/vecset" ) // Mode selects miss behavior for a keyspace. @@ -45,6 +46,8 @@ const ( ModeTopK // ModeCMS is a named Count-Min Sketch (CMSIncr / CMSQuery). ModeCMS + // ModeVectorSet is a named embedding set (VAdd / VSim / VRem / …). + ModeVectorSet ) func (m Mode) String() string { @@ -77,6 +80,8 @@ func (m Mode) String() string { return "TopK" case ModeCMS: return "CMS" + case ModeVectorSet: + return "VectorSet" default: return fmt.Sprintf("Mode(%d)", int(m)) } @@ -172,6 +177,31 @@ type Config struct { // TopKSize is K for ModeTopK (0 → DefaultTopKSize). Per-name RESERVE is not v1. TopKSize int + + // VectorDim is the locked dim for ModeVectorSet (0 = first VAdd locks). Range 2..256. + VectorDim int + // VectorMetric is cosine (0), L2, or IP for ModeVectorSet. + VectorMetric VectorMetric +} + +// VectorMetric is the keyspace K-NN formula. +type VectorMetric int + +const ( + VectorMetricCosine VectorMetric = iota + VectorMetricL2 + VectorMetricIP +) + +func (m VectorMetric) String() string { + switch m { + case VectorMetricL2: + return "l2" + case VectorMetricIP: + return "ip" + default: + return "cosine" + } } // Validate checks config invariants. @@ -208,6 +238,35 @@ func (c Config) Validate() error { return fmt.Errorf("keyspace: ModeTopK WorstEncodedSize %d > MaxValueSize %d", need, maxVal) } } + if c.Mode == ModeVectorSet { + if c.VectorDim < 0 || c.VectorDim == 1 || c.VectorDim > vecset.MaxDim { + return fmt.Errorf("keyspace: VectorDim %d out of range", c.VectorDim) + } + switch c.VectorMetric { + case VectorMetricCosine, VectorMetricL2, VectorMetricIP: + default: + return fmt.Errorf("keyspace: unknown VectorMetric %d", int(c.VectorMetric)) + } + dim := c.VectorDim + if dim == 0 { + dim = vecset.MaxDim + } + maxItem := DefaultMaxKeyLen + if c.MaxKeyLen > 0 { + maxItem = c.MaxKeyLen + } + if maxItem > vecset.MaxMemberLen { + maxItem = vecset.MaxMemberLen + } + maxVal := DefaultMaxValueSize + if c.MaxValueSize > 0 { + maxVal = c.MaxValueSize + } + need := vecset.WorstEncodedSize(dim, vecset.MaxMembers, maxItem) + if maxVal > 0 && need > maxVal { + return fmt.Errorf("keyspace: ModeVectorSet WorstEncodedSize %d > MaxValueSize %d", need, maxVal) + } + } if c.Mode == ModeBloom { bits := c.EffectiveBloomBits() if bits < 64 || c.EffectiveBloomHashes() < 1 { @@ -269,6 +328,8 @@ func (c Config) ConfigHash() string { BloomBits int BloomHashes int TopKSize int + VectorDim int + VectorMetric int } b, _ := json.Marshal(wire{ Name: c.Name, @@ -292,6 +353,8 @@ func (c Config) ConfigHash() string { BloomBits: c.BloomBits, BloomHashes: c.BloomHashes, TopKSize: c.TopKSize, + VectorDim: c.VectorDim, + VectorMetric: int(c.VectorMetric), }) sum := sha256.Sum256(b) return hex.EncodeToString(sum[:8]) diff --git a/pkg/keyspace/config_test.go b/pkg/keyspace/config_test.go index 6be7694..dcaf57c 100644 --- a/pkg/keyspace/config_test.go +++ b/pkg/keyspace/config_test.go @@ -99,6 +99,9 @@ func TestModeString(t *testing.T) { if ModeCMS.String() != "CMS" { t.Fatal(ModeCMS.String()) } + if ModeVectorSet.String() != "VectorSet" { + t.Fatal(ModeVectorSet.String()) + } if Mode(99).String() != "Mode(99)" { t.Fatal(Mode(99).String()) } @@ -150,6 +153,15 @@ func TestValidate(t *testing.T) { if err := (Config{Name: "c", Mode: ModeCMS, MaxBytes: 1}).Validate(); err != nil { t.Fatal(err) } + if err := (Config{Name: "v", Mode: ModeVectorSet, VectorDim: 1, MaxBytes: 1 << 20}).Validate(); err == nil { + t.Fatal("VectorDim 1") + } + if err := (Config{Name: "v", Mode: ModeVectorSet, VectorMetric: 99, MaxBytes: 1 << 20}).Validate(); err == nil { + t.Fatal("bad metric") + } + if err := (Config{Name: "v", Mode: ModeVectorSet, MaxBytes: 1 << 20}).Validate(); err != nil { + t.Fatal(err) + } if (Config{TopKSize: 0}).EffectiveTopKSize() != DefaultTopKSize { t.Fatal("default K") } @@ -192,6 +204,20 @@ func TestConfigHashIncludesTopKSize(t *testing.T) { } } +func TestConfigHashIncludesVectorFields(t *testing.T) { + a := Config{Name: "k", Mode: ModeVectorSet, MaxBytes: 1 << 20, VectorDim: 8} + b := a + b.VectorDim = 16 + if a.ConfigHash() == b.ConfigHash() { + t.Fatal("VectorDim must change hash") + } + c := a + c.VectorMetric = VectorMetricL2 + if a.ConfigHash() == c.ConfigHash() { + t.Fatal("VectorMetric 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 6fb11e7..2d6b954 100644 --- a/pkg/store/entry.go +++ b/pkg/store/entry.go @@ -35,6 +35,7 @@ const ( 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] ) // Entry is the on-node stored value envelope (versioned LWW + TTL). @@ -182,6 +183,10 @@ func (e Entry) IsCMSIncr() bool { return e.Flags&FlagCMSIncr != 0 } +func (e Entry) IsVectorSet() bool { + return e.Flags&FlagVectorSet != 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 aca43f2..efe6dfc 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.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.Expired(m.now()) { continue } return el diff --git a/pkg/store/store.go b/pkg/store/store.go index 80d3e41..4ea0c2c 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -207,10 +207,26 @@ type Store interface { // CMSInstall installs a versioned full-sketch snapshot (incoming > local). CMSInstall(key string, blob []byte, version uint64, expireAt int64) bool + // VAdd upserts member/vec (creates the set if missing). Incoming version is a tombstone gate. + VAdd(key string, member []byte, vec []float32, version uint64, expireAt int64, maxValue int) (applied, tooLarge bool) + VRem(key string, member []byte, version uint64, expireAt int64) bool + VSim(key string, vec []float32, k int, metric int) (hits []VSimHit, ok bool) + VCard(key string) (n int, ok bool) + VDim(key string) (dim int, ok bool) + VEmb(key string, member []byte) (vec []float32, ok bool) + HasVectorSet(key string) bool + VSInstall(key string, blob []byte, version uint64, expireAt int64) bool + // Close releases resources. Close() } +// VSimHit is one neighbor returned by VSim. +type VSimHit struct { + Member []byte + Score float32 +} + // TopKEntry is one chart row returned by TopKList. type TopKEntry struct { Item []byte diff --git a/pkg/store/vecset.go b/pkg/store/vecset.go new file mode 100644 index 0000000..461bb6b --- /dev/null +++ b/pkg/store/vecset.go @@ -0,0 +1,272 @@ +package store + +import ( + "container/list" + + "github.com/Code0987/supercache/pkg/vecset" +) + +// VAdd upserts member/vec. version is a tombstone-gate floor; stored = 1 or local+1. +func (m *Memory) VAdd(key string, member []byte, vec []float32, version uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { + m.mu.Lock() + defer m.mu.Unlock() + if len(member) < 1 || len(member) > vecset.MaxMemberLen || !vecset.ValidDim(len(vec)) || !vecset.Finite(vec) { + return false, false + } + if el, exists := m.items[key]; exists { + 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 + } + return m.vsInsertOverLocked(el, it, key, member, vec, it.entry.Version+1, expireAt, maxValue) + } + if !it.entry.IsVectorSet() { + return false, false + } + return m.vsCommitLocked(el, it, key, member, vec, it.entry.Version+1, expireAt, maxValue) + } + m.removeElement(el) + } + return m.vsInsertLocked(key, member, vec, 1, expireAt, maxValue) +} + +func (m *Memory) vsCommitLocked(el *list.Element, it *lruItem, key string, member []byte, vec []float32, stored uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { + s, err := vecset.DecodeSnapshot(it.entry.Value) + if err != nil { + return false, false + } + if s.Dim != len(vec) { + return false, false + } + if err := s.Add(member, vec); err != nil { + return false, false + } + blob := s.Encode() + if maxValue > 0 && len(blob) > maxValue { + return false, true + } + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = stored + it.entry.Flags = FlagVectorSet + 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 still, false +} + +func (m *Memory) vsInsertOverLocked(el *list.Element, it *lruItem, key string, member []byte, vec []float32, stored uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { + s := vecset.NewSet(len(vec)) + if err := s.Add(member, vec); err != nil { + return false, false + } + blob := s.Encode() + if maxValue > 0 && len(blob) > maxValue { + return false, true + } + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = stored + it.entry.Flags = FlagVectorSet + 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 still, false +} + +func (m *Memory) vsInsertLocked(key string, member []byte, vec []float32, stored uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { + s := vecset.NewSet(len(vec)) + if err := s.Add(member, vec); err != nil { + return false, false + } + blob := s.Encode() + if maxValue > 0 && len(blob) > maxValue { + return false, true + } + ent := Entry{Value: blob, Version: stored, ExpireAt: expireAt, Flags: FlagVectorSet} + return m.insertVSLocked(key, ent), false +} + +func (m *Memory) insertVSLocked(key string, ent Entry) bool { + 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 +} + +// VRem removes member. Missing name is a successful no-op (true). +func (m *Memory) VRem(key string, member []byte, version uint64, expireAt int64) bool { + 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.IsVectorSet() { + return false + } + s, err := vecset.DecodeSnapshot(it.entry.Value) + if err != nil { + return false + } + s.Rem(member) + blob := s.Encode() + oldCost := it.cost + it.entry.Value = blob + it.entry.Version = it.entry.Version + 1 + it.entry.Flags = FlagVectorSet + 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) liveVSLocked(key string) (*vecset.Set, bool) { + if !m.hasVSLocked(key) { + return nil, false + } + it := m.items[key].Value.(*lruItem) + s, err := vecset.DecodeSnapshot(it.entry.Value) + if err != nil { + return nil, false + } + return s, true +} + +func (m *Memory) VSim(key string, vec []float32, k int, metric int) ([]VSimHit, bool) { + m.mu.Lock() + defer m.mu.Unlock() + s, ok := m.liveVSLocked(key) + if !ok { + return nil, false + } + hits, err := s.Sim(vec, k, vecset.Metric(metric)) + if err != nil { + return nil, false + } + out := make([]VSimHit, len(hits)) + for i, h := range hits { + out[i] = VSimHit{Member: h.Member, Score: h.Score} + } + return out, true +} + +func (m *Memory) VCard(key string) (int, bool) { + m.mu.Lock() + defer m.mu.Unlock() + s, ok := m.liveVSLocked(key) + if !ok { + return 0, false + } + return s.Card(), true +} + +func (m *Memory) VDim(key string) (int, bool) { + m.mu.Lock() + defer m.mu.Unlock() + s, ok := m.liveVSLocked(key) + if !ok { + return 0, false + } + return s.Dim, true +} + +func (m *Memory) VEmb(key string, member []byte) ([]float32, bool) { + m.mu.Lock() + defer m.mu.Unlock() + s, ok := m.liveVSLocked(key) + if !ok { + return nil, false + } + return s.Emb(member) +} + +func (m *Memory) HasVectorSet(key string) bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.hasVSLocked(key) +} + +func (m *Memory) hasVSLocked(key string) bool { + el, ok := m.items[key] + if !ok { + return false + } + it := el.Value.(*lruItem) + if it.entry.Expired(m.now()) { + m.removeElement(el) + return false + } + return !it.entry.IsTombstone() && it.entry.IsVectorSet() +} + +// VSInstall is LWW snapshot handoff. +func (m *Memory) VSInstall(key string, blob []byte, version uint64, expireAt int64) bool { + if _, err := vecset.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 it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + } else if it.entry.IsVectorSet() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + } else if version <= it.entry.Version { + return false + } + } + m.removeElement(el) + } + ent := Entry{Value: append([]byte(nil), blob...), Version: version, ExpireAt: expireAt, Flags: FlagVectorSet} + return m.insertVSLocked(key, ent) +} diff --git a/pkg/store/vecset_bench_test.go b/pkg/store/vecset_bench_test.go new file mode 100644 index 0000000..2b2a082 --- /dev/null +++ b/pkg/store/vecset_bench_test.go @@ -0,0 +1,37 @@ +package store + +import ( + "fmt" + "testing" + + "github.com/Code0987/supercache/pkg/vecset" +) + +func BenchmarkStoreVAdd(b *testing.B) { + m := NewMemory(8 << 20) + defer m.Close() + vec := make([]float32, 64) + vec[0] = 1 + b.ReportAllocs() + for i := 0; i < b.N; i++ { + id := []byte(fmt.Sprintf("%d", i%128)) + m.VAdd("v", id, vec, 1, 0, 0) + } +} + +func BenchmarkStoreVSim(b *testing.B) { + m := NewMemory(8 << 20) + defer m.Close() + q := make([]float32, 64) + q[0] = 1 + for i := 0; i < 128; i++ { + v := make([]float32, 64) + v[i%64] = 1 + m.VAdd("v", []byte(fmt.Sprintf("%d", i)), v, 1, 0, 0) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + m.VSim("v", q, 10, int(vecset.MetricCosine)) + } +} diff --git a/pkg/store/vecset_test.go b/pkg/store/vecset_test.go new file mode 100644 index 0000000..7e4cb45 --- /dev/null +++ b/pkg/store/vecset_test.go @@ -0,0 +1,144 @@ +package store + +import ( + "testing" + + "github.com/Code0987/supercache/pkg/vecset" +) + +func TestStoreVAddLocksDimAndReplace(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + ok, too := m.VAdd("v", []byte("a"), []float32{1, 0}, 1, 0, 0) + if !ok || too { + t.Fatal(ok, too) + } + ok, too = m.VAdd("v", []byte("b"), []float32{1, 0, 0}, 1, 0, 0) + if ok || too { + t.Fatal("dim mismatch applied") + } + ent, _ := m.Peek("v") + if ent.Version != 1 { + t.Fatalf("version bumped on reject: %d", ent.Version) + } + ok, _ = m.VAdd("v", []byte("a"), []float32{0, 1}, 1, 0, 0) + if !ok { + t.Fatal("replace") + } + n, present := m.VCard("v") + if !present || n != 1 { + t.Fatal(n, present) + } + vec, ok := m.VEmb("v", []byte("a")) + if !ok || vec[1] != 1 { + t.Fatalf("%v %v", vec, ok) + } + ent, _ = m.Peek("v") + if ent.Version != 2 { + t.Fatalf("replace ver=%d", ent.Version) + } +} + +func TestStoreVRemKeepsEmptyDim(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + m.VAdd("v", []byte("a"), []float32{1, 0}, 1, 0, 0) + if !m.VRem("v", []byte("a"), 1, 0) { + t.Fatal("rem") + } + if !m.HasVectorSet("v") { + t.Fatal("dropped") + } + n, ok := m.VCard("v") + if !ok || n != 0 { + t.Fatal(n, ok) + } + dim, ok := m.VDim("v") + if !ok || dim != 2 { + t.Fatal(dim, ok) + } +} + +func TestStoreVAddFull(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + for i := 0; i < vecset.MaxMembers; i++ { + id := []byte{byte(i >> 8), byte(i)} + ok, _ := m.VAdd("v", id, []float32{1, 0}, 1, 0, 0) + if !ok { + t.Fatal(i) + } + } + ok, too := m.VAdd("v", []byte("zz"), []float32{1, 0}, 1, 0, 0) + if ok || too { + t.Fatal("513th new id") + } + ok, _ = m.VAdd("v", []byte{0, 0}, []float32{0, 1}, 1, 0, 0) + if !ok { + t.Fatal("replace full") + } +} + +func TestStoreVSimCosine(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + m.VAdd("v", []byte("far"), []float32{0, 1}, 1, 0, 0) + m.VAdd("v", []byte("near"), []float32{1, 0.1}, 1, 0, 0) + hits, ok := m.VSim("v", []float32{1, 0}, 2, int(vecset.MetricCosine)) + if !ok || len(hits) != 2 || string(hits[0].Member) != "near" { + t.Fatalf("%v %+v", ok, hits) + } +} + +func TestStoreMemberTooLong(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + m.VAdd("v", []byte("a"), []float32{1, 0}, 1, 0, 0) + ok, _ := m.VAdd("v", make([]byte, 256), []float32{1, 0}, 1, 0, 0) + if ok { + t.Fatal("256-byte member") + } + ent, _ := m.Peek("v") + if ent.Version != 1 { + t.Fatal(ent.Version) + } +} + +func TestStoreVSInstallLWW(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + s := vecset.NewSet(2) + _ = s.Add([]byte("a"), []float32{1, 0}) + blob := s.Encode() + if !m.VSInstall("v", blob, 2, 0) { + t.Fatal("install") + } + if m.VSInstall("v", blob, 2, 0) || m.VSInstall("v", blob, 1, 0) { + t.Fatal("stale") + } +} + +func TestStoreVectorSetNotEvicted(t *testing.T) { + m := NewMemory(1) + defer m.Close() + ok, _ := m.VAdd("v", []byte("a"), []float32{1, 0}, 1, 0, 0) + if !ok { + t.Fatal("add") + } + // pressure: a normal KV should not evict the live vector set + _ = m.Set("other", Entry{Value: []byte("x"), Version: 1}) + if !m.HasVectorSet("v") { + t.Fatal("evicted") + } +} + +func TestStoreVSimTiesAndK(t *testing.T) { + m := NewMemory(1 << 20) + defer m.Close() + m.VAdd("v", []byte("bb"), []float32{1, 0}, 1, 0, 0) + m.VAdd("v", []byte("aa"), []float32{1, 0}, 1, 0, 0) + hits, ok := m.VSim("v", []float32{1, 0}, 50, int(vecset.MetricCosine)) + if !ok || len(hits) != 2 || string(hits[0].Member) != "aa" { + t.Fatalf("%v %+v", ok, hits) + } +} diff --git a/pkg/vecset/codec.go b/pkg/vecset/codec.go new file mode 100644 index 0000000..d8135cc --- /dev/null +++ b/pkg/vecset/codec.go @@ -0,0 +1,373 @@ +// Package vecset is the ModeVectorSet codec (snapshot + owner inbox). +package vecset + +import ( + "encoding/binary" + "errors" + "math" + "sort" +) + +const ( + PrefixSnapshot byte = 'S' + PrefixAdd byte = 'A' + PrefixRem byte = 'R' + + MinDim = 2 + MaxDim = 256 + MaxMembers = 512 + MaxMemberLen = 255 + DefaultK = 10 + MaxK = 50 + snapshotHeader = 5 +) + +// Metric is the keyspace K-NN formula (mirrors keyspace.VectorMetric). +type Metric int + +const ( + MetricCosine Metric = iota + MetricL2 + MetricIP +) + +// Hit is one VSim row. +type Hit struct { + Member []byte + Score float32 +} + +var ( + errBadBlob = errors.New("vecset: bad blob") + errBadMember = errors.New("vecset: bad member") + errBadVec = errors.New("vecset: bad vector") + errFull = errors.New("vecset: full") +) + +// WorstEncodedSize is the snapshot size for Validate (header + count records). +func WorstEncodedSize(dim, count, idLen int) int { + if dim < MinDim { + dim = MaxDim + } + if idLen < 1 { + idLen = MaxMemberLen + } + if count < 0 { + count = MaxMembers + } + return snapshotHeader + count*(1+idLen+4*dim) +} + +// ClampK maps k<=0 → 10 and k>50 → 50. +func ClampK(k int) int { + if k <= 0 { + return DefaultK + } + if k > MaxK { + return MaxK + } + return k +} + +// ValidDim reports whether dim is in 2..256. +func ValidDim(dim int) bool { return dim >= MinDim && dim <= MaxDim } + +// IsZero reports an all-zero vector (cosine-undefined). +func IsZero(vec []float32) bool { + for _, x := range vec { + if x != 0 { + return false + } + } + return len(vec) > 0 +} + +// Finite reports no NaN/Inf. +func Finite(vec []float32) bool { + for _, x := range vec { + if math.IsNaN(float64(x)) || math.IsInf(float64(x), 0) { + return false + } + } + return true +} + +// Set is a decoded vector set (dim locked even when empty). +type Set struct { + Dim int + vals map[string][]float32 +} + +// NewSet creates an empty set with a locked dim. +func NewSet(dim int) *Set { + return &Set{Dim: dim, vals: make(map[string][]float32)} +} + +func (s *Set) Card() int { + if s == nil { + return 0 + } + return len(s.vals) +} + +func (s *Set) Emb(member []byte) ([]float32, bool) { + if s == nil { + return nil, false + } + v, ok := s.vals[string(member)] + if !ok { + return nil, false + } + out := make([]float32, len(v)) + copy(out, v) + return out, true +} + +func (s *Set) Add(member []byte, vec []float32) error { + if s == nil || !ValidDim(s.Dim) || len(vec) != s.Dim || !Finite(vec) { + return errBadVec + } + if len(member) < 1 || len(member) > MaxMemberLen { + return errBadMember + } + k := string(member) + if _, exists := s.vals[k]; !exists && len(s.vals) >= MaxMembers { + return errFull + } + cp := make([]float32, len(vec)) + copy(cp, vec) + if s.vals == nil { + s.vals = make(map[string][]float32) + } + s.vals[k] = cp + return nil +} + +func (s *Set) Rem(member []byte) { + if s == nil || s.vals == nil { + return + } + delete(s.vals, string(member)) +} + +// Encode writes a snapshot blob ('S' | u16 dim | u16 count | records). +func (s *Set) Encode() []byte { + if s == nil { + return nil + } + keys := make([]string, 0, len(s.vals)) + for k := range s.vals { + keys = append(keys, k) + } + sort.Strings(keys) + n := snapshotHeader + for _, k := range keys { + n += 1 + len(k) + 4*s.Dim + } + buf := make([]byte, n) + buf[0] = PrefixSnapshot + binary.LittleEndian.PutUint16(buf[1:3], uint16(s.Dim)) + binary.LittleEndian.PutUint16(buf[3:5], uint16(len(keys))) + off := snapshotHeader + tmp := make([]byte, 4) + for _, k := range keys { + buf[off] = byte(len(k)) + off++ + off += copy(buf[off:], k) + for _, x := range s.vals[k] { + binary.LittleEndian.PutUint32(tmp, math.Float32bits(x)) + off += copy(buf[off:], tmp) + } + } + return buf +} + +// DecodeSnapshot parses a FlagVectorSet snapshot. Empty count is valid. +func DecodeSnapshot(blob []byte) (*Set, error) { + if len(blob) < snapshotHeader || blob[0] != PrefixSnapshot { + return nil, errBadBlob + } + dim := int(binary.LittleEndian.Uint16(blob[1:3])) + count := int(binary.LittleEndian.Uint16(blob[3:5])) + if !ValidDim(dim) || count < 0 || count > MaxMembers { + return nil, errBadBlob + } + s := NewSet(dim) + off := snapshotHeader + for i := 0; i < count; i++ { + if off >= len(blob) { + return nil, errBadBlob + } + idLen := int(blob[off]) + off++ + if idLen < 1 || idLen > MaxMemberLen || off+idLen+4*dim > len(blob) { + return nil, errBadBlob + } + id := blob[off : off+idLen] + off += idLen + vec := make([]float32, dim) + for j := 0; j < dim; j++ { + vec[j] = math.Float32frombits(binary.LittleEndian.Uint32(blob[off : off+4])) + off += 4 + if math.IsNaN(float64(vec[j])) || math.IsInf(float64(vec[j]), 0) { + return nil, errBadBlob + } + } + if err := s.Add(append([]byte(nil), id...), vec); err != nil { + return nil, err + } + } + if off != len(blob) { + return nil, errBadBlob + } + return s, nil +} + +// EncodeInboxAdd is owner-inbox 'A' | u8 | member | floats. +func EncodeInboxAdd(member []byte, vec []float32) ([]byte, error) { + if len(member) < 1 || len(member) > MaxMemberLen || !ValidDim(len(vec)) || !Finite(vec) { + return nil, errBadVec + } + buf := make([]byte, 2+len(member)+4*len(vec)) + buf[0] = PrefixAdd + buf[1] = byte(len(member)) + copy(buf[2:], member) + off := 2 + len(member) + for _, x := range vec { + binary.LittleEndian.PutUint32(buf[off:off+4], math.Float32bits(x)) + off += 4 + } + return buf, nil +} + +// EncodeInboxRem is owner-inbox 'R' | member. +func EncodeInboxRem(member []byte) ([]byte, error) { + if len(member) < 1 || len(member) > MaxMemberLen { + return nil, errBadMember + } + buf := make([]byte, 1+len(member)) + buf[0] = PrefixRem + copy(buf[1:], member) + return buf, nil +} + +// DecodeInboxAdd parses an 'A' inbox. +func DecodeInboxAdd(blob []byte) (member []byte, vec []float32, err error) { + if len(blob) < 2 || blob[0] != PrefixAdd { + return nil, nil, errBadBlob + } + idLen := int(blob[1]) + if idLen < 1 || idLen > MaxMemberLen || 2+idLen >= len(blob) { + return nil, nil, errBadBlob + } + rest := len(blob) - 2 - idLen + if rest%4 != 0 { + return nil, nil, errBadBlob + } + dim := rest / 4 + if !ValidDim(dim) { + return nil, nil, errBadBlob + } + member = append([]byte(nil), blob[2:2+idLen]...) + vec = make([]float32, dim) + off := 2 + idLen + for i := 0; i < dim; i++ { + vec[i] = math.Float32frombits(binary.LittleEndian.Uint32(blob[off : off+4])) + off += 4 + if math.IsNaN(float64(vec[i])) || math.IsInf(float64(vec[i]), 0) { + return nil, nil, errBadBlob + } + } + return member, vec, nil +} + +// DecodeInboxRem parses an 'R' inbox (remainder is the member). +func DecodeInboxRem(blob []byte) ([]byte, error) { + if len(blob) < 2 || blob[0] != PrefixRem { + return nil, errBadBlob + } + member := blob[1:] + if len(member) < 1 || len(member) > MaxMemberLen { + return nil, errBadBlob + } + return append([]byte(nil), member...), nil +} + +// Score one pair. Cosine of a zero vector is 0 (caller should reject). +func Score(a, b []float32, m Metric) float32 { + if len(a) == 0 || len(a) != len(b) { + return 0 + } + switch m { + case MetricL2: + var s float64 + for i := range a { + d := float64(a[i]) - float64(b[i]) + s += d * d + } + return float32(math.Sqrt(s)) + case MetricIP: + var dot float64 + for i := range a { + dot += float64(a[i]) * float64(b[i]) + } + return float32(dot) + default: // cosine + var dot, na, nb float64 + for i := range a { + fa, fb := float64(a[i]), float64(b[i]) + dot += fa * fb + na += fa * fa + nb += fb * fb + } + if na == 0 || nb == 0 { + return 0 + } + return float32(dot / (math.Sqrt(na) * math.Sqrt(nb))) + } +} + +func better(scoreA, scoreB float32, m Metric) bool { + if m == MetricL2 { + return scoreA < scoreB + } + return scoreA > scoreB +} + +// Sim ranks members in a snapshot against query. +func Sim(blob []byte, query []float32, k int, m Metric) ([]Hit, error) { + s, err := DecodeSnapshot(blob) + if err != nil { + return nil, err + } + return s.Sim(query, k, m) +} + +func (s *Set) Sim(query []float32, k int, m Metric) ([]Hit, error) { + if s == nil || len(query) != s.Dim || !Finite(query) { + return nil, errBadVec + } + k = ClampK(k) + type row struct { + id string + score float32 + } + rows := make([]row, 0, len(s.vals)) + for id, vec := range s.vals { + rows = append(rows, row{id: id, score: Score(query, vec, m)}) + } + sort.Slice(rows, func(i, j int) bool { + if rows[i].score != rows[j].score { + return better(rows[i].score, rows[j].score, m) + } + return rows[i].id < rows[j].id + }) + if k > len(rows) { + k = len(rows) + } + out := make([]Hit, k) + for i := 0; i < k; i++ { + out[i] = Hit{Member: []byte(rows[i].id), Score: rows[i].score} + } + return out, nil +} diff --git a/pkg/vecset/codec_test.go b/pkg/vecset/codec_test.go new file mode 100644 index 0000000..991ae0b --- /dev/null +++ b/pkg/vecset/codec_test.go @@ -0,0 +1,163 @@ +package vecset + +import ( + "bytes" + "math" + "testing" +) + +func TestCodecRoundTrip(t *testing.T) { + s := NewSet(2) + if err := s.Add([]byte("a"), []float32{1, 0}); err != nil { + t.Fatal(err) + } + if err := s.Add([]byte("b"), []float32{0, 1}); err != nil { + t.Fatal(err) + } + blob := s.Encode() + got, err := DecodeSnapshot(blob) + if err != nil { + t.Fatal(err) + } + if got.Dim != 2 || got.Card() != 2 { + t.Fatalf("dim=%d card=%d", got.Dim, got.Card()) + } + va, ok := got.Emb([]byte("a")) + if !ok || va[0] != 1 || va[1] != 0 { + t.Fatalf("a=%v", va) + } +} + +func TestCodecRejectsNaNAndLeftover(t *testing.T) { + s := NewSet(2) + _ = s.Add([]byte("a"), []float32{1, 0}) + blob := s.Encode() + // corrupt a float to NaN + bad := append([]byte(nil), blob...) + binaryPutNaN := func(b []byte) { + u := math.Float32bits(float32(math.NaN())) + b[len(b)-4] = byte(u) + b[len(b)-3] = byte(u >> 8) + b[len(b)-2] = byte(u >> 16) + b[len(b)-1] = byte(u >> 24) + } + binaryPutNaN(bad) + if _, err := DecodeSnapshot(bad); err == nil { + t.Fatal("NaN") + } + if _, err := DecodeSnapshot(append(blob, 0)); err == nil { + t.Fatal("leftover") + } + if _, err := DecodeSnapshot([]byte("nope")); err == nil { + t.Fatal("junk") + } +} + +func TestEmptySnapshotKeepsDim(t *testing.T) { + s := NewSet(4) + blob := s.Encode() + got, err := DecodeSnapshot(blob) + if err != nil || got.Dim != 4 || got.Card() != 0 { + t.Fatalf("%v dim=%d card=%d", err, got.Dim, got.Card()) + } +} + +func TestInboxRoundTrip(t *testing.T) { + add, err := EncodeInboxAdd([]byte("m"), []float32{1, 2, 3, 4}) + if err != nil { + t.Fatal(err) + } + mem, vec, err := DecodeInboxAdd(add) + if err != nil || !bytes.Equal(mem, []byte("m")) || len(vec) != 4 || vec[3] != 4 { + t.Fatalf("%v %q %v", err, mem, vec) + } + rem, err := EncodeInboxRem([]byte("m")) + if err != nil { + t.Fatal(err) + } + got, err := DecodeInboxRem(rem) + if err != nil || !bytes.Equal(got, []byte("m")) { + t.Fatalf("%v %q", err, got) + } +} + +func TestCosineOrderAndTies(t *testing.T) { + s := NewSet(2) + _ = s.Add([]byte("far"), []float32{0, 1}) + _ = s.Add([]byte("near"), []float32{1, 0.1}) + hits, err := s.Sim([]float32{1, 0}, 2, MetricCosine) + if err != nil || len(hits) != 2 || string(hits[0].Member) != "near" { + t.Fatalf("%v %+v", err, hits) + } + if hits[0].Score <= hits[1].Score { + t.Fatalf("scores %v %v", hits[0].Score, hits[1].Score) + } + s2 := NewSet(2) + _ = s2.Add([]byte("aa"), []float32{1, 0}) + _ = s2.Add([]byte("bb"), []float32{1, 0}) + ties, err := s2.Sim([]float32{1, 0}, 2, MetricCosine) + if err != nil || string(ties[0].Member) != "aa" || string(ties[1].Member) != "bb" { + t.Fatalf("tie order %+v", ties) + } +} + +func TestL2AndIPOrder(t *testing.T) { + s := NewSet(2) + if err := s.Add([]byte("a"), []float32{0, 0}); err != nil { + t.Fatal(err) + } + if err := s.Add([]byte("b"), []float32{10, 0}); err != nil { + t.Fatal(err) + } + l2, err := s.Sim([]float32{1, 0}, 1, MetricL2) + if err != nil || len(l2) != 1 || string(l2[0].Member) != "a" || l2[0].Score < 0 { + t.Fatalf("l2 %+v %v", l2, err) + } + ip, err := s.Sim([]float32{1, 0}, 1, MetricIP) + if err != nil || len(ip) != 1 || string(ip[0].Member) != "b" { + t.Fatalf("ip %+v %v", ip, err) + } +} + +func TestKGreaterThanCard(t *testing.T) { + s := NewSet(2) + _ = s.Add([]byte("a"), []float32{1, 0}) + hits, err := s.Sim([]float32{1, 0}, 50, MetricCosine) + if err != nil || len(hits) != 1 { + t.Fatalf("%d %v", len(hits), err) + } +} + +func TestFullAndMemberLen(t *testing.T) { + s := NewSet(2) + for i := 0; i < MaxMembers; i++ { + id := []byte{byte(i >> 8), byte(i)} + if err := s.Add(id, []float32{1, 0}); err != nil { + t.Fatal(i, err) + } + } + if err := s.Add([]byte("zz"), []float32{1, 0}); err != errFull { + t.Fatalf("full: %v", err) + } + // replace existing + id0 := []byte{0, 0} + if err := s.Add(id0, []float32{0, 1}); err != nil { + t.Fatal(err) + } + if s.Card() != MaxMembers { + t.Fatal(s.Card()) + } + if err := s.Add(make([]byte, 256), []float32{1, 0}); err != errBadMember { + t.Fatalf("256: %v", err) + } +} + +func TestWorstEncodedSizeFits1MiB(t *testing.T) { + n := WorstEncodedSize(MaxDim, MaxMembers, MaxMemberLen) + if n != 5+512*(1+255+1024) { + t.Fatal(n) + } + if n > 1<<20 { + t.Fatal(n) + } +} diff --git a/pkg/vecset/eng/cluster.go b/pkg/vecset/eng/cluster.go new file mode 100644 index 0000000..30582d2 --- /dev/null +++ b/pkg/vecset/eng/cluster.go @@ -0,0 +1,25 @@ +package eng + +import ( + "context" + + "github.com/Code0987/supercache/pkg/store" +) + +// FetchOwner GetOrLoads a snapshot. Installs only if this node holds a replica. +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.IsVectorSet() { + return store.Entry{}, false, nil + } + if h.HoldsReplica(name) { + _ = h.Store().VSInstall(name, ent.Value, ent.Version, ent.ExpireAt) + } + return ent, true, nil +} diff --git a/pkg/vecset/eng/host.go b/pkg/vecset/eng/host.go new file mode 100644 index 0000000..b92665c --- /dev/null +++ b/pkg/vecset/eng/host.go @@ -0,0 +1,23 @@ +package eng + +import ( + "context" + + "github.com/Code0987/supercache/pkg/store" +) + +// Host is the engine-owned wiring vecset needs (no engine import). +type Host interface { + Store() store.Store + KeyspaceName() string + ExpireAt() int64 + MaxValue() int + VectorDim() int + VectorMetric() 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 +} diff --git a/pkg/vecset/eng/local.go b/pkg/vecset/eng/local.go new file mode 100644 index 0000000..f9e41fb --- /dev/null +++ b/pkg/vecset/eng/local.go @@ -0,0 +1,100 @@ +package eng + +import ( + "errors" + + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/vecset" +) + +var ( + ErrRejected = errors.New("rejected") + ErrTooLarge = errors.New("too large") +) + +func ReplicateSnapshot(h Host, name string, ver uint64, expire int64) { + ent, ok := h.Store().Peek(name) + if !ok || !ent.IsVectorSet() || len(ent.Value) == 0 || ent.Value[0] != vecset.PrefixSnapshot { + return + } + h.Replicate(name, store.Entry{ + Value: ent.Value, + Version: ver, + ExpireAt: expire, + Flags: store.FlagVectorSet, + }) +} + +func AddLocal(h Host, name string, member []byte, vec []float32) error { + expire := h.ExpireAt() + cur, _ := h.Store().PeekVersion(name) + applied, tooLarge := h.Store().VAdd(name, member, vec, cur+1, expire, h.MaxValue()) + if tooLarge { + return ErrTooLarge + } + if !applied { + return ErrRejected + } + ver, _ := h.Store().PeekVersion(name) + h.ObserveVersion(name, ver) + ReplicateSnapshot(h, name, ver, expire) + return nil +} + +func RemLocal(h Host, name string, member []byte) error { + expire := h.ExpireAt() + cur, _ := h.Store().PeekVersion(name) + if !h.Store().VRem(name, member, cur+1, expire) { + return ErrRejected + } + if !h.Store().HasVectorSet(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 vecset.PrefixAdd: + member, vec, err := vecset.DecodeInboxAdd(blob) + if err != nil { + return false + } + cur, _ := h.Store().PeekVersion(name) + ok, too := h.Store().VAdd(name, member, vec, cur+1, expireAt, h.MaxValue()) + if !ok || too { + return false + } + case vecset.PrefixRem: + member, err := vecset.DecodeInboxRem(blob) + if err != nil { + return false + } + cur, _ := h.Store().PeekVersion(name) + if !h.Store().VRem(name, member, cur+1, expireAt) { + return false + } + default: + return false + } + ver, _ := h.Store().PeekVersion(name) + h.ObserveVersion(name, ver) + ReplicateSnapshot(h, name, ver, expireAt) + return true +} + +func ApplyInstall(h Host, name string, blob []byte, version uint64, expireAt int64) bool { + if expireAt == 0 { + expireAt = h.ExpireAt() + } + return h.Store().VSInstall(name, blob, version, expireAt) +} diff --git a/pkg/vecset/engine_cluster_test.go b/pkg/vecset/engine_cluster_test.go new file mode 100644 index 0000000..df2b020 --- /dev/null +++ b/pkg/vecset/engine_cluster_test.go @@ -0,0 +1,104 @@ +package vecset_test + +import ( + "context" + "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/vecset" +) + +func vsClusterKS() keyspace.Config { + return keyspace.Config{ + Name: "items", Mode: keyspace.ModeVectorSet, + MaxBytes: 1 << 20, ReplicationFactor: 2, + } +} + +func TestVectorSetReplicaSnapshot(t *testing.T) { + c, err := testcluster.Start(testcluster.Config{ + Nodes: 3, + Keyspaces: []keyspace.Config{vsClusterKS()}, + }) + if err != nil { + t.Fatal(err) + } + defer c.Close() + ctx := context.Background() + if err := c.Nodes()[0].Engine.VAdd(ctx, "items", "set", []byte("a"), []float32{1, 0}); err != nil { + t.Fatal(err) + } + deadline := time.Now().Add(2 * time.Second) + var locals int + for time.Now().Before(deadline) { + locals = 0 + for _, n := range c.Nodes() { + if n.Engine.HasLocal("items", "set") { + locals++ + } + } + if locals == 2 { + break + } + time.Sleep(15 * time.Millisecond) + } + if locals != 2 { + t.Fatalf("local copies=%d want 2", locals) + } + var nonReplica *engine.Engine + for _, n := range c.Nodes() { + hits, err := n.Engine.VSim(ctx, "items", "set", []float32{1, 0}, 1) + if err != nil || len(hits) != 1 || string(hits[0].Member) != "a" { + t.Fatalf("VSim on %s: %v %v", n.ID, hits, err) + } + if !n.Engine.HasLocal("items", "set") { + nonReplica = n.Engine + } + } + if nonReplica == nil { + t.Fatal("expected a non-replica") + } + if nonReplica.HasLocal("items", "set") { + t.Fatal("non-replica installed") + } +} + +func TestVectorSetInboxIgnoredOnReplica(t *testing.T) { + c, err := testcluster.Start(testcluster.Config{ + Nodes: 3, + Keyspaces: []keyspace.Config{vsClusterKS()}, + }) + if err != nil { + t.Fatal(err) + } + defer c.Close() + ctx := context.Background() + name := "set" + // pick a non-owner + var nonOwner *engine.Engine + for _, n := range c.Nodes() { + if o, ok := n.Engine.OwnerOf(name); ok && o.ID != n.ID { + nonOwner = n.Engine + break + } + } + if nonOwner == nil { + t.Fatal("no non-owner") + } + inbox, err := vecset.EncodeInboxAdd([]byte("x"), []float32{1, 0}) + if err != nil { + t.Fatal(err) + } + ok, err := nonOwner.ApplyPut("items", name, store.Entry{Value: inbox, Flags: store.FlagVectorSet, Version: 1}) + if err != nil { + t.Fatal(err) + } + if ok { + t.Fatal("non-owner applied inbox") + } + _ = ctx +} diff --git a/pkg/vecset/engine_test.go b/pkg/vecset/engine_test.go new file mode 100644 index 0000000..eb8c90e --- /dev/null +++ b/pkg/vecset/engine_test.go @@ -0,0 +1,143 @@ +package vecset_test + +import ( + "context" + "errors" + "math" + "strings" + "testing" + "time" + + "github.com/Code0987/supercache/pkg/engine" + "github.com/Code0987/supercache/pkg/keyspace" + "github.com/Code0987/supercache/pkg/store" + "github.com/Code0987/supercache/pkg/vecset" +) + +func vsKS() keyspace.Config { + return keyspace.Config{Name: "items", Mode: keyspace.ModeVectorSet, MaxBytes: 1 << 20, TTL: time.Hour} +} + +func TestEngineVAddVSim(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + ctx := context.Background() + if err := e.VAdd(ctx, "items", "set", []byte("near"), []float32{1, 0}); err != nil { + t.Fatal(err) + } + if err := e.VAdd(ctx, "items", "set", []byte("far"), []float32{0, 1}); err != nil { + t.Fatal(err) + } + hits, err := e.VSim(ctx, "items", "set", []float32{1, 0}, 2) + if err != nil || len(hits) != 2 || string(hits[0].Member) != "near" { + t.Fatalf("%v %+v", err, hits) + } + n, ok, err := e.VCard(ctx, "items", "set") + if err != nil || !ok || n != 2 { + t.Fatal(n, ok, err) + } + dim, ok, err := e.VDim(ctx, "items", "set") + if err != nil || !ok || dim != 2 { + t.Fatal(dim, ok, err) + } + vec, ok, err := e.VEmb(ctx, "items", "set", []byte("near")) + if err != nil || !ok || vec[0] != 1 { + t.Fatal(vec, ok, err) + } +} + +func TestEngineZeroVecCosineVsL2(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + _ = e.UpdateKeySpace(keyspace.Config{Name: "l2", Mode: keyspace.ModeVectorSet, MaxBytes: 1 << 20, VectorMetric: keyspace.VectorMetricL2}) + ctx := context.Background() + if err := e.VAdd(ctx, "items", "s", []byte("a"), []float32{0, 0}); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("cosine zero add: %v", err) + } + if _, err := e.VSim(ctx, "items", "missing", []float32{0, 0}, 1); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("cosine zero sim: %v", err) + } + if err := e.VAdd(ctx, "l2", "s", []byte("a"), []float32{0, 0}); err != nil { + t.Fatal(err) + } +} + +func TestEngineVSimMissingVsBad(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + ctx := context.Background() + if _, err := e.VSim(ctx, "items", "nope", []float32{float32(math.Inf(1))}, 1); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("inf: %v", err) + } + hits, err := e.VSim(ctx, "items", "nope", []float32{1, 0}, 3) + if err != nil || len(hits) != 0 { + t.Fatalf("missing: %v %v", err, hits) + } +} + +func TestEngineGetPutWrongMode(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + _ = e.UpdateKeySpace(keyspace.Config{Name: "tags", Mode: keyspace.ModeSet, MaxBytes: 1 << 20}) + ctx := context.Background() + if _, err := e.Get(ctx, "items", "s"); !errors.Is(err, engine.ErrInvalidArgument) || !strings.Contains(err.Error(), "VEmb") { + t.Fatalf("get: %v", err) + } + if err := e.Put(ctx, "items", "s", []byte("x")); !errors.Is(err, engine.ErrInvalidArgument) || !strings.Contains(err.Error(), "VAdd") { + t.Fatalf("put: %v", err) + } + if err := e.VAdd(ctx, "tags", "s", []byte("a"), []float32{1, 0}); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatalf("vadd on set: %v", err) + } +} + +func TestEngineGetOrLoadLocalPeek(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + ctx := context.Background() + if err := e.VAdd(ctx, "items", "s", []byte("a"), []float32{1, 0}); err != nil { + t.Fatal(err) + } + ent, err := e.GetOrLoadLocal(ctx, "items", "s") + if err != nil || !ent.IsVectorSet() || len(ent.Value) == 0 || ent.Value[0] != vecset.PrefixSnapshot { + t.Fatalf("%v %+v", err, ent) + } + if _, err := e.Get(ctx, "items", "s"); !errors.Is(err, engine.ErrInvalidArgument) { + t.Fatal(err) + } +} + +func TestEngineDeleteTombstone(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + ctx := context.Background() + _ = e.VAdd(ctx, "items", "s", []byte("a"), []float32{1, 0}) + if err := e.Delete(ctx, "items", "s"); err != nil { + t.Fatal(err) + } + _, ok, err := e.VCard(ctx, "items", "s") + if err != nil || ok { + t.Fatal(ok, err) + } +} + +func TestEngineInboxIgnoredOnNonOwner(t *testing.T) { + e := engine.New() + defer e.Close() + _ = e.UpdateKeySpace(vsKS()) + inbox, err := vecset.EncodeInboxAdd([]byte("a"), []float32{1, 0}) + if err != nil { + t.Fatal(err) + } + // no cluster: apply inbox as owner + ok, err := e.ApplyPut("items", "s", store.Entry{Value: inbox, Flags: store.FlagVectorSet, Version: 1}) + if err != nil || !ok { + t.Fatalf("owner apply: %v %v", ok, err) + } +}