chconn

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package chconn owns the process's ClickHouse connections: one pool per distinct address, database, user, password and tls tuple (ClickHouse authenticates per connection, so different credentials never share one), shared by the tenants whose settings name it and reconciled after every settings reload under the boot config's connection ceiling. Manager implements driver.Conn by delegating every call to the connection current at that instant, so a consumer resolves its tenant's Manager per call and never learns a resize happened; the HTTP-side consumers (ingest INSERTs, the raw-SQL proxy) read their tenant's Target per request and take their client from an HTTPClients.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExceptionCode

func ExceptionCode(err error) (int32, bool)

ExceptionCode is the ClickHouse exception code err carries, if any.

func HTTPStatus

func HTTPStatus(err error) (int, bool)

HTTPStatus is the status of the non-2xx HTTP answer err carries, if any.

func Splittable

func Splittable(err error) bool

Splittable reports whether err is a retried failure that a smaller request may avoid — so a caller holding a batch should split it before backing off.

func TableScoped

func TableScoped(err error) bool

TableScoped reports whether err is a retried failure of the one table the request wrote to, not of the server or the identity — so a caller backing off can hold back that table alone.

Types

type Class

type Class int

Class is what a failed ClickHouse request says about the request itself: whether sending it again, unchanged, can succeed. The ingest worker retries every class but Rejected and dead-letters only Rejected; the query handlers map the same classes onto HTTP statuses (api/ch_errors.go).

const (
	// Unknown is a failure with no verdict: ClickHouse sent no exception
	// code, and the error is not one the way to ClickHouse is known to
	// produce. Nothing says the request was judged.
	Unknown Class = iota
	// Unavailable means ClickHouse, or the way to it, could not take the
	// request now — refused or dropped connections, timeouts, overload,
	// read-only tables, lost replicas or Keeper. The request was not judged;
	// the same request can succeed later.
	Unavailable
	// Denied means ClickHouse refused the credentials or grants the request
	// ran under: the connection's configuration, not the request's content.
	Denied
	// Rejected means ClickHouse judged the request and refused it — a value
	// it cannot parse, a type mismatch, a table or column it does not have,
	// bad SQL. Sent again unchanged, it fails the same way.
	Rejected
)

func ClassOfCode

func ClassOfCode(code int32) Class

ClassOfCode classes a ClickHouse exception code. A code on neither list is Rejected: an exception code means the server was up and read the request, and most of ClickHouse's several hundred codes are about what it read. The availability codes are the enumerated exception, not the other way round.

func Classify

func Classify(err error) Class

Classify classes a failed ClickHouse request, over the HTTP interface (*HTTPError) or the driver (*clickhouse.Exception, *clickhouse.HTTPError, its sentinels, and the network errors under them). A nil error is Unknown.

func (Class) String

func (c Class) String() string

type HTTPClients

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

HTTPClients hands an HTTP-interface consumer the client for a target: one per TLS config, made by the consumer's own factory (the worker's tuned transport, the proxy's redirect policy) and kept for the process lifetime. A config is built each time a pool opens on a tls block, so the set grows with the pools ever opened on one — a block whose pool closed and reopened adds another — not with requests; a client whose pool is gone keeps only its transport, whose idle connections time out on their own.

The factory gets a copy of the config, never the target's own: net/http appends its HTTP/2 protocols to TLSClientConfig.NextProtos in place when a transport first dials, which would race the driver's handshakes on the shared config and offer HTTP protocols on the native hop. The target's pointer stays the cache key.

func NewHTTPClients

func NewHTTPClients(build func(*tls.Config) *http.Client) *HTTPClients

NewHTTPClients returns a cache whose clients build makes from a target's TLS config, nil meaning net/http's defaults.

func (*HTTPClients) For

func (c *HTTPClients) For(t Target) *http.Client

For returns the client for t.

type HTTPError

type HTTPError struct {
	StatusCode int
	// Code is the ClickHouse exception code, 0 when the response carried
	// none (usually a proxy's answer, not ClickHouse's).
	Code int32
	Body string
}

HTTPError is a non-2xx answer from the ClickHouse HTTP interface. Its message keeps the body verbatim — the dead-letter headers carry it.

func NewHTTPError

func NewHTTPError(resp *http.Response) *HTTPError

NewHTTPError reads a non-2xx ClickHouse HTTP response into an HTTPError, taking the exception code from the X-ClickHouse-Exception-Code header, or failing that from the body's "Code: NNN." prefix. The caller closes the body.

func (*HTTPError) Error

func (e *HTTPError) Error() string

type Identity

type Identity struct {
	Addr     string
	Database string
	Username string
	Password string
	TLS      TLS
}

Identity is the tuple that decides which pool a tenant shares. Comparable as it stands — the TLS block is a value of strings and bools — so it is the map key as well.

func (Identity) String

func (id Identity) String() string

String names the tuple for logs and errors: the address, database and user, never the password.

type Manager

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

Manager is a driver.Conn over one tuple's pool, whose backing connection is swapped by Resize.

func Open

func Open(id Identity, s Sizes) (*Manager, error)

Open builds the pool for id at sizes s. Like clickhouse.Open it does not dial — boot tolerates an unreachable ClickHouse (schema discovery degrades and retries) — so only a malformed option or a certificate file that cannot be read or parsed errors here.

func (*Manager) AsyncInsert

func (m *Manager) AsyncInsert(ctx context.Context, query string, wait bool, args ...any) error

func (*Manager) Close

func (m *Manager) Close() error

Close closes the current connection now. Replaced and released connections close on their own grace timers.

func (*Manager) Contributors

func (m *Manager) Contributors() []string

func (*Manager) Exec

func (m *Manager) Exec(ctx context.Context, query string, args ...any) error

func (*Manager) Identity

func (m *Manager) Identity() Identity

Identity returns the tuple this pool is for.

func (*Manager) InsertFormat

func (m *Manager) InsertFormat(ctx context.Context, format, query string, data io.Reader) error

func (*Manager) Ping

func (m *Manager) Ping(ctx context.Context) error

func (*Manager) PrepareBatch

func (m *Manager) PrepareBatch(ctx context.Context, query string, opts ...driver.PrepareBatchOption) (driver.Batch, error)

func (*Manager) Query

func (m *Manager) Query(ctx context.Context, query string, args ...any) (driver.Rows, error)

func (*Manager) QueryFormat

func (m *Manager) QueryFormat(ctx context.Context, format, query string, args ...any) (io.ReadCloser, error)

func (*Manager) QueryRow

func (m *Manager) QueryRow(ctx context.Context, query string, args ...any) driver.Row

func (*Manager) Release

func (m *Manager) Release(grace time.Duration)

Release closes the pool after grace, for a tuple no tenant names any more: a consumer that resolved this Manager just before the reload's swap finishes its query on it.

func (*Manager) Resize

func (m *Manager) Resize(s Sizes, grace time.Duration) error

Resize swaps in a connection sized s when s differs from the current size — the tls block is the tuple's and never changes under a Manager, so no file is re-read. The replaced connection is closed after grace, so the queries in flight on it finish; grace is the longest query timeout among the tenants sharing the pool.

func (*Manager) Select

func (m *Manager) Select(ctx context.Context, dest any, query string, args ...any) error

func (*Manager) ServerVersion

func (m *Manager) ServerVersion() (*driver.ServerVersion, error)

func (*Manager) Sizes

func (m *Manager) Sizes() Sizes

Sizes returns the pool's current size.

func (*Manager) Stats

func (m *Manager) Stats() driver.Stats

func (*Manager) TLSConfig

func (m *Manager) TLSConfig() *tls.Config

TLSConfig returns the tuple's TLS config, nil for a zero tls block: what the https hop of the HTTP interface is configured with.

type Member

type Member struct {
	Tenant tenant.ID
	Params Params
}

Member is one tenant's ask of the pools: the tuple it shares, and its own sizes, deadline and HTTP wiring.

type Params

type Params struct {
	Addr         string
	HTTPPort     int
	HTTPScheme   string
	Database     string
	Username     string
	Password     string
	QueryTimeout time.Duration
	TLS          TLS
	// Headers go on every HTTP-interface request; read-only once handed in.
	Headers      map[string]string
	MaxOpenConns int
	MaxIdleConns int
}

Params is what one tenant asks of its connection: the settings directory's clickhouse block, password included. The Identity picks the pool; the rest is the tenant's own — the HTTP-interface wiring and headers, the query deadline, and the pool sizes the shared pool is at least as large as.

func (Params) Identity

func (p Params) Identity() Identity

Identity returns the tuple p names.

func (Params) Sizes

func (p Params) Sizes() Sizes

Sizes returns the pool size p asks for.

type Pools

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

Pools holds one Manager per tuple the served tenants name, reconciled after every settings reload: a new tuple opens a pool (no dial), a tenant whose tuple changed is repointed, a tuple no tenant names any more is released after its grace, and a pool shared by several tenants is sized to their largest ask. The ceiling — the boot config's clickhouse.max_total_conns, 0 for none — bounds the sum of the open pools' MaxOpenConns: at boot it refuses to open, and at a reload a resize above it is refused with the pool kept at its size, and a tuple that cannot be opened leaves its tenants where they were, on their previous pool, or with none when they had none. Both are logged by the caller and retried by the next reload. Resolution is per tenant: For, Target, SharingTables.

func NewPools

func NewPools(ceiling int, want []Member) (*Pools, error)

NewPools opens the pools want names, under ceiling. Any refusal — a certificate file that cannot be read, options the driver refuses, or pools that would add up to more than the ceiling, named with the sum — refuses boot: nothing is left open.

func (*Pools) Close

func (p *Pools) Close() error

Close closes every open pool now and leaves none.

func (*Pools) For

func (p *Pools) For(id tenant.ID) *Manager

For returns the pool of tenant id, or nil when the tenant is on none: it is not served, or its tuple could not be opened.

func (*Pools) Ping

func (p *Pools) Ping(ctx context.Context) error

Ping pings every open pool at once and returns nil at the first answer, or every pool's error joined when none answers — including when none is open. At once, not in turn: the driver waits up to its dial timeout on a host that does not answer, longer than a readiness probe's, so one such pool must not hide the ones that do answer.

func (*Pools) Reconcile

func (p *Pools) Reconcile(want []Member) (stale []tenant.ID, err error)

Reconcile sets the pools to what want names (the served tenants, in id order), and returns the tenants whose cache is stale, for the caller to orphan, with every refusal joined: the ones it admitted that were not on a pool before — new, or back after a rejection or removal, so out of the fan-out (SharingTables) while away — and the ones it moved to another address or database, whose cached results were read from other tables. A move that keeps the address and database (a username or tls change) reads the same tables and is not stale. The walk keeps the ceiling at every step: tenants no longer wanted leave first, then each wanted tenant is placed in turn, and a refused placement — the ceiling, or a pool that cannot be opened — is undone before the next, so a refused move leaves the tenant on the pool it had, with the Params it had. What was refused is placed once more at the end, since a shrink or a move placed after it may have freed the budget it needed; only what the second pass refuses is reported.

func (*Pools) SharingTables

func (p *Pools) SharingTables(id tenant.ID) []tenant.ID

SharingTables returns the tenants reading the same tables as tenant id — the same address and database, whatever their user or tls — id included, in id order; nil when id is on no pool.

func (*Pools) Target

func (p *Pools) Target(id tenant.ID) Target

Target returns the HTTP-interface wiring of tenant id — its own HTTP port, scheme and headers over its pool's host, credentials, database and TLS config — or the zero Target when it is on no pool.

type Sizes

type Sizes struct {
	MaxOpenConns int
	MaxIdleConns int
}

Sizes is a pool's size: the driver's MaxOpenConns and MaxIdleConns.

type TLS

type TLS struct {
	Enabled            bool
	CAFile             string
	CertFile           string
	KeyFile            string
	InsecureSkipVerify bool
	ServerName         string
}

TLS is the settings directory's clickhouse.tls block. Enabled switches the native protocol to TLS; the material applies to whichever hop uses TLS — native when Enabled, HTTP when http_scheme is https.

type Target

type Target struct {
	// URL is the HTTP base, e.g. http://host:8123.
	URL      string
	Username string
	Password string
	Database string
	// TLS configures an https URL; nil means net/http's defaults. The
	// pointer is the tuple's, so it changes only when the tenant moves to a
	// tuple with another tls block, which is what HTTPClients keys on.
	TLS *tls.Config
	// Headers go on every request ahead of the consumer's own; read-only.
	Headers map[string]string
}

Target is what the HTTP-interface consumers need per request.

Jump to

Keyboard shortcuts

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