
Here at DoltHub, we think version control for databases is a critical feature that is long overdue. That’s why when we learn about another entry into the field, we get excited. It means more people are seeing the value in version-controlled databases.
Argon is exactly the type of project I’m talking about. Their README sums it up with this:

Three ideas, thirty seconds:
- A branch is a pointer, not a copy — created with a metadata write. Checkout separately materializes the data.
checkoutturns a branch into a real MongoDB database — pymongo, mongoose, mongosh, indexes, aggregation, transactions: all real, and supported document writes become history while capture is healthy.- Review and recover changes — diff, merge, undo and pin states within the configured retention and capture guarantees.
We’ve been working on DumboDB for the last several months, and it’s a full rewrite of MongoDB’s API without any shared code. We wanted to highlight the Argon project today, and discuss their approach to the problem space. Let’s dive in!
How Argon Works#
Argon does its work by watching a Mongo Replica Set. Argon isn’t a member of the replica set, and it isn’t a database either. It’s a client that reads the replica set’s change stream and records each document change in its own log.
- Create a branch: one metadata document is written, and no data moves.
- Check it out: Argon rebuilds the branch’s documents in memory from its log, then bulk-inserts them, 1,000 at a time, into a new database
argon_br_<id>on your mongod. - Connect: the app talks to that database directly, or through Argon’s proxy, which only renames the database in each request. mongod handles every query.
- Capture:
argon watchopens a change stream on the database and appends each changed document, before and after, to its log.
Side by Side#
| Argon | DumboDB | |
|---|---|---|
| Query engine | Stock mongod 6+, run as a replica set | Its own, parity-tested against MongoDB 8.0 |
| History unit | Every captured write gets a sequence number; no commits | Explicit commits over a working set, like Git |
| History retention | argon gc, run on a schedule, deletes history older than the retention window (default 7 days); pins protect chosen points | Every commit is kept; dumboGC only reclaims data that no commit or branch references |
| Branch access | Checkout copies the branch into a new database | db@branch in the database name, on the same port |
| Querying the past | Rebuilt in memory, with no indexes or aggregation | db@main~1 or db@<hash>, with the full query engine and indexes |
| Merge | Child into parent only; whole-document comparison; one resolution for the whole merge | Any branch into any branch; four merge modes; per-document conflict resolution |
| What is versioned | Documents only; indexes, validators and collection options are not | Documents and validators |
| Consistency | History lags the write, and capture can fall behind or have gaps | A commit is atomic |
| Branch storage | Each checked-out branch is a full copy in mongod | Branches share storage |
Branch AND Merge Support#
Here at DoltHub, we are a little particular about what we call a “branch.” Many databases claim that they have branching support, and virtually none of them support merging. Merging is a critical feature if you are going to claim you support branches. Don’t listen to us, listen to Wikipedia:
Branching generally implies the ability to later merge or integrate changes back onto the parent branch.
Argon supports merging, to an extent. You can import an existing database, and from
that point forward your copy is fixed in time. The argon_br_<id> database it creates
does allow for branch and merge support, which is pretty neat!
The Write-Ahead Log (WAL) in Argon is the source of truth. When you want to show a diff between
two branches, Argon uses the events in the WAL to rebuild the full state of each
branch, then compares them document by document. The same goes for merging. When you
merge one branch into another, it rebuilds three states (the fork point, the source branch
and the target branch), works out what happened to each document, and writes the
result into the target branch.
Here is a branch and merge in action, on a shop project whose main branch is already
checked out and has three products in it:
$ argon branches create price-update -p shop --from main
⚡ Created branch 'price-update' (a metadata write, no data copied)
Project: shop
Based on: main
Ready for instant experimentation!
$ argon checkout -p shop -b price-update
Checked out at LSN 5: 1 collection(s), 3 document(s)
Connection string:
mongodb://127.0.0.1:27120/argon_br_6af38338d2fdd6e63afb6612?replicaSet=rs0
Run "argon watch" for this branch to capture direct writes into the WAL.
$ argon watch -p shop -b price-update &
Watching for changes (Ctrl-C to stop)...
$ mongosh "mongodb://127.0.0.1:27120/argon_br_6af38338d2fdd6e63afb6612?replicaSet=rs0" --quiet \
--eval 'printjson(db.products.updateOne({_id: 1}, {$set: {price: 15}})); printjson(db.products.insertOne({_id: 4, name: "teapot", price: 30}))'
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
acknowledged: true,
insertedId: 4
}
$ argon diff -p shop -b price-update
Merging price-update → main
put products/~argon:bson:DAAAABBpAAEAAAAA
put products/~argon:bson:DAAAABBpAAQAAAAA
2 change(s), 0 conflict(s)
$ argon merge preview -p shop -b price-update
Merging price-update → main
put products/~argon:bson:DAAAABBpAAEAAAAA
put products/~argon:bson:DAAAABBpAAQAAAAA
2 change(s), 0 conflict(s)
Plan 6af3834c3c0ca05da0c91d68 saved (pending).
Apply with: argon merge apply 6af3834c3c0ca05da0c91d68
$ argon merge apply 6af3834c3c0ca05da0c91d68
Merged: 2 change(s) applied.
Benchmarks#
Comparing the performance behaviors of each server highlights how each approach
has the expected results based on design goals. DumboDB’s use of
Prolly Trees gives it algorithmic benefits when
performing diff and merge operations. It may be the case that this is a
secondary access pattern compared to regular MongoDB queries, which Argon will be
better at given it is Mongo native.
Argon is faster for all of the things you would expect MongoDB to be faster for:
| Step | Documents | Argon | DumboDB |
|---|---|---|---|
| 100 bulk updates on a branch | 100k | 25 ms | 43 ms |
| 10,000 bulk updates on a branch | 100k | 2.2 s | 4.1 s |
Point read by _id | 1M | 344 µs | 783 µs |
| Initial load | 1M | 230 s | 399 s |
DumboDB is faster on operations which leverage the shared storage of Prolly Trees:
| Step | Documents | Argon | DumboDB |
|---|---|---|---|
| New branch to first read | 10k / 100k / 1M | 500 / 4,000 / 47,200 ms | 3 / 3 / 4 ms |
| Diff | 10k / 100k / 1M | 500 / 2,200 / 23,700 ms | 4 / 4 / 5 ms |
| Merge | 10k / 100k / 1M | 500 / 2,300 / 24,100 ms | 3 / 4 / 4 ms |
For select Mongo operations where Dumbo is slower, you pay about a 2X tax for version control compared to Argon. But with Dumbo you get fast version control at scale: on a million-document database, diff and merge are thousands of times faster than Argon (up to 6,000X), and a new branch is ready to read nearly 12,000X sooner. This is by design.
This makes sense, and it’s why DoltHub and all of its products use Prolly Trees.
The operations of diff and merge require storage to be optimized for them.
If you have an application which does those operations a lot, DumboDB may be
the right choice. If your application branches and merges very rarely, Argon
may be a better choice.
Takeaways#
The first takeaway is that version-controlled databases are here and you should use them. Dolt may have been early in the space, but it seems like we are regularly seeing people interested in solving this problem.
Where Argon shines: Full MongoDB compatibility because it is real mongod, it fits beside an existing deployment, it has focused agent tooling (sandboxes, MCP server, SDK, console), and plain reads and writes on a checked-out branch are faster because it’s native MongoDB.
Where DumboDB shines: Branching directly off production. Immediate ability to query
on a new branch. diff and merge perform proportionally to the size of the delta.
Join us on our Discord and tell us what you’re building!