using Microsoft.Data.Sqlite; namespace ZB.MOM.WW.OtOpcUa.Runtime.DeploymentCache; /// /// DDL for the node-local deployment-artifact cache: the chunked artifact table plus the /// per-cluster current-deployment pointer. /// /// /// /// Deliberately depends on nothing but so it can be applied /// to any connection — the host's LocalDbSetup.OnReady in production, and a bare /// connection in a test — without dragging the DI graph along. /// /// /// Why the artifact is chunked base64 TEXT and not one BLOB row. Two independent /// constraints force it. RegisterReplicated rejects BLOB columns outright; and the /// replication engine batches by row count against gRPC's 4 MB default message cap, /// so a single multi-megabyte artifact row would simply never be deliverable — at any /// batch size. A 128 KiB raw chunk is ≈ 171 KB once base64-encoded, which keeps a /// worst-case batch comfortably inside the cap. /// /// /// Why no autoincrement PKs. Convergence is last-writer-wins keyed on the primary /// key. Two nodes independently allocating rowid 7 for different rows would silently /// overwrite one another. Every key here is TEXT or a composite of caller-supplied values. /// /// public static class DeploymentCacheSchema { /// Table holding the artifact bytes, split into base64 chunks. public const string ArtifactsTable = "deployment_artifacts"; /// Table holding one current-deployment pointer per cluster. public const string PointerTable = "deployment_pointer"; /// /// Creates both cache tables if they do not already exist. Idempotent. /// /// /// An already-open connection. ILocalDb.CreateConnection() hands out open, /// pragma-configured connections — do not call Open() on one. /// public static void Apply(SqliteConnection connection) { ArgumentNullException.ThrowIfNull(connection); using var cmd = connection.CreateCommand(); cmd.CommandText = """ CREATE TABLE IF NOT EXISTS deployment_artifacts ( deployment_id TEXT NOT NULL, chunk_index INTEGER NOT NULL, cluster_id TEXT NOT NULL, revision_hash TEXT NOT NULL, chunk_count INTEGER NOT NULL, chunk_base64 TEXT NOT NULL, cached_at_utc TEXT NOT NULL, PRIMARY KEY (deployment_id, chunk_index) ); CREATE TABLE IF NOT EXISTS deployment_pointer ( cluster_id TEXT NOT NULL PRIMARY KEY, deployment_id TEXT NOT NULL, revision_hash TEXT NOT NULL, artifact_sha256 TEXT NOT NULL, applied_at_utc TEXT NOT NULL ); """; cmd.ExecuteNonQuery(); } }