foundationdb

package
v0.0.0-...-e08b47a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

README

FoundationDB Testcontainer

Testcontainer module for FoundationDB. Spins up a single-node FDB instance for testing with direct bridge IP connectivity — no port mapping, no socat proxy, no DNAT.

Installation

go get github.com/birdayz/fdb-record-layer-go/pkg/testcontainers/foundationdb

Quick Start

import (
    "context"
    foundationdbtc "github.com/birdayz/fdb-record-layer-go/pkg/testcontainers/foundationdb"
)

func TestSomething(t *testing.T) {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
    defer cancel()

    // Start container — auto-initialized, ready to use.
    container, err := foundationdbtc.Run(ctx, "")
    if err != nil {
        t.Fatal(err)
    }
    defer container.Terminate(ctx)

    // Get cluster file path for your FDB client.
    path, _ := container.ClusterFilePath(ctx)
    db, _ := fdb.OpenDatabase(path)
}

How It Works

  1. Starts FDB container with Docker port mapping (4500/tcp exposed)
  2. Waits for FDBD joined cluster log
  3. Reads the cluster file from inside the container (via Exec with multiplexed stream demuxing)
  4. Constructs external cluster file using localhost:mappedPort
  5. Runs fdbcli configure new single memory tenant_mode=optional_experimental
  6. Returns ready-to-use container

External clients (host, Bazel sandbox, CI) connect via localhost:mappedPort. Internal clients (containers on same Docker network) connect via containerIP:4500.

Options

// FDB version (Docker image tag). Default: FDB_VERSION env or 7.3.77.
foundationdbtc.WithVersion("7.3.77")

// FDB API version (metadata for callers). Default: 730.
foundationdbtc.WithAPIVersion(730)

// Skip auto-initialization (call InitializeDatabase manually).
foundationdbtc.WithoutInit()

// Storage engine: "memory" (default), "ssd", "ssd-redwood-1", etc.
foundationdbtc.WithStorageEngine("memory")

// Redundancy: "single" (default), "double", "triple".
foundationdbtc.WithRedundancyMode("single")

// Tenant mode: "disabled", "optional_experimental" (default), "required".
foundationdbtc.WithTenantMode("optional_experimental")

// Custom FDB port (default: 4500).
foundationdbtc.WithFDBPort(4500)

// Attach to an existing Docker network (for multi-container setups).
foundationdbtc.WithNetwork(nw, "fdb-node-1")

// Startup timeout (default: 60s).
foundationdbtc.WithStartupTimeout(2 * time.Minute)

// Mix with standard testcontainers options:
testcontainers.WithEnv(map[string]string{"KEY": "VALUE"})

Multi-Container Setup

For tests that need multiple containers on the same network (e.g., binding tester):

// Create a shared network first.
nw, _ := foundationdbtc.CreateNetwork(ctx)
defer nw.Remove(ctx)

fdb, _ := foundationdbtc.Run(ctx, "", foundationdbtc.WithNetwork(nw))

// Attach another container to the same network.
testerReq := testcontainers.ContainerRequest{
    Networks: []string{fdb.NetworkName()},
}

// The other container can reach FDB via the internal cluster file.
clusterFile := fdb.InternalClusterFile()  // "docker:docker@172.17.0.2:4500"

Chaos Testing

// Pause FDB (simulates network partition).
err := container.Pause(ctx)

// ... verify your application handles the outage ...

// Resume FDB.
err = container.Unpause(ctx)

Container Methods

Method Returns Description
ClusterFile(ctx) (string, error) Cluster file content for external clients
MustClusterFile(ctx) string Panics on error
ClusterFilePath(ctx) (string, error) Writes cluster file to temp file
ConnectionString(ctx) (string, error) host:port string
NetworkName() string Docker network name
InternalAddress() string containerIP:4500 (bridge IP)
InternalClusterFile() string Cluster file for containers on same network
FDBCLIExec(ctx, cmd) (string, error) Run fdbcli command
Status(ctx) (string, error) FDB status details
Pause(ctx) error Freeze container (Docker pause)
Unpause(ctx) error Resume container
InitializeDatabase(ctx) error Configure database (auto-called by Run)
Terminate(ctx) error Stop container, clean up network

Requirements

  • Docker (Linux recommended — bridge IP connectivity required)
  • No FDB client libraries needed on host (pure Go client or connects via cluster file)

Documentation

Overview

Package foundationdb provides a testcontainers module for FoundationDB.

This module creates a single FoundationDB container (no socat proxy) and provides two connectivity modes:

  • External (host/sandbox): via Docker port mapping (localhost:mappedPort). Works from Bazel sandboxes, CI runners, Docker Desktop, etc.
  • Internal (cross-container): via Docker bridge IP (containerIP:4500). Used by sidecar containers on the same Docker network.

The container is auto-initialized by default (configured for single-node operation with tenant support). Use WithoutInit to skip.

Basic usage:

container, err := foundationdb.Run(ctx, "")
clusterFile, _ := container.ClusterFile(ctx)
db, _ := fdb.OpenDatabase(writeToFile(clusterFile))

Multi-container usage (e.g., binding tester):

nw, _ := foundationdb.CreateNetwork(ctx)
fdb, _ := foundationdb.Run(ctx, "", foundationdb.WithNetwork(nw))
// Attach another container to the same network:
otherReq.Networks = []string{fdb.NetworkName()}
// Use InternalClusterFile() for the other container's cluster file.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CreateNetwork

func CreateNetwork(ctx context.Context) (*testcontainers.DockerNetwork, error)

CreateNetwork creates a new Docker network. This is a convenience for tests that need to share a network between multiple containers without letting each Run call create its own.

The caller is responsible for removing the network.

Types

type Cluster

type Cluster struct {
	Coordinator *Container
	Replicas    []*Container // all containers including coordinator
	// contains filtered or unexported fields
}

Cluster represents a multi-container FDB cluster. Each container runs one fdbserver process. The first is the coordinator.

Example:

cluster, err := foundationdb.RunCluster(ctx, 3,
    foundationdb.WithStorageEngine("ssd"),
    foundationdb.WithRedundancyMode("double"),
)
defer cluster.Terminate(ctx)
cf, _ := cluster.ClusterFile(ctx)

func RunCluster

func RunCluster(ctx context.Context, size int, opts ...testcontainers.ContainerCustomizer) (*Cluster, error)

RunCluster creates an N-container FDB cluster on a shared Docker network. The first container is the coordinator; additional containers join as storage.

All options from Run() are supported. WithNetwork is set automatically.

func (*Cluster) ClusterFile

func (c *Cluster) ClusterFile(ctx context.Context) (string, error)

ClusterFile returns the cluster file for external clients (via coordinator).

func (*Cluster) InternalClusterFile

func (c *Cluster) InternalClusterFile() string

InternalClusterFile returns the cluster file for containers on the same network.

func (*Cluster) Size

func (c *Cluster) Size() int

Size returns the number of containers in the cluster.

func (*Cluster) Terminate

func (c *Cluster) Terminate(ctx context.Context) error

Terminate stops all containers and removes the network.

type Container

type Container struct {
	testcontainers.Container
	// contains filtered or unexported fields
}

Container represents a running FoundationDB container.

External clients (host, Bazel sandbox) connect via localhost:mappedPort. Internal clients (containers on the same Docker network) connect via containerIP:4500.

func Run

Run creates, starts, and (by default) initializes a FoundationDB container.

The container exposes FDB port 4500 via Docker port mapping. External clients (including Bazel sandbox tests) connect via localhost:mappedPort. For cross-container communication, use [InternalClusterFile] with a shared network via WithNetwork.

By default, the database is auto-initialized for single-node operation with tenant_mode=optional_experimental. Use WithoutInit to skip initialization.

The image parameter can be empty ("") to use the default image with the version from FDB_VERSION env var or the built-in default.

Example
ctx := context.Background()

// Start a FoundationDB container — auto-initialized, ready to use.
container, err := Run(ctx, "")
if err != nil {
	log.Fatal(err)
}
defer container.Terminate(ctx)

clusterFile, err := container.ClusterFile(ctx)
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Cluster file: %s\n", clusterFile)
Example (WithCustomConfiguration)
ctx := context.Background()

container, err := Run(ctx, "",
	WithDatabase("my_test_db"),
	WithAPIVersion(720),
	WithVersion("7.1.61"),
)
if err != nil {
	log.Fatal(err)
}
defer container.Terminate(ctx)

fmt.Printf("Database: %s\n", container.Database())
fmt.Printf("API Version: %d\n", container.APIVersion())
fmt.Printf("Version: %s\n", container.Version())

func (*Container) APIVersion

func (c *Container) APIVersion() int

APIVersion returns the configured FDB API version.

func (*Container) ClusterFile

func (c *Container) ClusterFile(_ context.Context) (string, error)

ClusterFile returns the cluster file content for external clients. Uses localhost:mappedPort, which works from Bazel sandboxes, CI runners, etc.

func (*Container) ClusterFilePath

func (c *Container) ClusterFilePath(_ context.Context) (string, error)

ClusterFilePath writes the cluster file to a temp file and returns the path. The file persists until the caller removes it.

func (*Container) ConnectionString

func (c *Container) ConnectionString(ctx context.Context) (string, error)

ConnectionString returns "host:port" for the FDB server (external, port-mapped).

func (*Container) Database

func (c *Container) Database() string

Database returns the configured database name.

func (*Container) FDBCLIExec

func (c *Container) FDBCLIExec(ctx context.Context, command string) (string, error)

FDBCLIExec runs an fdbcli command inside the container and returns the output.

Example:

output, err := container.FDBCLIExec(ctx, "status details")
Example
ctx := context.Background()

container, err := Run(ctx, "")
if err != nil {
	log.Fatal(err)
}
defer container.Terminate(ctx)

output, err := container.FDBCLIExec(ctx, "status minimal")
if err != nil {
	log.Fatal(err)
}

fmt.Printf("FDB status: %s\n", output)

func (*Container) FDBPort

func (c *Container) FDBPort() int

FDBPort returns the FDB port inside the container.

func (*Container) InitializeDatabase

func (c *Container) InitializeDatabase(ctx context.Context) error

InitializeDatabase configures the database for single-node operation. This is called automatically by Run unless WithoutInit is used.

The configure command uses the storage engine and redundancy mode from options. Default: "memory" engine, "single" redundancy, tenant_mode=optional_experimental.

This method is idempotent — calling it on an already-configured database is safe.

func (*Container) InternalAddress

func (c *Container) InternalAddress() string

InternalAddress returns the FDB address reachable from within Docker networks. Uses the container's bridge IP: "containerIP:4500".

func (*Container) InternalClusterFile

func (c *Container) InternalClusterFile() string

InternalClusterFile returns a cluster file string usable from containers on the same Docker network. Uses the container's bridge IP (no port mapping needed).

func (*Container) MappedPort

func (c *Container) MappedPort() int

MappedPort returns the host port mapped to FDB's container port.

func (*Container) MustClusterFile

func (c *Container) MustClusterFile(ctx context.Context) string

MustClusterFile returns the cluster file content, panicking on error.

func (*Container) NetworkName

func (c *Container) NetworkName() string

NetworkName returns the Docker network name. Returns empty string if the container is on the default bridge (no custom network). Use this to attach other containers to the same network for inter-container communication.

func (*Container) Pause

func (c *Container) Pause(ctx context.Context) error

Pause freezes all processes in the container using Docker pause. The container remains running but FDB becomes unreachable. TCP connections stay open but will time out. Use this to simulate network partitions or FDB hangs.

Call [Unpause] to resume.

func (*Container) Status

func (c *Container) Status(ctx context.Context) (string, error)

Status returns the FDB cluster status output.

func (*Container) Terminate

func (c *Container) Terminate(ctx context.Context) error

Terminate terminates the container. The network, if user-provided via WithNetwork, is NOT removed — the caller owns it.

func (*Container) Unpause

func (c *Container) Unpause(ctx context.Context) error

Unpause resumes a paused container. See [Pause].

func (*Container) Version

func (c *Container) Version() string

Version returns the configured FoundationDB version.

type Option

type Option struct {
	// contains filtered or unexported fields
}

Option configures the FoundationDB container. Options implement testcontainers.ContainerCustomizer so they can be mixed with standard testcontainers options in the Run call.

func WithAPIVersion

func WithAPIVersion(version int) Option

WithAPIVersion sets the FDB API version. This is metadata for callers to know which version to pass to fdb.MustAPIVersion(). It doesn't configure the container.

func WithDataOnDisk

func WithDataOnDisk() Option

WithDataOnDisk stores /var/fdb/data on disk (the container's writable layer) instead of the default tmpfs. The default tmpfs keeps tests fast and avoids a leaked anonymous volume, but caps a dataset at host RAM; use this (with an SSD storage engine, e.g. WithStorageEngine("ssd-redwood-1")) for datasets larger than memory. The container's anonymous data volume is on disk and is cleaned up with the container.

func WithDatabase

func WithDatabase(name string) Option

WithDatabase sets the database name. This is metadata for callers — FDB itself doesn't have named databases (it has one keyspace per cluster).

func WithDirectIP

func WithDirectIP() Option

WithDirectIP makes Container.ClusterFile return the container's bridge IP instead of localhost:mappedPort. This avoids Docker DNAT which causes FDB canonicalRemotePort assertion spam under high connection churn.

Direct IP requires the test process to route to Docker bridge IPs. This works on Linux hosts but NOT from Bazel's linux-sandbox.

func WithFDBPort

func WithFDBPort(port int) Option

WithFDBPort overrides the default FDB port (4500). This sets the FDB_PORT environment variable in the container.

func WithKnob

func WithKnob(name, value string) Option

WithKnob sets a server-side FDB knob. Knobs are passed as --knob_NAME=VALUE to the fdbserver process. This is useful for forcing shard splits (e.g., WithKnob("min_shard_bytes", "40000")) or other server behavior changes.

The knob is injected by modifying the entrypoint script inside the container. Multiple WithKnob calls accumulate.

func WithNetwork

func WithNetwork(nw *testcontainers.DockerNetwork, aliases ...string) Option

WithNetwork attaches the container to an existing Docker network instead of creating a new one. The network will NOT be removed on Container.Terminate. Additional aliases can be specified for DNS resolution within the network.

Example
ctx := context.Background()

// Create a shared network for multi-container setups.
nw, err := CreateNetwork(ctx)
if err != nil {
	log.Fatal(err)
}
defer nw.Remove(ctx)

container, err := Run(ctx, "",
	WithNetwork(nw, "fdb-primary"),
)
if err != nil {
	log.Fatal(err)
}
defer container.Terminate(ctx)

fmt.Printf("Network: %s\n", container.NetworkName())
fmt.Printf("Internal address: %s\n", container.InternalAddress())

func WithNetworkAliases

func WithNetworkAliases(aliases ...string) Option

WithNetworkAliases adds network aliases for the container. These are DNS names resolvable by other containers on the same Docker network.

func WithProcessCount

func WithProcessCount(n int) Option

WithProcessCount sets the number of fdbserver processes to run inside the container. Default is 1. When n > 1, additional processes are started on ports 4501..4500+n-1 after the primary process joins the cluster.

Use with WithRedundancyMode("double") or ("triple") to enable data replication across processes. Multiple storage servers enable shard splits, which is required for testing cross-shard GetRange behavior.

Example:

Run(ctx, "",
    WithProcessCount(3),
    WithRedundancyMode("double"),
    WithKnob("min_shard_bytes", "40000"),
)

func WithRedundancyMode

func WithRedundancyMode(mode string) Option

WithRedundancyMode configures FDB replication for the configure command. Valid values: "single" (default), "double", "triple".

func WithStartupTimeout

func WithStartupTimeout(timeout time.Duration) Option

WithStartupTimeout sets the timeout for waiting for the FDB container to start. Default: 60 seconds.

func WithStorageEngine

func WithStorageEngine(engine string) Option

WithStorageEngine configures the FDB storage engine for the configure command. Valid values: "memory" (default, fast for tests), "ssd" (persistent).

func WithTenantMode

func WithTenantMode(mode string) Option

WithTenantMode configures FDB tenant mode for the configure command. Valid values: "disabled", "optional_experimental", "required". Default: "optional_experimental".

func WithVersion

func WithVersion(version string) Option

WithVersion sets the FoundationDB Docker image tag.

func WithoutInit

func WithoutInit() Option

WithoutInit skips automatic database initialization in Run. Use Container.InitializeDatabase for manual initialization.

func (Option) Customize

Customize implements testcontainers.ContainerCustomizer. Module options don't modify the container request directly — they configure our options struct.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL