Help us shape the future of Go! Take the 2026 Go Developer Survey

boltcache

command module
v2.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 1 Imported by: 0

README ΒΆ

BoltCache ⚑

Note: This project was coded by vibe.

A fast, Redis-compatible in-memory cache with a REST API, written in Go.

BoltCache speaks the Redis protocol (RESP), so redis-cli, redis-benchmark and client libraries such as go-redis, ioredis and redis-py work out of the box. It also serves a JSON REST API with WebSocket pub/sub, real Lua scripting, transactions, eviction policies, persistence and Prometheus metrics, in a single dependency-free binary.

CI Release Go Reference Go Version License

Keywords: redis, resp, cache, in-memory, golang, rest-api, pub-sub, lua, high-performance, docker, kubernetes

✨ Features

  • ⚑ Fast on multi-core machines: sharded keyspace and an event-driven network layer (gnet); see benchmarks
  • πŸ”Œ Redis compatible: 120 commands over RESP2 for strings, lists, sets, hashes, keys/TTL, SCAN, pub/sub, MULTI/EXEC/WATCH, Lua and server commands
  • 🌐 REST API: JSON over HTTP, WebSocket subscriptions, a generic /command endpoint, OpenAPI docs at /docs and a built-in web client
  • πŸ”§ Real Lua scripting: EVAL/EVALSHA/SCRIPT with redis.call/redis.pcall; scripts run atomically on their keys, in a sandbox, with a timeout
  • πŸ”’ Atomic by design: every command is atomic, and multi-key commands, transactions and scripts lock all their keys
  • 🧹 Memory management: max_memory/max_keys limits with lru, lfu, random, ttl or noeviction policies
  • πŸ’Ύ Persistence: typed, crash-safe snapshots (atomic rename + fsync), optional gzip, rotating backups, restored on startup
  • πŸ›‘οΈ Security: token auth on every protocol (REST, RESP, TCP), CORS allow-list, per-IP rate limiting, TLS for HTTP, request size limits
  • πŸ“ˆ Observability: Prometheus /metrics, /health, INFO, structured JSON logs, optional pprof
  • 🐳 Cloud native: multi-arch distroless Docker image, configuration through ${ENV} variables, graceful shutdown

πŸš€ Quick Start

Docker
docker run -d --name boltcache -p 6382:6382 -p 8090:8090 -v boltcache-data:/data ghcr.io/wutlu/boltcache:latest

redis-cli -p 6382 SET greeting "hello world" EX 60
curl localhost:8090/cache/greeting

Enable authentication with -e BOLTCACHE_AUTH=true -e BOLTCACHE_TOKEN=change-me. See docker-compose.yml.

Binary

Download a binary for Linux, macOS, Windows or FreeBSD from Releases, or install with Go 1.26+:

go install github.com/wutlu/boltcache/v2@latest
boltcache config generate   # writes a documented config.yaml
boltcache server
From source
git clone https://github.com/wutlu/boltcache.git
cd boltcache
make run          # or: make run-dev (auth enabled, token dev-token-123)

By default the server listens on:

Port Protocol Use it with
6382 RESP (Redis protocol) redis-cli -p 6382, go-redis, ioredis, redis-py, redis-benchmark
6380 Text protocol + RESP telnet/nc (SET k v β†’ OK), BoltCache 1.x clients, and Redis clients
8090 HTTP REST API, WebSocket pub/sub, /metrics, /docs, web client at /

πŸ“– Usage

Redis clients
redis-cli -p 6382
127.0.0.1:6382> HSET user:1 name Ada lang Go
(integer) 2
127.0.0.1:6382> EXPIRE user:1 3600
(integer) 1
127.0.0.1:6382> EVAL "return redis.call('HGET', KEYS[1], 'name')" 1 user:1
"Ada"
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6382"})
rdb.Set(ctx, "key", "value", time.Minute)
Supported commands
Group Commands
Strings GET SET (EX/PX/EXAT/PXAT/NX/XX/KEEPTTL/GET) SETNX SETEX PSETEX GETSET GETDEL GETEX MGET MSET MSETNX INCR DECR INCRBY DECRBY INCRBYFLOAT APPEND STRLEN GETRANGE SUBSTR
Keys DEL UNLINK EXISTS TOUCH TYPE EXPIRE PEXPIRE EXPIREAT PEXPIREAT (NX/XX/GT/LT) TTL PTTL EXPIRETIME PEXPIRETIME PERSIST KEYS SCAN (MATCH/COUNT/TYPE) RANDOMKEY RENAME RENAMENX DBSIZE FLUSHALL FLUSHDB
Lists LPUSH RPUSH LPUSHX RPUSHX LPOP RPOP (with count) LLEN LRANGE LINDEX LSET LTRIM LREM RPOPLPUSH LMOVE
Sets SADD SREM SMEMBERS SISMEMBER SMISMEMBER SCARD SPOP SRANDMEMBER SINTER SUNION SDIFF SINTERSTORE SUNIONSTORE SDIFFSTORE SMOVE SSCAN
Hashes HSET HMSET HSETNX HGET HMGET HGETALL HDEL HEXISTS HLEN HKEYS HVALS HSTRLEN HINCRBY HINCRBYFLOAT HSCAN
Pub/Sub SUBSCRIBE PSUBSCRIBE UNSUBSCRIBE PUNSUBSCRIBE PUBLISH PUBSUB (CHANNELS/NUMSUB/NUMPAT)
Transactions MULTI EXEC DISCARD WATCH UNWATCH
Scripting EVAL EVALSHA EVAL_RO EVALSHA_RO SCRIPT (LOAD/EXISTS/FLUSH)
Server PING ECHO AUTH HELLO QUIT RESET SELECT TIME INFO CONFIG GET CLIENT COMMAND SAVE BGSAVE LASTSAVE MEMORY WAIT

Not supported yet: sorted sets, streams, bitmaps, HyperLogLog, geo commands, blocking commands (BLPOP...), RESP3 and multiple databases.

REST API
# Strings (with optional TTL)
curl -X PUT localhost:8090/cache/session:42 -d '{"value": "active", "ttl": "30m"}'
curl localhost:8090/cache/session:42
curl localhost:8090/cache/session:42/ttl

# Lists, sets, hashes
curl -X POST localhost:8090/list/jobs -d '["job1", "job2"]'
curl -X POST localhost:8090/set/tags -d '["go", "cache"]'
curl -X PUT  localhost:8090/hash/user:1/name -d '{"value": "Ada"}'
curl localhost:8090/hash/user:1            # {"value": {"name": "Ada"}}

# Any Redis command
curl -X POST localhost:8090/command -d '{"args": ["INCRBY", "visits", "5"]}'

# Pub/Sub: WebSocket subscribers receive messages from REST and RESP publishers
websocat ws://localhost:8090/subscribe/news &
curl -X POST localhost:8090/publish/news -d '{"message": "hello"}'

Full reference: README-REST.md and the interactive docs at http://localhost:8090/docs.

Lua scripting

Scripts get KEYS and ARGV and call commands with redis.call/redis.pcall, using the same type conversions as Redis. A script holds the locks of the keys passed in KEYS, so it runs atomically; touching keys that weren't declared is an error (the same rule Redis Cluster has).

# A fixed-window rate limiter
redis-cli -p 6382 EVAL "
  local n = redis.call('INCR', KEYS[1])
  if n == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end
  return n" 1 rate:client42 60

Scripts run in a sandbox (no file access, no load), cannot leak globals and are stopped after features.lua_timeout (5s by default).

Text protocol

Port 6380 keeps the line-based protocol of BoltCache 1.x for telnet/nc users and existing clients:

$ nc localhost 6380
SET user:1 John
OK
GET user:1
VALUE John
SET session abc 5m
OK
SMEMBERS tags
ARRAY go cache

βš™οΈ Configuration

All settings are optional; see the documented config.yaml and README-CONFIG.md.

cache:
  max_memory: "1GB"
  eviction_policy: "lru"     # lru, lfu, random, ttl, noeviction
persistence:
  enabled: true
  file: "./data/boltcache.json"
  interval: "30s"
security:
  auth:
    enabled: true
    tokens: ["${BOLTCACHE_TOKEN}"]   # read from the environment
boltcache config generate     # write a documented default config
boltcache config validate     # check a config file (reports every error)
boltcache config show         # print the effective configuration
boltcache server --config config-prod.yaml

πŸ›‘οΈ Security

When security.auth.enabled is true, every protocol requires a token:

curl -H "Authorization: Bearer $TOKEN" localhost:8090/cache/key   # or X-API-Token, or ?token=
redis-cli -p 6382 -a "$TOKEN" GET key                             # AUTH <token>

/ping, /health, /ready, /docs and the web client stay public; everything else, including /metrics, requires the token. Tokens are compared in constant time. See README-AUTH.md and SECURITY.md.

πŸ“ˆ Monitoring

  • GET /metrics: Prometheus metrics (boltcache_keys, boltcache_memory_used_bytes, boltcache_keyspace_hits_total, boltcache_evicted_keys_total, boltcache_commands_processed_total...)
  • GET /health, GET /ready: liveness/readiness probes
  • INFO over RESP, GET /info over HTTP
  • logging.format: json for structured logs; monitoring.profiling.enabled exposes pprof

πŸ“Š Performance

redis-benchmark against BoltCache 2.0 and Redis 8.4 (persistence on), 100 connections, 1M requests, 4 client threads. Apple M1 Pro (10 cores), macOS, loopback:

Command BoltCache Redis 8.4 BoltCache (pipeline 16) Redis 8.4 (pipeline 16)
SET 137,836/s 128,916/s 1,000,000/s 799,361/s
GET 133,067/s 128,916/s 1,996,008/s 999,001/s
INCR 137,836/s 133,245/s 1,331,558/s 999,001/s
LPUSH 137,817/s 128,949/s 1,331,558/s 800,000/s
HSET 142,674/s 128,999/s 1,331,558/s 799,361/s

Compared with BoltCache 1.0, measured with boltcache client benchmark (50 clients) on the same machine:

1.0 2.0
SET 78,456/s 146,914/s
GET 78,813/s 147,780/s
SET, pipeline 16 1,189,133/s 2,081,800/s
GET, pipeline 16 1,132,152/s 2,319,861/s

Redis runs commands on one thread while BoltCache uses every core, so the gap grows with core count and with pipelining. Numbers on your hardware will differ; measure them yourself:

redis-benchmark -p 6382 -t set,get,incr,lpush,hset -n 1000000 -c 100 -P 16 --threads 4 -q
boltcache client benchmark --addr localhost:6382 --clients 50 --requests 500000 --pipeline 16

πŸ—οΈ Architecture

 redis-cli / go-redis ─┐                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 telnet / 1.x clients ─┼─ gnet event loops ─┐  β”‚ command engine (120 cmds)   β”‚
                       β”‚  (RESP + text)     β”œβ”€β–Άβ”‚  auth Β· MULTI/EXEC Β· Lua    │──▢ sharded keyspace
 HTTP / WebSocket ─────┴─ net/http (REST) β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    1024 shards, RWMutex each
                                                         β”‚                         typed values, TTL,
                                                  pub/sub hub                      LRU/LFU eviction
                                                                                        β”‚
                                                                         snapshots (atomic, gzip, backups)
  • Keyspace: 1024 shards, each with its own lock and counters, so cores don't contend. Every command runs under its key's shard lock, which makes single-key commands atomic. Multi-key commands, MULTI/EXEC and scripts lock their shards in a fixed order, so they're atomic and deadlock-free.
  • Network: gnet event loops (one per core) parse pipelined RESP incrementally without copying; HTTP uses the standard library.
  • One engine: RESP, the text protocol, REST and Lua all execute through the same command implementation, so behaviour is identical everywhere.
  • Eviction: like Redis, approximated by sampling: candidates are compared by last access (LRU), a logarithmic access counter (LFU) or expiry time (TTL).

⬆️ Upgrading from 1.x

  • Snapshots written by 1.x are converted automatically on first start; the next save uses the new typed format.
  • Authentication is now enforced when security.auth.enabled is true. In 1.x it was configured but never checked.
  • The Redis-compatible port (6382) now supports every command listed above instead of only PING/SET/GET. The separate gnet text port (6381) is gone: port 6380 is served by gnet now and understands both protocols.
  • Writing a different type to an existing key returns WRONGTYPE (1.x silently replaced it), and commands follow Redis semantics, e.g. HSET returns the number of new fields.
  • The cluster command was removed: it never replicated data. Replication is on the roadmap.
  • The Go module path is now github.com/wutlu/boltcache/v2, and Go 1.26+ is required to build.

See CHANGELOG.md for everything that changed.

πŸ—ΊοΈ Roadmap

  • Replication (primary/replica) and clustering
  • Sorted sets, streams and blocking list commands
  • RESP3, ACL users and TLS for the RESP listener
  • Append-only file persistence

πŸ”§ Development

make help          # list targets
make test-race     # unit + integration tests with the race detector
make lint          # golangci-lint
make fuzz          # fuzz the protocol parser
make bench         # micro benchmarks

Integration tests start real servers and drive them with go-redis, raw TCP and WebSocket clients. See CONTRIBUTING.md.

πŸ“š Documentation

πŸ“„ License

MIT. See LICENSE.

πŸ“ž Support


Created by Alper Mutlu Toksâz with ❀️

Documentation ΒΆ

The Go Gopher

There is no documentation for this package.

Directories ΒΆ

Path Synopsis
Package appinfo holds build and version information.
Package appinfo holds build and version information.
internal
auth
Package auth manages API tokens shared by every protocol: REST requests (Authorization: Bearer / X-API-Token / ?token=), and RESP/TCP clients (AUTH <token>).
Package auth manages API tokens shared by every protocol: REST requests (Authorization: Bearer / X-API-Token / ?token=), and RESP/TCP clients (AUTH <token>).
cache
Package cache implements BoltCache's in-memory keyspace: typed values (strings, lists, sets, hashes), expiration, memory/key limits with eviction, multi-key atomic transactions and snapshot persistence.
Package cache implements BoltCache's in-memory keyspace: typed values (strings, lists, sets, hashes), expiration, memory/key limits with eviction, multi-key atomic transactions and snapshot persistence.
command
Package command implements BoltCache's Redis-compatible command set.
Package command implements BoltCache's Redis-compatible command set.
glob
Package glob implements Redis-style glob matching used by KEYS, SCAN MATCH and PSUBSCRIBE.
Package glob implements Redis-style glob matching used by KEYS, SCAN MATCH and PSUBSCRIBE.
pubsub
Package pubsub implements channel and pattern based publish/subscribe.
Package pubsub implements channel and pattern based publish/subscribe.
resp
Package resp implements the Redis Serialization Protocol (RESP2): an incremental, zero-copy command parser that also accepts inline commands (what you type in telnet), and reply writers.
Package resp implements the Redis Serialization Protocol (RESP2): an incremental, zero-copy command parser that also accepts inline commands (what you type in telnet), and reply writers.
server
Package server wires the cache, command engine and network listeners (TCP text/RESP, RESP and HTTP) together and manages their lifecycle.
Package server wires the cache, command engine and network listeners (TCP text/RESP, RESP and HTTP) together and manages their lifecycle.
Package logger configures structured logging (log/slog) and prints the startup banner.
Package logger configures structured logging (log/slog) and prints the startup banner.
Package swaggerui serves the OpenAPI document and a Swagger UI page.
Package swaggerui serves the OpenAPI document and a Swagger UI page.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL