feat(reconcile): site-reconcile messages + expected-set/stage-if-absent repo
- Commons: ReconcileSiteRequest / ReconcileSiteResponse / ReconcileGapItem message contracts (site→central ClusterClient on startup; central reply with gap fetch tokens + orphan list + base URL). - Commons: ExpectedDeployment projection record (Commons/Types/Deployment/), lightweight join of DeployedConfigSnapshot + Instance (no ConfigJson). - IDeploymentManagerRepository: GetExpectedDeploymentsForSiteAsync (inner-join query returning deployed set for a site, excluding snapshot-less instances) + StagePendingIfAbsentAsync (insert-if-absent, self-contained save, returns bool; does NOT supersede — an existing pending row signals in-flight delivery). - DeploymentManagerRepository: implement both methods; StagePendingIfAbsent commits internally (matches PurgeExpiredPendingDeployments convention). - ReconcileRepositoryTests: 4 tests covering expected-set filter/IsEnabled/ cross-site isolation, empty-site, stage-absent (true + row retrievable), stage-present (false + existing row unchanged); all pass.
This commit is contained in:
+37
@@ -1,5 +1,6 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Deployment;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
@@ -174,6 +175,42 @@ public interface IDeploymentManagerRepository
|
||||
/// <returns>The number of pending deployments purged.</returns>
|
||||
Task<int> PurgeExpiredPendingDeploymentsAsync(DateTimeOffset nowUtc, CancellationToken cancellationToken = default);
|
||||
|
||||
// Startup reconciliation: expected-set query and insert-if-absent staging
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of instances that central considers deployed for the given site — the join of
|
||||
/// <c>DeployedConfigSnapshot</c> (by InstanceId) with <c>Instance</c> (where SiteId == siteId).
|
||||
/// Instances without a snapshot are excluded. No <c>ConfigurationJson</c> is loaded; the full
|
||||
/// config is fetched on demand via the HTTP endpoint using a freshly-minted token from
|
||||
/// <see cref="StagePendingIfAbsentAsync"/>.
|
||||
/// </summary>
|
||||
/// <param name="siteId">The site primary key to filter on.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
|
||||
/// <returns>All deployed instances for the site, as lightweight <see cref="ExpectedDeployment"/> projections.</returns>
|
||||
Task<IReadOnlyList<ExpectedDeployment>> GetExpectedDeploymentsForSiteAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a <see cref="PendingDeployment"/> for the instance ONLY IF no pending row already
|
||||
/// exists for that <c>InstanceId</c>. An existing pending row signals an in-flight deploy that
|
||||
/// is already delivering to the node — do NOT supersede it (contrast with
|
||||
/// <see cref="AddPendingDeploymentAsync"/>, which supersedes). Returns <c>true</c> if the row
|
||||
/// was staged, <c>false</c> if an existing row was found and left untouched.
|
||||
/// This method is self-contained: it commits its own save and returns a meaningful result,
|
||||
/// matching the convention of <see cref="PurgeExpiredPendingDeploymentsAsync"/>.
|
||||
/// </summary>
|
||||
/// <param name="instanceId">The instance to stage the pending deployment for.</param>
|
||||
/// <param name="deploymentId">The deployment ID (fetch key).</param>
|
||||
/// <param name="revisionHash">Revision hash of the flattened configuration.</param>
|
||||
/// <param name="configurationJson">JSON-serialized flattened configuration.</param>
|
||||
/// <param name="token">Short-TTL fetch token the site node will present to the HTTP endpoint.</param>
|
||||
/// <param name="createdAtUtc">UTC timestamp for this pending row.</param>
|
||||
/// <param name="expiresAtUtc">UTC expiry after which the token is no longer valid.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
|
||||
/// <returns><c>true</c> if staged; <c>false</c> if a pending row already existed and was left unchanged.</returns>
|
||||
Task<bool> StagePendingIfAbsentAsync(int instanceId, string deploymentId, string revisionHash,
|
||||
string configurationJson, string token, DateTimeOffset createdAtUtc, DateTimeOffset expiresAtUtc,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
// Instance lookups for deployment pipeline
|
||||
/// <summary>
|
||||
/// Gets an instance by ID.
|
||||
|
||||
Reference in New Issue
Block a user