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
- func NodeIDPrefix(nodeID string) string
- func NodeIDStripped(nodeID string) string
- type ElementKind
- type Persister
- type Registry
- func (r *Registry) Count() int
- func (r *Registry) GetOrRegister(ctx context.Context, name string) (TenantID, error)
- func (r *Registry) List() map[string]TenantID
- func (r *Registry) LoadFrom(ctx context.Context) error
- func (r *Registry) Lookup(name string) (TenantID, bool)
- func (r *Registry) Name(id TenantID) (string, bool)
- func (r *Registry) Register(ctx context.Context, name string, id TenantID) error
- func (r *Registry) SetPersister(p Persister)
- type TenantID
- func (t TenantID) AdaptedEdgeIndexField(relType, field string) string
- func (t TenantID) AdaptedEdgeTableName(relType string) string
- func (t TenantID) AdaptedNodeIndexField(entityType, field string) string
- func (t TenantID) AdaptedNodeIndexTenant(entityType string) string
- func (t TenantID) AdaptedNodeTableName(entityType string) string
- func (t TenantID) CacheKey(entity string, id int) string
- func (t TenantID) CacheListPattern(entity string) string
- func (t TenantID) CachePattern(entity string) string
- func (t TenantID) CacheTenantPattern() string
- func (t TenantID) DirName() string
- func (t TenantID) EdgeFTSTableName() string
- func (t TenantID) EdgePropsTableName() string
- func (t TenantID) EdgeSchemaTableName() string
- func (t TenantID) EdgeSeqIndexRelType() string
- func (t TenantID) EdgeSeqTableName() string
- func (t TenantID) GraphEdgesTableName() string
- func (t TenantID) GraphIndexRel() string
- func (t TenantID) GraphIndexSource() string
- func (t TenantID) GraphIndexTarget() string
- func (t TenantID) GraphNodePrefix() string
- func (t TenantID) GraphTableName() string
- func (t TenantID) IsZero() bool
- func (t TenantID) NodeFTSTableName() string
- func (t TenantID) NodeID(entity string, id int) string
- func (t TenantID) NodeSchemaTableName() string
- func (t TenantID) NodeSeqIndexEntityType() string
- func (t TenantID) NodeSeqTableName() string
- func (t TenantID) NodesIndexEntityType() string
- func (t TenantID) NodesIndexUpdatedAt() string
- func (t TenantID) NodesTableName() string
- func (t TenantID) ScopeKey(key string) string
- func (t TenantID) StorageDirSegment() string
- func (t TenantID) String() string
- func (t TenantID) TablePrefix() string
Constants ¶
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 ¶
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 ¶
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) GetOrRegister ¶
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) LoadFrom ¶
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) Register ¶
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 ¶
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
AdaptedEdgeIndexField returns a field index name on an adapted edge table.
func (TenantID) AdaptedEdgeTableName ¶ added in v0.26.0
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
AdaptedNodeIndexField returns a field index name on an adapted node table.
func (TenantID) AdaptedNodeIndexTenant ¶ added in v0.26.0
AdaptedNodeIndexTenant returns the tenant index name on an adapted node table.
func (TenantID) AdaptedNodeTableName ¶ added in v0.26.0
AdaptedNodeTableName returns the adapted native-column table name for a schema-registered node entity type on this tenant. Example: t0001_ndata_user
func (TenantID) CacheListPattern ¶ added in v0.26.0
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
CachePattern returns a cache-invalidation pattern scoped to this tenant.
func (TenantID) CacheTenantPattern ¶ added in v0.26.0
CacheTenantPattern returns a pattern matching every key for this tenant.
func (TenantID) DirName ¶ added in v0.26.0
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
EdgeFTSTableName returns the edge full-text search table name for this tenant. Example: t0001_efts
func (TenantID) EdgePropsTableName ¶ added in v0.26.0
EdgePropsTableName returns the blob edge property table name for this tenant. Example: t0001_edges
func (TenantID) EdgeSchemaTableName ¶ added in v0.26.0
EdgeSchemaTableName returns the edge schema registry table name for this tenant. Example: t0001_e_sch
func (TenantID) EdgeSeqIndexRelType ¶ added in v0.26.0
EdgeSeqIndexRelType returns the index name on the edge sequence table.
func (TenantID) EdgeSeqTableName ¶ added in v0.26.0
EdgeSeqTableName returns the edge ID sequence table name for this tenant. Example: t0001_eseq
func (TenantID) GraphEdgesTableName ¶ added in v0.26.0
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
GraphIndexRel returns the index name for relationship_name lookups.
func (TenantID) GraphIndexSource ¶ added in v0.26.0
GraphIndexSource returns the index name for source-side graph lookups.
func (TenantID) GraphIndexTarget ¶ added in v0.26.0
GraphIndexTarget returns the index name for target-side graph lookups.
func (TenantID) GraphNodePrefix ¶ added in v0.26.0
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
GraphTableName returns the topology table name for this tenant. Example: t0001_graph
func (TenantID) IsZero ¶ added in v0.26.0
IsZero reports whether t is the reserved unscoped/default tenant.
func (TenantID) NodeFTSTableName ¶ added in v0.26.0
NodeFTSTableName returns the node full-text search table name for this tenant. Example: t0001_nfts
func (TenantID) NodeID ¶ added in v0.26.0
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
NodeSchemaTableName returns the node schema registry table name for this tenant. Example: t0001_n_sch
func (TenantID) NodeSeqIndexEntityType ¶ added in v0.26.0
NodeSeqIndexEntityType returns the index name on the node sequence table.
func (TenantID) NodeSeqTableName ¶ added in v0.26.0
NodeSeqTableName returns the node ID sequence table name for this tenant. Example: t0001_nseq
func (TenantID) NodesIndexEntityType ¶ added in v0.26.0
NodesIndexEntityType returns the index name for entity_type lookups.
func (TenantID) NodesIndexUpdatedAt ¶ added in v0.26.0
NodesIndexUpdatedAt returns the index name for updated_at ordering.
func (TenantID) NodesTableName ¶ added in v0.26.0
NodesTableName returns the blob node store table name for this tenant. Example: t0001_nodes
func (TenantID) ScopeKey ¶ added in v0.26.0
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
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
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
TablePrefix is the per-tenant table-name prefix WITH the trailing underscore ("t0000_"), for primitives that own their own table families (bal).