Help us shape the future of Go! Take the 2026 Go Developer Survey

client

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package client implements the Rostam TCP client with a chpool-style connection pool, NotLeader auto-retry, and ping-on-stale validation.

Index

Constants

View Source
const (
	// OpKVIndexSet upserts one index definition into the meta catalog. A
	// definition with Enabled=false DROPS the named index cluster-wide — the
	// same op, which is why CreateKVIndex and DropKVIndex send the same name.
	OpKVIndexSet = "__kv_index_set__"
	// OpKVIndexList returns the whole catalog with a per-definition ready bit.
	OpKVIndexList = "__kv_index_list__"
	// OpKVQuery is the client-facing filtered read over the KV keyspace.
	OpKVQuery = "kv_query"
)

The KV record-search op names, spelled ONCE per op for the whole system. The cluster twins (cluster/kv_index_admin.go, cluster/kv_query_broadcast.go) are unexported, so the two spellings are pinned equal from the cluster side (cluster/kv_index_client_names_test.go) and pinned to their literals from this side (TestClientKVIndexOpNames). A rename that touches only one end fails both.

There is no constant for the INTERNAL per-group wrapper __kv_query_shard__ or for the readiness leaf __kv_index_ready__: both are peer-to-peer legs of a fan-out, authorised at admin, and a client that could name them would be addressing one shard group's slice of an answer rather than the answer.

View Source
const (
	StatusOK           uint8 = 0
	StatusNotFound     uint8 = 1
	StatusNotLeader    uint8 = 2
	StatusError        uint8 = 3
	StatusUnauthorized uint8 = 4
)

Status codes carried in the response frame.

View Source
const KVQueryDefaultLimit uint16 = 100

KVQueryDefaultLimit is the page size KVQuery sends when the caller left Limit zero. It is a DEFAULT rather than a "no limit": kv_query has no unbounded mode (wire.EncodeKVQueryArgs rejects limit 0 outright), because a page is what bounds a fan-out read's memory on every node it touches. 100 is small enough that a first page is cheap on a wide cluster and large enough that paging a modest result set is not dominated by round trips.

View Source
const MaxFrameSize = 16 << 20

MaxFrameSize bounds each individual frame.

Variables

View Source
var (
	// ErrNotFound is returned when the server responded with StatusNotFound.
	ErrNotFound = errors.New("client: not found")
	// ErrNoLeaderKnown indicates retries were exhausted without a successful
	// leader response.
	ErrNoLeaderKnown = errors.New("client: no leader known after retries")
	// ErrClientClosed indicates Call was made after Close.
	ErrClientClosed = errors.New("client: closed")
	// ErrUnauthorized indicates the server rejected the request because the
	// supplied auth token was missing or invalid.
	ErrUnauthorized = errors.New("client: unauthorized")
)

Errors surfaced to callers.

View Source
var (
	// ErrKVIndexNotFound: no definition by that name is installed, and the
	// coordinator found none in the meta catalog either. PERMANENT — create the
	// index (or fix the name); retrying cannot change it.
	ErrKVIndexNotFound = errors.New("client: kv_query: no such index")
	// ErrKVIndexBuilding: the definition exists but this group has not finished
	// installing or backfilling it, or it changed under the query. RETRYABLE —
	// the very next page may succeed, and a create-then-query lands here.
	ErrKVIndexBuilding = errors.New("client: kv_query: index is still building; retry")
	// ErrKVQueryUnavailable: the shard could not serve the read — it is being
	// removed from the node mid-scan, or the store is draining for close.
	// RETRYABLE against another replica.
	ErrKVQueryUnavailable = errors.New("client: kv_query: shard unavailable; retry")
	// ErrKVQueryFilter: a fact about the QUERY, not about the cluster — an
	// uncompilable filter, a scan with no consent, or a scan over budget.
	// PERMANENT; the remedy is a different query.
	ErrKVQueryFilter = errors.New("client: kv_query: invalid query")
)

The kv_query refusals a caller ACTS on, re-typed at this end.

WHY THE CLIENT RE-TYPES THEM. These errors are raised inside a shard's query leaf, possibly on another node, and reach this process as TEXT wrapped in a RemoteError — errors.Is against the server-side sentinels is impossible here (they live in ops/ops-kvindex, which this module deliberately cannot import; see TestClientIsEngineFree). Without re-typing, the one distinction a caller must make — "wait and retry" versus "fix the query" — would be a substring search in every calling application. Same job mapWriteErr does for the CAS conflict, one layer up.

The original message is always wrapped, never replaced: it names the index and the shard group that refused, which is what an operator reading a log needs.

View Source
var (
	ErrVersionConflict    = errors.New("client: version conflict (CAS expected_version mismatch)")
	ErrCollectionExists   = errors.New("client: collection already exists")
	ErrCollectionNotFound = errors.New("client: collection not found")
)
View Source
var ErrFrameTooLarge = errors.New("client: frame too large")

ErrFrameTooLarge indicates a frame exceeds MaxFrameSize.

View Source
var ErrFrameTruncated = errors.New("client: frame truncated")

ErrFrameTruncated indicates a frame buffer is shorter than its header claims.

Functions

func ClassifyKVQueryMessage added in v0.2.0

func ClassifyKVQueryMessage(msg string) error

ClassifyKVQueryMessage reports which of this package's kv_query sentinels a server's message belongs to, or nil for a message that is none of them.

It is exported so the ENGINE side can pin the classification against the sentinels that actually produce these messages (cluster/kv_index_client_names_test.go). The engine cannot be imported here and the texts are therefore copied; a test that renders the real errors and runs them through this function is what stops the copies from drifting.

func DecodeOperateValue added in v0.2.0

func DecodeOperateValue(b []byte) (wire.Cell, error)

DecodeOperateValue decodes one scalar value from an OperateResult.Values entry (design doc §3.4's tagged VALUE encoding: [type u8][n iff FIXED][data]). An absent node ([]byte{wire.OperateTypeUnset}) decodes to wire.Cell{Type: wire.OperateTypeUnset} rather than an error, so a caller can read res.Values[i] uniformly without special-casing "not present" against a decode failure.

The tagged cell must consume b exactly. A value that decodes and leaves bytes over is not the value it claims to be — a table's or a row's encoding read as a scalar, or a corrupt frame — and returning its first cell would silently hand the caller a prefix of something else.

func IsAmbiguous added in v0.2.0

func IsAmbiguous(err error) bool

IsAmbiguous reports whether err (returned by Client.Call) is a post-transmission transport failure: the request may have committed on the server before the failure, so its outcome is UNKNOWN. Exposed for callers that forward a non-replayable op across peers and must not try another target after an ambiguous send — notably cluster.proposeFlush, whose per-group flush leg must surface such an error rather than re-issue the wipe against the next owner. A PRE-transmission failure (dial refused, no leader yet) is not ambiguous and is safe to retry elsewhere.

func KeyU8 added in v0.2.0

func KeyU8(k uint8) []byte

KeyU8 encodes k as the 1-byte row key a schema-mode table with a U8 key type expects.

func KeyU16 added in v0.2.0

func KeyU16(k uint16) []byte

KeyU16 encodes k as the little-endian 2-byte row key a schema-mode table with a U16 key type expects.

func KeyU32 added in v0.2.0

func KeyU32(k uint32) []byte

KeyU32 encodes k as the little-endian 4-byte row key a schema-mode table with a U32 key type expects.

func KeyU64 added in v0.2.0

func KeyU64(k uint64) []byte

KeyU64 encodes k as the little-endian 8-byte row key a schema-mode table with a U64 key type expects (design doc §2.3: numeric keys are stored little-endian and compared as integers).

Types

type BatchError

type BatchError struct {
	ID  uint64
	Err error
}

BatchError reports a failure for a specific point id.

type Client

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

Client manages one pool per server address. Safe for concurrent use.

func New

func New(cfg Config) (*Client, error)

New constructs a Client. Pools are created lazily on first reference, except those for servers that need warmup (MinConnsPerServer > 0), which are created eagerly.

func NewRouted

func NewRouted(cfg Config) (*Client, error)

NewRouted is like New, but wires Rostam's builtin routing registry so shard-aware routing is on by default: each keyed op is dispatched to the shard that owns its key instead of round-robin. Use it for typed Collection work against a multi-node cluster.

New itself is intentionally left with nil-Ops semantics (no self-routing): internal callers such as cluster/node.go's peerClient must dial a specific, freshly-resolved address verbatim for leader-pinned (LeaderOnly/Linearizable) reads, and rely on nil Ops to disable client-side routing. Do not move this wiring into New.

func (*Client) CAS added in v0.2.0

func (c *Client) CAS(ctx context.Context, key, value, expected []byte, ttl time.Duration) (bool, error)

CAS is compare-and-swap: it sets key to value only if the key's current value equals expected. Returns true if the value was stored, false on a mismatch (or if the key is absent). ttl > 0 sets an expiry on the stored value. To store only when the key is absent, use SetNX.

Like SetNX, an ambiguous transport failure returns a non-nil error rather than a possibly-wrong false; the write is not replayed against another server.

func (*Client) Call

func (c *Client) Call(ctx context.Context, op string, args []byte) ([]byte, error)

Call sends one request, following NotLeader hints up to cfg.MaxNotLeaderHops. getOrCreatePool handles hinted addrs that are not in cfg.Servers. Transport errors (dial refused, EOF, etc.) are retried against another server within the same hop budget — EXCEPT that an outcome-returning conditional write (set_nx/cas/cad) is NOT replayed across an AMBIGUOUS failure (one that happened after the request was transmitted): the first server may have committed before the response was lost, so a replay would report a wrong result. Such a failure is returned to the caller instead.

func (*Client) CallFunc

func (c *Client) CallFunc(ctx context.Context, op string, args []byte, fn func(payload []byte) error) error

CallFunc is like Call but invokes fn with the response payload before the payload's owner moves on — payload is valid ONLY for the duration of fn. On the pooled path it aliases the connection's read buffer, which is still held; on the pipelined path (PipelineDepth > 0) it is that response's own allocation, which the reader drops on return. Either way fn must not retain it. Use this to skip the per-Call defensive copy when the response is consumed in place (e.g., parsed, written to an io.Writer, or copied into a caller-owned buffer). fn is not invoked on non-OK statuses; the appropriate error is returned instead. fn may be nil to discard the payload.

func (*Client) Close

func (c *Client) Close() error

Close stops the background refresh goroutine (if running) and all pools. Idempotent.

func (*Client) Collection

func (c *Client) Collection(name string) *Collection

Collection returns a handle bound to the named collection.

func (*Client) CompareAndDelete added in v0.2.0

func (c *Client) CompareAndDelete(ctx context.Context, key, expected []byte) (bool, error)

CompareAndDelete deletes key only if its current value equals expected — the safe-unlock primitive (release a lock only if you still hold it). Returns true if the key was deleted, false on a value mismatch or an absent key.

Like SetNX/CAS, an ambiguous transport failure returns a non-nil error rather than a possibly-wrong false; the delete is not replayed against another server.

func (*Client) CompareAndExpire added in v0.2.0

func (c *Client) CompareAndExpire(ctx context.Context, key, expected []byte, ttl time.Duration) (bool, error)

CompareAndExpire refreshes key's TTL only if its current value equals expected — the lock-renewal primitive (extend a lease only while you still hold the token). Returns true if the TTL was refreshed, false on a value mismatch or an absent key.

Like the other conditional writes, an ambiguous transport failure returns a non-nil error rather than a possibly-wrong false; the write is not replayed.

func (*Client) CreateKVIndex added in v0.2.0

func (c *Client) CreateKVIndex(ctx context.Context, d wire.KVIndexDef) error

CreateKVIndex durably records d in the cluster's KV index catalog. The write goes through meta-Raft, so it can be issued against any node; it returns once the definition is committed, NOT once every shard group has backfilled it — poll ListKVIndexes for the ready bit, or expect ErrKVIndexBuilding from a query issued in between.

d is validated locally first, so a definition the wire codec cannot represent (an over-long name, a Kind disagreeing with a "#count" path) costs no round trip. AppendKVIndexDef is the trusting side of the codec — it writes u8 length prefixes without checking them — so skipping Validate here would put a silently truncated definition on the wire.

func (*Client) DropKVIndex added in v0.2.0

func (c *Client) DropKVIndex(ctx context.Context, name string) error

DropKVIndex removes the named index cluster-wide. A drop is the SAME meta-log write as a create with Enabled=false — there is no separate delete op — so the definition it carries must still satisfy the codec's shape rules, which is why it names a placeholder path rather than an empty one.

Every query naming the index starts failing immediately afterwards, and re-creating it costs a full cache walk on every node. That is why the op is admin-scoped.

func (*Client) Exists added in v0.2.0

func (c *Client) Exists(ctx context.Context, key []byte) (bool, error)

Exists reports whether key is currently present (and live). An expired key reads as absent.

func (*Client) Flush added in v0.2.0

func (c *Client) Flush(ctx context.Context) error

Flush wipes the ENTIRE KV keyspace. A single call fans out SERVER-SIDE: flush is keyless, so it falls to a round-robin server in pickInitialTarget, and the receiving node's Call intercept broadcasts it into every shard group's Raft log (see cluster.broadcastFlush). flush IS a nonReplayableOp: it is idempotent only while nothing else writes between attempts, so a blind replay after an AMBIGUOUS (post-transmission) transport failure could re-wipe data another client wrote in the interval. On an ambiguous failure Call therefore surfaces the error instead of silently re-flushing; the caller decides whether re-issuing is safe.

func (*Client) GetDel added in v0.2.0

func (c *Client) GetDel(ctx context.Context, key []byte) (value []byte, found bool, err error)

GetDel atomically returns key's value and deletes it. found is false (and value nil) when the key was absent; a found empty value comes back as a non-nil zero-length slice.

func (*Client) GetSet added in v0.2.0

func (c *Client) GetSet(ctx context.Context, key, value []byte, ttl time.Duration) (old []byte, found bool, err error)

GetSet atomically replaces key's value (with ttl > 0 setting an expiry) and returns the OLD value. found is false when the key had no prior value.

func (*Client) IncrEx added in v0.2.0

func (c *Client) IncrEx(ctx context.Context, key []byte, delta int64, ttl time.Duration) (int64, error)

IncrEx atomically adds delta and returns the new value, setting the TTL only when the key is newly created — on an existing key the increment leaves the deadline untouched. This is the fixed-window rate-limit primitive: the first hit in a window creates the counter with the window's TTL, and subsequent hits increment it without extending the window.

func (*Client) KVQuery added in v0.2.0

func (c *Client) KVQuery(ctx context.Context, a wire.KVQueryArgs) (*KVQueryPage, error)

KVQuery runs one page of a filtered read over the KV keyspace and returns it.

a is taken BY VALUE and defaulted on the copy: a zero Limit becomes KVQueryDefaultLimit and a zero Consistency becomes wire.ConsistencyLeaderOnly. A caller paging in a loop reuses one args variable, so writing the defaults back would quietly rewrite its request; and LeaderOnly is the right default for a read whose whole point is that the page is a complete answer.

THE COST OF ZERO-MEANS-DEFAULT: wire.ConsistencyAnyReplica IS zero, so an any-replica read cannot be asked for through this call — it reads as "the caller did not choose" and becomes LeaderOnly. That is the safe direction (a stale replica silently omitting matching rows is the one failure this feature may not have), and the cheaper read stays available by encoding the args and calling Call("kv_query", …) directly. Giving it a first-class spelling means a pointer field or an explicit "unset" sentinel, which is an API change worth making deliberately rather than as a side effect of this defaulting.

When a.Return is wire.KVQueryReturnRecords each row's value is decoded HERE, on the client: the server has already validated that the bytes are a record, and decoding them into a tree is work that does not belong on a node serving every other caller's queries too. A row that came back key-only (its value did not fit the page) gets a nil record rather than an error — fetch those keys with Get.

Paging: feed the returned Cursor straight back as a.Cursor. An empty Cursor means the result set is exhausted.

func (*Client) ListKVIndexes added in v0.2.0

func (c *Client) ListKVIndexes(ctx context.Context) ([]wire.KVIndexDef, []bool, error)

ListKVIndexes returns every definition in the catalog, sorted by name, paired with a readiness bit per definition.

ready[i] HOLDS ONLY WHEN EVERY SHARD GROUP REPORTS THE INDEX READY, and it fails closed: a group still backfilling, a group that never installed the definition, and a group that could not be reached all make the bit false. A definition is never dropped from the answer because nothing could be said about its progress — a caller polling for readiness would read the name vanishing as "the create failed".

The two slices are always the same length. An empty catalog is an empty answer (nil, nil, nil), not an error.

func (*Client) MGet added in v0.2.0

func (c *Client) MGet(ctx context.Context, keys [][]byte) ([][]byte, error)

MGet reads many keys in one round trip per shard, returning a slice aligned to keys where a missing key is a nil entry (a stored empty value is a non-nil zero-length slice). It groups keys by their owning shard — the same hash as PutBatch — issues one mget per shard (chunked to maxMGetKeys), and stitches the results back into the original key order. When the topology is unknown (no cache / NumShards==0) it cannot group, so it falls back to one get per key (the correctness floor), like PutBatch.

func (*Client) Operate added in v0.2.0

func (c *Client) Operate(ctx context.Context, a *wire.OperateArgs) (*wire.OperateResult, error)

Operate runs one atomic multi-field update against a's key and returns its result (design doc for operate v2, §3-§3.5): a generic list of scalar/table/control ops applied to a record, evaluated in order, with return specs read back in the same round trip.

An error from wire.EncodeOperateArgs (a's op/ret list, key, or schema blob exceeds a wire cap, or carries a structurally invalid field) is returned unchanged, without a network round trip — build a's OperateArgs with OperateBuilder to avoid ever hitting one by hand. A nil a is wire.ErrOperateArgs rather than a panic: it is the shape a caller lands on by ignoring OperateBuilder.Args's error, which is exactly when a panic is least welcome.

func (*Client) Persist added in v0.2.0

func (c *Client) Persist(ctx context.Context, key []byte) (bool, error)

Persist removes key's TTL so it never expires. Returns true if a TTL was removed, false if the key is absent or already had no expiry.

func (*Client) PutBatch

func (c *Client) PutBatch(ctx context.Context, entries []wire.PutEntry) error

PutBatch writes many key/value pairs as bulk put_batch ops — the ~10x bulk-insert fast path. It groups entries by their owning shard (the same hash as pickInitialTarget) so each shard receives ONE Raft log entry per chunk, and chunks each group to wire.MaxPutBatchSize. Each Call follows NotLeader hints like a single put. When the topology is unknown (no cache / NumShards==0) it cannot group, so it falls back to one put per entry (the correctness floor). If a stale topology (mid-reshard) makes the server reject a group as cross-shard, it refreshes the topology once and retries that group re-grouped.

func (*Client) RegisterWASM

func (c *Client) RegisterWASM(ctx context.Context, r wire.WASMRegistration, module []byte) (pushReport string, err error)

RegisterWASM ships a compiled WASM module via __register_wasm__. The Client routes the call to a node that broadcasts the registration into EVERY shard group's Raft log and returns once each group has committed it.

UPDATING A LIVE MODULE IS SUPPORTED; CHANGING ITS CONTRACT IS NOT. A second registration under a live name may change the module — the bytes, the export symbol, the fuel cap — and should carry a higher Epoch. It may NOT change the op's Kind; that is refused at propose time with cluster.ErrWASMUpdateUnsupported (HTTP 400), and a registration needing either belongs under a NEW op name.

The asymmetry is structural, not a policy choice. The module a committed entry executes is resolved from the SHARD GROUP whose log carried it, so every replica of that group derives the same version for the same entry however far along its other groups it is — which is what makes an in-place module update safe. Kind cannot be resolved that way: it is read before any group is known (it decides whether the invocation is replicated at all), so it is frozen at first registration. The key extractor is the other field that cannot be per group — it is what COMPUTES the group index — and it is not frozen so much as CONSTANT: every WASM op uses the same one, so there is no field to change. See wire.WASMKeyExtractorHandle.

The propose-time refusal is BEST-EFFORT — it is judged against the receiving node's own install state, so a node that has not applied the original registration cannot recognise a contract change and will accept it. It is backed by an apply-time refusal that is not best-effort: each shard group refuses a contract change against its own binding, identically on every replica of that group.

AN UPDATE ROLLS OUT PER GROUP, NOT ATOMICALLY. Each shard group switches to the new module when its own log commits the registration, so for a short window different groups run different versions. Every replica of a given group agrees throughout; what an update does not give you is a single instant at which the whole cluster changes over.

SUCCESS DOES NOT MEAN THE OP IS IMMEDIATELY INVOCABLE EVERYWHERE. A node will not propose an invocation into a shard group it hosts until it has itself applied the registration from that group's log (cluster's route gate — the invariant that keeps a replica from ever meeting an invocation for an op it cannot look up). A return from here means every group COMMITTED the entry; the remaining nodes apply it moments later, each opening the gate for its own groups as it does. Until then an invocation can come back with cluster.ErrWASMOpNotInThisGroup ("op not registered in this shard group yet", HTTP 503) or, on a node that has applied nothing yet, cluster.ErrUnknownOp (HTTP 404).

Both are transient and both are safe to retry: registration is idempotent, so re-running RegisterWASM with the SAME r is also safe and is explicitly allowed by the no-update rule above. A caller that needs the op live before it proceeds should retry the first invocation briefly rather than assume it.

An error return does NOT mean nothing happened: the broadcast attempts every group even after one fails, and the groups that accepted keep the entry. The op is then usable for keys routing to those groups and errors for the rest, until a retry lands it everywhere.

pushReport IS PART OF THE RESULT, NOT DIAGNOSTICS. Before the registration enters any log, the receiving node pushes the module's BYTES to every member it can reach and requires a compile verdict from each one that answers; a member that refuses fails this call. pushReport is empty when every member acked. When it is not, it names the members that rendered no verdict — unreachable, or on a build that does not know the push op — and those are exactly the members that do not hold the bytes. THE MARKER NO LONGER CARRIES THE MODULE, so a member named here has to FETCH it before it can execute the op, and an invocation that arrives first BLOCKS that shard group until it does — which also stops the group snapshotting and compacting its Raft log. A caller that ignores this is choosing not to know which nodes are one step from a stalled shard group.

The push is also subject to a DURABILITY FLOOR: a majority of cluster members must hold the module before the marker is proposed at all, so a call that SUCCEEDS guarantees a reachable source exists for every later fetch. A cluster too degraded to reach that majority fails this call rather than accepting a registration that could become permanently unrunnable. See cluster.pushWASMBlob. THE MODULE IS A SEPARATE ARGUMENT NOW, and that is the visible face of thin registration markers. r describes the OP — its name, contract, epoch — and module is the WASM binary. Only the module's 32-byte content address travels in the replicated marker; the binary itself goes to the receiving node once, is pushed from there to a majority of cluster members before anything is proposed, and is fetched on demand by any member the push missed. r.Blob is derived from module by the encoder, so the two can never disagree.

func (*Client) Reset

func (c *Client) Reset()

Reset closes all connections in all pools (puddle keeps the pool object). Use after a known network blip or server restart.

func (*Client) SetNX added in v0.2.0

func (c *Client) SetNX(ctx context.Context, key, value []byte, ttl time.Duration) (bool, error)

SetNX sets key to value only if the key is currently absent or expired — the atomic set-if-absent primitive. Returns true if the value was stored, false if the key was already present. ttl > 0 sets an expiry on the stored value.

On an ambiguous transport failure (the request may have committed before the response was lost) it returns a non-nil error rather than a possibly-wrong false — the write is NOT transparently replayed against another server. A caller that gets a transport error must re-check state itself.

func (*Client) Stat

func (c *Client) Stat() map[string]Stat

Stat returns a snapshot per server address.

func (*Client) TTL added in v0.2.0

func (c *Client) TTL(ctx context.Context, key []byte) (int64, error)

TTL returns key's remaining time-to-live in milliseconds, following the Redis convention: -2 if the key is absent, -1 if it exists but has no expiry, else the remaining ms (>= 0).

func (*Client) Topology

func (c *Client) Topology() *wire.Topology

Topology returns the most recent cluster topology snapshot, or nil if the refresh loop has not yet populated it. Callers must not mutate the returned value — it is shared with concurrent readers.

type Collection

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

Collection is a typed, name-scoped handle for vector operations against a remote Rostam collection. Constructing one performs no I/O and does not require the collection to exist.

func (*Collection) Create

func (col *Collection) Create(ctx context.Context, req CreateRequest) error

Create creates the collection backing this handle. If a collection with the same name already exists, it returns ErrCollectionExists.

func (*Collection) Delete

func (col *Collection) Delete(ctx context.Context, req DeleteRequest) error

Delete removes the point at req.ID. If req.HasExpectedVersion is set, the delete only applies when the point's current version matches req.ExpectedVersion; otherwise it returns ErrVersionConflict.

func (*Collection) Drop

func (col *Collection) Drop(ctx context.Context) error

Drop deletes the collection backing this handle. The server's CollectionStore.DropCollection treats a never-created (or already-dropped) name as a no-op rather than an error, so Drop is idempotent: it does not return ErrCollectionNotFound. (isCollectionNotFound is still applied below as a defensive translation in case that server contract ever changes; see Get/Search for the paths where a missing collection actually surfaces.)

func (*Collection) Get

func (col *Collection) Get(ctx context.Context, req GetRequest) (Point, error)

Get fetches the point at req.ID. It returns ErrNotFound when no such point exists, or ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) GetBatch

func (col *Collection) GetBatch(ctx context.Context, req GetBatchRequest) (GetBatchResponse, error)

GetBatch fetches multiple points in a single round trip. Requested ids that do not exist are reported in Missing rather than causing an error. Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) HybridSearch

func (col *Collection) HybridSearch(ctx context.Context, req HybridSearchRequest) (SearchResponse, error)

HybridSearch runs a dense+sparse hybrid search, fusing both lanes server-side. Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) HybridText

func (col *Collection) HybridText(ctx context.Context, req HybridTextRequest) (SearchResponse, error)

HybridText runs a dense+BM25 hybrid search, fusing both lanes server-side. Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) Insert

func (col *Collection) Insert(ctx context.Context, req WriteRequest) error

Insert writes req only if no point exists at req.ID (subject to the same CAS semantics as Upsert when req.HasExpectedVersion is set).

func (*Collection) Operate added in v0.2.0

func (col *Collection) Operate(ctx context.Context, req OperateRequest) (found bool, res *wire.OperateResult, version uint64, err error)

Operate applies one atomic operate op-list to the record stored under req.PayloadKey in the point at req.ID (design doc for operate v2, §3-§3.5; record search brief §3). Build req.Args with NewOperate(nil) — the call names its target with Collection/ID/PayloadKey, so the builder's KEY stays empty and the builder's TTL must stay unset: a point's TTL belongs to the point, and EncodeVectorOperateArgs rejects a call that carries either.

found is false when the point is absent, tombstoned or expired — a FLAG, not an error, exactly like every other point write. res is nil then.

version is the point's version AFTER the call: bumped when the op-list applied, and the CURRENT unbumped one when it was a deliberate no-op (a failed CHECK). It is 0 when found is false. Feed it straight into the next call's ExpectedVersion to run a CAS loop without re-reading the point — a re-read is both a round trip and a race, since another writer can land between it and the retry:

found, res, v, err := col.Operate(ctx, OperateRequest{ID: id, PayloadKey: k, Args: a})
// ... next attempt: ExpectedVersion: v, HasExpectedVersion: true

ONLY A CALL THAT RETURNED CARRIES A USABLE VERSION. A call that fails the precondition returns (false, nil, 0, ErrVersionConflict): the server sends no result frame for a refused write, so there is no version in it to report, and 0 is not a placeholder — it is the value that means "expect an ABSENT point", which conflicts again against a live one. After a conflict, retry with the last version this collection handed back if the caller knows no one else writes the point; otherwise re-read it, because the conflict says somebody did.

The op is NOT replayable (nonReplayableOp): an ADD applied twice after an ambiguous post-commit transport failure double-counts, so an ambiguous error surfaces to the caller instead of being retried.

func (*Collection) Query

func (col *Collection) Query(ctx context.Context, req QueryRequest) (SearchResponse, error)

Query runs a raw vector.QuerySpec against the collection, decoding the result into the same SearchResponse shape as Search/Recommend.

If req.Spec.Mode is ModeFusion, this has the same single-node limitation as Recommend: the reply carries unfused lanes, and only a routed/multi-node (coordinator) deployment fuses and re-tags them into the flat result this method decodes. Against a single coordinator-less node, a ModeFusion spec's result cannot be decoded.

Returns ErrCollectionNotFound when the collection itself does not exist (this also covers Recommend, which delegates to Query).

func (*Collection) Recommend

func (col *Collection) Recommend(ctx context.Context, req RecommendRequest) (SearchResponse, error)

Recommend runs a RECOMMEND query, returning points similar to the positive examples (and dissimilar to any negative examples), ranked by score/distance.

The recommend leaf rides as the sole ModeFusion Prefetch lane (no Root) — the canonical shape the HTTP /query handler builds for a recommend request (httpapi/vector.go's (queryLeafReq).toLeaf, appended via vector.LeafSource(leaf) into spec.Prefetch). With one prefetch lane, the fused result is that lane unchanged; the engine's recommend pre-pass (resolveRecommendLeaves) resolves the example ids and rewrites the leaf to a derived dense query before it runs, excluding the examples from the results.

This sends a ModeFusion vector_query, whose single-node reply carries the UNFUSED lane(s) (EncodeQueryResult's FUSION tag); only a routed/multi-node (coordinator) deployment fuses those lanes and re-tags the reply as the flat RERANK result DecodeQueryResultDegraded expects. Against a single coordinator-less node, the fused result cannot be decoded.

func (*Collection) Scroll

func (col *Collection) Scroll(ctx context.Context, req ScrollRequest) (ScrollResponse, error)

Scroll returns a page of points matching req.Filter, ordered by id. Pass the previous response's NextCursor as req.Cursor to fetch the next page. Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) Search

func (col *Collection) Search(ctx context.Context, req SearchRequest) (SearchResponse, error)

Search runs a dense-vector KNN search, returning the top-k results ranked by distance/score. Returns ErrCollectionNotFound when the collection does not exist.

func (*Collection) SearchDocs

func (col *Collection) SearchDocs(ctx context.Context, req SearchDocsRequest) (DocsResponse, error)

SearchDocs runs a dense-vector KNN search, returning the top-k points as content-bearing Documents (reusing the Document type from Scroll/reads). Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) SearchGroups

func (col *Collection) SearchGroups(ctx context.Context, req GroupSearchRequest) (GroupResponse, error)

SearchGroups runs a group-by-document search, returning the top-k groups (ranked by best member) each with up to GroupSize best hits. Returns ErrCollectionNotFound when the collection itself does not exist.

func (*Collection) Upsert

func (col *Collection) Upsert(ctx context.Context, req WriteRequest) error

Upsert writes req, creating or overwriting the point at req.ID. If req.HasExpectedVersion is set, the write only applies when the point's current version matches req.ExpectedVersion; otherwise it returns ErrVersionConflict.

func (*Collection) UpsertBatch

func (col *Collection) UpsertBatch(ctx context.Context, pts []PointInput) []BatchError

UpsertBatch upserts each point. There is no single batch op on the wire, so this issues one upsert per point over the (optionally pipelined) connection and returns the failures, if any, keyed by id. A nil/empty return means all succeeded.

type Config

type Config struct {
	// Servers is the list of "host:port" addresses to use for Calls.
	// On NotLeader, the client may temporarily prefer a hinted server.
	Servers []string

	// MaxConnsPerServer caps the puddle pool per server. Default = max(4, NumCPU).
	MaxConnsPerServer int32

	// MinConnsPerServer is the warm-pool floor per server. Default 0 (lazy).
	MinConnsPerServer int32

	// MaxConnLifetime closes connections older than this. Default 1 hour.
	MaxConnLifetime time.Duration

	// MaxConnLifetimeJitter adds up to this much randomness to MaxConnLifetime
	// per connection to avoid simultaneous reconnect storms. Default 0.
	MaxConnLifetimeJitter time.Duration

	// MaxConnIdleTime closes idle connections after this. Default 30 minutes.
	MaxConnIdleTime time.Duration

	// HealthCheckPeriod is how often the background loop scans for expired
	// or idle conns. Default 1 minute.
	HealthCheckPeriod time.Duration

	// DialTimeout caps how long a single TCP dial can take. Default 5s.
	DialTimeout time.Duration

	// CallTimeout caps how long an individual Call can take (when ctx has
	// no deadline). Default 5s. It is one budget for the WHOLE call: under
	// PipelineDepth a call that has to queue for an in-flight slot spends part
	// of this waiting for one, and the rest waiting for its answer.
	CallTimeout time.Duration

	// MaxNotLeaderHops bounds how many NotLeader hints the client follows
	// after the initial attempt. Total attempts = MaxNotLeaderHops + 1.
	// Default 3 (one initial + three redirects).
	MaxNotLeaderHops int

	// PipelineDepth, when > 0, enables PIPELINED Calls: up to this many requests
	// in flight per connection instead of one-at-a-time. It removes the
	// "throughput = connections / per-op latency" ceiling — the big win on a
	// latency-bound (e.g. replicated) write workload where a request-response
	// client would otherwise be connection-limited. 0 (default) keeps the strict
	// request-response pool, byte-identical to before. SEMANTICS: pipelined
	// requests are concurrently in flight, so two writes to the SAME key on one
	// connection may apply in either order — do not pipeline order-dependent
	// writes (see client/pipeline.go). NotLeader auto-retry still applies. A
	// reasonable value is 32–64. Both Call and CallFunc take the pipelined path.
	PipelineDepth int32

	// PipelineConns is the number of pipelined connections per server (round-
	// robin) when PipelineDepth > 0. Default 4. More conns spread concurrent
	// Calls and cap head-of-line blocking behind a slow response.
	//
	// It cuts the other way too, and the trade is easy to get backwards. Writes
	// from callers that are in flight AT THE SAME INSTANT on the SAME connection
	// leave in one syscall; spreading the same rate over more connections drops
	// the concurrency on each, and below roughly one concurrent caller per
	// connection there is nothing left to combine. Pipelining is what lets a
	// client serve its load from FEWER connections -- keeping the pooled count
	// while enabling it gives up that saving for no benefit.
	PipelineConns int32

	// Ops is the routing-only op registry for smart routing. If nil, the
	// client falls back to round-robin + NotLeader retry. It is a
	// *wire.Registry (routing metadata only, no handlers); the server uses a
	// separate *ops.Registry (routing + handlers). They are DIFFERENT types
	// and are never the same object — the client cannot import the server's
	// cache-bound Handler/TxContext. What must match is the routing metadata:
	// populate the client with wire.RegisterRoutableBuiltins (or use
	// NewRouted, which does it) while the server uses ops.RegisterBuiltins —
	// both draw from the shared wire.BuiltinOps table, so the mappings agree.
	// To route from an already-built server registry, adapt it with
	// (*ops.Registry).ExportRouting.
	Ops *wire.Registry

	// TopologyRefreshInterval is how often the background goroutine
	// polls __topology__. Default 5s. Ignored when Ops == nil.
	TopologyRefreshInterval time.Duration

	// AuthToken, when non-empty, is sent on every RPC via a protocol-v2
	// frame prefix. The server must validate it through its Authenticator
	// hook; otherwise the token is harmlessly ignored (legacy server).
	// Empty AuthToken keeps the v1 frame layout (no per-request overhead).
	//
	// WARNING: if TLSConfig is nil the token is transmitted in cleartext over
	// a plaintext TCP connection. Set TLSConfig to encrypt the connection and
	// protect the credential in transit.
	AuthToken string

	// TLSConfig, when non-nil, makes the client dial each server over TLS
	// (tls.Client over the raw TCP conn) instead of plaintext. Build it via
	// tlsutil.ClientTLS(caFile, certFile, keyFile, serverName): set RootCAs to
	// verify the server's cert, and (for mTLS) a client cert/key the server
	// verifies — the cert's CN then becomes the client's principal on a server
	// with no bearer token. nil ⇒ plaintext (the default; unchanged hot path).
	//
	// NOTE: the inter-node cluster peerClient sets this (a per-peer clone with
	// ServerName) ONLY when the cluster is configured with InterNodeTLS — i.e. when
	// client-facing TLS is on, so the inter-node dial matches the TLS-wrapped peer
	// port. A plaintext cluster leaves it nil (plaintext inter-node, unchanged).
	TLSConfig *tls.Config

	// Optional hooks (mirror chpool). Any may be nil.
	BeforeConnect func(ctx context.Context, addr string) error
	AfterConnect  func(ctx context.Context, c *Conn) error
	BeforeAcquire func(ctx context.Context, c *Conn) bool
	AfterRelease  func(c *Conn) bool
	BeforeClose   func(c *Conn)
}

Config governs a Client and its per-server pools.

func (Config) Validate

func (c Config) Validate() error

Validate enforces invariants. Returns error on misconfiguration.

type Conn

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

Conn is the per-connection handle exposed to lifecycle hooks. Users do not construct or call methods on Conn directly — Client.Call manages it. The fields are unexported to discourage poking; hooks receive the pointer to enable cfg-level identity checks (e.g. tagging).

func (*Conn) Addr

func (c *Conn) Addr() string

Addr returns the server address this conn is dialed to.

type Consistency

type Consistency uint8

Consistency selects the read-consistency level for reads/searches. It is passed through as the server's read_consistency byte; 0 is the default (strong) path.

const (
	ConsistencyDefault Consistency = 0 // strong / leader read (server default)

)

type CreateRequest

type CreateRequest struct {
	Dim            int
	Metric         vtypes.Metric // zero value = vtypes.Cosine
	M              int
	EfConstruction int
	EfSearch       int
	Persistent     bool
	FullText       *vtypes.FullTextConfig
}

CreateRequest configures a new collection. Only Dim is required. Set FullText to enable the server-side BM25 lane (required for HybridText).

type DeleteRequest

type DeleteRequest struct {
	ID                 uint64
	ExpectedVersion    uint64
	HasExpectedVersion bool
}

DeleteRequest removes a point, optionally guarded by ExpectedVersion.

type DocsResponse

type DocsResponse struct {
	Documents []Document
	Degraded  bool
	Missing   []uint16
}

DocsResponse carries content-bearing documents plus degraded-partition info.

type Document

type Document struct {
	ID       uint64
	Distance float32
	Score    float32
	Content  string
	Metadata vtypes.Metadata
}

Document is a payload-bearing row from Scroll and doc searches.

type GetBatchRequest

type GetBatchRequest struct {
	IDs         []uint64
	WithVector  bool
	WithPayload bool
}

GetBatchRequest selects multiple points by id, sharing one projection.

type GetBatchResponse

type GetBatchResponse struct {
	Points  []Point
	Missing []uint64
}

GetBatchResponse reports the points that were found plus the ids from the request that were not.

type GetRequest

type GetRequest struct {
	ID          uint64
	WithVector  bool
	WithPayload bool
}

GetRequest selects a single point by id and its projection.

type Group

type Group struct {
	Key  string
	Hits []Document
}

Group is one group-by-document result: the grouping key plus its best hits, best-first.

type GroupResponse

type GroupResponse struct {
	Groups   []Group
	Degraded bool
	Missing  []uint16
}

GroupResponse carries grouped results plus degraded-partition info.

type GroupSearchRequest

type GroupSearchRequest struct {
	Query       []float32
	K           int
	GroupBy     string
	GroupSize   int
	FetchK      int
	Filter      vtypes.Filter
	Consistency Consistency
}

GroupSearchRequest collapses KNN hits sharing a metadata field value into groups, returning the top-k groups ranked by their best member.

type HybridSearchRequest

type HybridSearchRequest struct {
	Dense       []float32
	Sparse      vtypes.SparseVector
	K           int
	Filter      vtypes.Filter
	Method      vtypes.FusionMethod
	Alpha       float64
	RRFK        int
	DenseK      int
	SparseK     int
	Consistency Consistency
}

HybridSearchRequest fuses a dense KNN lane with an explicit sparse-vector lane.

type HybridTextRequest

type HybridTextRequest struct {
	Dense       []float32
	Text        string
	K           int
	Filter      vtypes.Filter
	Method      vtypes.FusionMethod // zero = FusionRRF
	Alpha       float64
	RRFK        int
	DenseK      int
	SparseK     int
	GlobalIDF   bool
	Consistency Consistency
}

HybridTextRequest fuses a dense KNN lane with a BM25 full-text lane, the text lane analyzed server-side from raw query text.

type KVQueryPage added in v0.2.0

type KVQueryPage struct {
	// Rows are the matching keys, each with its value when the query asked for
	// one. A row whose value did not fit the page comes back key-only.
	Rows []wire.KVQueryRow
	// Records is populated ONLY when the query's Return was
	// wire.KVQueryReturnRecords, and is then ROW-ALIGNED with Rows: entry i is
	// row i's decoded record, or nil when that row came back key-only.
	Records []*wire.Record
	// Cursor resumes the query. Empty means every group answered in full and
	// the result set is exhausted; otherwise feed it back as Args.Cursor.
	Cursor []wire.KVQueryCont
}

KVQueryPage is one page of a kv_query answer.

type OperateBuilder added in v0.2.0

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

OperateBuilder builds a wire.OperateArgs for one Client.Operate call (design doc §3-§4): a fluent alternative to constructing wire.OperateOp/wire.OperateRet by hand. With a schema attached (WithSchema), every Field/Col name is resolved to its schema position and an untyped op's Type is wire.OperateTypeFromSchema; without one (Dynamic), names ride the wire as name segments and a typed op's Type is whatever the caller passed.

Every method except Args returns the builder so calls chain (see the package example in design doc §4). The first resolution error encountered — an unknown field name, a wrong-width row key, a column path into a non-table field, a typed op whose type disagrees with the schema — is recorded and returned by Args, so no intermediate call needs its own error check. schema and createSchema are deliberately two fields. createSchema is the blob the CALL carries, which the server checks against the version the record is stored at when it opens it — so it must stay the schema the record has NOW, and only WithSchema sets it. schema is what Field/Col names resolve against, which a MIGRATE moves forward: MIGRATE is the first op, so every other op and every return spec runs against the record as the migration leaves it, and must be resolved against the target schema.

func NewOperate added in v0.2.0

func NewOperate(key []byte) *OperateBuilder

NewOperate starts building an operate call against key. The call defaults to wire.OperateCreateNone (valid only against an existing record) until WithSchema or Dynamic is called.

func (*OperateBuilder) Add added in v0.2.0

func (ob *OperateBuilder) Add(path Path, v int64) *OperateBuilder

Add adds v (saturating) to the int scalar at path, typed from the schema.

func (*OperateBuilder) AddFloat added in v0.2.0

func (ob *OperateBuilder) AddFloat(path Path, v float64) *OperateBuilder

AddFloat adds v (IEEE) to the float scalar at path, typed from the schema.

func (*OperateBuilder) AddFloatT added in v0.2.0

func (ob *OperateBuilder) AddFloatT(path Path, typ uint8, v float64) *OperateBuilder

AddFloatT adds v (IEEE) to the float scalar at path, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) AddT added in v0.2.0

func (ob *OperateBuilder) AddT(path Path, typ uint8, v int64) *OperateBuilder

AddT adds v (saturating) to the scalar at path, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) And added in v0.2.0

func (ob *OperateBuilder) And(path Path, v int64) *OperateBuilder

And bitwise-ANDs the fixed-width int scalar at path with v (masked to the field width), typed from the schema.

func (*OperateBuilder) AndT added in v0.2.0

func (ob *OperateBuilder) AndT(path Path, typ uint8, v int64) *OperateBuilder

AndT bitwise-ANDs the scalar at path with v, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Args added in v0.2.0

func (ob *OperateBuilder) Args() (*wire.OperateArgs, error)

Args resolves every op and return spec's Path against the builder's schema (if any) and returns the finished wire.OperateArgs. It never panics on a malformed builder: WithSchema's validation failure, or any resolution error hit along the way (an unknown field name, a wrong-width row key, a column path into a non-table field, a typed op whose type disagrees with the schema), is returned here instead.

func (*OperateBuilder) Check added in v0.2.0

func (ob *OperateBuilder) Check(path Path, cmp uint8, v int64) *OperateBuilder

Check aborts the whole call (record unchanged, result status OperateStatusCheckFailed) when node(path) cmp v is false — the server-side compare-and-swap primitive on any field (design doc §3.3). Use CheckBytes to compare against a bytes/fixed operand.

func (*OperateBuilder) CheckBytes added in v0.2.0

func (ob *OperateBuilder) CheckBytes(path Path, cmp uint8, v []byte) *OperateBuilder

CheckBytes aborts the whole call when node(path) cmp v is false, comparing bytewise.

func (*OperateBuilder) Config added in v0.2.0

func (ob *OperateBuilder) Config(path Path, capacity uint32, policy uint8, byCol string) *OperateBuilder

Config sets the eviction triple (capacity, policy, and, for a *_COL policy, byCol) on the dynamic-mode table field at path, creating an empty table if absent (design doc §3.2: dynamic mode only — in schema mode eviction is part of the schema and changes via Migrate). byCol may be "" when policy does not need one.

Because it is dynamic-mode only, Args returns an error rather than a call when a schema is attached (WithSchema): the server would answer wire.ErrOperateOpcode for it.

func (*OperateBuilder) Count added in v0.2.0

func (ob *OperateBuilder) Count(path Path) *OperateBuilder

Count adds a return spec that reads back path's count after all ops apply: row count for a table, field count for the record, 1/0 for a present/absent scalar or row (design doc §3.4).

func (*OperateBuilder) Del added in v0.2.0

func (ob *OperateBuilder) Del(path Path) *OperateBuilder

Del removes path: a record field becomes UNSET, a row is removed, a table field is emptied, and the record path deletes the whole record (design doc §2.5/§3.1).

func (*OperateBuilder) Dynamic added in v0.2.0

func (ob *OperateBuilder) Dynamic() *OperateBuilder

Dynamic marks the call as creating an absent record in dynamic mode (design doc §2.9): no schema, every field and column named and typed by the ops that first touch it. It is exclusive with WithSchema, for the reason given there.

func (*OperateBuilder) If added in v0.2.0

func (ob *OperateBuilder) If(path Path, cmp uint8, v int64, n int) *OperateBuilder

If skips the next n ops when node(path) cmp v is false (design doc §3.3); n is bounded by the remaining op list. Use IfBytes to compare against a bytes/fixed operand.

func (*OperateBuilder) IfBytes added in v0.2.0

func (ob *OperateBuilder) IfBytes(path Path, cmp uint8, v []byte, n int) *OperateBuilder

IfBytes skips the next n ops when node(path) cmp v is false, comparing bytewise.

func (*OperateBuilder) Max added in v0.2.0

func (ob *OperateBuilder) Max(path Path, v int64) *OperateBuilder

Max sets the int scalar at path to max(current, v), typed from the schema.

func (*OperateBuilder) MaxFloat added in v0.2.0

func (ob *OperateBuilder) MaxFloat(path Path, v float64) *OperateBuilder

MaxFloat sets the float scalar at path to max(current, v), typed from the schema.

func (*OperateBuilder) MaxFloatT added in v0.2.0

func (ob *OperateBuilder) MaxFloatT(path Path, typ uint8, v float64) *OperateBuilder

MaxFloatT sets the float scalar at path to max(current, v), creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) MaxT added in v0.2.0

func (ob *OperateBuilder) MaxT(path Path, typ uint8, v int64) *OperateBuilder

MaxT sets the scalar at path to max(current, v), creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Migrate added in v0.2.0

func (ob *OperateBuilder) Migrate(from int64, s *wire.Schema, dropExtra bool) *OperateBuilder

Migrate moves the record from its stored version (from, or wire.OperateMigrateFromDynamic when the record is dynamic-mode) to s's version in this atomic call, an append-only evolution or a dynamic-mode freeze (design doc §2.8/§2.9). dropExtra sets wire.OperateMigrateDropExtra, discarding dynamic fields s does not declare rather than rejecting the call. s is validated here, as WithSchema validates its own; a nil s or a validation failure is recorded and returned by Args rather than panicking.

It must be the FIRST op: applyOps rejects a MIGRATE at any other index with wire.ErrOperateOpcode (design doc §2.8), so calling it after another op can only ever build a request the server refuses. That is recorded as a builder error here instead.

It also moves the builder's resolution schema to s, without touching the call's `create` byte or the schema blob the call carries. Every op after the MIGRATE — which is all of them — and every return spec runs against the record as the migration leaves it, so their Field/Col names resolve against the TARGET schema. The call still declares the version the record is stored at, which is what the server checks when it opens it.

func (*OperateBuilder) Min added in v0.2.0

func (ob *OperateBuilder) Min(path Path, v int64) *OperateBuilder

Min sets the int scalar at path to min(current, v), typed from the schema.

func (*OperateBuilder) MinFloat added in v0.2.0

func (ob *OperateBuilder) MinFloat(path Path, v float64) *OperateBuilder

MinFloat sets the float scalar at path to min(current, v), typed from the schema.

func (*OperateBuilder) MinFloatT added in v0.2.0

func (ob *OperateBuilder) MinFloatT(path Path, typ uint8, v float64) *OperateBuilder

MinFloatT sets the float scalar at path to min(current, v), creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) MinT added in v0.2.0

func (ob *OperateBuilder) MinT(path Path, typ uint8, v int64) *OperateBuilder

MinT sets the scalar at path to min(current, v), creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Mul added in v0.2.0

func (ob *OperateBuilder) Mul(path Path, v int64) *OperateBuilder

Mul multiplies (saturating) the int scalar at path by v, typed from the schema.

func (*OperateBuilder) MulFloat added in v0.2.0

func (ob *OperateBuilder) MulFloat(path Path, v float64) *OperateBuilder

MulFloat multiplies (IEEE) the float scalar at path by v, typed from the schema.

func (*OperateBuilder) MulFloatT added in v0.2.0

func (ob *OperateBuilder) MulFloatT(path Path, typ uint8, v float64) *OperateBuilder

MulFloatT multiplies (IEEE) the float scalar at path by v, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) MulT added in v0.2.0

func (ob *OperateBuilder) MulT(path Path, typ uint8, v int64) *OperateBuilder

MulT multiplies (saturating) the scalar at path by v, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Or added in v0.2.0

func (ob *OperateBuilder) Or(path Path, v int64) *OperateBuilder

Or bitwise-ORs the fixed-width int scalar at path with v, typed from the schema.

func (*OperateBuilder) OrT added in v0.2.0

func (ob *OperateBuilder) OrT(path Path, typ uint8, v int64) *OperateBuilder

OrT bitwise-ORs the scalar at path with v, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Return added in v0.2.0

func (ob *OperateBuilder) Return(path Path) *OperateBuilder

Return adds a return spec that reads back path's full, self-describing value after all ops apply (design doc §3.4).

func (*OperateBuilder) Set added in v0.2.0

func (ob *OperateBuilder) Set(path Path, v int64) *OperateBuilder

Set writes v to the int/float-domain scalar at path, typed from the schema (design doc §3.1). Use SetT in dynamic mode to create a new target, or SetFloat/SetBytes for a float or bytes/fixed operand.

func (*OperateBuilder) SetBytes added in v0.2.0

func (ob *OperateBuilder) SetBytes(path Path, v []byte) *OperateBuilder

SetBytes writes v to the bytes/fixed scalar at path, typed from the schema.

func (*OperateBuilder) SetBytesT added in v0.2.0

func (ob *OperateBuilder) SetBytesT(path Path, typ uint8, v []byte) *OperateBuilder

SetBytesT writes v (BYTES or FIXED) to the scalar at path, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) SetFloat added in v0.2.0

func (ob *OperateBuilder) SetFloat(path Path, v float64) *OperateBuilder

SetFloat writes v to the float scalar at path, typed from the schema.

func (*OperateBuilder) SetFloatT added in v0.2.0

func (ob *OperateBuilder) SetFloatT(path Path, typ uint8, v float64) *OperateBuilder

SetFloatT writes v to the float scalar at path, creating it with type typ (wire.OperateTypeF32 or F64) in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) SetT added in v0.2.0

func (ob *OperateBuilder) SetT(path Path, typ uint8, v int64) *OperateBuilder

SetT writes v to the int/float-domain scalar at path, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) Shl added in v0.2.0

func (ob *OperateBuilder) Shl(path Path, n int64) *OperateBuilder

Shl shifts the fixed-width int scalar at path left by n (0..64, masked to the field width), typed from the schema.

func (*OperateBuilder) Shr added in v0.2.0

func (ob *OperateBuilder) Shr(path Path, n int64) *OperateBuilder

Shr shifts the fixed-width int scalar at path right by n (0..64; arithmetic for signed fields, logical for unsigned), typed from the schema.

func (*OperateBuilder) Stamp added in v0.2.0

func (ob *OperateBuilder) Stamp(path Path, unit uint8) *OperateBuilder

Stamp writes tx.applyStamp() (in unit's resolution — wire.OperateStampMs or wire.OperateStampS) to the int scalar at path, typed from the schema.

func (*OperateBuilder) StampT added in v0.2.0

func (ob *OperateBuilder) StampT(path Path, typ uint8, unit uint8) *OperateBuilder

StampT writes tx.applyStamp() (in unit's resolution) to the scalar at path, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

func (*OperateBuilder) TTL added in v0.2.0

func (ob *OperateBuilder) TTL(d time.Duration, mode uint8) *OperateBuilder

TTL sets the call's TTL and ttlMode (design doc §3.5: KEEP, SET, or CREATE_ONLY).

func (*OperateBuilder) Trim added in v0.2.0

func (ob *OperateBuilder) Trim(path Path, keep uint32, policy uint8, byCol string) *OperateBuilder

Trim shrinks the table field at path to at most keep rows by policy (and, for a *_COL policy, byCol), without changing the table's stored eviction config (design doc §3.2). Unlike Config it is valid in BOTH modes: it is a one-off shrink, not a change to the table's configuration, so it does not collide with a schema-mode table's schema-declared eviction triple. byCol may be "" when policy does not need one.

func (*OperateBuilder) WithSchema added in v0.2.0

func (ob *OperateBuilder) WithSchema(s *wire.Schema) *OperateBuilder

WithSchema attaches s to the builder: the call creates an absent record in schema mode with s's encoded blob (design doc §2.8), and every subsequent Field/Col name is resolved against s. s is validated once, here; a nil s or a validation failure is recorded and returned by Args rather than panicking.

It is exclusive with Dynamic: the two set the same `create` byte to opposite values, and a builder that has been through both would ship a dynamic call carrying a schema blob and schema-POSITION paths. The wire decoder accepts that frame, so nothing catches it until the dynamic engine rejects every path with wire.ErrOperatePath — calling the second one is recorded as an error here instead.

func (*OperateBuilder) Xor added in v0.2.0

func (ob *OperateBuilder) Xor(path Path, v int64) *OperateBuilder

Xor bitwise-XORs the fixed-width int scalar at path with v, typed from the schema.

func (*OperateBuilder) XorT added in v0.2.0

func (ob *OperateBuilder) XorT(path Path, typ uint8, v int64) *OperateBuilder

XorT bitwise-XORs the scalar at path with v, creating it with type typ in dynamic mode (or checking typ against the schema's type in schema mode).

type OperateRequest added in v0.2.0

type OperateRequest struct {
	ID         uint64
	PayloadKey string
	Args       *wire.OperateArgs
	// ExpectedVersion enables optimistic concurrency; HasExpectedVersion must be
	// true for it to apply.
	ExpectedVersion    uint64
	HasExpectedVersion bool
}

OperateRequest is the payload for Collection.Operate.

type Path added in v0.2.0

type Path struct {
	Field  string
	Col    string
	Key    []byte
	HasKey bool
	HasCol bool
}

Path addresses one node inside an operate record (design doc §2.4): the record itself (RecordPath), a record field (F), a whole row of a table field (Row), or one column of that row (Col). Field and Col are always names here; OperateBuilder.Args resolves them to schema positions when a schema is attached (WithSchema) and otherwise carries them as name segments on the wire (Dynamic).

func Col added in v0.2.0

func Col(field string, key []byte, col string) Path

Col addresses the column named col of the row keyed by key in the table field named field.

func F added in v0.2.0

func F(field string) Path

F addresses the record field named field.

func RecordPath added in v0.2.0

func RecordPath() Path

RecordPath addresses the record itself — the only valid path for CHECK EXISTS/ABSENT, DEL, and a whole-record return (design doc §2.4).

func Row added in v0.2.0

func Row(field string, key []byte) Path

Row addresses the row keyed by key in the table field named field.

type Point

type Point struct {
	ID       uint64
	Vector   []float32
	Metadata vtypes.Metadata
	TTLMs    uint64
	Version  uint64
}

Point is a stored point returned by reads.

type PointInput

type PointInput struct {
	ID       uint64
	Vector   []float32
	Content  string
	Metadata vtypes.Metadata
	Sparse   vtypes.SparseVector
	TTL      time.Duration
}

PointInput is one point in an UpsertBatch.

type QueryRequest

type QueryRequest struct {
	Spec        vtypes.QuerySpec
	Consistency Consistency
}

QueryRequest is the power-user escape hatch: run any QuerySpec (multi-leaf fusion, rerank, discover) directly.

type RecommendRequest

type RecommendRequest struct {
	Positive    []uint64
	Negative    []uint64
	Strategy    vtypes.RecommendStrategy // zero = RecommendAverageVector
	K           int
	Filter      vtypes.Filter
	Consistency Consistency
}

RecommendRequest recommends points similar to a set of positive examples and dissimilar to a set of negative examples, identified by point id. The engine resolves the example ids to their stored vectors and excludes them from the results.

type RemoteError

type RemoteError struct {
	Op  string
	Msg string
}

RemoteError represents a StatusError response from the server.

func (*RemoteError) Error

func (e *RemoteError) Error() string

type ScrollRequest

type ScrollRequest struct {
	Filter vtypes.Filter
	Limit  int
	Cursor string
}

ScrollRequest pages through a collection's points in id order (or resuming from a prior page's Cursor), applying an optional filter.

type ScrollResponse

type ScrollResponse struct {
	Documents  []Document
	NextCursor string
}

ScrollResponse is one page of Scroll results. NextCursor is empty when there is no further page.

type SearchDocsRequest

type SearchDocsRequest struct {
	Query       []float32
	K           int
	Filter      vtypes.Filter
	Consistency Consistency
}

SearchDocsRequest is a dense-vector KNN search that returns content-bearing Documents instead of bare Results.

type SearchRequest

type SearchRequest struct {
	Query       []float32
	K           int
	Filter      vtypes.Filter
	Consistency Consistency
}

SearchRequest is a plain dense-vector KNN search.

type SearchResponse

type SearchResponse struct {
	Results  []vtypes.Result
	Degraded bool
	Missing  []uint16
}

SearchResponse carries ranked results plus explicit degraded-partition info.

type Stat

type Stat struct {
	// AcquireCount is the number of successful Acquires.
	AcquireCount int64
	// Total active connections (in use + idle).
	TotalConns int32
	IdleConns  int32
	// Counters for connections destroyed by lifecycle policies.
	NewConnsCount        int64
	LifetimeDestroyCount int64
	IdleDestroyCount     int64
}

Stat is a per-server pool snapshot suitable for logging or metrics.

type WriteRequest

type WriteRequest struct {
	ID       uint64
	Vector   []float32
	Content  string              // raw text tokenized into the BM25 lane
	Metadata vtypes.Metadata     // build with vtypes.NewString/NewInt/...
	Sparse   vtypes.SparseVector // optional client sparse vector
	TTL      time.Duration       // 0 = no expiry
	// ExpectedVersion enables optimistic concurrency; HasExpectedVersion must be
	// true for it to apply.
	ExpectedVersion    uint64
	HasExpectedVersion bool
	KeyTTLMs           map[string]int64
}

WriteRequest is the payload for Insert and Upsert.

Jump to

Keyboard shortcuts

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