Open-source project
ankur-anand/unisondb avatar
ankur-anand/unisondb

UnisonDB claims a few milliseconds for the fan-out its own topology prices at 250-500 ms

A streaming multimodal database for Edge AI, and Edge Computing.

427 stars21 forksGoApache-2.0

At a glance

What is it?
UnisonDB is a Go database for edge AI that keeps a B+Tree with a write-ahead log and replicates it over gRPC or object storage. The headline latency claim, the benchmark tables, the Go version floor and the container build each disagree with something else in the repository.
Who is it for?
UnisonDB is worth reading as a design document and treating as unproven as a database. The architecture hangs together: a B+Tree with a write-ahead log, replication over gRPC or by polling object storage, namespaces for isolation, and a local MinIO example for the blob path.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The same section promises a few milliseconds and prices a hop at 250-500 ms

The fan-out section opens by saying UnisonDB can push updates to 100+ edge nodes in just a few milliseconds from a single upstream, and that reach compounds through multi-hop relaying. The feature list names the mechanism behind it as in-sync replica (ISR) replication, where the relayer or replica tracks what the upstream considers in sync. The next paragraph prices a hop. In a two-hop topology, hop 1 goes from the primary to 100 hubs at roughly 250-500 ms, hop 2 sends each of those hubs to 100 downstream edge nodes at a similar latency, and the reach works out to 100 + 10,000 = 10,100 nodes. Those two figures describe the same first hop, so they cannot both be right, and nothing says whether the millisecond count excludes queueing and network time or measures something narrower than a fan-out. The section does close with a rate: at 60k-80k `SET` ops/sec with 1 KB values, updates reach 10,000+ nodes within seconds, and it sends readers to the relayer benchmarks for the measured numbers behind that.

Four databases are introduced, three are tested under identical conditions

The Redis-compatible benchmark states that it compares the write and read performance of four databases, UnisonDB, BadgerDB, LMDB and BoltDB, through a Redis-compatible interface and the official `redis-benchmark` tool. A few lines later, under the test environment, the sentence is that all three databases were tested under identical conditions. Three is a different set from four, and the gap matters because UnisonDB is not an independent engine in that comparison: its B+Tree storage layer is either bbolt or LMDB, so with LMDB selected as the backend the row labelled UnisonDB is measuring UnisonDB's WAL and replication path against LMDB running on LMDB. Both Results headings are empty as well. The one the fan-out section points at ends at a bare Results subheading, followed by a Test Replication Flow subheading with nothing beneath it.

One laptop, one run length, and no tag to reproduce any of it

The benchmark does pin down the shape of a run: 50 iterations of mixed `SET` and `GET` operations, 200k ops per run, 10 parallel clients, 10 pipelined requests, 4 threads and 1KB payloads, on an Apple M2 Pro with 10 cores split into 6 performance and 4 efficiency, 16 GB of memory, and LMDB as the UnisonDB BTree backend. Throughput is requests per second for `SET` and `GET` and latency is p50 in milliseconds, so both axes are averages or percentiles over one workload shape rather than a distribution across payload sizes. Specific enough to be useful, narrow enough to bound what it shows, since the machine is a laptop and the replication numbers come from the local `pkg/replicator` component rather than a real edge fleet. The Redis-compatible server the tool talks to lives at `internal/benchtests/cmd/redis-server/`. The replication harness reuses the same tool with `n=[100,200,500,750,1000]` independent WAL readers and records p50, p90, p99 and max latencies against the relayer, plus `SET` and `GET` latency and throughput against it. The repository has no GitHub releases, so none of those figures can be tied to a version.

go.mod asks for 1.25.2 while the builder image is 1.24

The module file opens with `go 1.25.2`. The container build opens with `FROM golang:1.24-alpine`. The two disagree by a minor version and nothing in the Dockerfile reconciles them: there is no toolchain directive, no GOTOOLCHAIN environment line, and the go.mod plus go.sum copy happens ahead of the build step, so the mismatch only surfaces once the compiler runs. The rest of that file is deliberate, from the C toolchain and LMDB headers installed in the builder to a static link:

code
RUN go build -tags 'osusergo netgo static_build' -ldflags '-extldflags "-static" -s -w' -o unisondb ./cmd/unisondb

The build targets ./cmd/unisondb, the same entry point the quick start builds, so the binary path is consistent. The dependency list tells you what a running server pulls in: bbolt and LMDB for the two BTree paths, gorilla/mux for HTTP routing, urfave/cli for the command line, flatbuffers for the schema layer, Prometheus client_golang plus tally for metrics, gopsutil for process and host stats, and bloom for filters. `dgraph-io/badger/v4` is required as a module even though BadgerDB is one of the benchmark baselines, and fake-gcs-server and gofakes3 sit alongside it as in-process stand-ins for GCS and S3.

FROM scratch keeps the binary and drops the config the server wants

The quick start builds a binary and puts it into server mode:

bash
# Build
go build -o unisondb ./cmd/unisondb

# Run in server mode (primary)
./unisondb server --config config.toml

The final image stage is `FROM scratch`, and the only thing copied in is that binary, under `WORKDIR /root/`. No config.toml, no certificates, no data directory and no plugin payload are copied, so the server command cannot run against the image as built without mounting a configuration file and whatever else the deployment needs. The backend is not settled at build time either: the builder installs gcc, musl-dev, linux-headers and lmdb-dev, so both the bbolt and the LMDB paths get compiled in, and the documentation leaves the choice to whatever the configuration file says. The two BTree options are a single-file ACID store and a memory-mapped ACID store with copy-on-write semantics.

The only API call shown puts the namespace and the modality in the path

One request carries the whole data model:

bash
curl -X PUT http://localhost:4000/api/v1/default/kv/mykey \
  -H "Content-Type: application/json" \
  -d '{"value":"bXl2YWx1ZQ=="}'

Port 4000, a versioned `/api/v1` prefix, then a namespace segment (`default`) and a modality segment (`kv`) ahead of the key, with the payload carried as a base64 string inside a `value` field rather than as the raw request body. That single call encodes the three axes the feature list names: namespace-based isolation for multi-tenancy, key-value storage alongside wide columns and large objects, and a versioned HTTP surface. It also marks where this repository stops. Everything else is handed to seven hosted pages at unisondb.io: getting started, the complete configuration guide, the architecture overview, the HTTP API reference, backup and restore, deployment topologies, and a roadmap kept in the wiki. The last section of the document, headed The Problem: Storage and Streaming Live in Different Worlds, breaks off partway through its first sentence, so the argument the project is making for itself is not finished here.

The install target runs go install tool

The Makefile declares seven phony targets, and one of the recipes has nothing to install:

make
.PHONY: install
install:
	@go install tool

`tool` is not a directory in the tree and not a package the module can resolve, so as written that target does nothing useful. Two habits in the same file matter more. The `test` target depends on `lint` and begins by running `go fmt ./...` inside every directory that has its own go.mod, so a test run can rewrite source files before it tests them. And `html-coverage` is the only target with a recipe and no `.PHONY` line, so it keeps working only while no file of that name ever appears. The coverage directory comes from `?=` and is removed by an `@-rm -r` whose leading dash swallows failures, which turns a wrong COVERAGE_DIR into a deleted directory instead of an error message. The lint path has its own bootstrap: `lint-check-deps` installs golangci-lint at v2.11.3 whenever the binary is missing from PATH, so a clean machine runs a `go install` as a side effect of asking for tests. Two more targets are worth knowing: `gen-fb` shells out to `flatc --go flatbuffer.fbs`, matching the gen.sh at the root, and `test-release-act` runs `scripts/test_release_act.sh` against `.github/act-events/release-tag.json`.

A ZeroMQ notifier pinned to a version that was never tagged

Real-time notifications are listed as a headline capability, described as ZeroMQ-based side-car change notifications with sub-millisecond latency, and the notifier sits in its own module: the path `github.com/ankur-anand/unisondb/plugin/notifier/zeromq` appears in go.mod at the all-zero pseudo-version `v0.0.0-00010101000000-000000000000`, the form Go uses for a module that has never carried a tag. The feature the summary leads with therefore resolves at build time to a path with no release behind it. The root is a mix of toolchains as well: a `.goreleaser.yml` for release automation in a repository with no GitHub releases, a `requirements.py.list` in a Go project, `flatbuffer.fbs` beside `buf.yaml` and `gen.sh` for schemas, `gen_certs.sh`, and directories for scripts, benchmarks, tests, Terraform under `infra-tf/`, the `dbkernel/` core, `internal/`, `pkg/` and `docs/`. Two more of the direct dependencies come from the same author rather than the ecosystem: an object logger pulled at a dated pseudo-version, and `ksuid` for sortable identifiers. The dependency graph is therefore part author-internal, part provider SDK, which matters for anyone planning to vendor the build.

Editorial conclusion

UnisonDB is worth reading as a design document and treating as unproven as a database. The architecture hangs together: a B+Tree with a write-ahead log, replication over gRPC or by polling object storage, namespaces for isolation, and a local MinIO example for the blob path. The evidence does not. Both benchmark tables are empty, the hop latency in the fan-out section contradicts the millisecond figure printed above it, and the four-against-three comparison cannot be read as written. Anyone trying it should expect to supply the config file the container image does not ship, choose a BTree backend by hand, and pin a commit, because there is no release tag to pin.

Frequently asked questions

What is UnisonDB?

An Apache-2.0 database written in Go for edge AI and edge computing, described as reactive, log-native and multi-model. It pairs a B+Tree storage engine with write-ahead logging, replicates the log over gRPC or object storage, and stores key-value pairs, wide columns and large objects.

How does UnisonDB replicate updates to edge replicas?

Writes are committed on the write server, and read-only edge replicas and relayers consume the write-ahead log either through a live gRPC stream or through blob-backed replication, where the writer publishes immutable WAL segment files plus bounded catalog metadata and any number of readers poll the object store directly. S3, MinIO, GCS and Azure Blob are named as the providers, with a local MinIO example under cmd/examples/blobstore-minio.

Which storage engines can UnisonDB run on?

The backend is pluggable with two BTree implementations: BoltDB, a single-file ACID-compliant store, and LMDB, a memory-mapped ACID-compliant store with copy-on-write semantics. The container build compiles both in and does not choose between them.

Does UnisonDB have tagged releases I can pin to?

No GitHub releases exist for it, although the root carries a .goreleaser.yml for release automation. The last push is dated 2026-09-29, so the code moves, and uv-style pinning has no counterpart here beyond pinning a commit hash yourself.

Official sources

  1. ankur-anand/unisondb on GitHub
  2. Issues
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ankur-anand-unisondb.svg)](https://hysenlabs.com/projects/ankur-anand-unisondb)