namespace ZB.MOM.WW.OtOpcUa.Runtime.DeploymentCache;
///
/// Node-local durable cache of the deployment artifact this node last applied.
///
///
///
/// Exists so a driver node can boot into its last-known-good address space when the central
/// configuration database is unreachable. Without it, a control-plane outage that outlives a
/// node restart leaves that node with no address space at all — the plant loses visibility
/// for a reason that has nothing to do with the plant.
///
///
public interface IDeploymentArtifactCache
{
///
/// Persists as the cluster's current deployment.
///
///
///
/// Idempotent per : re-storing the same deployment
/// replaces its chunks rather than accumulating orphans. Retention is bounded to the two
/// newest deployments per cluster, so a long-lived node cannot grow its local database
/// without limit.
///
///
Task StoreAsync(string clusterId, string deploymentId, string revisionHash,
byte[] artifact, CancellationToken ct = default);
///
/// Reads the cached artifact for , or
/// when nothing is cached or the cached bytes fail their integrity check.
///
///
///
/// A failed integrity check is deliberately indistinguishable from a miss. Booting a
/// plant from a truncated address space is strictly worse than booting from none: the
/// missing half looks like a deliberate configuration rather than an error.
///
///
Task GetCurrentAsync(string clusterId, CancellationToken ct = default);
///
/// Reads the single cached pointer without knowing the cluster id.
///
///
///
/// The boot seam needs this. A node's cluster id is only derivable from an artifact it
/// has already loaded, or from the central database — which is precisely what is
/// unreachable in the scenario this cache exists to survive. So the cold path reads the
/// newest pointer unkeyed.
///
///
/// More than one pointer row means the node was re-homed between clusters. The newest
/// wins, but the choice is logged as a warning naming both clusters — a node silently
/// booting a neighbouring cluster's configuration is exactly the failure that must never
/// be quiet.
///
///
Task GetCurrentUnkeyedAsync(CancellationToken ct = default);
}
///
/// A deployment artifact recovered from the node-local cache, with the metadata needed to decide
/// whether it is still current once the control plane is reachable again.
///
public sealed record CachedDeploymentArtifact(
string DeploymentId, string RevisionHash, byte[] Artifact, DateTimeOffset AppliedAtUtc);