AtomDB is a Redis‑compatible, in‑memory key‑value database written in Go. It’s designed as a learning project and demonstrates how a simple database server works under the hood.
I built AtomDB to learn:
- How Redis’s RESP protocol works
- Basic TCP servers in Go with concurrent clients
- Simple caching (LRU) and expiry (TTL) logic
- Thread‑safe in‑memory storage
It’s not production‑grade, but it shows each piece clearly.
- 🔐 AUTH: per‑user secret key
- 💾 SET / GET: store and retrieve strings
- ⏲️ EXPIRE: set key time‑to‑live
- ♻️ LRU: evict least‑recently‑used keys when >30 entries per user
- ⚡ RESP protocol: same wire format as Redis
- 🧵 Concurrency: goroutines + mutexes
-
Clone Repo
git clone https://github.com/your-user/atomdb.git cd atomdb -
Run the server
go run server.go
-
Connect with Redis CLI
redis-cli -p 8080
-
Authenticate
AUTH mySecret123
-
Listens on port (default
:8080) -
Accepts connections, spawns a goroutine per client
-
Tracks each client in a
clientstruct:type client struct { conn net.Conn // network socket reader *bufio.Reader // RESP reader isAuthenticated bool // auth flag remoteAddr string // client address secretKey string // user’s secret key LRU *LRUList // per‑user LRU cache }
-
Starts a background TTL watcher
-
Main loop:
- Read raw RESP bytes
- Parse into
[]string - If not authenticated → only
AUTHallowed - Else → pass to
HandleCommand
Parses Redis Serialization Protocol:
func ReadRESP(reader *bufio.Reader) ([]byte, error) { … }
func RespParsing(input []byte) ([]string, error) { … }- Reads
*<count>header - For each item, reads
$<len>then payload - Returns slice like
["SET","name","Alice"]
Routes commands based on first token:
switch cmd {
case "SET":
SetKey(...)
case "GET":
GetKey(...)
case "EXPIRE":
SetTTL(...)
default:
ERR unknown command
}-
Checks argument count
-
Sends Redis‑style replies:
+OK- `-$len
`
-ERR <message>
Global map:
var globalStore = map[string]map[string]string{}
// secretKey → (key → value)-
SetKey:
- Locks with
mut.Lock() - Creates user map if missing
- Updates value
- Calls
AddNodeon LRU
- Locks with
-
GetKey:
- RLocks with
mut.RLock() - Reads value
- Calls
RecentlyUsedon LRU
- RLocks with
Tracks expirations:
var TTLmap = map[string]map[string]time.Time{}
// secretKey → (key → expiryTime)-
SetTTL: parse seconds, store expiry
-
TTLWatcher:
- Runs every second
- Locks TTL and store
- Deletes expired keys from
globalStoreand LRU - Unlocks
Per‑user LRU cache:
type Node struct {
prev, next *Node
key string
}
type LRUList struct {
head, tail *Node
count int
LRUnodeMap map[string]*Node
}-
AddNode:
- If count ≥30: remove
tail(delete from store) - Insert new node at
head
- If count ≥30: remove
-
RecentlyUsed: move accessed node to
head -
RemoveNode: remove node when TTL fires