Skip to content

Releases: wutlu/boltcache

v2.1.0

Choose a tag to compare

@wutlu wutlu released this 22 Oct 16:27
a2796a6

Sorted sets, reproducible benchmarks against Redis, Valkey and Dragonfly, and faster hot keys. This release follows up on the feedback from BoltCache's first Hacker News thread.

Added

  • Sorted sets: ZADD (NX/XX/GT/LT/CH/INCR), ZINCRBY, ZSCORE, ZMSCORE, ZCARD, ZCOUNT, ZRANK/ZREVRANK (WITHSCORE), ZREM, ZRANGE (BYSCORE/REV/LIMIT/WITHSCORES), ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZPOPMIN/ZPOPMAX, ZREMRANGEBYRANK/ZREMRANGEBYSCORE and ZSCAN. They're backed by a skiplist with O(log n) ranks, persisted in snapshots, filterable with SCAN ... TYPE zset and returned by GET /cache/{key}.
  • Reproducible benchmarks: bench/run.sh compares BoltCache with Redis (with and without I/O threads), Valkey and Dragonfly under the same pinned CPU budget, and a GitHub Actions workflow runs it. Results and methodology are in BENCHMARKS.md.
  • README sections on project status, when (not) to use BoltCache with a comparison to Redis/Valkey, Dragonfly, NATS and etcd, and how the project is built and verified.
  • Issue templates; private vulnerability reporting is enabled.

Performance

  • Commands pipelined by a client that hit the same shard now run under a single lock acquisition, and shard locks spin briefly before blocking. This removes most lock contention on hot keys. On an M1 Pro, unpipelined ZADD to one key went from 55K to 114K ops/s, and pipelined HSET from 800K to 1.33M.
  • Skiplist nodes embed their level arrays, so a random insert into a 100K-member sorted set takes 2.4 µs instead of 3.2 µs.

Changed

  • Performance claims in the README now come only from the reproducible benchmark, including where BoltCache is slower: pipelined ZADD, and SET/GET on 2 cores.

Install

docker run -d -p 6382:6382 -p 8090:8090 -v boltcache-data:/data ghcr.io/wutlu/boltcache:2.1.0
go install github.com/wutlu/boltcache/v2@v2.1.0

Or download a binary below.

v2.0.1

Choose a tag to compare

@wutlu wutlu released this 22 Oct 13:26
28219e0

BoltCache 2.0 is a ground-up rework: it now works with redis-cli, redis-benchmark and Redis client libraries, and it's about 2x faster than 1.0. This patch release makes go install work and restores the release notes that were missing from 2.0.0.

Fixed in 2.0.1

  • go install github.com/wutlu/boltcache/v2@latest works. Go requires a /v2 suffix in the module path of v2+ releases, so the module path is now github.com/wutlu/boltcache/v2.
  • Release notes are included in GitHub releases again.

What's new in 2.0

  • Redis compatible: 120 commands over RESP: strings, lists, sets, hashes, TTL/SCAN, pub/sub, MULTI/EXEC/WATCH, INFO... 1.x answered only PING/SET/GET and hung on everything else.
  • Real Lua scripting with EVAL/EVALSHA. Scripts are atomic, sandboxed and time-limited.
  • Eviction: max_memory and max_keys are enforced with lru, lfu, random, ttl or noeviction.
  • Crash-safe persistence: types and binary data survive restarts, and 1.x data files are converted automatically.
  • Security: token auth is now actually enforced on REST, RESP and TCP. There's also CSRF protection, rate limiting and TLS for HTTP.
  • Ops: Prometheus /metrics, /health, JSON logs, a multi-arch distroless Docker image, and binaries for Linux, macOS, Windows and FreeBSD.
  • Fixes for data races, data loss after restarts, dropped commands and an XSS in the web client.

On an M1 Pro with redis-benchmark, BoltCache is ahead of Redis 8.4: +5–10% without pipelining and 1.25–2x with pipeline 16.

⚠️ Upgrading from 1.x

  • Authentication is enforced when security.auth.enabled is true.
  • Writing a different type to an existing key returns WRONGTYPE.
  • Port 6381 and the cluster command were removed.
  • cors_origins defaults to same-origin.

See the full changelog and the README's Upgrading from 1.x section.

Install

docker run -d -p 6382:6382 -p 8090:8090 -v boltcache-data:/data ghcr.io/wutlu/boltcache:2.0.1
go install github.com/wutlu/boltcache/v2@v2.0.1

Or download a binary below. Then: redis-cli -p 6382 PING.

BoltCache 2.0.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 13:14
0a03564

Use 2.0.1: it fixes go install (module path github.com/wutlu/boltcache/v2).

BoltCache 2.0 is a ground-up rework of the engine, the network layer and the tooling. It is now a real Redis-compatible server: redis-cli, redis-benchmark, go-redis and other clients work against it, with real Lua scripting, transactions, eviction and crash-safe persistence. It is also about 2x faster than 1.0 and outperforms Redis 8.4 on a multi-core machine in redis-benchmark (see the README for numbers and methodology).

Highlights

  • Redis compatibility: 120 commands over RESP2, up from PING/SET/GET. Strings with all SET options, keys/TTL (EXPIRE NX/XX/GT/LT, SCAN with MATCH/COUNT/TYPE), lists, sets, hashes, pub/sub with patterns, MULTI/EXEC/WATCH, Lua, INFO, CONFIG GET, CLIENT, COMMAND, HELLO and more.
  • Real Lua scripting (gopher-lua) replaces the line-based imitation: EVAL, EVALSHA, EVAL_RO, SCRIPT LOAD/EXISTS/FLUSH, redis.call/redis.pcall with Redis type conversions. Scripts are atomic, sandboxed and time-limited.
  • Atomicity everywhere: every command runs under its key's shard lock; multi-key commands, transactions and scripts lock all their keys in a fixed order, so they're atomic and can't deadlock.
  • Eviction and limits: cache.max_memory and cache.max_keys are now enforced with lru, lfu, random, ttl or the new noeviction policy.
  • Crash-safe, typed persistence: snapshots keep value types and binary data, are written atomically (temp file + fsync + rename), can be gzipped and are skipped when nothing changed. 1.x snapshots are converted automatically.
  • Security: token authentication is enforced on REST, RESP and TCP; CORS allow-list (also for WebSockets) with CSRF protection, per-IP rate limiting, request size limits (strict ones for unauthenticated clients), HTTPS for the REST API, constant-time token checks, sandboxed Lua.
  • Observability: Prometheus /metrics, /health and /ready, structured logging with log/slog (text/JSON), optional pprof.
  • Distribution: prebuilt binaries for Linux, macOS, Windows and FreeBSD (amd64/arm64), multi-arch distroless images on ghcr.io/wutlu/boltcache, go install github.com/wutlu/boltcache@latest.

Added

  • RESP listener (server.resp, port 6382) with incremental, zero-copy parsing: pipelining, partial reads and inline commands are handled correctly. The TCP port (6380) auto-detects RESP, so Redis clients work there too.
  • REST endpoints: GET /list/{key}, DELETE /set/{key}/{member}, GET /hash/{key}, DELETE /hash/{key}/{field}, GET /cache/{key}/ttl, GET /keys (paginated), POST /command (run any command), /health, /ready, /metrics. GET /cache/{key} returns lists, sets and hashes too.
  • Real token management: POST/GET/DELETE /auth/tokens create, list (masked, with usage statistics) and revoke runtime tokens valid for every protocol.
  • ${VAR} / ${VAR:-default} environment expansion in configuration files and BOLTCACHE_CONFIG.
  • boltcache config show, boltcache version, boltcache config generate --force; config validate reports every problem at once.
  • boltcache client benchmark: parallel, pipelined load generator with p50/p99 latency. client interactive is now a RESP client similar to redis-cli, and client test runs a feature check.
  • features.lua_timeout, cache.default_ttl/max_ttl for the REST API, persistence.compression, security.rate_limit and security.tls now take effect.
  • Web client: "run any command" box, token stored in the browser, works on any host/port.
  • CI on Linux and macOS with the race detector, fuzzing and golangci-lint; GoReleaser and Docker release automation; Dependabot.

Fixed

  • The Redis-compatible port hung on any command other than PING, SET and GET (no reply was sent), which also made redis-benchmark hang at startup.
  • Commands split across TCP packets were silently dropped, and only the first three arguments of a command were parsed.
  • On the high-performance text port, any command starting with s or g was treated as SET/GET (e.g. SADD x y stored a value).
  • Authentication was configured but never enforced: the middleware was commented out, so tokens in config-dev.yaml protected nothing.
  • Data races between hash/set/list writes, reads and snapshots could crash the server with concurrent map read and map write.
  • After a restart, values written over RESP came back base64-encoded, and writing to a restored hash/set/list wiped its previous contents.
  • Persistence wrote the snapshot in place, so a crash during a save could leave a truncated file.
  • Pub/sub: dead subscribers were never removed, one slow subscriber blocked publishers, and concurrent writes to a WebSocket could panic the server.
  • POST /eval panicked when Lua scripting was disabled; ParseMemorySize("1") panicked.
  • The web client was vulnerable to XSS through cached values and pub/sub messages, and was hard-wired to localhost:8090.
  • KEYS/PSUBSCRIBE glob matching could take exponential time on hostile patterns.
  • config validate ignored --config; config generate overwrote existing files; SIGTERM didn't stop the HTTP server gracefully.

Changed

  • Breaking: the Go module path is github.com/wutlu/boltcache and Go 1.26 or newer is required to build.
  • Breaking: authentication is enforced when security.auth.enabled is true (see above).
  • Breaking: writing a different type to an existing key returns WRONGTYPE instead of silently replacing it, and replies follow Redis (e.g. HSET returns the number of new fields, DEL the number of deleted keys).
  • Breaking: the separate gnet text listener on tcp.port + 1 (6381) was removed; port 6380 now runs on gnet and serves both protocols.
  • Breaking: the cluster command was removed. It started an isolated node and never replicated data; replication is on the roadmap.
  • gorilla/mux was replaced with the standard library router; gopkg.in/yaml.v3 (archived) with go.yaml.in/yaml/v3. All dependencies were updated.
  • Defaults: server.mode is both, the REST port is 8090 everywhere, eviction_threshold is 1.0, snapshot compression is off, and cors_origins is empty (same origin only) instead of *, so web pages you visit can't use a local BoltCache. Non-GET requests from origins that aren't allowed get 403.
  • The Docker image is distroless, runs as non-root, is multi-arch and is configured through BOLTCACHE_* environment variables; docker-compose.yml was fixed.
  • Swagger UI assets are pinned to a fixed version; the OpenAPI document covers every endpoint and declares the auth schemes.

Install

docker run -d -p 6382:6382 -p 8090:8090 -v boltcache-data:/data ghcr.io/wutlu/boltcache:2.0.0
go install github.com/wutlu/boltcache/v2@latest   # from 2.0.1

Or download a binary below. Upgrading from 1.x? Read the Upgrading from 1.x section of the README first: authentication is now enforced and a few behaviours changed.