shard

package
v0.0.6 Latest Latest
Warning

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

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

Documentation

Overview

Package shard routes a tenant's source-of-truth operations to the Postgres shard that holds it. A tenant lives entirely on one shard (ADR 0007), so routing is a directory lookup, never a distributed transaction or a scatter-gather read.

The router is engine-neutral: it speaks only the fabriq capability ports (command.Store, query.RelationalQuerier/VectorQuerier/TSQuerier/SpatialQuerier) and implements those same ports by delegating to the resolved shard. The facade, executor and every call site are therefore unchanged — sharding is one more adapter behind a port. Single-Postgres deployments use the degenerate one-shard Set (Single), for which routing is a no-op.

Index

Constants

This section is empty.

Variables

View Source
var (
	DirGrow   = scaleGrow
	DirShrink = scaleShrink
)

DirGrow / DirShrink expose scale directions to other packages (wiring, tests). The underlying type stays unexported; only the values escape.

Functions

This section is empty.

Types

type Analytics added in v0.0.6

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

Analytics routes query.AnalyticsQuerier (per-tenant customer-facing analytics: event ingest, cube query, raw SQL escape hatch).

func NewAnalytics added in v0.0.6

func NewAnalytics(set Router) *Analytics

NewAnalytics wraps a Set as a routing query.AnalyticsQuerier.

func (*Analytics) Query added in v0.0.6

func (a *Analytics) Query(ctx context.Context, q query.AnalyticsQuery, into any) error

Query routes a cube aggregation to the tenant's shard.

func (*Analytics) QueryRaw added in v0.0.6

func (a *Analytics) QueryRaw(ctx context.Context, into any, sql string, args ...any) error

QueryRaw routes the raw-SQL escape hatch to the tenant's shard.

func (*Analytics) Track added in v0.0.6

func (a *Analytics) Track(ctx context.Context, events []query.AnalyticsEvent) error

Track routes a customer-analytics event batch to the tenant's shard.

func (*Analytics) UpsertInsightFacts added in v0.0.6

func (a *Analytics) UpsertInsightFacts(ctx context.Context, facts []insights.Fact) error

UpsertInsightFacts implements insights.FactSink by routing a proj:insights fact batch to the ctx tenant's shard: Acquire resolves the shard from the tenant already stamped on ctx (the consumer's handle stamps tenant+scope before calling the sink), so this lands on the same shard — and through the same inTenantTx/RLS containment — as every other per-tenant analytics call the Track/Query/QueryRaw methods above make. sh.Analytics is typed query.AnalyticsQuerier on the Shard struct, but the concrete adapter (*postgres.InsightsAdapter) also implements insights.FactSink; the type assertion recovers that second facet.

type AutoscaleConfig added in v0.0.6

type AutoscaleConfig struct {
	Min, Max      int           // hard floor / ceiling
	Interval      time.Duration // controller tick (default 5s)
	ConnBudget    int           // total server-side conns; 0 = no budget clamp
	PerShardConns int           // conns per shard pool (default 4)
	HeapSoftLimit uint64        // heap-in-use bytes above which growth freezes; 0 = off

	GrowFactor    float64 // default 1.5
	ShrinkStep    int     // default 1
	ConfirmTicks  int     // consecutive same-direction ticks before acting (default 3)
	CooldownTicks int     // ticks suppressed after a change (default 3)

	MissRatioHigh  float64 // grow above this (default 0.20)
	EvictRateHigh  float64 // evictions/acquires above this (default 0.10)
	OpenFillLow    float64 // open/cap below this = oversized (default 0.75)
	HeldUtilLow    float64 // held/open below this = idle (default 0.50)
	HeapShrinkMult float64 // heap > softLimit*this ⇒ force shrink (default 1.10)
}

AutoscaleConfig fully parameterizes the pool autoscaler. The user-facing config (fabriq.AdaptivePoolConfig) sets the load-bearing few; the policy knobs default in withDefaults so tests and callers can omit them.

type CatalogDirOption added in v0.0.6

type CatalogDirOption func(*catalogDirectory)

CatalogDirOption configures a CatalogDirectory.

func WithCatalogClock added in v0.0.6

func WithCatalogClock(now func() time.Time) CatalogDirOption

WithCatalogClock injects the clock (TTL tests).

func WithMinVersion added in v0.0.6

func WithMinVersion(v string) CatalogDirOption

WithMinVersion fails routing CLOSED for tenants whose database is recorded below the binary's migration floor (spec D7): serving a schema older than the code expects corrupts, so the tenant gets a typed CodeUnavailable until the fleet roller catches it up.

type Dialer added in v0.0.6

type Dialer func(ctx context.Context, shardID string) (Shard, func() error, error)

Dialer opens the adapter stack for one tenant database. close tears the stack down when the pool manager evicts the shard.

type Directory

type Directory interface {
	Shard(ctx context.Context, tenantID string) (string, error)
}

Directory resolves a tenant to its shard id. Implementations range from a constant (one shard) to a cached, versioned table (many shards) — the router does not care which.

func Cached

func Cached(inner Directory, ttl time.Duration) Directory

Cached memoizes a directory's tenant→shard answers for ttl, so the hot path does not pay a lookup per operation. Placement is stable (a tenant's shard does not change without an explicit move), so a short TTL is purely a freshness bound for future catalog-backed directories; the hash directory is constant and caching it is free insurance.

func CatalogDirectory added in v0.0.6

func CatalogDirectory(cat catalog.Catalog, ttl time.Duration, opts ...CatalogDirOption) Directory

CatalogDirectory routes tenants by the db-per-tenant control plane (spec 2026-07-03, D1/D2): each ACTIVE catalog entry resolves to its own shard, addressed "{clusterId}/{database}". Non-active states resolve to typed errors the gateway maps to honest statuses instead of 500s:

unknown tenant            → CodeNotFound
pending/creating/migrating → CodeUnavailable (provisioning)
suspended / failed        → CodeUnavailable

Both positive and negative answers are cached for ttl. Placement is stable, so the TTL is a freshness bound: a suspension or a just-provisioned tenant takes effect within one TTL — and repeated lookups of unknown tenants cannot storm the control database.

func CatalogDirectoryWithClock added in v0.0.6

func CatalogDirectoryWithClock(cat catalog.Catalog, ttl time.Duration, now func() time.Time) Directory

CatalogDirectoryWithClock is retained for tests predating the option form.

func HashDirectory

func HashDirectory(ids ...string) Directory

HashDirectory places tenants across a fixed set of shards by hashing the tenant id — stateless and deterministic, so every replica routes a tenant the same way without coordination. It is the simplest directory: it needs no catalog table, but changing the shard set re-places tenants (and so requires a data move). A catalog-backed directory with explicit placement (ADR 0007) is the drop-in upgrade when tenants must be moved individually.

func PinnedDirectory added in v0.0.6

func PinnedDirectory(pins map[string]string, fallback Directory) Directory

PinnedDirectory routes explicitly pinned tenants to their pinned shard and delegates every other tenant to fallback. It is the config-driven override for residency / high-value tenants that must live on a specific shard without replacing the whole directory: an exact tenant-id match wins, the hash placement stays in charge of the rest. Pins are copied, so later caller mutations do not change routing. PinnedDirectory does not know the shard set — a pin naming an unknown shard is rejected by Config.Validate at Open time, and Set.ForTenant fails loudly for any directory that names a shard the set does not hold.

type Documents added in v0.0.6

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

Documents routes document.Store by the ctx tenant — the catalog-mode document plane, where each tenant's CRDT tables live in its own database (lifting ADR 0007 step 2's primary-only restriction).

func NewDocuments added in v0.0.6

func NewDocuments(set Router) *Documents

NewDocuments builds the document-plane router.

func (*Documents) ApplyUpdate added in v0.0.6

func (d *Documents) ApplyUpdate(ctx context.Context, docID string, update []byte) error

ApplyUpdate implements document.Store.

func (*Documents) ApplyUpdateWithSeq added in v0.0.6

func (d *Documents) ApplyUpdateWithSeq(ctx context.Context, docID string, update []byte) (int64, error)

ApplyUpdateWithSeq routes the seq-returning apply (the live fan-out decorator needs the log seq for gap detection).

func (*Documents) Compact added in v0.0.6

func (d *Documents) Compact(ctx context.Context, docID string) error

Compact implements document.Store.

func (*Documents) Snapshot added in v0.0.6

func (d *Documents) Snapshot(ctx context.Context, docID string) (document.Materialized, error)

Snapshot implements document.Store.

func (*Documents) Sync added in v0.0.6

func (d *Documents) Sync(ctx context.Context, docID string, stateVector []byte) ([]byte, error)

Sync implements document.Store.

type DynamicSet added in v0.0.6

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

DynamicSet is the catalog-mode Router: the directory resolves the ctx tenant to its dedicated shard id and the provider keeps that shard's pools alive.

func NewDynamicSet added in v0.0.6

func NewDynamicSet(dir Directory, provider Provider) *DynamicSet

NewDynamicSet builds the catalog-mode router.

func (*DynamicSet) Acquire added in v0.0.6

func (d *DynamicSet) Acquire(ctx context.Context) (Shard, context.Context, func(), error)

Acquire implements Router (ctx unchanged — schema stamping, if any, is the SchemaRouter decorator's job).

func (*DynamicSet) AcquireFor added in v0.0.6

func (d *DynamicSet) AcquireFor(ctx context.Context, tenantID string) (Shard, context.Context, func(), error)

AcquireFor implements Router (ctx unchanged).

type Live added in v0.0.6

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

Live is the catalog-mode live-query router: it resolves the ctx tenant to its shard and serves snapshots/refills from that shard's own database. Mirrors the Documents/Store/Relational routers.

func NewLive added in v0.0.6

func NewLive(set Router) *Live

NewLive builds the live-query router over a Router.

func (*Live) After added in v0.0.6

func (l *Live) After(ctx context.Context, q livequery.LiveQuery, after livequery.Cursor, limit int) ([]livequery.Row, error)

func (*Live) Members added in v0.0.6

func (l *Live) Members(ctx context.Context, q livequery.LiveQuery) ([]string, error)

func (*Live) Snapshot added in v0.0.6

func (l *Live) Snapshot(ctx context.Context, q livequery.LiveQuery, limit int) ([]livequery.Row, error)

type LiveSource added in v0.0.6

type LiveSource interface {
	Snapshot(ctx context.Context, q livequery.LiveQuery, limit int) ([]livequery.Row, error)
	After(ctx context.Context, q livequery.LiveQuery, after livequery.Cursor, limit int) ([]livequery.Row, error)
	Members(ctx context.Context, q livequery.LiveQuery) ([]string, error)
}

LiveSource is the shard's live-query snapshot/refill oracle (Postgres is the exact-top-N truth). Static single-shard deployments serve it from the primary; catalog mode fills Shard.Live per tenant database.

type PoolManager added in v0.0.6

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

PoolManager implements Provider: lazy dial on first touch (singleflight), refcounted release, LRU idle-first eviction at the cap, and a per-shard dial breaker so a down database cannot be dial-stormed.

func NewPoolManager added in v0.0.6

func NewPoolManager(dial Dialer, cfg PoolManagerConfig) *PoolManager

NewPoolManager builds a PoolManager over the dialer.

func (*PoolManager) Acquire added in v0.0.6

func (p *PoolManager) Acquire(ctx context.Context, shardID string) (Shard, func(), error)

Acquire implements Provider.

func (*PoolManager) Cap added in v0.0.6

func (p *PoolManager) Cap() int

Cap reports the current effective MaxActive (static or adaptive).

func (*PoolManager) CloseAll added in v0.0.6

func (p *PoolManager) CloseAll() error

CloseAll tears down every open shard (process shutdown). Held shards are closed too — pgx pool Close blocks until in-flight connections return.

func (*PoolManager) Stats added in v0.0.6

func (p *PoolManager) Stats() (open, held int)

Stats reports live pool-manager counters (observability).

type PoolManagerConfig added in v0.0.6

type PoolManagerConfig struct {
	// MaxActive caps concurrently-open shards (LRU-evicted, idle first).
	// Zero falls back to 128.
	MaxActive int
	// AcquireTimeout bounds how long Acquire waits for capacity when every
	// open shard is busy. Zero falls back to 5s.
	AcquireTimeout time.Duration
	// DialBackoff is the base negative-cache window after a failed dial
	// (doubled per consecutive failure, capped at 32x). Zero = 2s.
	DialBackoff time.Duration

	// Adaptive, when non-nil, enables autoscaling of MaxActive. The static
	// MaxActive is the default (nil).
	Adaptive *AutoscaleConfig
	// OnScale is called after each cap change (best-effort; must not block).
	OnScale func(ScaleEvent)
	// contains filtered or unexported fields
}

PoolManagerConfig tunes the dynamic pool lifecycle.

type ProjectionStateStore added in v0.0.6

type ProjectionStateStore interface {
	projection.StateRepo
	// SetApplied records the applied version (projection.AppliedRecorder).
	SetApplied(ctx context.Context, tenantID, proj, aggregate, aggID string, version int64) error
	// Tenants lists every tenant this shard has bookkeeping for.
	Tenants(ctx context.Context) ([]string, error)
}

ProjectionStateStore is the per-shard projection bookkeeping the engines, rebuilder and WaitForProjection route to (satisfied by the Postgres adapter's StateRepo).

type Provider added in v0.0.6

type Provider interface {
	Acquire(ctx context.Context, shardID string) (Shard, func(), error)
}

Provider owns shard lifecycles in catalog mode: Acquire returns a live shard for the id (dialing it on first touch), and the release func MUST be called when the operation completes so eviction can make progress.

type Relational

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

Relational routes query.RelationalQuerier (reads of the source of truth).

func NewRelational

func NewRelational(set Router) *Relational

NewRelational wraps a Set as a routing query.RelationalQuerier.

func (*Relational) Get

func (r *Relational) Get(ctx context.Context, entity, id string, into any) error

Get routes a single-row read.

func (*Relational) GetMany

func (r *Relational) GetMany(ctx context.Context, entity string, ids []string, into any) error

GetMany routes a batched read (the hydration primitive).

func (*Relational) List

func (r *Relational) List(ctx context.Context, entity string, q query.ListQuery, into any) error

List routes a structured query.

func (*Relational) Query

func (r *Relational) Query(ctx context.Context, into any, sql string, args ...any) error

Query routes the raw-SQL escape hatch.

type ReplaySource added in v0.0.6

type ReplaySource interface {
	SnapshotEntities(ctx context.Context, tenantID string, fn func(env event.Envelope) error) error
	AggregateVersions(ctx context.Context, tenantID, entity string) (map[string]int64, error)
	Repair(ctx context.Context, tenantID string, d projection.Drift) error
}

ReplaySource is the shard's event-truth surface for blue-green rebuilds and the drift reconciler: snapshot the tenant's aggregates as events, read their current versions, and republish a repair event onto the tenant's outbox. Satisfied by the Postgres adapter; static deployments leave Shard.Replay nil (the worker plane routes via its concrete adapter map), catalog mode fills it per tenant database.

type Router added in v0.0.6

type Router interface {
	Acquire(ctx context.Context) (Shard, context.Context, func(), error)
	AcquireFor(ctx context.Context, tenantID string) (Shard, context.Context, func(), error)
}

Router resolves the ctx tenant to a live shard. Acquire pairs the shard with the routing context to use downstream and a release func: the static Set releases nothing and returns ctx unchanged (its pools live for the process), while catalog-mode DynamicSet refcounts pooled shards so the pool manager never closes a shard mid-operation. The returned context is the seam through which schema-per-tenant mode stamps search_path (SchemaRouter); every other Router returns the input ctx verbatim.

type ScaleEvent added in v0.0.6

type ScaleEvent struct {
	OldCap, NewCap int
	Direction      scaleDir
	Reason         string
	Signals        poolSignals
}

ScaleEvent is emitted after each cap change (OnScale hook).

type SchemaRouter added in v0.0.6

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

SchemaRouter wraps a Router and stamps the tenant's consolidation-mode schema (pathctx) onto the routing context, so the adapter's per-tx stamp sets search_path to the tenant's schema within the shared consolidation database. It is wired only in schema isolation (Config.Catalog.Isolation == "schema"); the shard it wraps (a DynamicSet over a consolidation database) is shared by many tenants, and this decorator is the one seam that distinguishes them per request.

The schema is derived deterministically from the tenant id (pathctx.SchemaForTenant), matching what the provisioner persisted in Entry.Schema — so routing needs no extra catalog lookup on the hot path.

func NewSchemaRouter added in v0.0.6

func NewSchemaRouter(inner Router) *SchemaRouter

NewSchemaRouter builds the schema-stamping decorator over inner.

func (*SchemaRouter) Acquire added in v0.0.6

func (r *SchemaRouter) Acquire(ctx context.Context) (Shard, context.Context, func(), error)

Acquire implements Router: resolve the ctx tenant, then stamp its schema.

func (*SchemaRouter) AcquireFor added in v0.0.6

func (r *SchemaRouter) AcquireFor(ctx context.Context, tenantID string) (Shard, context.Context, func(), error)

AcquireFor implements Router: acquire the consolidation shard through the inner router, then return a context carrying the tenant's search_path.

type Set

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

Set is the tenant -> shard routing table: a directory plus the shards it can name.

func New

func New(dir Directory, shards ...Shard) (*Set, error)

New builds a routing Set from a directory and its shards. Every shard needs a non-empty, unique id; the set needs at least one.

func Single

func Single(sh Shard) *Set

Single builds the degenerate one-shard Set: every tenant routes to sh. This is what single-Postgres deployments use, so the routing layer is a no-op until a second shard is configured. An empty sh.ID defaults to "0".

func (*Set) Acquire added in v0.0.6

func (s *Set) Acquire(ctx context.Context) (Shard, context.Context, func(), error)

Acquire implements Router on the static Set (no-op release, ctx unchanged).

func (*Set) AcquireFor added in v0.0.6

func (s *Set) AcquireFor(ctx context.Context, tenantID string) (Shard, context.Context, func(), error)

AcquireFor implements Router on the static Set (no-op release, ctx unchanged).

func (*Set) All

func (s *Set) All() []Shard

All returns the shards, ordered by id — for the worker plane to start a per-shard relay / reconciler against each.

func (*Set) For

func (s *Set) For(ctx context.Context) (Shard, error)

For resolves the shard owning the ctx tenant. It requires a tenant (the same precondition every source-of-truth port already enforces) and fails loudly if the directory names a shard the set does not hold.

func (*Set) ForTenant

func (s *Set) ForTenant(ctx context.Context, tenantID string) (Shard, error)

ForTenant resolves the shard owning an explicit tenant id — the seam the worker plane uses to route by the tenant carried in a method argument (projection bookkeeping, snapshot, reconcile) rather than on ctx.

func (*Set) IDs

func (s *Set) IDs() []string

IDs returns the shard ids, sorted — for building per-shard worker runners.

func (*Set) Len

func (s *Set) Len() int

Len reports the number of shards.

func (*Set) ResolveID

func (s *Set) ResolveID(ctx context.Context, tenantID string) (string, error)

ResolveID returns just the shard id a tenant routes to — for the worker plane to pick the matching concrete adapter (relay, reconcile, snapshot) from its own per-shard map.

type Shard

type Shard struct {
	ID         string
	Store      command.Store
	Relational query.RelationalQuerier
	Vector     query.VectorQuerier
	Timeseries query.TSQuerier
	Spatial    query.SpatialQuerier
	// Analytics is the shard's per-tenant customer-facing analytics port
	// (Task 4+). Static deployments and catalog mode both fill it with the
	// postgres insights adapter; nil until Config.Insights.Enabled routes it
	// through the shard set (f.Analytics() degrades to
	// notConfiguredAnalytics{} until then).
	Analytics query.AnalyticsQuerier
	// Documents is the shard's CRDT document plane. Static deployments
	// leave it nil (the primary's doc store serves, ADR 0007 step 2);
	// catalog mode fills it per tenant database.
	Documents document.Store
	// Maintenance is the shard's single-pass worker surface for the
	// catalog-mode sweeper. Static deployments leave it nil (the worker
	// plane runs its own boot-time loops).
	Maintenance sweep.Maintainer
	// Projection is the shard's projection bookkeeping surface
	// (projection_applied/_state stay co-located with the aggregates they
	// track). Static deployments leave it nil (the worker plane holds
	// concrete adapters); catalog mode fills it per tenant database.
	Projection ProjectionStateStore
	// Replay is the shard's event-truth surface for rebuilds/reconcile.
	// Static deployments leave it nil; catalog mode fills it per tenant DB.
	Replay ReplaySource
	// Live is the shard's live-query snapshot/refill oracle. Static
	// deployments leave it nil (the primary's LiveStore serves, wired
	// directly into Ports.Live); catalog mode fills it per tenant DB.
	Live LiveSource
}

Shard is one tenant-home: the source-of-truth ports for the tenants that live on it. In production a single Postgres adapter satisfies all five; tests supply stubs.

type Spatial

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

Spatial routes query.SpatialQuerier (geometry upsert/search/delete).

func NewSpatial

func NewSpatial(set Router) *Spatial

NewSpatial wraps a Set as a routing query.SpatialQuerier.

func (*Spatial) Covering added in v0.0.6

func (s *Spatial) Covering(ctx context.Context, q query.CoverQuery, into any) error

Covering routes a containment search to the tenant's shard, provided that shard's spatial adapter implements query.SpatialCoverer. Returns an error when the underlying adapter is radius-only.

func (*Spatial) Delete

func (s *Spatial) Delete(ctx context.Context, entity, id string) error

Delete routes a geometry delete to the tenant's shard.

func (*Spatial) Get added in v0.0.5

func (s *Spatial) Get(ctx context.Context, entity, id string) (geom query.Geometry, meta map[string]any, ok bool, err error)

Get routes a single-geometry read to the tenant's shard.

func (*Spatial) Upsert

func (s *Spatial) Upsert(ctx context.Context, entity, id string, geom query.Geometry, meta map[string]any) error

Upsert routes a geometry write to the tenant's shard.

func (*Spatial) Within

func (s *Spatial) Within(ctx context.Context, q query.SpatialQuery, into any) error

Within routes a radius/nearest search to the tenant's shard.

type Store

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

Store routes command.Store (the write path) by tenant.

func NewStore

func NewStore(set Router) *Store

NewStore wraps a Set as a routing command.Store.

func (*Store) InTenantTx

func (s *Store) InTenantTx(ctx context.Context, fn func(ctx context.Context, tx command.Tx) error) error

InTenantTx routes the write transaction to the tenant's shard. The whole transaction (aggregate row + event + outbox) runs there, so atomicity is preserved without any distributed transaction.

type Timeseries

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

Timeseries routes query.TSQuerier (telemetry ingest/range reads).

func NewTimeseries

func NewTimeseries(set Router) *Timeseries

NewTimeseries wraps a Set as a routing query.TSQuerier.

func (*Timeseries) BulkWrite

func (t *Timeseries) BulkWrite(ctx context.Context, series string, points []query.Point) error

BulkWrite routes a telemetry batch.

func (*Timeseries) Range

func (t *Timeseries) Range(ctx context.Context, q query.RangeQuery, into any) error

Range routes a time-window read.

type Vector

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

Vector routes query.VectorQuerier (embedding upsert/search).

func NewVector

func NewVector(set Router) *Vector

NewVector wraps a Set as a routing query.VectorQuerier.

func (*Vector) Delete

func (v *Vector) Delete(ctx context.Context, entity, id string) error

Delete routes an embedding delete to the tenant's shard.

func (*Vector) DeleteByMeta added in v0.0.3

func (v *Vector) DeleteByMeta(ctx context.Context, entity string, filter map[string]string) error

DeleteByMeta routes a metadata-filtered delete to the tenant's shard.

func (*Vector) Get added in v0.0.3

func (v *Vector) Get(ctx context.Context, entity, id string) ([]float32, error)

Get routes an embedding lookup to the tenant's shard.

func (*Vector) Similar

func (v *Vector) Similar(ctx context.Context, q query.VectorQuery, into any) error

Similar routes a nearest-neighbour search.

func (*Vector) Upsert

func (v *Vector) Upsert(ctx context.Context, entity, id string, embedding []float32, meta map[string]any) error

Upsert routes an embedding write.

Jump to

Keyboard shortcuts

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