Table of Contents

Class DatabaseRegistry

Namespace
Typhon.Engine
Assembly
Typhon.Engine.dll

The machine-local index of databases any Typhon process has opened — %LOCALAPPDATA%\Typhon\databases</code> (#622, design D-7).

public sealed class DatabaseRegistry
Inheritance
DatabaseRegistry
Inherited Members

Remarks

Discoverability, not correctness. D-8 bounds this deliberately: an index of paths cannot tell you which capture belongs to which database — captures live inside the bundle (D-1) and carry their own identity (D-2). Nothing in the engine or the Workbench may depend on this registry being complete, present or right. It exists so a database created by a game server three directories away can be *found*, and for nothing else.

A directory of small files, not one shared JSON. Several engines starting at once would otherwise contend on a read-modify-write of a single document; here each database owns one file, written whole and atomically. Pruning is a delete, and a file corrupted by a crash or a hand-edit costs its own row rather than the list.

Default-on, with three guards. The asymmetry decides it: a discoverability failure is *silent* — an empty list teaches the user the feature is useless and they stop looking — while a noisy list is irritating, self-correcting, and at least proves the mechanism works. The guards are SuppressForProcess (one line per test project), the DisableEnvironmentVariable environment variable, a DisabledMarkerFileName file in the registry directory, and automatic suppression of anything under the OS temp directory (which covers the whole test estate without anyone remembering to). The README.txt written beside the entries documents the switches, so whoever finds the index finds the way to turn it off without reading source.

Every write is best-effort. Opening a database must never fail because a machine-local convenience index could not be updated — an unwritable or redirected %LOCALAPPDATA% costs the host this feature and nothing else. Same discipline as the profiler's capture destination (ProfilerBootstrap.ApplyDefaultCaptureDestination).

Constructors

DatabaseRegistry(string)

Creates a registry rooted at directory. Hosts and tests supply their own root; the engine uses EffectiveDirectory.

public DatabaseRegistry(string directory)

Parameters

directory string

Fields

DisableEnvironmentVariable

Environment variable that turns registration off machine-wide for a process tree. Any of off, 0, false, no.

public const string DisableEnvironmentVariable = "TYPHON_DATABASE_REGISTRY"

Field Value

string

DisabledMarkerFileName

Name of a file in the registry directory whose mere presence disables registration. The most discoverable switch: it sits with the data.

public const string DisabledMarkerFileName = "disabled"

Field Value

string

EntryExtension

Extension of an entry file.

public const string EntryExtension = ".json"

Field Value

string

ReadmeFileName

Name of the self-documenting file written beside the entries.

public const string ReadmeFileName = "README.txt"

Field Value

string

Properties

DefaultDirectory

%LOCALAPPDATA%\Typhon\databases</code> on Windows, the XDG data-home equivalent on POSIX — the same root the Workbench's bootstrap token already uses.

public static string DefaultDirectory { get; }

Property Value

string

Directory

The directory this instance reads and writes.

public string Directory { get; }

Property Value

string

DirectoryOverride

Redirects the registry away from DefaultDirectory. Set by tests and by hosts that keep their state elsewhere.

public static string DirectoryOverride { get; set; }

Property Value

string

EffectiveDirectory

The directory the engine registers into: DirectoryOverride when set, otherwise DefaultDirectory.

public static string EffectiveDirectory { get; }

Property Value

string

SuppressForProcess

Turns registration off for this process. One line in a shared test base or [SetUpFixture], inherited by every fixture — the explicit half of D-7's guards, for suites whose databases do not live under the OS temp directory and so are not caught automatically.

public static bool SuppressForProcess { get; set; }

Property Value

bool

Methods

EnsureReadme()

Writes the self-documenting README.txt if it is absent. This is D-7's "findable without reading source": whoever goes looking for the index because they are uneasy about it finds the instructions for switching it off sitting next to it.

public void EnsureReadme()

Remarks

Best-effort and atomic, for one reason: two engines starting at once would otherwise both try to write this file, and the loser's Record(string, string, Guid) would fail on a sharing violation — costing a real registration for the sake of a help file. Failing to explain the directory is never worse than failing to populate it.

Forget(string)

Removes one database from the registry. Returns false when it was not there. Never deletes the database itself.

public bool Forget(string bundleDirectory)

Parameters

bundleDirectory string

Returns

bool

IsEnabled(out string)

Whether this registry accepts registrations, and — when it does not — which switch stopped it.

public bool IsEnabled(out string disabledReason)

Parameters

disabledReason string

A human-readable sentence naming the responsible switch, or null when enabled.

Returns

bool

Remarks

The reason is produced here rather than reconstructed by a caller because a UI that renders an empty list for a disabled registry recreates exactly the failure D-7 warns about: the user concludes the feature is useless instead of learning it is switched off. "Off" and "nothing yet" must not look alike.

IsUnderTempDirectory(string)

True when bundleDirectory sits under the OS temp directory. Catches unit tests, POCs and throwaway fixtures automatically, which is what makes the explicit per-project opt-out a backstop rather than the primary defence.

public static bool IsUnderTempDirectory(string bundleDirectory)

Parameters

bundleDirectory string

Returns

bool

List()

Every known database, most-recently-opened first, each carrying a freshly-computed Exists.

public IReadOnlyList<DatabaseRegistryEntry> List()

Returns

IReadOnlyList<DatabaseRegistryEntry>

Remarks

Never throws, and a bad file costs only itself. Containing corruption to one row is the reason D-7 chose a directory of files over a single shared document, so an unparseable entry is skipped and its neighbours are still returned.

PruneMissing()

Drops every entry whose bundle is no longer on disk, returning how many went. Explicit by design — D-7 asks for validate-on-listing and an offer to prune, so List() never deletes anything on its own.

public int PruneMissing()

Returns

int

Record(string, string, Guid)

Records an open. Returns false when a guard declined it. Preserves FirstSeenUtc from any existing entry.

public bool Record(string bundleDirectory, string databaseName, Guid databaseId)

Parameters

bundleDirectory string

The database's bundle directory. Normalised to an absolute path before use.

databaseName string

The database name.

databaseId Guid

The database's durable identity.

Returns

bool

Remarks

The entry is written whole, to a process-unique temporary file, then moved over the target — one atomic replace, so a concurrent open of the same database can never produce a half-written row. The two writers then race on the content, which is benign: they are describing the same database and disagree only about which millisecond it was opened.