ValKeyper uses a simple but effective in-memory data model based on Go’s native map types:
type KVStore struct { store map[string]string // Main key-value storage expiryMap map[string]chan int // Expiry management Stream map[string][]StreamEntry // Stream data structures // ... other fields ...}
ValKeyper determines the type of a key using the TYPE command:
case "TYPE": _, ok := kv.store[buff[1]] if ok { res = []byte("+string\r\n") } else { _, ok2 := kv.Stream[buff[1]] if ok2 { res = []byte("+stream\r\n") } else { res = []byte("+none\r\n") } }
When a key is explicitly deleted, its expiry goroutine is notified:
case "DEL": key := buff[1] delete(kv.store, key) ch, ok := kv.expiryMap[key] if ok { ch <- 1 // Signal the goroutine to stop }
This approach creates one goroutine per expiring key. While Go’s goroutines are lightweight, this may not scale to millions of keys with TTLs. Consider using a heap-based expiry queue for production workloads.
// Must be greater than 0-0if currEntryTime < 1 && currEntrySeq < 1 { res = []byte("-ERR The ID specified in XADD must be greater than 0-0\r\n") break}// Must be greater than last entryif lastEntryTime > currEntryTime { res = []byte("-ERR The ID specified in XADD is equal or smaller than the target stream top item\r\n") break}if lastEntryTime == currEntryTime && lastEntrySeq >= currEntrySeq { res = []byte("-ERR The ID specified in XADD is equal or smaller than the target stream top item\r\n") break}
type Database struct { Index int // Database number (0-15) Size int // Number of keys Expiry int // Number of keys with expiry DbStore map[string]string // Regular keys ExpiryStore []expiryEntry // Keys with expiry times}type expiryEntry struct { Key string Value string Expiry uint64 // Unix timestamp in milliseconds}
RDB Loading Process
Parse RDB file using the rdb package
Load regular keys directly into kv.store
Load expiring keys into kv.store
Calculate remaining TTL from stored expiry timestamp
Even though all values are strings, ValKeyper supports numeric operations:
case "INCR": v, ok := kv.store[buff[1]] if !ok { kv.store[buff[1]] = "1" } else { val, err := strconv.Atoi(v) if err == nil { kv.store[buff[1]] = fmt.Sprintf("%d", val+1) } else { res = []byte("-ERR value is not an integer or out of range\r\n") break } } res = []byte(fmt.Sprintf(":%s\r\n", kv.store[buff[1]]))