a733fc1993b548bb2e64a5d94e72cda364fb917f
Collection gains an indexes list; evict_doc removes entries at the single document-death chokepoint; upsert does build → check → reserve → log → evict → publish so entry insertion after the append is infallible and a rejected unique write never reaches the log. Replay registers empty indexes from create/drop records (types 3/4) and Engine.open rebuilds them from live docs. compact re-emits index-create records. Engine gains create_index/drop_index and dup_index for E11000 naming.
mongo-light
A lightweight, embedded MongoDB-compatible document database written in
Zig 0.16. Like SQLite, it stores everything in a single file; unlike SQLite,
it speaks the MongoDB wire protocol, so real clients — mongosh, the Node.js
driver, PyMongo — connect over TCP and just work.
Quick start
zig build # build the server
zig build test # run the unit test suite
zig-out/bin/mongo-light --port 27017 --db data.log
# in another terminal:
mongosh --port 27017
> db.users.insertOne({name: "alice", age: 30})
> db.users.find({age: {$gt: 25}}).toArray()
> db.users.updateOne({name: "alice"}, {$set: {vip: true}})
> db.users.deleteOne({name: "bob"})
Features
- Wire protocol: OP_MSG (2013) plus legacy OP_QUERY/OP_REPLY (2004/2001)
for the driver handshake; hello/isMaster with
maxWireVersion: 8, so modern drivers (Node, Python, mongosh) connect without workarounds. - BSON: full parse/serialize round-trip for all common types (including binary, regex, timestamps, ObjectId), canonical MongoDB comparison order for sorting and range queries.
- CRUD:
insert,find(filter, sort, skip/limit, projection),update(multi/upsert),delete,findAndModify,count,aggregate($match,$sort,$skip,$limit,$project,$count,$groupwith$sum), pluscreate/drop/listCollections/listDatabases/dropDatabase. - Query operators:
$eq$ne$gt$gte$lt$lte$in$nin$exists$regex(hand-rolled engine: anchors,.,* + ?, character classes, groups, alternation,i/soptions)$not$and$or$nor$size$all$elemMatch, with dot paths and array multikey semantics. - Update operators:
$set$unset$inc$push($each)$pull$rename, with dot-path creation (including array indices). - Storage: append-only record log (CRC32-checked,
fsyncper write, torn-tail tolerant) with in-memory indexes rebuilt on open and automatic compaction (rewrite + atomic rename when the log grows past 16 MB). Killed mid-write (kill -9), the database recovers all committed writes; the log and compaction both work with relative or absolute--dbpaths. Records up to the announced 16 MBmaxBsonObjectSizereplay correctly. - Concurrency: a writer-preferring read/write lock splits command
execution — reads (
find,count,aggregate,list*) run concurrently across connections, writes (CRUD, DDL) are exclusive and totally ordered, and handshake/no-op commands run lock-free. The log append +fsyncstill happen under the write lock, so the crash guarantees are unchanged. Fine for light workloads.
Layout
src/
bson.zig BSON parse/serialize, ObjectId, canonical comparison order
wire.zig OP_MSG/OP_QUERY framing, message + reply builders
commands.zig command dispatch (hello, CRUD, aggregate, admin)
server.zig TCP accept loop, per-connection handlers
db.zig in-memory engine: db → collection → _id → document maps
storage.zig append-only log: records, replay, CRC validation
query.zig filter matcher, regex engine, sort, projection
update.zig update operators with dot-path navigation
main.zig CLI: --port, --bind, --db
Not (yet) implemented
- Authentication (SCRAM) — run without credentials
- Real cursors (all results are returned in one batch, cursor id 0)
- Indexes (O(n) scans)
- Transactions, change streams, replicasets
- Compression (OP_COMPRESSED)
Code style
Zig 0.16 idioms (std.Io threaded through everything, unmanaged
containers); user-declared functions use snake_case per this repo's house
style.
Languages
Zig
82.8%
JavaScript
15.5%
Shell
1.7%