datastore

package
v0.6.21 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package datastore is the analytics plane: a client for Hanzo Datastore, the columnar warehouse behind the usage and observability ledgers.

One store, not one per tenant. Rows carry the tenant as a column and as the leading sort key, so a single-tenant read is a bound predicate over a shared table and a fleet-wide aggregate is the same table with that predicate left off. This is the opposite of the relational plane in orm/db, where a tenant IS a database file resolved through db.Registry. The two planes share no connection and are configured independently; keeping them apart is what lets an aggregate read span every tenant while an entity read cannot leave one.

The surface is Exec for statements and Query for reads. Analytics SQL is written by hand — aggregates, windows and engine-specific DDL have no useful typed builder — so this plane maps no records: orm/db models entities, this carries measurements.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnavailable = errors.New("datastore: unavailable")

ErrUnavailable is returned by Exec and Query when there is no live connection: the plane is unconfigured, still connecting, or closed. Callers gate on Ready to report an honest gap instead of reaching this.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Addr is host:port of the warehouse's native protocol port. Empty
	// disables the plane — see Open.
	Addr string

	// Database holds the ledger tables. Defaults to "hanzo". It must be a bare
	// identifier: CREATE DATABASE takes no bound parameters, so this value
	// reaches the wire by concatenation.
	Database string

	// User and Password authenticate the connection. User defaults to
	// "default".
	User     string
	Password string

	// DialTimeout bounds one connection attempt. Defaults to 5s. It does not
	// bound queries — Exec and Query are governed by the caller's context, so
	// a deadline lives in exactly one place.
	DialTimeout time.Duration

	// Log receives connection lifecycle events. Defaults to slog.Default().
	Log *slog.Logger
}

Config addresses one warehouse.

func Env

func Env() Config

Env reads a Config from the process environment:

DATASTORE_ADDR      host:port of the warehouse's native port; unset disables the plane
DATASTORE_DB        database (default "hanzo")
DATASTORE_USER      user (default "default")
DATASTORE_PASSWORD  password

Open never reads the environment itself, so a caller that configures the plane another way — a test, a job, a second warehouse — builds a Config and this function stays out of it.

func (Config) LogValue

func (c Config) LogValue() slog.Value

LogValue renders the config for logs with the password withheld. A config is a plausible thing to log wholesale and this one holds a credential.

type Conn

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

Conn is a client for one warehouse. It is safe for concurrent use, and its zero value behaves like a disabled client, so a nil *Conn is a usable "no datastore here" rather than a panic.

func Open

func Open(cfg Config) *Conn

Open returns a client for the configured warehouse. It never blocks and never fails: the connection is established in the background and Ready reports when it is usable. A warehouse that is down at boot therefore leaves the ledgers unavailable instead of taking down the process that owns them.

An empty Addr yields a client that is never ready. That is the disabled state, and it is deliberately the same state as "not connected yet": callers already gate on Ready and report an honest gap, so being unconfigured needs no second code path. A Database that is not a bare identifier disables the client the same way, since it cannot be sent safely.

func (*Conn) Close

func (c *Conn) Close() error

Close releases the connection and ends connecting. It is idempotent, and Exec and Query return ErrUnavailable afterwards.

func (*Conn) Exec

func (c *Conn) Exec(ctx context.Context, stmt string, args ...any) error

Exec runs a statement — DDL, or an insert. Placeholders bind positionally from args.

func (*Conn) Query

func (c *Conn) Query(ctx context.Context, query string, args ...any) ([]map[string]any, error)

Query runs a read and materialises the result, decoding each column into its native scan type (uint64, string, time.Time, float64, ...) keyed by column name. Placeholders bind positionally from args and are never interpolated into the statement, so a tenant predicate holds whatever the tenant value contains.

The result is fully read before returning: analytics reads are aggregates, small in rows and wide in the work behind them, so there is no cursor to manage and no open rows to leak.

func (*Conn) Ready

func (c *Conn) Ready() bool

Ready reports whether there is a live connection. The read paths gate on it to return an honest gap rather than zeroes, and the write paths to skip an insert that would be lost.

func (*Conn) Wait

func (c *Conn) Wait(ctx context.Context) error

Wait blocks until the client is ready, the context ends, or the client is closed. It returns ErrUnavailable for a client that can never become ready, so a readiness probe fails fast on a misconfiguration instead of timing out.

Jump to

Keyboard shortcuts

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