Native Go Java Wire Compatible
FoundationDB for Go
A Go ecosystem for FoundationDB, essentials first: a native, pure-Go client and a wire-compatible Record Layer, with a SQL engine on top. No cgo, and 2-4x faster reads than libfdb_c.
import "fdb.dev/pkg/fdbgo/fdb" // pure Go. CGO_ENABLED=0. no libfdb_c.
fdb.MustAPIVersion(730)
db, _ := fdb.OpenDatabase("/etc/foundationdb/fdb.cluster")
db.Transact(func(tx fdb.WritableTransaction) (any, error) {
tx.Set(fdb.Key("greeting"), []byte("hello"))
return tx.Get(fdb.Key("greeting")).MustGet(), nil
})Pre-1.0. The wire format is the part to trust first, and it’s conformance- and differential-tested against Java. Pin a commit and run the suites before you ship. Maturity →
No cgo. Static binary. Faster reads.
Most FDB tooling links Apple’s C library through cgo. That means no static binaries, painful cross-compilation, and a glibc dependency. The pure-Go client speaks the FoundationDB wire protocol directly. It’s the default backend and produces byte-identical reads and writes. The read speedup comes from skipping the C client’s network-thread hop and multi-version-client shim. Writes go through the same commit path, so they run at parity.
The 10 ms-RTT row is the one that matters: localhost microbenchmarks are syscall-bound, so they flatter the pure-Go client. Under real network latency the read advantage holds (2.4x at 10 ms) and converges to parity at high RTT, where both clients are waiting on the network. Writes run at parity throughout. The numbers are reproducible from TestBenchmarkSanity, and the method is in PERFORMANCE.md. Want Apple's C client instead? One build tag (-tags libfdbc) swaps it in, same bytes on the wire.
Quickstart
Install the driver, then open a database, create a schema, and read and write, all from Go. The pure-Go client is the default backend, so you only need a cluster file.
frl fdb up starts a single-node FoundationDB in Docker (the only prerequisite) and points the frl CLI at it. Remove it with frl fdb down.Install
go get fdb.dev/pkg/relational/sqldriverOpen a database, create a schema, write and read, in Go
package main
import (
"database/sql"
"fmt"
"log"
_ "fdb.dev/pkg/relational/sqldriver"
)
func main() {
db, err := sql.Open("fdbsql",
"fdbsql:///myapp?cluster_file=/etc/foundationdb/fdb.cluster&schema=main")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Create the database, a schema template, and a schema.
db.Exec(`CREATE DATABASE /myapp`)
db.Exec(`CREATE SCHEMA TEMPLATE app
CREATE TABLE users (id BIGINT, name STRING, email STRING, PRIMARY KEY (id))
CREATE INDEX by_email ON users (email)`)
db.Exec(`CREATE SCHEMA /myapp/main WITH TEMPLATE app`)
// Write a row, then read it back.
db.Exec(`INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com')`)
var name string
if err := db.QueryRow(
`SELECT name FROM users WHERE email = ?`, "alice@example.com",
).Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Println(name) // Alice
}Or query the same data from the CLI
$ frl sql --database /myapp --schema main
fdb> SELECT name, email FROM users WHERE email = 'alice@example.com';
NAME │ EMAIL
──────┼───────────────────
Alice │ alice@example.com
(1 row)The Cascades planner uses the by_email index for that query, so it isn't a full scan. The same store is reachable from Go's typed record API if you'd rather store protobuf records directly. See the record-layer guide.
A client, and layers on top.
FoundationDB is an ordered, transactional key-value store with strict-serializable ACID, and it’s what Snowflake and Apple’s CloudKit run on. Higher-level data models are built as layers on top of it. fdb.dev is the Go client plus a growing set of those layers.
Pure-Go Client
A from-scratch FDB wire-protocol client. No cgo, faster reads, read-your-writes, retries, commit_unknown_result handling. Validated against libfdb_c 7.3.77.
Record Layer
Structured records, secondary indexes, versions, continuations, split records, schema evolution. The record format is byte-identical to Java, so you can share a cluster.
SQL Engine
A database/sql driver backed by a Cascades optimizer ported from Java’s fdb-relational-core: index selection, sort elimination, streaming aggregation.
Share a cluster with Java.
Wire compatibility is the whole point of the project. Record, index, version, continuation, and split-record formats are byte-identical to Java Record Layer 4.12.11.0, and the client speaks the FoundationDB 7.3 wire protocol (validated against 7.3.77; 8.0 is future work). CI enforces all of this against real FoundationDB with a Java conformance suite, a cross-backend differential, and a binding-stress tester. No mocks.