Documentation
¶
Overview ¶
Package slabbis implements an in-process cache with an optional RESP-compatible server layer.
Design:
Values are stored in a slabber Arena, giving fixed-slot memory management with a lock-free read path. Variable-length values are accommodated via Arena size classes; values larger than the largest class are stored directly on the heap (escape hatch, not the common path).
Keys are managed in a sharded hash map — one shard per logical CPU — to distribute mutex contention. Each shard maps string keys to Entry values holding the slabber ArenaRef, the stored length, and the expiry.
TTL eviction runs in a background goroutine per shard, scanning for expired entries on a configurable interval.
The Cache interface is the public contract. The concrete *cache type satisfies it. A Server wraps a Cache and speaks RESP over a net.Listener.
Concurrency properties:
- Get: one shard RLock + one slabber Slot() call (lock-free after shard)
- Set: one shard Lock + one Arena Alloc + possible Arena Free of old value
- Del: one shard Lock + one Arena Free
- The slabber read path (Slot) holds no lock.
Index ¶
Constants ¶
const Version = "0.1.5"
Version is the current slabbis release.
Variables ¶
var DefaultClasses = []slabber.SizeClass{
{MaxSize: 64},
{MaxSize: 512},
{MaxSize: 4096},
{MaxSize: 32768},
{MaxSize: 262144},
}
DefaultClasses provides five size classes covering typical cache values: 64B, 512B, 4KB, 32KB, 256KB.
Functions ¶
This section is empty.
Types ¶
type Cache ¶
type Cache interface {
// Get returns the value for key and whether it was found.
// The returned slice is a direct view into slabber memory.
// Do not retain it across a subsequent Set or Del on the same key.
Get(key string) ([]byte, bool)
// GetCopy returns a heap-allocated copy of the value for key.
// The copy is made while holding the shard read lock, so it is safe
// to retain indefinitely. Use this wherever the caller cannot guarantee
// the slice will not outlive a concurrent Set or Del on the same key
// (e.g. in the server layer before writing to a network buffer).
GetCopy(key string) ([]byte, bool)
// GetInto copies the value for key into dst, growing dst if necessary,
// and returns the populated slice and whether the key was found.
// The copy is made while holding the shard read lock.
//
// Unlike GetCopy, GetInto does not allocate when cap(dst) >= len(value).
// Callers that pool or reuse dst (e.g. one buffer per server connection)
// achieve zero per-call allocations in steady state.
//
// The returned slice aliases dst. Callers must not retain it across a
// subsequent call that reuses the same dst.
GetInto(key string, dst []byte) ([]byte, bool)
// Set stores value under key with the given TTL.
// A zero TTL means the entry does not expire.
Set(key string, value []byte, ttl time.Duration)
// Del removes key. Returns true if the key existed.
Del(key string) bool
// Exists reports whether key is present and not expired.
Exists(key string) bool
// TTL returns the remaining lifetime of key.
// Returns 0, false if the key does not exist.
// Returns 0, true if the key exists but has no expiry.
// Returns remaining, true if the key exists with an expiry.
TTL(key string) (time.Duration, bool)
// Flush removes all entries from the cache.
Flush()
// Stats returns a point-in-time snapshot of cache state.
Stats() CacheStats
// Keys returns all live keys matching pattern.
// Pattern uses filepath.Match syntax: * matches any sequence, ? matches
// any single character. Use "*" to return all keys.
Keys(pattern string) []string
// MGet returns values for the given keys in order.
// Missing or expired keys produce a nil entry.
// The returned slices are direct views into slabber memory.
// Do not retain any entry across a subsequent Set or Del on any key
// in the batch — the underlying slot may be reused after a free.
MGet(keys ...string) [][]byte
// MSet sets multiple key/value pairs atomically within each shard.
// A zero TTL means no expiry. Existing keys are overwritten.
MSet(ttl time.Duration, pairs map[string][]byte)
// SetNX sets key to value only if the key does not already exist.
// Returns true if the key was set.
SetNX(key string, value []byte, ttl time.Duration) bool
// GetDel returns the value for key and removes it atomically.
GetDel(key string) ([]byte, bool)
// Rename renames key from to key to. Returns false if from does not exist.
Rename(from, to string) bool
// DBSize returns the total number of live keys across all shards.
DBSize() int
// SetTTL updates the expiry of an existing live key without changing its
// value. Returns false if the key does not exist or has already expired.
//
// A zero ttl removes the expiry, making the key permanent (equivalent to
// PERSIST). A positive ttl sets a new absolute deadline from now.
SetTTL(key string, ttl time.Duration) bool
// GetSet atomically replaces the value for key with newVal and returns the
// old value. The new entry is stored with the given ttl (0 = no expiry).
// Returns (nil, false) if the key did not previously exist.
GetSet(key string, newVal []byte, ttl time.Duration) ([]byte, bool)
// IncrBy atomically increments the integer value stored at key by delta
// (use negative delta for decrement) and returns the new value.
// The value must be a decimal integer string; if it is not, or if the
// result would overflow int64, an error is returned.
// If the key does not exist it is created with value "0" before applying
// the increment.
// The stored value is always the decimal string representation of the
// result, compatible with Redis INCR/DECR/INCRBY/DECRBY semantics.
IncrBy(key string, delta int64) (int64, error)
// Close stops background goroutines. The cache must not be used after Close.
Close()
}
Cache is the public interface for slabbis. All methods are safe for concurrent use.
type CacheStats ¶
type CacheStats struct {
Keys int // number of live (non-expired) keys
SlabStats []slabber.Stats // one entry per Arena size class
}
CacheStats holds a point-in-time snapshot of cache state.
type Config ¶
type Config struct {
// Shards is the number of key-space partitions.
// 0 defaults to runtime.NumCPU().
Shards int
// Classes defines the Arena size classes for value storage.
// 0 defaults to DefaultClasses.
Classes []slabber.SizeClass
// ReaperInterval controls how often the TTL reaper runs per shard.
// 0 defaults to 1 second.
ReaperInterval time.Duration
// BucketsPerShard is passed to each slabber Arena as the initial
// bucket count. 0 defaults to runtime.NumCPU().
BucketsPerShard int
}
Config controls cache construction.
func DevConfig ¶ added in v0.1.5
func DevConfig() Config
DevConfig returns a Config suitable for development, testing, and memory-constrained environments (CI containers, sandboxes).
Characteristics: two size classes (64B and 4KB), one shard, one bucket per shard, fast reaper interval. Total virtual address footprint is approximately 8MB — safe for any environment. Maximum storable value size is 4096 bytes.
For production use, prefer New(Config{}) which applies DefaultClasses and scales shards and buckets to the available CPU count.
func (Config) MaxValueSize ¶ added in v0.1.5
MaxValueSize returns the maximum value size in bytes that this configuration will accept. Values larger than this are silently dropped by Set.
The ceiling is the MaxSize of the largest configured size class. Callers can use this to guard against silent drops before calling Set:
if len(value) > cfg.MaxValueSize() {
// handle oversize value explicitly
}
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server listens on a network address and dispatches RESP commands to a Cache. It supports exactly the commands slabbis exposes; anything else returns an error response rather than a panic.
Supported commands:
GET key SET key value [EX seconds | PX milliseconds] GETSET key value GETEX key [EX seconds | PX milliseconds | PERSIST] MGET key [key ...] MSET key value [key value ...] SETNX key value GETDEL key DEL key [key ...] UNLINK key [key ...] EXISTS key [key ...] STRLEN key INCR key INCRBY key increment DECR key DECRBY key decrement KEYS pattern SCAN cursor [MATCH pattern] [COUNT count] RANDOMKEY COPY source destination RENAME from to DBSIZE TYPE key TTL key PTTL key EXPIRE key seconds PEXPIRE key milliseconds PERSIST key FLUSH (non-standard; equivalent to FLUSHALL) PING [message] COMMAND (returns empty array — satisfies redis-cli startup probe) QUIT
func NewServer ¶
NewServer returns a Server bound to addr using the provided Cache. addr may be a TCP address ("127.0.0.1:6399") or a Unix socket path ("unix:///tmp/slabbis.sock" — the "unix://" prefix is stripped).
func (*Server) Close ¶
Close stops the server and waits for all goroutines to return. It closes the listener first (causing Serve to return), then waits for Serve itself and all active connection handlers to finish.
func (*Server) Serve ¶
Serve accepts connections until the listener is closed. It returns the listener's close error, which is typically non-nil only when Close() has been called.
Serve registers itself with serveWg so that Close() can wait for the accept loop to return before declaring shutdown complete.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
slabbis
command
Command slabbis is a minimal cache server speaking a Redis-compatible protocol subset over TCP or a Unix socket.
|
Command slabbis is a minimal cache server speaking a Redis-compatible protocol subset over TCP or a Unix socket. |
|
internal
|
|
|
resp
Package resp implements a minimal subset of the Redis Serialisation Protocol (RESP2) sufficient for slabbis's supported command set.
|
Package resp implements a minimal subset of the Redis Serialisation Protocol (RESP2) sufficient for slabbis's supported command set. |