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 AdaptedEdgeIndexField(tenantID uint16, relType, field string) string
- func AdaptedEdgeTableName(tenantID uint16, relType string) string
- func AdaptedNodeIndexField(tenantID uint16, entityType, field string) string
- func AdaptedNodeIndexTenant(tenantID uint16, entityType string) string
- func AdaptedNodeTableName(tenantID uint16, entityType string) string
- func CacheKey(tenantID uint16, entity string, id int) string
- func CacheListPattern(tenantID uint16, entity string) string
- func CachePattern(tenantID uint16, entity string) string
- func CacheTenantPattern(tenantID uint16) string
- func EdgeFTSTableName(tenantID uint16) string
- func EdgePropsTableName(tenantID uint16) string
- func EdgeSchemaTableName(tenantID uint16) string
- func EdgeSeqIndexRelType(tenantID uint16) string
- func EdgeSeqTableName(tenantID uint16) string
- func GraphEdgesTableName(tenantID uint16) string
- func GraphIndexRel(tenantID uint16) string
- func GraphIndexSource(tenantID uint16) string
- func GraphIndexTarget(tenantID uint16) string
- func GraphNodePrefix(tenantID uint16) string
- func GraphTableName(tenantID uint16) string
- func NodeFTSTableName(tenantID uint16) string
- func NodeID(tenantID uint16, entity string, id int) string
- func NodeIDPrefix(nodeID string) string
- func NodeIDStripped(nodeID string) string
- func NodeSchemaTableName(tenantID uint16) string
- func NodeSeqIndexEntityType(tenantID uint16) string
- func NodeSeqTableName(tenantID uint16) string
- func NodesIndexEntityType(tenantID uint16) string
- func NodesIndexUpdatedAt(tenantID uint16) string
- func NodesTableName(tenantID uint16) string
- func ScopeKey(tenantID uint16, key string) string
- func StorageDirSegment(tenantID uint16) string
- type ElementKind
- type Persister
- type Registry
- func (r *Registry) Count() int
- func (r *Registry) GetOrRegister(ctx context.Context, name string) (uint16, error)
- func (r *Registry) List() map[string]uint16
- func (r *Registry) LoadFrom(ctx context.Context) error
- func (r *Registry) Lookup(name string) (uint16, bool)
- func (r *Registry) Name(id uint16) (string, bool)
- func (r *Registry) Register(ctx context.Context, name string, id uint16) error
- func (r *Registry) SetPersister(p Persister)
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 AdaptedEdgeIndexField ¶
AdaptedEdgeIndexField returns a field index name on an adapted edge table. Example: idx_t0001_edata_KNOWS_since
func AdaptedEdgeTableName ¶
AdaptedEdgeTableName returns the adapted native-column table name for a schema-registered edge label. Example: t0001_edata_KNOWS, t0001_edata_MEMBER_OF
func AdaptedNodeIndexField ¶
AdaptedNodeIndexField returns a field index name on an adapted node table. Example: idx_t0001_ndata_user_email
func AdaptedNodeIndexTenant ¶
AdaptedNodeIndexTenant returns the tenant index name on an adapted node table. Example: idx_t0001_ndata_user_tenant
func AdaptedNodeTableName ¶
AdaptedNodeTableName returns the adapted native-column table name for a schema-registered node entity type. Example: t0001_ndata_user, t0001_ndata_user_profile
func CacheKey ¶
CacheKey returns a cache key scoped to a tenant. For tenant 0 (unscoped): "entity:id" For non-zero tenants: "XXXX:entity:id"
func CacheListPattern ¶
CacheListPattern returns a pattern matching only list cache keys for an entity type, scoped to a tenant. This leaves individual GET cache entries intact, improving cache hit rate when a single entity is modified. For tenant 0 (unscoped): "entity:list:*" For non-zero tenants: "XXXX:entity:list:*"
func CachePattern ¶
CachePattern returns a pattern for cache invalidation scoped to a tenant. For tenant 0 (unscoped): "entity:*" For non-zero tenants: "XXXX:entity:*"
func CacheTenantPattern ¶
CacheTenantPattern returns a pattern matching all keys for a tenant. For tenant 0: "*" (everything) For non-zero: "XXXX:*"
func EdgeFTSTableName ¶
EdgeFTSTableName returns the edge full-text search virtual table name for a tenant. Used when edge properties carry free-text content that needs full-text search — e.g. a contract document on a MARRIED_TO relationship, a clinical note on a TREATS relationship, or a lease agreement on LEASES. Example: t0001_efts
func EdgePropsTableName ¶
EdgePropsTableName returns the blob edge property store table name for a tenant. Stores JSON property blobs for edges whose label has no registered schema. Example: t0001_edges
func EdgeSchemaTableName ¶
EdgeSchemaTableName returns the edge schema registry table name for a tenant. Stores the raw JSON schema, derived column spec, and warning-suppression flag for each relationship label. Example: t0001_e_sch
func EdgeSeqIndexRelType ¶
EdgeSeqIndexRelType returns the index on t<X>_eseq. Example: idx_t0001_eseq_rel
func EdgeSeqTableName ¶
EdgeSeqTableName returns the edge ID sequence table name for a tenant. Provides explicit control over surrogate edge ID assignment, consistent with NodeSeqTableName. One row per relationship label per tenant. Example: t0001_eseq
func GraphEdgesTableName ¶
GraphEdgesTableName is the legacy name for GraphTableName. Deprecated: use GraphTableName. Retained for the migration command only.
func GraphIndexRel ¶
GraphIndexRel returns the index name for relationship_name lookups on t<X>_graph. Example: idx_t0001_graph_rel
func GraphIndexSource ¶
GraphIndexSource returns the index name for source-side lookups on t<X>_graph. Example: idx_t0001_graph_src
func GraphIndexTarget ¶
GraphIndexTarget returns the index name for target-side lookups on t<X>_graph. Example: idx_t0001_graph_tgt
func GraphNodePrefix ¶
GraphNodePrefix returns the XXXX@ prefix used to namespace graph node IDs in a shared in-memory graph that spans multiple tenants. For tenant 0 (unscoped): "" (no prefix; node IDs are bare "entity:id") For non-zero tenants: "XXXX@" (e.g. "0001@")
Uses uppercase hex. NodeIDPrefix is the corresponding parser; both must agree on case. If this format ever changes, NodeIDPrefix must be updated.
func GraphTableName ¶
GraphTableName returns the topology table name for a tenant. Stores directed edges as (source_entity, source_id, target_entity, target_id, relationship_name, edge_id). Example: t0001_graph
func NodeFTSTableName ¶
NodeFTSTableName returns the node full-text search virtual table name for a tenant. Replaces the global shared-mode `entities_fts` virtual table. Example: t0001_nfts
func NodeID ¶
NodeID returns a graph node identifier scoped to a tenant. For tenant 0 (unscoped): "entity:id" For non-zero tenants: "XXXX@entity:id"
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.
func NodeSchemaTableName ¶
NodeSchemaTableName returns the node schema registry table name for a tenant. Stores both the raw JSON schema and the derived adapted-table column spec. Replaces the global `schemas` and `adapted_table_schemas` tables. Example: t0001_n_sch
func NodeSeqIndexEntityType ¶
NodeSeqIndexEntityType returns the index on t<X>_nseq (the PK already covers this, but an explicit name is needed for migration assertions). Example: idx_t0001_nseq_etype
func NodeSeqTableName ¶
NodeSeqTableName returns the node ID sequence table name for a tenant. Replaces the global shared-mode `entity_sequences` table. Example: t0001_nseq
func NodesIndexEntityType ¶
NodesIndexEntityType returns the index name for entity_type lookups on t<X>_nodes. Example: idx_t0001_nodes_etype
func NodesIndexUpdatedAt ¶
NodesIndexUpdatedAt returns the index name for updated_at ordering on t<X>_nodes. Example: idx_t0001_nodes_updated
func NodesTableName ¶
NodesTableName returns the blob node store table name for a tenant. Replaces the global shared-mode `entities` table. Example: t0001_nodes
func ScopeKey ¶
ScopeKey prepends the tenant's hex ID to an arbitrary cache or lookup key, producing a tenant-scoped key of the form "XXXX:key". For tenant 0 (unscoped): the key is returned unchanged. For non-zero tenants: "XXXX:key" (e.g. "0001:post:list:1:10")
This is the generic scoping primitive; use CacheKey, CachePattern, etc. for the specialised cache key formats.
func StorageDirSegment ¶
StorageDirSegment returns the directory name used for tenant-scoped file storage (timeseries data, JSON file store). The segment is intended to be joined with a base directory using filepath.Join. For tenant 0 (unscoped): "" (data lives directly in the base directory) For non-zero tenants: "tXXXX" (e.g. "t0001")
Uses uppercase hex, consistent with GraphNodePrefix.
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]uint16, 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 uint16) 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 uint16 IDs. 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.