namespace ZB.MOM.WW.ScadaBridge.DeploymentManager; /// /// Configuration options for the central-side Deployment Manager. /// public class DeploymentManagerOptions { /// /// Timeout for a lifecycle command round-trip (disable, enable, delete). /// Applied as a linked-CTS deadline in DeploymentService so a hung or /// unreachable site does not hold the per-instance operation lock indefinitely. /// public TimeSpan LifecycleCommandTimeout { get; set; } = TimeSpan.FromSeconds(30); /// Timeout per site for system-wide artifact deployment. public TimeSpan ArtifactDeploymentTimeoutPerSite { get; set; } = TimeSpan.FromSeconds(120); /// Timeout for acquiring an operation lock on an instance. public TimeSpan OperationLockTimeout { get; set; } = TimeSpan.FromSeconds(5); /// /// Maximum number of instance deployments whose SITE ROUND-TRIP runs /// concurrently during a bulk DeploySiteAsync. The flatten/validate and /// persistence phases stay serial regardless (they share one non-thread-safe /// DbContext); only the network wait is fanned out. /// /// /// Bounded rather than unbounded so a large site cannot open one in-flight /// gRPC command per instance against a two-node site pair. Four mirrors the /// conservative end of the site's own apply concurrency; raise it only with /// evidence from the target-scale load test. /// /// public int SiteDeploymentMaxParallelism { get; set; } = 4; /// /// Per-instance deadline applied to the site round-trip inside a bulk /// DeploySiteAsync. One unreachable or wedged instance is recorded as a /// failed row and the rest of the batch continues — the bulk operation never /// inherits a single instance's hang. /// public TimeSpan SiteDeploymentTimeoutPerInstance { get; set; } = TimeSpan.FromSeconds(120); /// /// Deadline applied to each query-before-redeploy reconciliation round-trip /// inside a bulk DeploySiteAsync (the single-instance path keeps the /// communication layer's own QueryTimeout). /// /// /// Phase 1 is strictly serial, so an unreachable site otherwise costs /// QueryTimeout (30 s) PER INSTANCE before a single deploy is sent — for /// a 500-instance site roughly four hours, far past the CLI's 5-minute bulk /// deploy timeout, which then cancels and re-arms the identical cost on the next /// attempt. Deliberately short (seconds): a reconcile that does not answer /// promptly falls through to a normal deploy anyway, which is exactly what its /// own timeout produces. Works together with the once-per-batch reachability /// circuit-breaker, which skips the remaining reconciles after the first /// timeout. /// /// public TimeSpan SiteDeploymentReconcileTimeout { get; set; } = TimeSpan.FromSeconds(5); /// /// Retention window for TERMINAL deployment records (Success, /// Failed). Deployments are insert-only — one row per attempt, forever — /// so without a window the table grows without bound and every unfiltered /// deployment query gets slower with the age of the system. /// /// /// Deliberately generous (one year) because deployment history is /// operator-facing forensics, and deliberately terminal-only: an /// InProgress or Pending row is never purged regardless of age, /// because it is exactly the row the query-before-redeploy reconciliation /// path needs to find. Set to to disable purging. /// /// public TimeSpan TerminalDeploymentRecordRetention { get; set; } = TimeSpan.FromDays(365); /// /// How long the Central UI pending-updates snapshot stays fresh; one fleet /// flatten per TTL while any Deployer/Administrator circuit is open. /// /// /// The snapshot is a whole-fleet read (every site's artifact hash plus every /// deployed instance's staleness), so it is held behind a single-flight TTL /// cache: however many circuits poll, and however often, the fleet is evaluated /// at most once per this window — and not at all when nobody is looking. Must be /// positive; zero would make every poll a cache miss and re-flatten the fleet per /// circuit, which is precisely the cost the cache exists to avoid. /// /// public TimeSpan PendingUpdatesCacheTtl { get; set; } = TimeSpan.FromSeconds(60); }