tenant

package
v0.27.2 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package tenant provides tenant-scoping helpers for the middle and handler tiers.

The storage tier enforces isolation by construction (see storage.StoreConfig). This package provides the supporting utilities that other tiers need:

  • NodeID / CacheKey: construct globally-unique identifiers when a shared in-memory structure (graph, cache) spans multiple tenants.
  • Registry: maps human-readable tenant names to uint16 identifiers. When a Persister is attached, mappings are durable across restarts.

Index

Constants

View Source
const (
	// TenantsTable is the tenant registry table. One per database file.
	TenantsTable = "tenants"

	// SchemaVersionTable tracks applied migrations. One per database file.
	SchemaVersionTable = "schema_version"
)

Variables

This section is empty.

Functions

func NodeIDPrefix

func NodeIDPrefix(nodeID string) string

NodeIDPrefix extracts the XXXX@ tenant prefix from a graph node ID, returning "" if the node ID carries no prefix.

Only uppercase hex digits (0-9, A-F) are recognised, matching the output of GraphNodePrefix. Lowercase hex will cause this function to return "". This intentional strictness prevents ambiguous or malformed prefixes from being silently accepted.

func NodeIDStripped

func NodeIDStripped(nodeID string) string

NodeIDStripped returns nodeID with its XXXX@ tenant prefix removed, if any. For bare node IDs (no prefix) the input is returned unchanged. Intended for use in error messages that must not leak internal prefixes.

Types

type ElementKind

type ElementKind uint8

ElementKind is the fundamental kind of a graph element.

const (
	// ElementNode is a real node with a confirmed entity type and property store.
	// Properties live in t<X>_nodes (blob) or t<X>_ndata_<label> (adapted).
	ElementNode ElementKind = iota

	// ElementEdge is a directed relationship between two nodes.
	// Properties live in t<X>_edges (blob) or t<X>_edata_<label> (adapted).
	ElementEdge

	// ElementVode is a forward-reference placeholder node created implicitly
	// by AddEdge when the target entity has not yet been written. A Vode has
	// topology (it exists in t<X>_graph adjacency) but no property store entry.
	// Its label is always NodeTypeVode ("__vode__"). Vodes are promoted to real
	// nodes when the entity data arrives; a non-zero Vode count at the end of
	// hydration indicates dangling references.
	//
	// Hydration must short-circuit for Vodes: there is no property row to fetch.
	// Query results that include Vodes should be excluded or flagged rather than
	// returned with empty property maps.
	ElementVode
)

func (ElementKind) String

func (k ElementKind) String() string

String returns a human-readable name for the ElementKind.

type Persister

type Persister interface {
	// LoadAll returns all persisted tenant mappings.
	LoadAll(ctx context.Context) (map[string]TenantID, error)
	// Save persists a single tenant mapping. It must be idempotent:
	// saving an already-persisted (name, id) pair is not an error.
	Save(ctx context.Context, name string, id TenantID) error
}

Persister stores and retrieves tenant name-to-ID mappings durably. Implementations must be safe for concurrent use.

type Registry

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

Registry maps human-readable tenant names (e.g. "acme") to TenantID values — the sole store of tenant identity assignment, and thus the sole place new TenantID values are minted. It is safe for concurrent use. When a Persister is attached, all mutations are durably stored, ensuring stable name-to-ID mappings across restarts.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty tenant registry with no persistence. Mappings will be lost on restart. Use SetPersister or LoadFrom to attach durable storage.

func (*Registry) Count

func (r *Registry) Count() int

Count returns the number of registered tenants.

func (*Registry) GetOrRegister

func (r *Registry) GetOrRegister(ctx context.Context, name string) (TenantID, error)

GetOrRegister returns the tenant ID for a name, auto-registering with the next available ID if the name is not yet known. This is intended for non-strict tenant modes where tenants are created on first access. If a persister is attached, new mappings are durably stored.

func (*Registry) List

func (r *Registry) List() map[string]TenantID

List returns all registered tenant name-ID pairs.

func (*Registry) LoadFrom

func (r *Registry) LoadFrom(ctx context.Context) error

LoadFrom loads all tenant mappings from the attached persister into the in-memory registry. Existing in-memory mappings are preserved; conflicts (same name with different ID) return an error. This should be called once at startup, after SetPersister.

func (*Registry) Lookup

func (r *Registry) Lookup(name string) (TenantID, bool)

Lookup returns the tenant ID for a name, or 0 and false if not found.

func (*Registry) Name

func (r *Registry) Name(id TenantID) (string, bool)

Name returns the tenant name for an ID, or "" and false if not found.

func (*Registry) Register

func (r *Registry) Register(ctx context.Context, name string, id TenantID) error

Register adds a tenant with an explicit ID. Returns an error if the name or ID is already registered. If a persister is attached, the mapping is durably stored.

func (*Registry) SetPersister

func (r *Registry) SetPersister(p Persister)

SetPersister attaches a persistence backend to the registry. Must be called before any Register/GetOrRegister calls.

type TenantID added in v0.26.0

type TenantID uint16

TenantID is the canonical, sole representation of a tenant's server-local identity. Every tenant-scoped string anywhere in this codebase (table names, directory names, cache keys, node-ID prefixes) must derive from a TenantID value via this type's own methods — never from a bare uint16 formatted by hand. That is the actual invariant this type exists to make impossible to violate by construction: found 2026-07-28 when a dxp integration test needed one tenant identifier comparable across bal, fsm, and entity, and discovered five independent places across the tree had each separately reimplemented the same "%04X" hex encoding — a bug class a bare uint16 can never prevent, because the compiler has no way to distinguish "a tenant ID" from any other uint16 in the program.

Deliberately NOT the wider federation address a future nolu project would need to route data between servers (docs/NOLU_EVENTS.md's LocalRef already answers "which server" with InstanceURL, a string — not a numeric composition of this type). TenantID is only ever "which tenant on THIS server," unchanged in width or meaning regardless of what federation scheme, if any, eventually wraps it. That boundary is deliberate, not an oversight: xolu's own local addressing shouldn't have to anticipate a design nolu itself owns.

func (TenantID) AdaptedEdgeIndexField added in v0.26.0

func (t TenantID) AdaptedEdgeIndexField(relType, field string) string

AdaptedEdgeIndexField returns a field index name on an adapted edge table.

func (TenantID) AdaptedEdgeTableName added in v0.26.0

func (t TenantID) AdaptedEdgeTableName(relType string) string

AdaptedEdgeTableName returns the adapted native-column table name for a schema-registered edge label on this tenant. Example: t0001_edata_KNOWS

func (TenantID) AdaptedNodeIndexField added in v0.26.0

func (t TenantID) AdaptedNodeIndexField(entityType, field string) string

AdaptedNodeIndexField returns a field index name on an adapted node table.

func (TenantID) AdaptedNodeIndexTenant added in v0.26.0

func (t TenantID) AdaptedNodeIndexTenant(entityType string) string

AdaptedNodeIndexTenant returns the tenant index name on an adapted node table.

func (TenantID) AdaptedNodeTableName added in v0.26.0

func (t TenantID) AdaptedNodeTableName(entityType string) string

AdaptedNodeTableName returns the adapted native-column table name for a schema-registered node entity type on this tenant. Example: t0001_ndata_user

func (TenantID) CacheKey added in v0.26.0

func (t TenantID) CacheKey(entity string, id int) string

CacheKey returns a cache key scoped to this tenant.

func (TenantID) CacheListPattern added in v0.26.0

func (t TenantID) CacheListPattern(entity string) string

CacheListPattern returns a pattern matching only list cache keys for an entity type, scoped to this tenant.

func (TenantID) CachePattern added in v0.26.0

func (t TenantID) CachePattern(entity string) string

CachePattern returns a cache-invalidation pattern scoped to this tenant.

func (TenantID) CacheTenantPattern added in v0.26.0

func (t TenantID) CacheTenantPattern() string

CacheTenantPattern returns a pattern matching every key for this tenant.

func (TenantID) DirName added in v0.26.0

func (t TenantID) DirName() string

DirName returns the bare per-tenant directory name ("t0000", no trailing underscore — directories don't need TablePrefix's SQL separator).

func (TenantID) EdgeFTSTableName added in v0.26.0

func (t TenantID) EdgeFTSTableName() string

EdgeFTSTableName returns the edge full-text search table name for this tenant. Example: t0001_efts

func (TenantID) EdgePropsTableName added in v0.26.0

func (t TenantID) EdgePropsTableName() string

EdgePropsTableName returns the blob edge property table name for this tenant. Example: t0001_edges

func (TenantID) EdgeSchemaTableName added in v0.26.0

func (t TenantID) EdgeSchemaTableName() string

EdgeSchemaTableName returns the edge schema registry table name for this tenant. Example: t0001_e_sch

func (TenantID) EdgeSeqIndexRelType added in v0.26.0

func (t TenantID) EdgeSeqIndexRelType() string

EdgeSeqIndexRelType returns the index name on the edge sequence table.

func (TenantID) EdgeSeqTableName added in v0.26.0

func (t TenantID) EdgeSeqTableName() string

EdgeSeqTableName returns the edge ID sequence table name for this tenant. Example: t0001_eseq

func (TenantID) GraphEdgesTableName added in v0.26.0

func (t TenantID) GraphEdgesTableName() string

GraphEdgesTableName is the legacy name for GraphTableName. Deprecated: use GraphTableName. Retained for real production callers (pkg/server, pkg/storage) as well as the migration command.

func (TenantID) GraphIndexRel added in v0.26.0

func (t TenantID) GraphIndexRel() string

GraphIndexRel returns the index name for relationship_name lookups.

func (TenantID) GraphIndexSource added in v0.26.0

func (t TenantID) GraphIndexSource() string

GraphIndexSource returns the index name for source-side graph lookups.

func (TenantID) GraphIndexTarget added in v0.26.0

func (t TenantID) GraphIndexTarget() string

GraphIndexTarget returns the index name for target-side graph lookups.

func (TenantID) GraphNodePrefix added in v0.26.0

func (t TenantID) GraphNodePrefix() string

GraphNodePrefix returns the XXXX@ prefix used to namespace graph node IDs in a shared in-memory graph spanning multiple tenants. Tenant 0 (unscoped): "" — no prefix; node IDs are bare "entity:id". Non-zero tenants: "XXXX@" (e.g. "0001@").

func (TenantID) GraphTableName added in v0.26.0

func (t TenantID) GraphTableName() string

GraphTableName returns the topology table name for this tenant. Example: t0001_graph

func (TenantID) IsZero added in v0.26.0

func (t TenantID) IsZero() bool

IsZero reports whether t is the reserved unscoped/default tenant.

func (TenantID) NodeFTSTableName added in v0.26.0

func (t TenantID) NodeFTSTableName() string

NodeFTSTableName returns the node full-text search table name for this tenant. Example: t0001_nfts

func (TenantID) NodeID added in v0.26.0

func (t TenantID) NodeID(entity string, id int) string

NodeID returns a graph node identifier scoped to this tenant. Tenant 0: "entity:id". Non-zero: "XXXX@entity:id".

func (TenantID) NodeSchemaTableName added in v0.26.0

func (t TenantID) NodeSchemaTableName() string

NodeSchemaTableName returns the node schema registry table name for this tenant. Example: t0001_n_sch

func (TenantID) NodeSeqIndexEntityType added in v0.26.0

func (t TenantID) NodeSeqIndexEntityType() string

NodeSeqIndexEntityType returns the index name on the node sequence table.

func (TenantID) NodeSeqTableName added in v0.26.0

func (t TenantID) NodeSeqTableName() string

NodeSeqTableName returns the node ID sequence table name for this tenant. Example: t0001_nseq

func (TenantID) NodesIndexEntityType added in v0.26.0

func (t TenantID) NodesIndexEntityType() string

NodesIndexEntityType returns the index name for entity_type lookups.

func (TenantID) NodesIndexUpdatedAt added in v0.26.0

func (t TenantID) NodesIndexUpdatedAt() string

NodesIndexUpdatedAt returns the index name for updated_at ordering.

func (TenantID) NodesTableName added in v0.26.0

func (t TenantID) NodesTableName() string

NodesTableName returns the blob node store table name for this tenant. Example: t0001_nodes

func (TenantID) ScopeKey added in v0.26.0

func (t TenantID) ScopeKey(key string) string

ScopeKey prepends this tenant's hex ID to an arbitrary cache or lookup key, producing "XXXX:key" (or the bare key, unscoped, for tenant 0).

func (TenantID) StorageDirSegment added in v0.26.0

func (t TenantID) StorageDirSegment() string

StorageDirSegment returns the directory name for tenant-scoped file storage (timeseries data, JSON file store). Tenant 0 (unscoped): "" — data lives directly in the base directory. Non-zero: "tXXXX".

func (TenantID) String added in v0.26.0

func (t TenantID) String() string

String is the canonical bare tenant-ID string: 4-digit uppercase hex, no decoration. Every other method on this type adds its own prefix/suffix for a specific naming purpose (TablePrefix adds "t" and "_" for SQL table names; GraphNodePrefix adds a trailing "@" for node-ID namespacing) — this is the undecorated form underneath all of them, for contexts that need a tenant represented as an opaque, cross-primitive-comparable string with no naming convention baked in (dxp.Cache tenant keys are the motivating case).

func (TenantID) TablePrefix added in v0.26.0

func (t TenantID) TablePrefix() string

TablePrefix is the per-tenant table-name prefix WITH the trailing underscore ("t0000_"), for primitives that own their own table families (bal).

Jump to

Keyboard shortcuts

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