From 34c4d5466992ef2ecfb9a1a70f5c6b8005bc819c Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Wed, 12 Aug 2026 09:07:05 +0800 Subject: [PATCH] docs: define zero-wait shadow safety fixes --- .../plans/2026-08-11-ft546wms-shadow-only.md | 49 ++++++++++++++++++- .../2026-08-11-ft546wms-shadow-only-design.md | 24 +++++---- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/docs/superpowers/plans/2026-08-11-ft546wms-shadow-only.md b/docs/superpowers/plans/2026-08-11-ft546wms-shadow-only.md index 7e0be30..944ff39 100644 --- a/docs/superpowers/plans/2026-08-11-ft546wms-shadow-only.md +++ b/docs/superpowers/plans/2026-08-11-ft546wms-shadow-only.md @@ -14,6 +14,9 @@ - No license, TLS, Security, frontend, tidy, replay, PDA, WCS callback, reservation, or ACTIVE workflow integration changes. - Legacy procedures execute exactly once and their current response construction remains authoritative. - Shadow observation is disabled by default and fails open on every runtime error. +- Legacy request threads never execute, queue behind, or wait for shadow database work. +- A single daemon worker with zero queue capacity admits at most one shadow observation cycle per JVM; busy submissions are dropped. +- Installation refuses an existing `alg` schema; uninstall requires the exact package manifest and object allow-list. - Shadow database scripts never insert, update, or delete WMS business rows. - Shadow database scripts never create triggers on WMS business tables. @@ -139,7 +142,51 @@ - [ ] Document backup-copy rehearsal, IDEA dual-workspace deployment, startup checks, data export, fast application rollback, and full SQL uninstall rollback. - [ ] Commit as `docs: add IDEA shadow deployment and rollback guide`. -### Task 7: Review, verify, and publish +### Task 7: Remove shadow latency from Legacy threads + +**Files:** +- Create: `ruoyi-admin/src/main/java/com/ruoyi/algorithm/shadow/InboundShadowCompletion.java` +- Create: `ruoyi-admin/src/main/java/com/ruoyi/algorithm/shadow/OutboundShadowCompletion.java` +- Modify: `ruoyi-admin/src/main/java/com/ruoyi/algorithm/shadow/ShadowObservationGateway.java` +- Modify: `ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/ToWmsServiceImpl.java` +- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/outStockout/service/impl/OutStockoutServiceImpl.java` +- Test: `ruoyi-admin/src/test/java/com/ruoyi/algorithm/shadow/ShadowObservationGatewayTest.java` +- Test: `ruoyi-admin/src/test/java/com/ruoyi/api/service/impl/ToWmsServiceImplShadowTest.java` +- Test: `ruoyi-admin/src/test/java/com/ruoyi/out/outStockout/service/impl/OutStockoutServiceImplShadowTest.java` + +**Interfaces:** +- Produces: `observeInbound`, `observeProjectOutbound`, and `observeExactStockOutbound`, each returning a nonblocking completion token. +- Guarantees: one daemon worker, `SynchronousQueue`, `AbortPolicy`, no caller-runs fallback, zero queued observations, and background-only comparison. + +- [ ] Add gateway tests that block the planning engine and prove submission returns before planning completes, a concurrent submission is dropped, and Legacy completion is accepted without waiting. +- [ ] Update service tests to require order: observation submission, Legacy procedure exactly once, Legacy reload/result construction, completion-token notification. +- [ ] Run focused tests and confirm RED against the synchronous gateway. +- [ ] Implement completion tokens and the dedicated executor; catch rejection, timeout, interruption, and runtime failures inside the gateway. +- [ ] Replace synchronous plan/compare calls in both Legacy services with submit/complete calls only. +- [ ] Run focused tests and confirm GREEN. +- [ ] Commit as `fix: keep shadow work off legacy threads`. + +### Task 8: Protect SQL package ownership + +**Files:** +- Modify: `fortucky-algorithm/src/main/resources/db/sqlserver/install-shadow.sql` +- Modify: `fortucky-algorithm/src/main/resources/db/sqlserver/uninstall-shadow.sql` +- Modify: `fortucky-algorithm/src/main/resources/db/sqlserver/diagnostics-shadow.sql` +- Modify: `fortucky-algorithm/src/test/java/com/ruoyi/algorithm/repository/ShadowSqlServerMigrationScriptTest.java` +- Modify: `doc/algorithm-shadow/README.md` + +**Interfaces:** +- Produces: manifest id `FT546WMS_SHADOW_ONLY` and version `2026-08-12-1`. +- Guarantees: install refuses any existing `alg` schema; uninstall refuses missing/wrong manifest or any unknown top-level `alg` object. + +- [ ] Add static tests requiring the exact manifest, fail-fast existing-schema guard, uninstall manifest validation, and unknown-object rejection. +- [ ] Run the SQL migration test and confirm RED. +- [ ] Add `alg.shadow_package_manifest`, fail-fast install guard, and refusal-first uninstall validation before any `DROP`. +- [ ] Add manifest output to diagnostics and exact recovery instructions to the field guide. +- [ ] Run the SQL migration test and confirm GREEN. +- [ ] Commit as `fix: verify shadow SQL package ownership`. + +### Task 9: Review, verify, and publish **Files:** - Review all changes relative to `origin/main`. diff --git a/docs/superpowers/specs/2026-08-11-ft546wms-shadow-only-design.md b/docs/superpowers/specs/2026-08-11-ft546wms-shadow-only-design.md index 6becad6..134fefa 100644 --- a/docs/superpowers/specs/2026-08-11-ft546wms-shadow-only-design.md +++ b/docs/superpowers/specs/2026-08-11-ft546wms-shadow-only-design.md @@ -8,12 +8,12 @@ Create a clean field-test branch directly from `origin/main` that runs the Java 1. The branch is based on `origin/main` and contains no license, UI, TLS, tidy, replay, PDA, WCS callback, or ACTIVE workflow integration changes. 2. Legacy stored procedures are called exactly once with the same inputs and their return values remain authoritative. -3. Shadow planning runs before the Legacy allocation so it observes the pre-decision state, but it runs in a separate `REQUIRES_NEW`, `READ_UNCOMMITTED` transaction with a configurable timeout. +3. Shadow planning is submitted immediately before Legacy allocation to maximize pre-decision observation, but runs only on a dedicated background worker in a separate `REQUIRES_NEW`, `READ_UNCOMMITTED` transaction with a configurable timeout. The Legacy thread never waits for planning or comparison. 4. A shadow exception, timeout, missing optional input, missing algorithm object, or comparison logging failure is caught and logged; it cannot skip, replace, roll back, or rewrite the Legacy result. 5. No ACTIVE entry point is exposed by `ruoyi-admin`. Startup rejects any algorithm mode other than `SHADOW` when shadow observation is enabled. 6. Shadow mode writes only `alg.*` read-model and diagnostic objects. It never calls reservation, tidy, deep-workflow, replay, move-completion, or other procedures that write WMS business tables. 7. Shadow observation is disabled by default and must be enabled explicitly in the IDEA Run Configuration. -8. At most one shadow planning operation runs per JVM. Concurrent observations are skipped immediately without queueing or delaying Legacy allocation. +8. At most one shadow observation cycle runs per JVM. The dedicated executor has one daemon worker, zero queue capacity, and rejects busy submissions immediately without running work on the Legacy thread. ## Architecture @@ -21,8 +21,8 @@ The existing `fortucky-algorithm` planner and JDBC read model are reused as an i - `ShadowObservationProperties` controls enablement, warehouse fallback, and timeout. - `ShadowSafetyGuard` validates the effective mode at startup. -- `ShadowPlanningObserver` performs fail-open inbound/outbound planning in an independent transaction, admits at most one plan per JVM, and returns an immutable observation token. -- Legacy services invoke the observer immediately before their existing stored procedure, execute the original Legacy code unchanged, then submit the Legacy result for comparison logging. +- `ShadowObservationGateway` submits fail-open inbound/outbound planning to a dedicated single-thread, zero-queue executor and returns a nonblocking completion token. +- Legacy services submit the observation immediately before their existing stored procedure, execute the original Legacy code unchanged, then complete the in-memory token with the Legacy result. The background worker performs comparison only if both results arrive within the configured observation deadline. There are no algorithm controllers, ACTIVE schedulers, workflow callbacks, reservation calls, or changes to the frontend. @@ -31,22 +31,24 @@ There are no algorithm controllers, ACTIVE schedulers, workflow callbacks, reser ### Inbound 1. Load the same inbound job that `origin/main` already loads. -2. Ask the shadow observer to compute a proposed location. Failures become a `shadow planning failed` log entry. +2. Submit shadow planning to the background worker without waiting. Busy/rejected submissions become no-op completion tokens. 3. Invoke the existing `allotInJobSpace` stored procedure exactly once. 4. Reload and return the Legacy job exactly as `origin/main` does. -5. Log the proposed shadow location beside the Legacy location. +5. Complete the in-memory token with the Legacy location; the background worker logs the comparison if planning and Legacy results both complete before the observation deadline. ### Outbound -1. Before each existing automatic or manual Legacy allocation call, ask the observer for a plan. +1. Before each existing automatic or manual Legacy allocation call, submit a background observation without waiting. 2. Missing project/stock data is recorded as a shadow rejection; it never blocks the Legacy procedure. 3. Invoke the original `autoAllot` or `manualAllot` procedure exactly once. 4. Preserve the original success/warning response construction. -5. Log the shadow status/code beside the Legacy procedure code. +5. Complete the in-memory token with the Legacy procedure code; comparison logging remains background-only. ## Transaction and failure isolation -Shadow planning uses `TransactionTemplate` with `PROPAGATION_REQUIRES_NEW`, `ISOLATION_READ_UNCOMMITTED`, read-model writes enabled, and a short timeout. The transaction completes or rolls back before Legacy allocation starts, so algorithm locks and rollback state cannot leak into the Legacy transaction. A JVM-local nonblocking gate permits one planning call at a time; a concurrent call returns an empty observation immediately, while successful and failed calls both release the gate in `finally`. Comparison logging is not gated. The adapter catches runtime failures at its public boundary and returns an empty observation. +Shadow planning uses `TransactionTemplate` with `PROPAGATION_REQUIRES_NEW`, `ISOLATION_READ_UNCOMMITTED`, read-model writes enabled, and a short timeout. A dedicated `ThreadPoolExecutor` has one daemon worker, a `SynchronousQueue`, and `AbortPolicy`: no task queues, rejection never falls back to the Legacy caller, and at most one observation cycle is active per JVM. The submitted task plans, waits only in the background for the Legacy completion token, and writes the comparison. Runtime failures, rejection, interruption, and observation-deadline expiry are caught in the gateway. + +Because the Legacy call starts immediately after submission, thread scheduling and database timing can let a shadow read overlap or follow the Legacy write. This is an accepted data-quality limitation of zero-wait shadowing. Shadow results are diagnostic only and must not be interpreted as a guaranteed pre-Legacy snapshot. The integration never queries `alg.tidy_mode` and never uses an update lock for admission control. @@ -62,7 +64,9 @@ The integration never queries `alg.tidy_mode` and never uses an update lock for It excludes tidy, replay, reservation mutation procedures, deep workflow, outbound workflow, WCS completion, PDA callback, and any stored procedure that inserts, updates, or deletes WMS business rows. -`uninstall-shadow.sql` removes only shadow-owned `alg` objects and the `alg` schema. It never modifies WMS business rows. Operators must export `alg.decision_log` before uninstalling. +Installation refuses to run if the `alg` schema already exists. It creates an `alg.shadow_package_manifest` row with the exact package id and version before creating the remaining objects. + +`uninstall-shadow.sql` refuses to run unless the manifest id/version matches and every top-level object in `alg` is on the package allow-list. Only then does it remove the shadow-owned objects and schema. It never modifies WMS business rows. Operators must export `alg.decision_log` before uninstalling. ## Configuration