perf(deploy): flatten-session caching, bulk DeploySiteAsync, paged management queries
This commit is contained in:
@@ -137,6 +137,57 @@ When deploying artifacts (shared scripts, external system definitions, etc.) to
|
||||
- Deployment is performed at the **individual instance level**.
|
||||
- The UI may provide convenience operations (e.g., "deploy all out-of-date instances at Site A"), but these decompose into individual instance deployments.
|
||||
|
||||
### Bulk site deployment (`DeploySiteAsync`, WP2.5)
|
||||
|
||||
`DeploySiteAsync(siteId, user)` deploys every instance at one site in a single
|
||||
operation, surfaced as the `MgmtDeploySite` management command and the CLI
|
||||
`deploy site --site-id`. It is the "deploy all at Site A" convenience above, made
|
||||
first-class — it still decomposes into individual instance deployments, and each
|
||||
instance keeps its own deployment id, revision hash, operation lock, and
|
||||
optimistically-concurrent status record.
|
||||
|
||||
It runs the ordinary deployment pipeline in three phases, of which only the middle
|
||||
one is parallel:
|
||||
|
||||
1. **Prepare (serial).** Validate transition, take the operation lock, flatten +
|
||||
validate, run query-before-redeploy reconciliation, stage the
|
||||
`PendingDeployment`, insert the `InProgress` record. Every step here touches the
|
||||
scoped, non-thread-safe `DbContext`, so the phase is strictly serial. All
|
||||
instances share ONE `FlattenSession`, so a template chain common to N instances
|
||||
is walked once and the session-global queries (shared scripts, schema library,
|
||||
the site's data connections) run once for the batch.
|
||||
2. **Send (bounded parallel).** The `RefreshDeploymentCommand` round-trips run
|
||||
concurrently up to `SiteDeploymentMaxParallelism` (default 4), each under a
|
||||
`SiteDeploymentTimeoutPerInstance` deadline (default 120 s). This phase touches
|
||||
no repository — that is exactly why it is the only phase allowed to run in
|
||||
parallel. Shape mirrors `ArtifactDeploymentService.DeployCoreAsync`.
|
||||
3. **Finalize (serial).** Commit terminal statuses, apply post-success side
|
||||
effects, write audit rows, release each operation lock.
|
||||
|
||||
**Not all-or-nothing.** An instance that fails for any reason (wrong state, failed
|
||||
validation, lock already held by another operation, site round-trip timed out) is
|
||||
reported as a failed row while the rest proceed, and is individually retryable via
|
||||
the ordinary single-instance deploy. This matches the artifact-deployment policy:
|
||||
successful targets are never rolled back because another target failed.
|
||||
|
||||
`DeployInstanceAsync` is composed from the same three phase helpers with a batch of
|
||||
one, so the two entry points cannot drift on deployment identity, idempotency, lock
|
||||
coverage, or optimistic concurrency.
|
||||
|
||||
### Terminal deployment-record retention
|
||||
|
||||
Deployment records are insert-only — one row per deploy attempt — so without a
|
||||
window the table grows for the life of the system and every unfiltered deployment
|
||||
query degrades with age rather than with page size.
|
||||
`TerminalDeploymentRecordRetention` (default **365 days**, deliberately generous
|
||||
because deployment history is operator forensics) bounds it, swept opportunistically
|
||||
and rate-limited on the deployment-list read path.
|
||||
|
||||
Only **terminal** rows (`Success` / `Failed`) are eligible. An `InProgress` row is
|
||||
never purged regardless of age: it is precisely the row the query-before-redeploy
|
||||
reconciliation reads to decide whether a prior deploy actually landed at the site,
|
||||
and expiring it by age would silently disable that idempotency guard.
|
||||
|
||||
## Diff View
|
||||
|
||||
Before deploying, the Deployment Manager can request a diff from the Template Engine showing:
|
||||
|
||||
Reference in New Issue
Block a user