storelayout

package
v0.30.38 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package storelayout is the single authority for xolu's on-disk directory layout. Every database file, timeseries directory, and blob directory is derived from one configurable input — the base directory — by fixed invariants, in the same spirit as the per-tenant table-naming invariants one level up. No other package composes data paths by hand.

The layout is tenant-first: everything belonging to one tenant lives under that tenant's own directory, subdivided by role.

<base>/
  t0000/                  tenant 0 (unscoped / single-tenant mode)
    store/xolu.db          primary entity store (SQLite today)
    ts/                   timeseries store (Pebble today)
    blobs/                blob store
  t0001/                  first registered tenant
    store/xolu.db
    ts/
    blobs/
  ...
  shared/                 shared-tenancy mode (all tenants in one store file)
    store/xolu.db
    ts/
    blobs/
  dynconfig.json          server-level config (not tenant data)
  schema/                 server-level entity schemas

Design rules:

  • The base directory is the ONLY configurable path. Everything below it is derived; there are no per-file or per-role path overrides.
  • Role directory names ("store", "ts", "blobs") describe the role, not the engine, so a future backend change does not change the layout contract.
  • Tenant 0 is a tenant like any other: it gets the directory "t0000". It is never special-cased to the base root.
  • Shared-tenancy mode is not a tenant; it gets its own segment, "shared", with the same internal shape as a tenant directory.

Index

Constants

View Source
const (
	// StoreFile is the SQLite database filename within a store/ directory.
	// It is the same in every tenant and in shared mode; tenants are
	// distinguished by their directory, not by the filename.
	StoreFile = "xolu.db"

	// DynConfigFileName is the server-level dynamic-config file, at the base root.
	DynConfigFileName = "dynconfig.json"

	// SchemaDirName is the server-level schema directory, at the base root.
	SchemaDirName = "schema"

	// BlobsDirName is the role-directory name for blobs. Blobs are a per-tenant
	// role (see TenantBlobDir: <base>/tXXXX/blobs); this constant names that
	// role directory. It is the same string as roleBlobs and retained for any
	// external reference to the blob directory name.
	BlobsDirName = "blobs"
)

Fixed filenames within a role directory.

Variables

This section is empty.

Functions

func BlobsDir deprecated

func BlobsDir(base string) string

BlobsDir returns the legacy server-level blob directory: <base>/blobs.

Deprecated: blobs are now a per-tenant role. Use TenantBlobDir(base, id). Retained only until the server construction path is migrated off it.

func DynConfigPath

func DynConfigPath(base string) string

DynConfigPath returns the server-level dynamic-config file: <base>/dynconfig.json. This is server data, not tenant data, so it lives at the base root, a sibling of the tenant directories.

func ParseTenantSegment

func ParseTenantSegment(name string) (tenantID tenant.TenantID, ok bool)

ParseTenantSegment parses a directory name of the form "tXXXX" (uppercase hex, four digits) back into a tenant id. It returns ok=false for any name that is not a well-formed tenant segment. "shared" is not a tenant segment and returns ok=false. Note that "t0000" parses to tenant 0 with ok=true — callers that scan tenant directories must decide for themselves whether to include tenant 0.

func SchemaDir

func SchemaDir(base string) string

SchemaDir returns the server-level schema directory: <base>/schema. This is server data, not tenant data, so it lives at the base root.

func SharedBlobDir

func SharedBlobDir(base string) string

SharedBlobDir returns the shared-segment blob directory: <base>/shared/blobs. Defined for symmetry with SharedStoreDir/SharedTSDir; the blob plane, like the timeseries plane, uses per-tenant directories (TenantBlobDir) and does not consume this.

func SharedCalDir

func SharedCalDir(base string) string

SharedCalDir returns the shared-mode scheduling (cal) directory: <base>/shared/cal.

func SharedRoot

func SharedRoot(base string) string

SharedRoot returns the directory holding the shared-tenancy store: <base>/shared. It has the same internal shape as a tenant root.

func SharedStoreDir

func SharedStoreDir(base string) string

SharedStoreDir returns the shared-mode primary-store directory: <base>/shared/store.

func SharedStorePath

func SharedStorePath(base string) string

SharedStorePath returns the shared-mode SQLite database file: <base>/shared/store/xolu.db.

func SharedTSDir

func SharedTSDir(base string) string

SharedTSDir returns the shared-mode timeseries directory: <base>/shared/ts.

func TenantBalRollupDir added in v0.26.0

func TenantBalRollupDir(base string, tenantID tenant.TenantID) string

TenantBalRollupDir returns the per-tenant bal rollup-cascade directory: <base>/tXXXX/bal_rollup. This is bal's derived rollup plane (T-62: moved off SQLite, since no guard ever reads it — @C04a, confirmed against the admission guard's own query). The authoritative journal and the checkpoints table (which the write path updates in-transaction, T-58) remain in the tenant's primary store. Analogous to TenantCalDir's H1/H3 split.

func TenantBlobDir

func TenantBlobDir(base string, tenantID tenant.TenantID) string

TenantBlobDir returns the per-tenant blob directory: <base>/tXXXX/blobs. This is the tenant-first blob layout, analogous to TenantTSDir: one blob directory per tenant, keyed by ID, tenant 0 included.

func TenantCalDir

func TenantCalDir(base string, tenantID tenant.TenantID) string

TenantCalDir returns the per-tenant scheduling (cal) directory: <base>/tXXXX/cal. This is the cal occupancy index (the derived Pebble bitmap, H3); the authoritative booking record (H1) lives in the tenant's primary store (TenantStorePath). Analogous to TenantTSDir.

func TenantLocDir added in v0.26.0

func TenantLocDir(base string, tenantID tenant.TenantID) string

TenantLocDir returns the per-tenant loc directory: <base>/tXXXX/loc. loc's own tables (locations, fences, capacity, journal) live here as their own SQLite file, not folded into the tenant's primary store — the same reasoning ts already applies with its own directory (T-115, wave 9, loc-02-implementation.md Stage 0). Unlike TenantBalRollupDir, this is not a derived plane: loc has no Pebble-plane source of truth at all, canonical state is SQL throughout (loc-00-design.md §6a), mirroring bal's own shape, not cal's or ts's.

func TenantObjDir added in v0.26.0

func TenantObjDir(base string, tenantID tenant.TenantID) string

TenantObjDir returns the per-tenant obj directory: <base>/tXXXX/obj. obj's own tables (subjects, position, containment edges, journal) live here as their own SQLite file, the identical reasoning TenantLocDir already documents — canonical state is SQL throughout (obj-00-design.md §4, mirroring bal's shape per obj-02-implementation.md's own Principles section), no Pebble-plane source of truth to derive from.

func TenantRoot

func TenantRoot(base string, tenantID tenant.TenantID) string

TenantRoot returns the directory holding all of one tenant's data: <base>/tXXXX. Every role directory (store, ts, blobs) lives beneath it.

func TenantSegment

func TenantSegment(tenantID tenant.TenantID) string

TenantSegment returns the directory-name segment for a tenant.

Unlike the older tenant.StorageDirSegment, tenant 0 is NOT special-cased to an empty segment: it returns "t0000", so tenant 0 gets its own directory like any other tenant. The format is "tXXXX" with uppercase hex, four digits, matching the historical format so that directory scans that parse the segment back (see ParseTenantSegment) continue to work.

Delegates to tenant.TenantDirName (added 2026-07-28) rather than reimplementing the encoding here — this package's own doc comment already named the intent ("in the same spirit as" pkg/tenant's invariants); this makes that literal rather than aspirational.

func TenantStoreDir

func TenantStoreDir(base string, tenantID tenant.TenantID) string

TenantStoreDir returns the per-tenant primary-store directory: <base>/tXXXX/store.

func TenantStorePath

func TenantStorePath(base string, tenantID tenant.TenantID) string

TenantStorePath returns the per-tenant SQLite database file: <base>/tXXXX/store/xolu.db.

func TenantTSDir

func TenantTSDir(base string, tenantID tenant.TenantID) string

TenantTSDir returns the per-tenant timeseries directory: <base>/tXXXX/ts.

Types

type Issue

type Issue struct {
	Severity Severity
	Path     string // the offending path, relative to BaseDir where meaningful
	Message  string
}

Issue is a single conformance problem found by Check.

func Check

func Check(m Model) []Issue

Check validates a scanned Model against the invariant directory structure and returns every conformance problem found. The policy is strict: anything that is not exactly what the invariant permits is a Violation. An empty result means the structure conforms.

Check is "pass 1": it validates the directory structure that storelayout owns, which is universal across all storage backends — the tree from the base down to and including each role directory. It deliberately renders NO verdict on the contents of a role directory. Everything beneath store/ and ts/ — files, nested subdirectories, and the backend's anchor file — is owned by the storage backend that occupies that role, not by storelayout. File-level conformance is "pass 2", delegated to the backend (which, holding its own configuration, is the only component that can say what files it expects).

The structural rules enforced:

  • Immediate children of the base may only be: tenant directories ("tXXXX"), the shared directory ("shared"), the schema directory, and the dynconfig file. Anything else (a stray file — including a misplaced store file — or a stray directory such as a leftover "sql"/"ts") is a violation.
  • Within a tenant or shared directory, only the role directories store, ts, blobs may appear, and only as directories. Any other entry is a violation.
  • The contents of a role directory are out of scope here (see pass 2).

func (Issue) String

func (i Issue) String() string

type LegacyFinding

type LegacyFinding struct {
	Path    string // path relative to the base directory
	Message string // what it is and why it blocks startup
}

LegacyFinding describes one detected pre-normalization data location.

func DetectLegacy

func DetectLegacy(m Model) []LegacyFinding

DetectLegacy returns findings for any pre-normalization data locations present in the model. An empty result means no legacy layout was detected (the directory is either empty, freshly created, or already conforms). A non-empty result means the caller is looking at data written by an older xolu and should refuse to start rather than create fresh stores alongside it.

Detection is conservative: it reports a legacy location only when the old directory/file is actually present, so a clean or new-layout directory never trips it.

func (LegacyFinding) String

func (f LegacyFinding) String() string

type Model

type Model struct {
	BaseDir string

	// Tenants holds every immediate subdirectory of BaseDir that looks like it
	// is meant to be a tenant or shared directory (i.e. is itself a directory),
	// whether or not its name is valid. Check decides validity.
	Tenants []TenantDir

	// RootFiles holds immediate non-directory entries directly under BaseDir
	// (e.g. "dynconfig.json", or an out-of-place "xolu.db").
	RootFiles []string

	// RootDirs holds immediate subdirectory names directly under BaseDir that
	// are NOT tenant/shared directories captured in Tenants — e.g. "schema",
	// or an out-of-place "sql"/"ts". (schema is valid; others are violations.)
	RootDirs []string
}

Model is the complete in-memory description of a scanned base directory.

type RoleDir

type RoleDir struct {
	Name    string   // the role directory name as found on disk
	Entries []string // immediate child entry names (files and dirs)
}

RoleDir describes one role subdirectory (store or ts) found within a tenant or shared directory.

type Severity

type Severity int

Severity is the classification of an Issue. The current policy is strict and binary: every problem is a Violation. The type exists so that callers can branch on severity without assuming there is only one kind.

const (
	// Violation means the layout does not conform to the invariant.
	Violation Severity = iota
)

func (Severity) String

func (s Severity) String() string

type TenantDir

type TenantDir struct {
	Segment string    // directory name as found on disk, e.g. "t0001", "shared", or something invalid
	Roles   []RoleDir // role subdirectories found within
	Extra   []string  // non-directory or unexpected immediate entries directly under the segment
}

TenantDir describes one tenant (or shared) directory found under the base.

Jump to

Keyboard shortcuts

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