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
- Variables
- func ClassifyKVQueryMessage(msg string) error
- func DecodeOperateValue(b []byte) (wire.Cell, error)
- func IsAmbiguous(err error) bool
- func KeyU8(k uint8) []byte
- func KeyU16(k uint16) []byte
- func KeyU32(k uint32) []byte
- func KeyU64(k uint64) []byte
- type BatchError
- type Client
- func (c *Client) CAS(ctx context.Context, key, value, expected []byte, ttl time.Duration) (bool, error)
- func (c *Client) Call(ctx context.Context, op string, args []byte) ([]byte, error)
- func (c *Client) CallFunc(ctx context.Context, op string, args []byte, fn func(payload []byte) error) error
- func (c *Client) Close() error
- func (c *Client) Collection(name string) *Collection
- func (c *Client) CompareAndDelete(ctx context.Context, key, expected []byte) (bool, error)
- func (c *Client) CompareAndExpire(ctx context.Context, key, expected []byte, ttl time.Duration) (bool, error)
- func (c *Client) CreateKVIndex(ctx context.Context, d wire.KVIndexDef) error
- func (c *Client) DropKVIndex(ctx context.Context, name string) error
- func (c *Client) Exists(ctx context.Context, key []byte) (bool, error)
- func (c *Client) Flush(ctx context.Context) error
- func (c *Client) GetDel(ctx context.Context, key []byte) (value []byte, found bool, err error)
- func (c *Client) GetSet(ctx context.Context, key, value []byte, ttl time.Duration) (old []byte, found bool, err error)
- func (c *Client) IncrEx(ctx context.Context, key []byte, delta int64, ttl time.Duration) (int64, error)
- func (c *Client) KVQuery(ctx context.Context, a wire.KVQueryArgs) (*KVQueryPage, error)
- func (c *Client) ListKVIndexes(ctx context.Context) ([]wire.KVIndexDef, []bool, error)
- func (c *Client) MGet(ctx context.Context, keys [][]byte) ([][]byte, error)
- func (c *Client) Operate(ctx context.Context, a *wire.OperateArgs) (*wire.OperateResult, error)
- func (c *Client) Persist(ctx context.Context, key []byte) (bool, error)
- func (c *Client) PutBatch(ctx context.Context, entries []wire.PutEntry) error
- func (c *Client) RegisterWASM(ctx context.Context, r wire.WASMRegistration, module []byte) (pushReport string, err error)
- func (c *Client) Reset()
- func (c *Client) SetNX(ctx context.Context, key, value []byte, ttl time.Duration) (bool, error)
- func (c *Client) Stat() map[string]Stat
- func (c *Client) TTL(ctx context.Context, key []byte) (int64, error)
- func (c *Client) Topology() *wire.Topology
- type Collection
- func (col *Collection) Create(ctx context.Context, req CreateRequest) error
- func (col *Collection) Delete(ctx context.Context, req DeleteRequest) error
- func (col *Collection) Drop(ctx context.Context) error
- func (col *Collection) Get(ctx context.Context, req GetRequest) (Point, error)
- func (col *Collection) GetBatch(ctx context.Context, req GetBatchRequest) (GetBatchResponse, error)
- func (col *Collection) HybridSearch(ctx context.Context, req HybridSearchRequest) (SearchResponse, error)
- func (col *Collection) HybridText(ctx context.Context, req HybridTextRequest) (SearchResponse, error)
- func (col *Collection) Insert(ctx context.Context, req WriteRequest) error
- func (col *Collection) Operate(ctx context.Context, req OperateRequest) (found bool, res *wire.OperateResult, version uint64, err error)
- func (col *Collection) Query(ctx context.Context, req QueryRequest) (SearchResponse, error)
- func (col *Collection) Recommend(ctx context.Context, req RecommendRequest) (SearchResponse, error)
- func (col *Collection) Scroll(ctx context.Context, req ScrollRequest) (ScrollResponse, error)
- func (col *Collection) Search(ctx context.Context, req SearchRequest) (SearchResponse, error)
- func (col *Collection) SearchDocs(ctx context.Context, req SearchDocsRequest) (DocsResponse, error)
- func (col *Collection) SearchGroups(ctx context.Context, req GroupSearchRequest) (GroupResponse, error)
- func (col *Collection) Upsert(ctx context.Context, req WriteRequest) error
- func (col *Collection) UpsertBatch(ctx context.Context, pts []PointInput) []BatchError
- type Config
- type Conn
- type Consistency
- type CreateRequest
- type DeleteRequest
- type DocsResponse
- type Document
- type GetBatchRequest
- type GetBatchResponse
- type GetRequest
- type Group
- type GroupResponse
- type GroupSearchRequest
- type HybridSearchRequest
- type HybridTextRequest
- type KVQueryPage
- type OperateBuilder
- func (ob *OperateBuilder) Add(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) AddFloat(path Path, v float64) *OperateBuilder
- func (ob *OperateBuilder) AddFloatT(path Path, typ uint8, v float64) *OperateBuilder
- func (ob *OperateBuilder) AddT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) And(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) AndT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Args() (*wire.OperateArgs, error)
- func (ob *OperateBuilder) Check(path Path, cmp uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) CheckBytes(path Path, cmp uint8, v []byte) *OperateBuilder
- func (ob *OperateBuilder) Config(path Path, capacity uint32, policy uint8, byCol string) *OperateBuilder
- func (ob *OperateBuilder) Count(path Path) *OperateBuilder
- func (ob *OperateBuilder) Del(path Path) *OperateBuilder
- func (ob *OperateBuilder) Dynamic() *OperateBuilder
- func (ob *OperateBuilder) If(path Path, cmp uint8, v int64, n int) *OperateBuilder
- func (ob *OperateBuilder) IfBytes(path Path, cmp uint8, v []byte, n int) *OperateBuilder
- func (ob *OperateBuilder) Max(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) MaxFloat(path Path, v float64) *OperateBuilder
- func (ob *OperateBuilder) MaxFloatT(path Path, typ uint8, v float64) *OperateBuilder
- func (ob *OperateBuilder) MaxT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Migrate(from int64, s *wire.Schema, dropExtra bool) *OperateBuilder
- func (ob *OperateBuilder) Min(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) MinFloat(path Path, v float64) *OperateBuilder
- func (ob *OperateBuilder) MinFloatT(path Path, typ uint8, v float64) *OperateBuilder
- func (ob *OperateBuilder) MinT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Mul(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) MulFloat(path Path, v float64) *OperateBuilder
- func (ob *OperateBuilder) MulFloatT(path Path, typ uint8, v float64) *OperateBuilder
- func (ob *OperateBuilder) MulT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Or(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) OrT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Return(path Path) *OperateBuilder
- func (ob *OperateBuilder) Set(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) SetBytes(path Path, v []byte) *OperateBuilder
- func (ob *OperateBuilder) SetBytesT(path Path, typ uint8, v []byte) *OperateBuilder
- func (ob *OperateBuilder) SetFloat(path Path, v float64) *OperateBuilder
- func (ob *OperateBuilder) SetFloatT(path Path, typ uint8, v float64) *OperateBuilder
- func (ob *OperateBuilder) SetT(path Path, typ uint8, v int64) *OperateBuilder
- func (ob *OperateBuilder) Shl(path Path, n int64) *OperateBuilder
- func (ob *OperateBuilder) Shr(path Path, n int64) *OperateBuilder
- func (ob *OperateBuilder) Stamp(path Path, unit uint8) *OperateBuilder
- func (ob *OperateBuilder) StampT(path Path, typ uint8, unit uint8) *OperateBuilder
- func (ob *OperateBuilder) TTL(d time.Duration, mode uint8) *OperateBuilder
- func (ob *OperateBuilder) Trim(path Path, keep uint32, policy uint8, byCol string) *OperateBuilder
- func (ob *OperateBuilder) WithSchema(s *wire.Schema) *OperateBuilder
- func (ob *OperateBuilder) Xor(path Path, v int64) *OperateBuilder
- func (ob *OperateBuilder) XorT(path Path, typ uint8, v int64) *OperateBuilder
- type OperateRequest
- type Path
- type Point
- type PointInput
- type QueryRequest
- type RecommendRequest
- type RemoteError
- type ScrollRequest
- type ScrollResponse
- type SearchDocsRequest
- type SearchRequest
- type SearchResponse
- type Stat
- type WriteRequest
Constants ¶
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.
const ( StatusOK uint8 = 0 StatusNotFound uint8 = 1 StatusNotLeader uint8 = 2 StatusError uint8 = 3 )
Status codes carried in the response frame.
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.
const MaxFrameSize = 16 << 20
MaxFrameSize bounds each individual frame.
Variables ¶
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") // supplied auth token was missing or invalid. ErrUnauthorized = errors.New("client: unauthorized") )
Errors surfaced to callers.
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") // 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.
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") )
var ErrFrameTooLarge = errors.New("client: frame too large")
ErrFrameTooLarge indicates a frame exceeds MaxFrameSize.
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
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
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
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
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
KeyU16 encodes k as the little-endian 2-byte row key a schema-mode table with a U16 key type expects.
Types ¶
type BatchError ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
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
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
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
Exists reports whether key is currently present (and live). An expired key reads as absent.
func (*Client) Flush ¶ added in v0.2.0
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
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
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
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
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 ¶
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
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.
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.
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).
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 ¶
DeleteRequest removes a point, optionally guarded by ExpectedVersion.
type DocsResponse ¶
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 ¶
GetBatchRequest selects multiple points by id, sharing one projection.
type GetBatchResponse ¶
GetBatchResponse reports the points that were found plus the ids from the request that were not.
type GetRequest ¶
GetRequest selects a single point by id and its projection.
type Group ¶
Group is one group-by-document result: the grouping key plus its best hits, best-first.
type GroupResponse ¶
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
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
Col addresses the column named col of the row keyed by key in the table 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).
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 ¶
RemoteError represents a StatusError response from the server.
func (*RemoteError) Error ¶
func (e *RemoteError) Error() string
type ScrollRequest ¶
ScrollRequest pages through a collection's points in id order (or resuming from a prior page's Cursor), applying an optional filter.
type ScrollResponse ¶
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 ¶
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.