Documentation
¶
Overview ¶
Package embeddedclickhouse provides an embedded ClickHouse server for Go tests. It downloads, caches, and manages a real ClickHouse server process, similar to how fergusstrange/embedded-postgres works for PostgreSQL.
Index ¶
- Constants
- Variables
- type ClickHouseVersion
- type Cluster
- type Config
- func (c Config) BinaryPath(path string) Config
- func (c Config) BinaryRepositoryURL(url string) Config
- func (c Config) CachePath(path string) Config
- func (c Config) DataPath(path string) Config
- func (c Config) HTTPPort(port uint32) Config
- func (c Config) Logger(w io.Writer) Config
- func (c Config) Settings(s map[string]string) Config
- func (c Config) StartTimeout(d time.Duration) Config
- func (c Config) StopTimeout(d time.Duration) Config
- func (c Config) TCPPort(port uint32) Config
- func (c Config) Version(v ClickHouseVersion) Config
- type EmbeddedClickHouse
Examples ¶
Constants ¶
const DefaultVersion = V25_8
DefaultVersion is the default ClickHouse version used when none is specified.
Variables ¶
var ErrBinaryNotFound = errors.New("embedded-clickhouse: binary not found in archive")
ErrBinaryNotFound is returned when the ClickHouse binary cannot be located inside a downloaded archive.
var ErrClusterAlreadyStarted = errors.New("embedded-clickhouse: cluster is already started")
ErrClusterAlreadyStarted is returned by Start when the cluster is already running.
var ErrClusterManaged = errors.New("embedded-clickhouse: node is managed by a cluster; use Cluster.Start/Stop")
ErrClusterManaged is returned when Start or Stop is called on a node owned by a Cluster.
var ErrClusterNotStarted = errors.New("embedded-clickhouse: cluster has not been started")
ErrClusterNotStarted is returned when accessing cluster resources before Start.
var ErrDownloadFailed = errors.New("embedded-clickhouse: download failed")
ErrDownloadFailed is returned when the HTTP download of a ClickHouse asset returns a non-200 status.
var ErrInvalidPath = errors.New("embedded-clickhouse: invalid destination path")
ErrInvalidPath is returned when a destination path contains a path traversal sequence ("..").
var ErrInvalidReplicaCount = errors.New("embedded-clickhouse: replica count must be at least 2")
ErrInvalidReplicaCount is returned when the replica count is less than 2.
var ErrInvalidSettingKey = errors.New("embedded-clickhouse: invalid setting key")
ErrInvalidSettingKey is returned when a settings key contains characters that are unsafe in an XML element name.
var ErrKeeperNotReady = errors.New("embedded-clickhouse: keeper quorum not ready")
ErrKeeperNotReady is returned when the embedded Keeper quorum is not established within the timeout.
var ErrNodeOutOfRange = errors.New("embedded-clickhouse: node index out of range")
ErrNodeOutOfRange is returned when Node() is called with an index outside [0, replicas).
var ErrSHA512Mismatch = errors.New("embedded-clickhouse: SHA512 mismatch")
ErrSHA512Mismatch is returned when the downloaded file's SHA512 hash does not match the expected value.
var ErrSHA512NotFound = errors.New("embedded-clickhouse: SHA512 hash not found")
ErrSHA512NotFound is returned when the SHA512 checksum file does not contain a hash for the expected filename.
var ErrServerAlreadyStarted = errors.New("embedded-clickhouse: server is already started")
ErrServerAlreadyStarted is returned by Start when the server is already running.
var ErrServerNotStarted = errors.New("embedded-clickhouse: server has not been started")
ErrServerNotStarted is returned by Stop when the server has not been started.
var ErrStopTimeout = errors.New("embedded-clickhouse: server did not stop within timeout, killed")
ErrStopTimeout is returned when the server does not stop within the configured StopTimeout; the process is killed.
var ErrUnexpectedAddrType = errors.New("embedded-clickhouse: unexpected listener address type")
ErrUnexpectedAddrType is returned when the listener address is not the expected *net.TCPAddr type.
var ErrUnknownAssetType = errors.New("embedded-clickhouse: unknown asset type")
ErrUnknownAssetType is returned when an unrecognised platform asset type is encountered.
var ErrUnsupportedPlatform = errors.New("embedded-clickhouse: unsupported platform")
ErrUnsupportedPlatform is returned when the current OS/architecture has no ClickHouse release asset.
Functions ¶
This section is empty.
Types ¶
type ClickHouseVersion ¶
type ClickHouseVersion string
ClickHouseVersion represents a ClickHouse server version string.
const V25_3 ClickHouseVersion = "25.3.14.14-lts"
V25_3 is ClickHouse 25.3 (LTS channel).
const V25_8 ClickHouseVersion = "25.8.16.34-lts"
V25_8 is ClickHouse 25.8 (LTS channel).
const V26_1 ClickHouseVersion = "26.1.3.52-stable"
V26_1 is ClickHouse 26.1 (stable channel).
type Cluster ¶ added in v0.3.0
type Cluster struct {
// contains filtered or unexported fields
}
Cluster manages a multi-replica ClickHouse cluster using embedded Keeper for coordination. All replicas run on localhost with auto-allocated ports. The cluster presents a single shard with N replicas, suitable for testing ReplicatedMergeTree tables with ON CLUSTER queries.
Example (ReplicatedTable) ¶
ExampleCluster_replicatedTable demonstrates a full ReplicatedMergeTree workflow: create a table ON CLUSTER, insert on one node, sync and read from another.
package main
import (
"database/sql"
"fmt"
"io"
"testing"
_ "github.com/ClickHouse/clickhouse-go/v2"
embeddedclickhouse "github.com/franchb/embedded-clickhouse"
)
func main() {
if testing.Short() {
fmt.Println(2)
return
}
cluster := embeddedclickhouse.NewCluster(2, embeddedclickhouse.DefaultConfig().Logger(io.Discard))
if err := cluster.Start(); err != nil {
panic(err)
}
defer cluster.Stop()
db0, err := sql.Open("clickhouse", cluster.Node(0).DSN())
if err != nil {
panic(err)
}
defer db0.Close()
db1, err := sql.Open("clickhouse", cluster.Node(1).DSN())
if err != nil {
panic(err)
}
defer db1.Close()
// Create a replicated table on all nodes.
_, err = db0.Exec(`
CREATE TABLE example_repl ON CLUSTER 'test_cluster' (
id UInt64,
name String
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/example_repl', '{replica}')
ORDER BY id
`)
if err != nil {
panic(err)
}
// Insert on node 0.
_, err = db0.Exec("INSERT INTO example_repl (id, name) VALUES (1, 'alice'), (2, 'bob')")
if err != nil {
panic(err)
}
// Sync and read from node 1.
_, err = db1.Exec("SYSTEM SYNC REPLICA example_repl")
if err != nil {
panic(err)
}
var count int
if err := db1.QueryRow("SELECT count() FROM example_repl").Scan(&count); err != nil {
panic(err)
}
fmt.Println(count)
}
Output: 2
func NewCluster ¶ added in v0.3.0
NewCluster creates a new Cluster with the given number of replicas. If no config is provided, DefaultConfig() is used with a 120s start timeout.
Example ¶
ExampleNewCluster demonstrates starting a 3-node ClickHouse cluster for replication testing.
package main
import (
"io"
"testing"
_ "github.com/ClickHouse/clickhouse-go/v2"
embeddedclickhouse "github.com/franchb/embedded-clickhouse"
)
func main() {
if testing.Short() {
return
}
cluster := embeddedclickhouse.NewCluster(3, embeddedclickhouse.DefaultConfig().Logger(io.Discard))
if err := cluster.Start(); err != nil {
panic(err)
}
defer cluster.Stop()
// cluster.DSN() => DSN for node 0
// cluster.Node(0).DSN() => same as above
// cluster.Node(1).DSN() => DSN for node 1
// cluster.ClusterName() => "test_cluster"
//
// Use ON CLUSTER queries with ReplicatedMergeTree:
// CREATE TABLE t ON CLUSTER 'test_cluster' (id UInt64)
// ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/t', '{replica}')
// ORDER BY id
}
func NewClusterForTest ¶ added in v0.3.0
NewClusterForTest creates a cluster, starts it, and registers tb.Cleanup(cluster.Stop). Calls tb.Fatal on Start() error.
Example ¶
ExampleNewClusterForTest demonstrates the per-test cluster helper pattern.
package main
import (
"testing"
_ "github.com/ClickHouse/clickhouse-go/v2"
)
func main() {
if testing.Short() {
return
}
// In a real test, pass *testing.T here. The cluster starts automatically
// and t.Cleanup registers Stop() for teardown.
//
// cluster := embeddedclickhouse.NewClusterForTest(t, 3)
// db, _ := sql.Open("clickhouse", cluster.DSN())
}
func (*Cluster) ClusterName ¶ added in v0.3.0
ClusterName returns the cluster name used in ON CLUSTER queries.
func (*Cluster) DSN ¶ added in v0.3.0
DSN returns the DSN for the first node (shortcut for Node(0).DSN()).
func (*Cluster) Node ¶ added in v0.3.0
func (c *Cluster) Node(index int) *EmbeddedClickHouse
Node returns the i-th node (0-indexed). Panics if the cluster is not started or index is out of range.
func (*Cluster) Nodes ¶ added in v0.3.0
func (c *Cluster) Nodes() []*EmbeddedClickHouse
Nodes returns all cluster nodes. Returns nil if the cluster is not started.
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config holds configuration for an embedded ClickHouse server.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
func (Config) BinaryPath ¶
BinaryPath uses a pre-existing ClickHouse binary, skipping download.
func (Config) BinaryRepositoryURL ¶
BinaryRepositoryURL sets a custom mirror URL for downloading ClickHouse binaries.
func (Config) HTTPPort ¶
HTTPPort sets the HTTP port for the ClickHouse HTTP interface. 0 means auto-allocate (default).
func (Config) Settings ¶
Settings sets arbitrary ClickHouse server settings. The provided map is copied; subsequent caller mutations do not affect the Config.
Example ¶
ExampleConfig_Settings demonstrates builder chaining and Settings usage.
package main
import (
"io"
"time"
embeddedclickhouse "github.com/franchb/embedded-clickhouse"
)
func main() {
cfg := embeddedclickhouse.DefaultConfig().
Version(embeddedclickhouse.V25_3).
TCPPort(19000).
HTTPPort(18123).
StartTimeout(60 * time.Second).
Logger(io.Discard).
Settings(map[string]string{
"max_threads": "2",
"max_server_memory_usage": "2147483648", // 2 GiB
})
_ = cfg
}
func (Config) StartTimeout ¶
StartTimeout sets the maximum time to wait for the server to become ready.
func (Config) StopTimeout ¶
StopTimeout sets the maximum time to wait for the server to shut down gracefully.
func (Config) TCPPort ¶
TCPPort sets the TCP port for the ClickHouse native protocol. 0 means auto-allocate (default).
func (Config) Version ¶
func (c Config) Version(v ClickHouseVersion) Config
Version sets the ClickHouse version to use.
type EmbeddedClickHouse ¶
type EmbeddedClickHouse struct {
// contains filtered or unexported fields
}
EmbeddedClickHouse manages a ClickHouse server process for testing.
func NewServer ¶
func NewServer(config ...Config) *EmbeddedClickHouse
NewServer creates a new EmbeddedClickHouse with the given config. If no config is provided, DefaultConfig() is used.
Example ¶
ExampleNewServer demonstrates the minimal Start/Stop usage pattern.
package main
import (
"io"
"testing"
embeddedclickhouse "github.com/franchb/embedded-clickhouse"
)
func main() {
if testing.Short() {
return
}
ch := embeddedclickhouse.NewServer(embeddedclickhouse.DefaultConfig().Logger(io.Discard))
if err := ch.Start(); err != nil {
panic(err)
}
defer ch.Stop()
// ch.DSN() => "clickhouse://127.0.0.1:<port>/default"
// ch.HTTPURL() => "http://127.0.0.1:<port>"
}
func NewServerForTest ¶
func NewServerForTest(tb testing.TB, config ...Config) *EmbeddedClickHouse
NewServerForTest creates a server, starts it, and registers t.Cleanup(server.Stop). Calls t.Fatal on Start() error.
Example ¶
ExampleNewServerForTest demonstrates the per-test helper pattern.
package main
import (
"testing"
)
func main() {
if testing.Short() {
return
}
// In a real test, pass *testing.T here. The server starts automatically
// and t.Cleanup registers Stop() for teardown.
//
// ch := embeddedclickhouse.NewServerForTest(t)
// db, _ := sql.Open("clickhouse", ch.DSN())
}
func (*EmbeddedClickHouse) DSN ¶
func (e *EmbeddedClickHouse) DSN() string
DSN returns a ClickHouse DSN for use with clickhouse-go (e.g., "clickhouse://127.0.0.1:19000/default").
Example ¶
ExampleEmbeddedClickHouse_DSN documents the DSN accessor.
package main
import (
"io"
"testing"
embeddedclickhouse "github.com/franchb/embedded-clickhouse"
)
func main() {
if testing.Short() {
return
}
ch := embeddedclickhouse.NewServer(embeddedclickhouse.DefaultConfig().Logger(io.Discard))
if err := ch.Start(); err != nil {
panic(err)
}
defer ch.Stop()
dsn := ch.DSN()
_ = dsn // "clickhouse://127.0.0.1:<port>/default"
}
func (*EmbeddedClickHouse) HTTPAddr ¶
func (e *EmbeddedClickHouse) HTTPAddr() string
HTTPAddr returns the HTTP address for the ClickHouse HTTP interface (e.g., "127.0.0.1:18123").
func (*EmbeddedClickHouse) HTTPURL ¶
func (e *EmbeddedClickHouse) HTTPURL() string
HTTPURL returns the base HTTP URL (e.g., "http://127.0.0.1:18123").
func (*EmbeddedClickHouse) Start ¶
func (e *EmbeddedClickHouse) Start() error
Start downloads the ClickHouse binary (if needed), generates config, and starts the server.
func (*EmbeddedClickHouse) Stop ¶
func (e *EmbeddedClickHouse) Stop() error
Stop gracefully shuts down the ClickHouse server and cleans up resources.
func (*EmbeddedClickHouse) TCPAddr ¶
func (e *EmbeddedClickHouse) TCPAddr() string
TCPAddr returns the TCP address for the ClickHouse native protocol (e.g., "127.0.0.1:19000").