Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Optional: `-gossip-secret <key>`.
### CLI (`sc`)

```bash
# With supercache-node running on defaults (-demo-keyspace: demo + tags + board + profile + doc + flags):
# With supercache-node running on defaults (-demo-keyspace: demo + tags + board + profile + doc + flags + embeddings):
go run ./cmd/sc put greeting "hello"
go run ./cmd/sc get greeting
go run ./cmd/sc del greeting
Expand All @@ -84,6 +84,11 @@ go run ./cmd/sc -keyspace doc jsonset user $.name '"Ada"'
go run ./cmd/sc -keyspace doc jsonget user $.name
go run ./cmd/sc -keyspace flags bitset seen 0 1
go run ./cmd/sc -keyspace flags bitget seen 0
go run ./cmd/sc -keyspace embeddings vadd products east 1,0
go run ./cmd/sc -keyspace embeddings vadd products north 0,1
go run ./cmd/sc -keyspace embeddings vadd products ne 0.7,0.7
go run ./cmd/sc -keyspace embeddings vcard products
go run ./cmd/sc -keyspace embeddings vsim products 1,0.05 3
go run ./cmd/sc -keyspace seen bloom add users alice # ModeBloom keyspace
# ModeTopK (register a ModeTopK keyspace, or use examples/billboard plays/hot):
# go run ./cmd/sc -keyspace plays topkadd hot t001
Expand Down Expand Up @@ -208,7 +213,7 @@ Apps: `client.DialTLS` with `pkg/tlsconfig.ClientFiles`. See [docs/OPERATIONS.md
| `pkg/warmup` | Hot keys, topology handoff (hot then rest), refresh-ahead |
| `pkg/client` | Application gRPC client (KV + Bloom + Set + ZSet + Geo + List + Hash + Counter + JSON + Bitmap + HLL + TopK) |
| `pkg/tlsconfig` | TLS/mTLS config from PEM files |
| `cmd/supercache-node` | Node binary (`-demo-keyspace`: demo / tags / board / profile / doc / flags) |
| `cmd/supercache-node` | Node binary (`-demo-keyspace`: demo / tags / board / profile / doc / flags / embeddings) |
| `cmd/sc` | CLI: get/put/del, bloom, sadd*, z*, geo*, l*, h*, incr/cget, json*, bit*, hlladd/hllcount, topkadd/topklist, admin diagnostics |
| `cmd/scbench` | SuperCache vs Redis load harness + in-process matrix |

Expand Down
77 changes: 24 additions & 53 deletions cmd/supercache-node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func main() {
gossipAdv = flag.String("gossip-advertise", "127.0.0.1", "gossip advertise address")
seeds = flag.String("seeds", "", "comma-separated gossip seeds host:port")
gossipSecret = flag.String("gossip-secret", "", "optional gossip shared secret")
demoKS = flag.Bool("demo-keyspace", true, "register demo CacheOnly keyspace")
demoKS = flag.Bool("demo-keyspace", true, "register stock demo keyspaces (demo/tags/board/profile/doc/flags/embeddings)")
globalRPS = flag.Float64("global-rps", 0, "global DataSource rate limit (0=off)")
cluster = flag.Bool("cluster", false, "enable gossip membership + peer fan-out")
showVersion = flag.Bool("version", false, "print version and exit")
Expand Down Expand Up @@ -87,60 +87,10 @@ func main() {
defer wm.Stop()

if *demoKS {
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "demo",
Mode: keyspace.ModeCacheOnly,
MaxBytes: 64 << 20,
TTL: 5 * time.Minute,
}); err != nil {
if err := registerDemoKeyspaces(eng); err != nil {
log.Fatal(err)
}
// ModeSet for exact membership demos (feature tags, allow-lists, …).
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "tags",
Mode: keyspace.ModeSet,
MaxBytes: 16 << 20,
TTL: 30 * time.Minute,
}); err != nil {
log.Fatal(err)
}
// ModeZSet for scored rankings (leaderboards, time-ordered feeds, …).
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "board",
Mode: keyspace.ModeZSet,
MaxBytes: 16 << 20,
TTL: 30 * time.Minute,
}); err != nil {
log.Fatal(err)
}
// ModeHash for per-field maps (user profiles, session attrs, …).
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "profile",
Mode: keyspace.ModeHash,
MaxBytes: 16 << 20,
TTL: 30 * time.Minute,
}); err != nil {
log.Fatal(err)
}
// ModeJSON for nested documents (path set/get/del).
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "doc",
Mode: keyspace.ModeJSON,
MaxBytes: 16 << 20,
TTL: 30 * time.Minute,
}); err != nil {
log.Fatal(err)
}
// ModeBitmap for packed flags (SETBIT / GETBIT / BITCOUNT / BITPOS).
if err := eng.UpdateKeySpace(keyspace.Config{
Name: "flags",
Mode: keyspace.ModeBitmap,
MaxBytes: 16 << 20,
TTL: 30 * time.Minute,
}); err != nil {
log.Fatal(err)
}
log.Printf("demo keyspaces: demo=CacheOnly tags=ModeSet board=ModeZSet profile=ModeHash doc=ModeJSON flags=ModeBitmap")
log.Printf("demo keyspaces: demo=CacheOnly tags=ModeSet board=ModeZSet profile=ModeHash doc=ModeJSON flags=ModeBitmap embeddings=ModeVectorSet")
}

cacheSrvOpts, peerSrvOpts, peerDialTLS, err := buildTLS(
Expand Down Expand Up @@ -334,3 +284,24 @@ func buildTLS(
}
return cacheOpts, peerOpts, peerDial, nil
}

// registerDemoKeyspaces installs the stock -demo-keyspace set.
func registerDemoKeyspaces(eng *engine.Engine) error {
type ks struct {
cfg keyspace.Config
}
for _, item := range []ks{
{keyspace.Config{Name: "demo", Mode: keyspace.ModeCacheOnly, MaxBytes: 64 << 20, TTL: 5 * time.Minute}},
{keyspace.Config{Name: "tags", Mode: keyspace.ModeSet, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
{keyspace.Config{Name: "board", Mode: keyspace.ModeZSet, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
{keyspace.Config{Name: "profile", Mode: keyspace.ModeHash, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
{keyspace.Config{Name: "doc", Mode: keyspace.ModeJSON, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
{keyspace.Config{Name: "flags", Mode: keyspace.ModeBitmap, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
{keyspace.Config{Name: "embeddings", Mode: keyspace.ModeVectorSet, MaxBytes: 16 << 20, TTL: 30 * time.Minute}},
} {
if err := eng.UpdateKeySpace(item.cfg); err != nil {
return err
}
}
return nil
}
2 changes: 1 addition & 1 deletion docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Peer mesh with mTLS: every node uses the same CA; each node presents a cert sign

Wrong verb for the mode → invalid argument. Configure the same modes on every node (see rollout above).

Demo node (`-demo-keyspace`): registers `demo` (CacheOnly), `tags` (ModeSet), `board` (ModeZSet), `profile` (ModeHash), `doc` (ModeJSON), `flags` (ModeBitmap). Geo/List/Counter/HLL/TopK keyspaces are configured by the app. Live plays billboard: [examples/billboard](../examples/billboard/README.md). Hash walkthrough: [examples/hash](../examples/hash/README.md). Rate limiter: [examples/ratelimit](../examples/ratelimit/README.md). JSON document: [examples/json](../examples/json/README.md). Bitmap flags: [examples/bitmap](../examples/bitmap/README.md). HLL sketch: [examples/hll](../examples/hll/README.md).
Demo node (`-demo-keyspace`): registers `demo` (CacheOnly), `tags` (ModeSet), `board` (ModeZSet), `profile` (ModeHash), `doc` (ModeJSON), `flags` (ModeBitmap), `embeddings` (ModeVectorSet). Geo/List/Counter/HLL/TopK/CMS keyspaces are configured by the app. Live plays billboard: [examples/billboard](../examples/billboard/README.md). Hash walkthrough: [examples/hash](../examples/hash/README.md). Rate limiter: [examples/ratelimit](../examples/ratelimit/README.md). JSON document: [examples/json](../examples/json/README.md). Bitmap flags: [examples/bitmap](../examples/bitmap/README.md). HLL sketch: [examples/hll](../examples/hll/README.md).

## Consistency cheatsheet

Expand Down
7 changes: 1 addition & 6 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ 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 |
Expand Down Expand Up @@ -39,6 +33,7 @@ Do not implement from a draft.
| [2026-09-02-refactor-zset.md](./2026-09-02-refactor-zset.md) | ModeZSet file layout (no contract) |
| [2026-09-02-refactor-geo.md](./2026-09-02-refactor-geo.md) | ModeGeo file layout (no contract) |
| [2026-09-02-feature-folders.md](./2026-09-02-feature-folders.md) | Feature folders (List first) |
| [2026-09-04-mode-vector-set.md](./2026-09-04-mode-vector-set.md) | `ModeVectorSet` |
| [2026-08-25-list-counter-version.md](./2026-08-25-list-counter-version.md) | List/Counter snapshot version |
| [2026-08-13-unify-grpc-error-map.md](./2026-08-13-unify-grpc-error-map.md) | grpcmap |

Expand Down
2 changes: 1 addition & 1 deletion examples/cluster3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Exercises **CacheOnly** (`demo`: sessions / KV), **ModeSet** (`tags`: feature flags), and **ModeZSet** (`board`: leaderboard via `ZAdd` / `ZScore` / `ZRange` / `ZRem`).

Requires `supercache-node` with `-demo-keyspace` (default), which registers `demo`, `tags`, `board`, `profile`, `doc`, and `flags` (this example uses the first three).
Requires `supercache-node` with `-demo-keyspace` (default), which registers `demo`, `tags`, `board`, `profile`, `doc`, `flags`, and `embeddings` (this example uses the first three).

Start three nodes (separate terminals), then run this example.

Expand Down
59 changes: 59 additions & 0 deletions examples/vecset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# ModeVectorSet example — similar items

A **self-contained 3-node** walkthrough of `ModeVectorSet`: a named set of
`float32` embeddings with brute-force K-NN. Metric is a **keyspace** knob
(cosine / L2 / IP), not an argument on `VSim`.

## What it shows

| Step | SuperCache behavior |
|------|---------------------|
| Why not ZSet / Put | ZSet score is a scalar you write. A JSON blob `Put` is one LWW value |
| `VAdd` | Three members on cosine `items`; first add locks dim=2 |
| `VCard` / `VDim` / `VEmb` | Count, locked dim, stored vector copy |
| RF=2 + `VSim` | Every node returns the same cosine order; non-replica does not install |
| Replace | `VAdd` same id overwrites the vector; card stays 3 |
| Dim lock / zero | Wrong dim and cosine `{0,0}` are invalid; Get/Put rejected |
| L2 vs IP | Same two members: L2 picks nearest, IP picks largest dot |
| `k` / missing | `k>card` returns card hits; missing name is empty / `present=false` |
| Last `VRem` | Empty live set keeps dim; `Delete` is what drops the name |
| Recreate | `Delete` + `VAdd` makes a new set |

## Run

```bash
# from repo root — starts 3 nodes, prints the walkthrough, exits 0 on success
go run ./examples/vecset
```

CI covers the same path: `go test ./examples/vecset`.

## Manual `sc` against a stock node

`supercache-node -demo-keyspace` (the default) registers **`embeddings`**
(`ModeVectorSet`, cosine, dim unlocked until first `vadd`).

```bash
# terminal 1
go run ./cmd/supercache-node \
-cache 127.0.0.1:9000 -peer 127.0.0.1:9001 -admin 127.0.0.1:8080

# terminal 2 — add, inspect, search
go run ./cmd/sc -keyspace embeddings vadd products east 1,0
go run ./cmd/sc -keyspace embeddings vadd products north 0,1
go run ./cmd/sc -keyspace embeddings vadd products ne 0.7,0.7
go run ./cmd/sc -keyspace embeddings vcard products
go run ./cmd/sc -keyspace embeddings vdim products
go run ./cmd/sc -keyspace embeddings vemb products east
go run ./cmd/sc -keyspace embeddings vsim products 1,0.05 3

# replace, rem, missing
go run ./cmd/sc -keyspace embeddings vadd products east 0,1
go run ./cmd/sc -keyspace embeddings vsim products 1,0 3
go run ./cmd/sc -keyspace embeddings vrem products north
go run ./cmd/sc -keyspace embeddings vcard products
go run ./cmd/sc -keyspace embeddings vdim missing # prints missing, exit 1

# cosine rejects a zero vector (L2/IP would allow it on another keyspace)
# go run ./cmd/sc -keyspace embeddings vadd products origin 0,0
```
Loading
Loading