feat(localdb): deployment-cache schema + OnReady registration
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Runtime.Deployment;
|
||||
|
||||
/// <summary>
|
||||
/// DDL for the node-local deployment-artifact cache: the chunked artifact table plus the
|
||||
/// per-cluster current-deployment pointer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Deliberately depends on nothing but <see cref="SqliteConnection"/> so it can be applied
|
||||
/// to any connection — the host's <c>LocalDbSetup.OnReady</c> in production, and a bare
|
||||
/// connection in a test — without dragging the DI graph along.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Why the artifact is chunked base64 TEXT and not one BLOB row.</b> Two independent
|
||||
/// constraints force it. <c>RegisterReplicated</c> rejects BLOB columns outright; and the
|
||||
/// replication engine batches by <i>row count</i> 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.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Why no autoincrement PKs.</b> 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static class DeploymentCacheSchema
|
||||
{
|
||||
/// <summary>Table holding the artifact bytes, split into base64 chunks.</summary>
|
||||
public const string ArtifactsTable = "deployment_artifacts";
|
||||
|
||||
/// <summary>Table holding one current-deployment pointer per cluster.</summary>
|
||||
public const string PointerTable = "deployment_pointer";
|
||||
|
||||
/// <summary>
|
||||
/// Creates both cache tables if they do not already exist. Idempotent.
|
||||
/// </summary>
|
||||
/// <param name="connection">
|
||||
/// An already-open connection. <c>ILocalDb.CreateConnection()</c> hands out open,
|
||||
/// pragma-configured connections — do not call <c>Open()</c> on one.
|
||||
/// </param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user