multidb

package
v1.2.3 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package multidb provides composite database support for multi-database functionality.

Composite databases are virtual databases that span multiple physical databases, allowing queries to transparently access data from multiple constituent databases.

Package multidb provides configuration helpers for multi-database support.

Package multidb provides limit enforcement for multi-database support.

This package implements resource limit checking and enforcement for NornicDB's multi-database feature. Limits are enforced at runtime to prevent any single database from consuming excessive resources.

Key Features:

  • Storage limits: MaxNodes, MaxEdges, MaxBytes (enforced with exact size calculation)
  • Query limits: MaxQueryTime, MaxResults, MaxConcurrentQueries
  • Connection limits: MaxConnections
  • Rate limits: MaxQueriesPerSecond, MaxWritesPerSecond

Example - Checking limits before creating a node:

checker, err := manager.GetLimitChecker("tenant_a")
if err != nil {
	return err
}

node := &storage.Node{
	ID:     storage.NodeID("user-123"),
	Labels: []string{"User"},
	Properties: map[string]any{"name": "Alice"},
}

// Check if creating this node would exceed limits
if err := checker.CheckStorageLimits("create_node", node, nil); err != nil {
	return fmt.Errorf("cannot create node: %w", err)
}

// Safe to create - limits checked
storage.CreateNode(node)

MaxBytes Enforcement:

MaxBytes enforcement uses exact size calculation, not estimation. When checking limits for a create operation, the actual serialized size of the node/edge is calculated using gob encoding (matching the storage format). The current total storage size is tracked incrementally in DatabaseInfo, and the check verifies that currentSize + newEntitySize <= MaxBytes.

Storage size is tracked incrementally:

  • Initialized lazily on first access (calculated from all existing nodes/edges)
  • Updated incrementally after successful creates/deletes
  • No recalculation needed - O(1) limit checks

Package multidb provides multi-database support for NornicDB.

This package implements Neo4j 4.x-style multi-database support, allowing multiple logical databases (tenants) to share a single physical storage backend while maintaining complete data isolation.

Package multidb provides resource limits for multi-database support.

Package multidb provides multi-database support for NornicDB.

This package implements Neo4j 4.x-style multi-database support, allowing multiple logical databases (tenants) to share a single physical storage backend while maintaining complete data isolation.

Package multidb provides metadata persistence for multi-database support.

Package multidb provides automatic migration of existing unprefixed data.

When upgrading from a pre-multi-database version of NornicDB, existing data without namespace prefixes is automatically migrated to the default database namespace. This ensures backwards compatibility and zero-downtime upgrades.

Migration Process:

  1. Detects unprefixed data (nodes/edges without "namespace:" prefix)
  2. Migrates all unprefixed data to default database namespace
  3. Updates all indexes automatically (via CreateNode/CreateEdge)
  4. Marks migration as complete in metadata (prevents re-running)

The migration runs automatically in NewDatabaseManager() and is completely transparent to users. No manual steps are required.

Example:

// Before upgrade: data stored as "node-123"
// After upgrade: automatically becomes "nornic:node-123"
// User access remains the same - no changes needed!

Package multidb provides routing strategies for composite databases.

Routing determines which constituent databases should be queried for a given operation. This enables efficient query execution by only accessing relevant constituents.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDatabaseNotFound        = errors.New("database not found")
	ErrDatabaseExists          = errors.New("database already exists")
	ErrInvalidDatabaseName     = errors.New("invalid database name")
	ErrMaxDatabasesReached     = errors.New("maximum number of databases reached")
	ErrCannotDropSystemDB      = errors.New("cannot drop system database")
	ErrCannotDropDefaultDB     = errors.New("cannot drop default database")
	ErrDatabaseOffline         = errors.New("database is offline")
	ErrAliasExists             = errors.New("alias already exists")
	ErrAliasNotFound           = errors.New("alias not found")
	ErrInvalidAliasName        = errors.New("invalid alias name")
	ErrAliasConflict           = errors.New("alias conflicts with existing database name")
	ErrDatabaseHasAliases      = errors.New("database has aliases - drop aliases first")
	ErrStorageLimitExceeded    = errors.New("storage limit exceeded")
	ErrQueryLimitExceeded      = errors.New("query limit exceeded")
	ErrConnectionLimitExceeded = errors.New("connection limit exceeded")
	ErrRateLimitExceeded       = errors.New("rate limit exceeded")
	ErrNotCompositeDatabase    = errors.New("database is not a composite database")
	ErrConstituentNotFound     = errors.New("constituent not found")
	ErrDuplicateConstituent    = errors.New("duplicate constituent alias")
)

Multi-database error types

Functions

This section is empty.

Types

type CompositeRouting

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

CompositeRouting combines multiple routing strategies. Tries each strategy in order until one returns a result.

func NewCompositeRouting

func NewCompositeRouting() *CompositeRouting

NewCompositeRouting creates a new composite routing strategy.

func (*CompositeRouting) AddStrategy

func (c *CompositeRouting) AddStrategy(strategy RoutingStrategy)

AddStrategy adds a routing strategy to try in order.

func (*CompositeRouting) RouteQuery

func (c *CompositeRouting) RouteQuery(queryInfo *QueryInfo) []string

RouteQuery tries each strategy until one returns a result.

func (*CompositeRouting) RouteWrite

func (c *CompositeRouting) RouteWrite(operation string, labels []string, properties map[string]interface{}) string

RouteWrite tries each strategy until one returns a result.

type Config

type Config struct {
	// DefaultDatabase is the database used when none is specified (default: "nornic")
	// This matches Neo4j's behavior where "neo4j" is the default, but NornicDB uses "nornic"
	DefaultDatabase string

	// SystemDatabase stores metadata (default: "system")
	SystemDatabase string

	// MaxDatabases limits total databases (0 = unlimited)
	MaxDatabases int

	// AllowDropDefault allows dropping the default database
	AllowDropDefault bool

	// RemoteEngineFactory creates a storage engine for a remote constituent.
	// If nil, remote constituents are not executable (metadata may still be stored).
	RemoteEngineFactory RemoteEngineFactory

	// RemoteCredentialEncryptionKey encrypts remote constituent user/password values
	// before metadata persistence. If empty, user_password auth mode is rejected.
	RemoteCredentialEncryptionKey string
}

Config holds DatabaseManager configuration.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns default configuration. The default database name is "nornic" (NornicDB's equivalent of Neo4j's "neo4j").

func NewConfigFromDefaultDatabase

func NewConfigFromDefaultDatabase(defaultDatabase string) *Config

NewConfigFromDefaultDatabase creates a DatabaseManager Config from a default database name. This allows the DatabaseManager to use the same default database name as configured in the main NornicDB configuration.

Example:

// Use default database name from main config
mainConfig := config.LoadDefaults()
dbConfig := multidb.NewConfigFromDefaultDatabase(mainConfig.Database.DefaultDatabase)
manager := multidb.NewDatabaseManager(inner, dbConfig)

type ConnectionLimits

type ConnectionLimits struct {
	// MaxConnections is the maximum concurrent connections (0 = unlimited).
	MaxConnections int `json:"max_connections,omitempty"`
}

ConnectionLimits controls connection count per database.

type ConnectionTracker

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

ConnectionTracker tracks active connections per database.

func NewConnectionTracker

func NewConnectionTracker() *ConnectionTracker

NewConnectionTracker creates a new connection tracker.

func (*ConnectionTracker) CheckConnectionLimit

func (t *ConnectionTracker) CheckConnectionLimit(manager *DatabaseManager, databaseName string) error

CheckConnectionLimit checks if a new connection is allowed.

func (*ConnectionTracker) DecrementConnection

func (t *ConnectionTracker) DecrementConnection(databaseName string)

DecrementConnection decrements the connection count for a database.

func (*ConnectionTracker) GetConnectionCount

func (t *ConnectionTracker) GetConnectionCount(databaseName string) int

GetConnectionCount returns the current connection count for a database.

func (*ConnectionTracker) IncrementConnection

func (t *ConnectionTracker) IncrementConnection(databaseName string)

IncrementConnection increments the connection count for a database.

func (*ConnectionTracker) TryIncrementConnection

func (t *ConnectionTracker) TryIncrementConnection(manager *DatabaseManager, databaseName string) error

TryIncrementConnection checks the connection limit and increments atomically.

This avoids the race window of a "check-then-increment" pattern under concurrent connection attempts.

type ConstituentRef

type ConstituentRef struct {
	// Alias is the name used within the composite database to reference this constituent.
	Alias string `json:"alias"`

	// DatabaseName is the actual database name (or alias) that this constituent points to.
	DatabaseName string `json:"database_name"`

	// Type is the type of constituent: "local" (same instance) or "remote" (another instance).
	Type string `json:"type"` // "local", "remote"

	// AccessMode controls what operations are allowed: "read", "write", "read_write".
	AccessMode string `json:"access_mode"` // "read", "write", "read_write"

	// URI points to the remote NornicDB endpoint when Type == "remote".
	URI string `json:"uri,omitempty"`

	// SecretRef identifies credentials/token material for remote access.
	// The actual secret is resolved outside of metadata persistence.
	SecretRef string `json:"secret_ref,omitempty"`

	// User and Password implement Neo4j-style explicit remote auth:
	// ... AT '<url>' USER <user> PASSWORD '<password>'
	//
	// Password is encrypted before persisting metadata to the system namespace.
	// At runtime, DatabaseManager decrypts it before invoking RemoteEngineFactory.
	User     string `json:"user,omitempty"`
	Password string `json:"password,omitempty"`

	// AuthMode defines remote auth behavior:
	// - "oidc_forwarding": forward caller Authorization header
	// - "user_password": use explicit User/Password for outbound Basic auth
	// Empty is treated as "oidc_forwarding" for remote constituents.
	AuthMode string `json:"auth_mode,omitempty"`
}

ConstituentRef represents a reference to a constituent database within a composite database.

func (*ConstituentRef) Validate

func (c *ConstituentRef) Validate() error

Validate validates a constituent reference.

type DatabaseInfo

type DatabaseInfo struct {
	Name         string           `json:"name"`
	CreatedAt    time.Time        `json:"created_at"`
	CreatedBy    string           `json:"created_by,omitempty"`
	Status       string           `json:"status"` // "online", "offline"
	Type         string           `json:"type"`   // "standard", "system"
	IsDefault    bool             `json:"is_default"`
	NodeCount    int64            `json:"node_count,omitempty"` // Cached, may be stale
	UpdatedAt    time.Time        `json:"updated_at"`
	Aliases      []string         `json:"aliases,omitempty"`      // Database aliases (Neo4j-compatible)
	Limits       *Limits          `json:"limits,omitempty"`       // Resource limits
	Constituents []ConstituentRef `json:"constituents,omitempty"` // Constituent databases (for composite type)
	// contains filtered or unexported fields
}

DatabaseInfo holds metadata about a database.

type DatabaseManager

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

DatabaseManager manages multiple logical databases within a single storage engine.

It provides:

  • Database creation and deletion
  • Database metadata tracking
  • Namespaced storage engine views
  • Neo4j 4.x multi-database compatibility

Thread-safe: all operations are protected by mutex.

Example:

// Create manager with shared storage
inner := storage.NewBadgerEngine("./data")
manager := multidb.NewDatabaseManager(inner, nil)

// Create databases
manager.CreateDatabase("tenant_a")
manager.CreateDatabase("tenant_b")

// Get namespaced storage for a tenant
tenantStorage, _ := manager.GetStorage("tenant_a")

// Use storage (isolated to tenant_a)
tenantStorage.CreateNode(&storage.Node{ID: "123"})

func NewDatabaseManager

func NewDatabaseManager(inner storage.Engine, config *Config) (*DatabaseManager, error)

NewDatabaseManager creates a new database manager.

Parameters:

  • inner: The underlying storage engine (shared by all databases)
  • config: Configuration (nil for defaults)

On creation, initializes:

  • System database (for metadata)
  • Default database ("nornic" by default, configurable)

func (*DatabaseManager) AddConstituent

func (m *DatabaseManager) AddConstituent(compositeName string, constituent ConstituentRef) error

AddConstituent adds a constituent to an existing composite database.

func (*DatabaseManager) Close

func (m *DatabaseManager) Close() error

Close releases resources.

func (*DatabaseManager) CreateAlias

func (m *DatabaseManager) CreateAlias(alias, databaseName string) error

CreateAlias creates an alias for a database (Neo4j-compatible).

func (*DatabaseManager) CreateCompositeDatabase

func (m *DatabaseManager) CreateCompositeDatabase(name string, constituents []ConstituentRef) error

CreateCompositeDatabase creates a new composite database.

A composite database is a virtual database that spans multiple constituent databases. Queries against a composite database transparently access data from all constituents.

Parameters:

  • name: The name of the composite database (must be unique)
  • constituents: List of constituent database references

Returns ErrDatabaseExists if a database with this name already exists. Returns ErrInvalidDatabaseName if the name is invalid. Returns an error if any constituent database doesn't exist.

func (*DatabaseManager) CreateDatabase

func (m *DatabaseManager) CreateDatabase(name string) error

CreateDatabase creates a new database.

Parameters:

  • name: Database name (must be unique, lowercase recommended)

Returns ErrDatabaseExists if database already exists. Returns ErrMaxDatabasesReached if limit exceeded.

func (*DatabaseManager) DecrementStorageSize

func (m *DatabaseManager) DecrementStorageSize(databaseName string, nodeSize, edgeSize int64)

DecrementStorageSize decrements the tracked storage size for a database.

This should be called after successful node/edge deletion to maintain accurate size tracking for MaxBytes limit enforcement. The sizes should be the same values that were used when the entities were created.

Parameters:

  • databaseName: The database to update
  • nodeSize: Size in bytes of the node that was deleted (0 if no node deleted)
  • edgeSize: Size in bytes of the edge that was deleted (0 if no edge deleted)

Example:

// After successfully deleting a node (size known from creation)
manager.DecrementStorageSize("tenant_a", nodeSize, 0)

// After successfully deleting an edge (size known from creation)
manager.DecrementStorageSize("tenant_a", 0, edgeSize)

Thread-safe: This method is safe to call from multiple goroutines. Defensive: Size is prevented from going negative (resets to 0 if underflow).

func (*DatabaseManager) DefaultDatabaseName

func (m *DatabaseManager) DefaultDatabaseName() string

DefaultDatabaseName returns the default database name.

func (*DatabaseManager) DropAlias

func (m *DatabaseManager) DropAlias(alias string) error

DropAlias removes an alias (Neo4j-compatible).

func (*DatabaseManager) DropCompositeDatabase

func (m *DatabaseManager) DropCompositeDatabase(name string) error

DropCompositeDatabase removes a composite database.

This only removes the composite database metadata. The constituent databases remain unchanged.

func (*DatabaseManager) DropDatabase

func (m *DatabaseManager) DropDatabase(name string) error

DropDatabase removes a database and all its data.

Parameters:

  • name: Database name to drop

Returns ErrDatabaseNotFound if database doesn't exist. Returns ErrCannotDropSystemDB for system/default databases.

func (*DatabaseManager) Exists

func (m *DatabaseManager) Exists(name string) bool

Exists checks if a database exists.

func (*DatabaseManager) ExistsOrIsConstituent

func (m *DatabaseManager) ExistsOrIsConstituent(name string) bool

ExistsOrIsConstituent returns true if the name refers to an existing database or a valid composite constituent in dotted "composite.alias" form. This is used by protocol entry paths (Bolt/HTTP) to avoid premature DB-not-found rejection for constituent graph references.

func (*DatabaseManager) GetCompositeConstituents

func (m *DatabaseManager) GetCompositeConstituents(compositeName string) ([]ConstituentRef, error)

GetCompositeConstituents returns the list of constituents for a composite database.

func (*DatabaseManager) GetDatabase

func (m *DatabaseManager) GetDatabase(name string) (*DatabaseInfo, error)

GetDatabase returns info for a specific database.

func (*DatabaseManager) GetDatabaseLimits

func (m *DatabaseManager) GetDatabaseLimits(databaseName string) (*Limits, error)

GetDatabaseLimits returns resource limits for a database.

func (*DatabaseManager) GetDefaultStorage

func (m *DatabaseManager) GetDefaultStorage() (storage.Engine, error)

GetDefaultStorage returns storage for the default database.

func (*DatabaseManager) GetStorage

func (m *DatabaseManager) GetStorage(name string) (storage.Engine, error)

GetStorage returns a namespaced storage engine for the specified database.

The returned engine is scoped to the database - all operations only affect data within that namespace.

func (*DatabaseManager) GetStorageSize

func (m *DatabaseManager) GetStorageSize(databaseName string) (int64, int64, int64)

GetStorageSize returns the current tracked storage size for a database.

Returns:

  • totalSize: Total storage size in bytes (sum of all nodes and edges)
  • nodeSize: Total size of all nodes in bytes
  • edgeSize: Total size of all edges in bytes

The size is tracked incrementally and initialized lazily on first access. This provides O(1) access for limit checking without recalculating from all entities.

Example:

totalSize, nodeSize, edgeSize := manager.GetStorageSize("tenant_a")
fmt.Printf("Database uses %d bytes (%d from nodes, %d from edges)\n",
	totalSize, nodeSize, edgeSize)

Thread-safe: This method is safe to call from multiple goroutines.

func (*DatabaseManager) GetStorageWithAuth

func (m *DatabaseManager) GetStorageWithAuth(name string, authToken string) (storage.Engine, error)

GetStorageWithAuth returns a storage engine for the specified database and forwards authToken to remote constituent factories when composite databases include remotes.

func (*DatabaseManager) IncrementStorageSize

func (m *DatabaseManager) IncrementStorageSize(databaseName string, nodeSize, edgeSize int64)

IncrementStorageSize increments the tracked storage size for a database.

This should be called after successful node/edge creation to maintain accurate size tracking for MaxBytes limit enforcement. The sizes should be calculated using the same gob encoding used by the storage engine.

Parameters:

  • databaseName: The database to update
  • nodeSize: Size in bytes of the node that was created (0 if no node created)
  • edgeSize: Size in bytes of the edge that was created (0 if no edge created)

Example:

// After successfully creating a node
nodeSize, _ := calculateNodeSize(node)
manager.IncrementStorageSize("tenant_a", nodeSize, 0)

// After successfully creating an edge
edgeSize, _ := calculateEdgeSize(edge)
manager.IncrementStorageSize("tenant_a", 0, edgeSize)

Thread-safe: This method is safe to call from multiple goroutines.

func (*DatabaseManager) IsCompositeDatabase

func (m *DatabaseManager) IsCompositeDatabase(name string) bool

IsCompositeDatabase checks if a database is a composite database.

func (*DatabaseManager) ListAliases

func (m *DatabaseManager) ListAliases(databaseName string) map[string]string

ListAliases returns all aliases for a database, or all aliases if database is empty.

func (*DatabaseManager) ListCompositeDatabases

func (m *DatabaseManager) ListCompositeDatabases() []*DatabaseInfo

ListCompositeDatabases returns all composite databases.

func (*DatabaseManager) ListDatabases

func (m *DatabaseManager) ListDatabases() []*DatabaseInfo

ListDatabases returns all database info.

func (*DatabaseManager) RemoveConstituent

func (m *DatabaseManager) RemoveConstituent(compositeName string, alias string) error

RemoveConstituent removes a constituent from a composite database.

func (*DatabaseManager) ResolveDatabase

func (m *DatabaseManager) ResolveDatabase(nameOrAlias string) (string, error)

ResolveDatabase resolves an alias or database name to the actual database name.

func (*DatabaseManager) SetDatabaseLimits

func (m *DatabaseManager) SetDatabaseLimits(databaseName string, limits *Limits) error

SetDatabaseLimits sets resource limits for a database.

func (*DatabaseManager) SetDatabaseStatus

func (m *DatabaseManager) SetDatabaseStatus(name, status string) error

SetDatabaseStatus sets a database online/offline.

type FullScanRouting

type FullScanRouting struct{}

FullScanRouting routes all queries to all constituents. This is the default strategy when no routing rules are configured.

func NewFullScanRouting

func NewFullScanRouting() *FullScanRouting

NewFullScanRouting creates a new full-scan routing strategy.

func (*FullScanRouting) RouteQuery

func (r *FullScanRouting) RouteQuery(queryInfo *QueryInfo) []string

RouteQuery returns nil, indicating all constituents should be queried.

func (*FullScanRouting) RouteWrite

func (r *FullScanRouting) RouteWrite(operation string, labels []string, properties map[string]interface{}) string

RouteWrite returns empty string, indicating write should go to first writable constituent.

type LabelRouting

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

LabelRouting routes queries based on node labels. Each label is mapped to one or more constituent aliases.

func NewLabelRouting

func NewLabelRouting() *LabelRouting

NewLabelRouting creates a new label-based routing strategy.

func (*LabelRouting) RouteQuery

func (r *LabelRouting) RouteQuery(queryInfo *QueryInfo) []string

RouteQuery routes a query based on labels.

func (*LabelRouting) RouteWrite

func (r *LabelRouting) RouteWrite(operation string, labels []string, properties map[string]interface{}) string

RouteWrite routes a write operation based on labels.

func (*LabelRouting) SetLabelRouting

func (r *LabelRouting) SetLabelRouting(label string, constituents []string)

SetLabelRouting configures which constituents should be queried for a given label.

type LimitChecker

type LimitChecker interface {
	// CheckStorageLimits checks if storage operations are within limits.
	// Returns error if limit would be exceeded.
	// For create operations, pass the node/edge being created to calculate exact size.
	CheckStorageLimits(operation string, node *storage.Node, edge *storage.Edge) error

	// CheckQueryLimits checks if query execution is allowed.
	// Returns error if limit would be exceeded.
	CheckQueryLimits(ctx context.Context) (context.Context, context.CancelFunc, error)

	// GetQueryLimits returns the query limits for this database.
	GetQueryLimits() *QueryLimits

	// GetRateLimits returns the rate limits for this database.
	GetRateLimits() *RateLimits

	// CheckQueryRate checks if query rate limit is allowed.
	CheckQueryRate() error

	// CheckWriteRate checks if write rate limit is allowed.
	CheckWriteRate() error
}

LimitChecker provides an interface for checking resource limits. This allows storage engines to check limits without depending on DatabaseManager. It implements both storage.LimitChecker and storage.QueryLimitChecker.

type Limits

type Limits struct {
	Storage    StorageLimits    `json:"storage,omitempty"`
	Query      QueryLimits      `json:"query,omitempty"`
	Connection ConnectionLimits `json:"connection,omitempty"`
	Rate       RateLimits       `json:"rate,omitempty"`
}

Limits holds resource limits for a database.

All limits are optional (0 = unlimited). Limits are enforced at runtime to prevent any single database from consuming excessive resources.

Example:

limits := &Limits{
	Storage: StorageLimits{
		MaxNodes: 1000000,
		MaxEdges: 5000000,
		MaxBytes: 10 * 1024 * 1024 * 1024, // 10GB
	},
	Query: QueryLimits{
		MaxQueryTime:        60 * time.Second,
		MaxResults:          10000,
		MaxConcurrentQueries: 10,
	},
	Connection: ConnectionLimits{
		MaxConnections: 50,
	},
	Rate: RateLimits{
		MaxQueriesPerSecond: 100,
		MaxWritesPerSecond:  50,
	},
}

func DefaultLimits

func DefaultLimits() *Limits

DefaultLimits returns default limits (all unlimited).

func (*Limits) GetMaxResults

func (l *Limits) GetMaxResults() int64

GetMaxResults returns the maximum number of results (for interface compatibility).

func (*Limits) IsUnlimited

func (l *Limits) IsUnlimited() bool

IsUnlimited returns true if all limits are unlimited (default state).

type PropertyRouting

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

PropertyRouting routes queries based on property values. Useful for database/shard-based routing (e.g., database_id property).

func NewPropertyRouting

func NewPropertyRouting(propertyName string) *PropertyRouting

NewPropertyRouting creates a new property-based routing strategy.

func (*PropertyRouting) RouteQuery

func (r *PropertyRouting) RouteQuery(queryInfo *QueryInfo) []string

RouteQuery routes a query based on property values.

func (*PropertyRouting) RouteWrite

func (r *PropertyRouting) RouteWrite(operation string, labels []string, properties map[string]interface{}) string

RouteWrite routes a write operation based on property values.

func (*PropertyRouting) SetDefaultConstituent

func (r *PropertyRouting) SetDefaultConstituent(constituent string)

SetDefaultConstituent sets the default constituent for values not in the map.

func (*PropertyRouting) SetPropertyRouting

func (r *PropertyRouting) SetPropertyRouting(value interface{}, constituent string)

SetPropertyRouting configures which constituent should be queried for a property value.

type QueryInfo

type QueryInfo struct {
	// Labels referenced in the query
	Labels []string

	// Properties used in WHERE clauses or CREATE/MERGE operations
	Properties map[string]interface{}

	// Property names that might be used for routing
	PropertyNames []string

	// Whether this is a write operation
	IsWrite bool

	// Whether this is a full scan (no specific labels/properties)
	IsFullScan bool
}

QueryInfo contains information extracted from a Cypher query for routing decisions.

type QueryLimits

type QueryLimits struct {
	// MaxQueryTime is the maximum query execution time (0 = unlimited).
	MaxQueryTime time.Duration `json:"max_query_time,omitempty"`

	// MaxResults is the maximum number of results returned (0 = unlimited).
	MaxResults int64 `json:"max_results,omitempty"`

	// MaxConcurrentQueries is the maximum concurrent queries (0 = unlimited).
	MaxConcurrentQueries int `json:"max_concurrent_queries,omitempty"`
}

QueryLimits controls query execution per database.

func (*QueryLimits) GetMaxResults

func (q *QueryLimits) GetMaxResults() int64

GetMaxResults returns the maximum number of results (for interface compatibility).

type RateLimits

type RateLimits struct {
	// MaxQueriesPerSecond is the maximum queries per second (0 = unlimited).
	MaxQueriesPerSecond int `json:"max_queries_per_second,omitempty"`

	// MaxWritesPerSecond is the maximum writes per second (0 = unlimited).
	MaxWritesPerSecond int `json:"max_writes_per_second,omitempty"`
}

RateLimits controls request rate per database.

type RemoteEngineFactory

type RemoteEngineFactory func(ref ConstituentRef, authToken string) (storage.Engine, error)

RemoteEngineFactory creates storage engines for remote composite constituents. authToken is the original caller's auth token/header value, forwarded to preserve authentication context across distributed constituent queries.

type RoutingStrategy

type RoutingStrategy interface {
	// RouteQuery determines which constituents should be queried for a given operation.
	// Returns list of constituent aliases to query.
	RouteQuery(queryInfo *QueryInfo) []string

	// RouteWrite determines which constituent should receive a write operation.
	// Returns constituent alias, or empty string if routing is ambiguous (full scan).
	RouteWrite(operation string, labels []string, properties map[string]interface{}) string
}

RoutingStrategy defines how queries are routed to constituent databases.

type StorageLimits

type StorageLimits struct {
	// MaxNodes is the maximum number of nodes allowed (0 = unlimited).
	// When exceeded, create operations fail with: "has reached max_nodes limit (N/M)"
	MaxNodes int64 `json:"max_nodes,omitempty"`

	// MaxEdges is the maximum number of edges allowed (0 = unlimited).
	// When exceeded, create operations fail with: "has reached max_edges limit (N/M)"
	MaxEdges int64 `json:"max_edges,omitempty"`

	// MaxBytes is the maximum storage size in bytes (0 = unlimited).
	// When exceeded, create operations fail with: "would exceed max_bytes limit
	// (current: X bytes, limit: Y bytes, new entity: Z bytes)"
	//
	// Size is calculated exactly using gob serialization (matching storage format).
	// No estimation - the actual size of each entity is known before creation.
	MaxBytes int64 `json:"max_bytes,omitempty"`
}

StorageLimits controls storage capacity per database.

All limits are enforced at runtime. When a limit is exceeded, the operation fails with a clear error message explaining the quota limits.

MaxBytes Enforcement:

MaxBytes enforcement uses exact size calculation, not estimation. The actual serialized size of each node/edge is calculated using gob encoding (matching the storage format). Storage size is tracked incrementally for O(1) limit checks.

Example:

limits := StorageLimits{
	MaxNodes: 1000000,                    // 1 million nodes
	MaxEdges: 5000000,                    // 5 million edges
	MaxBytes: 10 * 1024 * 1024 * 1024,   // 10GB
}

Jump to

Keyboard shortcuts

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