mirror of
https://github.com/hanzokv/go.git
synced 2026-09-24 00:31:51 +00:00
We do not use Redis. The client was a go-redis fork and still said so
everywhere: `package redis`, `redis.NewClient`, `redis://`, `extra/redisotel`.
Consumers wrote `redis.Client` against a Valkey-backed store called KV.
package redis -> package kv (93 decls)
redis.X -> kv.X (2,794 call sites)
"redis: …" errors -> "kv: …" (206)
extra/redis{otel,cmd,prometheus,census} -> extra/kv{otel,cmd,prometheus,census}
redis:// , rediss:// -> kv:// , kvs:// (ParseURL)
Redis*/*Redis* idents -> KV*/*KV*
REDIS_VERSION, REDIS_ENDPOINTS_CONFIG_PATH -> KV_*
I previously argued this rename was unsafe because much of it was "wire
contract". That was wrong, and the categorisation shows why: RESP command names
are GET/SET/HSET — they never contain the word. Of 5,771 occurrences only two
kinds were server-visible at all: the ParseURL scheme (we choose what we pass;
our configs already use kv://) and the CLIENT SETINFO lib-name label, which the
server merely records. Nothing here changes a byte of protocol.
Two things deliberately keep the old spelling:
RediSearch — names a specific external module the FT.* commands map to.
StoreDist — contains the letters by accident (sto-reDis-t), not the word.
Breaking for consumers by design: every import site must move redis.X -> kv.X.
Minor bump, never a major — this module stays v9 forever.
156 lines
3.2 KiB
Go
156 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/rand"
|
|
|
|
"github.com/hanzokv/go/v9"
|
|
)
|
|
|
|
func main() {
|
|
ctx := context.Background()
|
|
|
|
rdb := kv.NewClient(&kv.Options{
|
|
Addr: ":6379",
|
|
})
|
|
_ = rdb.FlushDB(ctx).Err()
|
|
|
|
fmt.Printf("# BLOOM\n")
|
|
bloomFilter(ctx, rdb)
|
|
|
|
fmt.Printf("\n# CUCKOO\n")
|
|
cuckooFilter(ctx, rdb)
|
|
|
|
fmt.Printf("\n# COUNT-MIN\n")
|
|
countMinSketch(ctx, rdb)
|
|
|
|
fmt.Printf("\n# TOP-K\n")
|
|
topK(ctx, rdb)
|
|
}
|
|
|
|
func bloomFilter(ctx context.Context, rdb *kv.Client) {
|
|
inserted, err := rdb.Do(ctx, "BF.ADD", "bf_key", "item0").Bool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if inserted {
|
|
fmt.Println("item0 was inserted")
|
|
} else {
|
|
fmt.Println("item0 already exists")
|
|
}
|
|
|
|
for _, item := range []string{"item0", "item1"} {
|
|
exists, err := rdb.Do(ctx, "BF.EXISTS", "bf_key", item).Bool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if exists {
|
|
fmt.Printf("%s does exist\n", item)
|
|
} else {
|
|
fmt.Printf("%s does not exist\n", item)
|
|
}
|
|
}
|
|
|
|
bools, err := rdb.Do(ctx, "BF.MADD", "bf_key", "item1", "item2", "item3").BoolSlice()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Println("adding multiple items:", bools)
|
|
}
|
|
|
|
func cuckooFilter(ctx context.Context, rdb *kv.Client) {
|
|
inserted, err := rdb.Do(ctx, "CF.ADDNX", "cf_key", "item0").Bool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if inserted {
|
|
fmt.Println("item0 was inserted")
|
|
} else {
|
|
fmt.Println("item0 already exists")
|
|
}
|
|
|
|
for _, item := range []string{"item0", "item1"} {
|
|
exists, err := rdb.Do(ctx, "CF.EXISTS", "cf_key", item).Bool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if exists {
|
|
fmt.Printf("%s does exist\n", item)
|
|
} else {
|
|
fmt.Printf("%s does not exist\n", item)
|
|
}
|
|
}
|
|
|
|
deleted, err := rdb.Do(ctx, "CF.DEL", "cf_key", "item0").Bool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if deleted {
|
|
fmt.Println("item0 was deleted")
|
|
}
|
|
}
|
|
|
|
func countMinSketch(ctx context.Context, rdb *kv.Client) {
|
|
if err := rdb.Do(ctx, "CMS.INITBYPROB", "count_min", 0.001, 0.01).Err(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
items := []string{"item1", "item2", "item3", "item4", "item5"}
|
|
counts := make(map[string]int, len(items))
|
|
|
|
for i := 0; i < 10000; i++ {
|
|
n := rand.Intn(len(items))
|
|
item := items[n]
|
|
|
|
if err := rdb.Do(ctx, "CMS.INCRBY", "count_min", item, 1).Err(); err != nil {
|
|
panic(err)
|
|
}
|
|
counts[item]++
|
|
}
|
|
|
|
for item, count := range counts {
|
|
ns, err := rdb.Do(ctx, "CMS.QUERY", "count_min", item).Int64Slice()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Printf("%s: count-min=%d actual=%d\n", item, ns[0], count)
|
|
}
|
|
}
|
|
|
|
func topK(ctx context.Context, rdb *kv.Client) {
|
|
if err := rdb.Do(ctx, "TOPK.RESERVE", "top_items", 3).Err(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
counts := map[string]int{
|
|
"item1": 1000,
|
|
"item2": 2000,
|
|
"item3": 3000,
|
|
"item4": 4000,
|
|
"item5": 5000,
|
|
"item6": 6000,
|
|
}
|
|
|
|
for item, count := range counts {
|
|
for i := 0; i < count; i++ {
|
|
if err := rdb.Do(ctx, "TOPK.INCRBY", "top_items", item, 1).Err(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
items, err := rdb.Do(ctx, "TOPK.LIST", "top_items").StringSlice()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
for _, item := range items {
|
|
ns, err := rdb.Do(ctx, "TOPK.COUNT", "top_items", item).Int64Slice()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Printf("%s: top-k=%d actual=%d\n", item, ns[0], counts[item])
|
|
}
|
|
}
|