qrrot - database with AI Here is a 2-3 sentence factual summary of the article: The article introduces **qrrot**, a Go-based in-memory database with a TCP interface, binary storage, and a built-in AI assistant powered by Google's Gemini model. It features a strictly typed engine supporting string, int, and JSON values, a highly optimized byte-level parser that minimizes memory allocations, and achieves benchmark speeds of over 80,000 requests per second. The AI agent can execute complex, multi-step queries by iteratively reading the database state and generating conditional write commands, with safety checks that pause execution before performing destructive operations. Writing your own in-memory database is a unique way to study Go under the hood and build a meaningful pet project. Creating a simple wrapper around a map is boring. That's why I asked myself: what if I wrote a truly fast engine with binary storage, and bolted an interactive AI assistant on top of it, allowing you to communicate in natural language and making it execute chains of queries autonomously? Thus qrrot was born — a Go-based in-memory store with a TCP interface, binary snapshots, and a built-in Gemini-based agent. In this article, I will provide the most detailed overview of my project: we'll break down the architecture, look at the benchmarks, explore how the AI works here, and at the end, I'll go over all the architectural pain points and flaws. 1. Under the hood: Data types and the engine At the core of qrrot lies a thread-safe Store struct, protected by a sync.RWMutex : type Store struct { mu sync.RWMutex data map string value.Value } Unlike primitive string-string stores, the engine is strictly typed and supports three classic data types: - string - classic strings; - int - 64-bit integers; - json - JSON objects. All values are stored in memory in a Value struct, which contains a byte slice and a type tag: type Type uint8 const TypeEmpty Type = iota TypeString TypeInt TypeJson type Value struct { valueType Type data byte } This makes it easy to serialize data and avoid reflection overhead when serving it to the client. The commands are as familiar as possible: put , get , del , exists , incr , decr , all . 2. The battle for nanoseconds: The parser and network layer When you're writing a database, the "hottest" spot is parsing incoming commands. A regular strings.Split won't work here: it allocates memory for every token, which will kill the garbage collector's performance at tens of thousands of requests per second. Zero-alloc almost parser I wrote a parser that iterates over a byte array, ignoring extra spaces and tabs. The real magic happens when handling strings with spaces for example, JSON objects . The parser understands double quotes " and escaped characters \" , carefully collecting tokens into a pre-allocated 8 byte buffer. The benchmark results Apple M4, Darwin ARM64 speak for themselves: - Raw in-memory operations: - BenchmarkStore Get - 6.29 ns/op , 0 B/op , 0 allocs/op - BenchmarkStore Put - 13.30 ns/op , 0 B/op , 0 allocs/op - - Command parsing: - BenchmarkParser ParseGet - 25.70 ns/op , 32 B/op , 1 allocs/op - BenchmarkParser ParsePut - 51.19 ns/op , 32 B/op , 2 allocs/op - Allocations in the parser are kept to an absolute minimum 1-2 per command — they are only spent on converting bytes to a string when creating the command object. TCP server Network communication is built on the standard net package. Each connection is handled in its own goroutine. The full cycle receiving a packet - parsing - locking the mutex - reading/writing - responding to the client works extremely fast: - BenchmarkTCPServer Get - 11 970 ns/op ~83 000 RPS - BenchmarkTCPServer Put - 12 157 ns/op ~82 000 RPS 3. The killer feature: Interactive AI assistant To activate it, simply start the database with the -ai flag and pass the API KEY . The ai command then becomes available in the REPL. Multi-step execution support Simply generating a single command doesn't work for complex tasks. For example, for the query "if the user ivan exists, increment his age" , the AI cannot immediately issue a write command, as it doesn't know the state of the database. To solve this, a loop up to 5 iterations is implemented under the hood, where the AI communicates with the DB engine using special tags: - QUERY READ: