fix(hold): dedup equipment cache, fix portal iframe, improve Hold dashboards

- Equipment cache: add freshness gate so only 1 Oracle query per 5-min cycle
  across 4 gunicorn workers; sync worker waits before first refresh
- Portal: add frame-busting to prevent recursive iframe nesting
- Hold Overview: remove redundant TreeMap, add Product & Future Hold Comment
  columns to LotTable
- Hold History: switch list.sql JOIN from DW_MES_LOT_V (WIP snapshot) to
  DW_MES_CONTAINER (historical master) for reliable Product data; add
  Future Hold Comment column; fix comment truncation with hover tooltip
- Page status: reorganize drawer groupings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
egg
2026-02-11 09:01:02 +08:00
parent be22571421
commit e2ce75b004
18 changed files with 420 additions and 237 deletions

View File

@@ -0,0 +1,32 @@
# equipment-sync-dedup Specification
## Purpose
Ensure multi-worker equipment status cache sync performs at most one Oracle query per sync cycle, preventing redundant identical queries across gunicorn workers.
## Requirements
### Requirement: Equipment Sync Refresh SHALL Skip Redundant Oracle Queries Within Same Cycle
When multiple workers attempt to refresh the equipment status cache within the same sync cycle, only the first successful refresh SHALL query Oracle. Subsequent workers that acquire the distributed lock MUST check the freshness of the existing cache and skip the Oracle query if the cache was recently updated.
#### Scenario: Another worker already refreshed within current cycle
- **WHEN** a worker acquires the distributed lock and the `equipment_status:meta:updated` timestamp is less than half the sync interval old
- **THEN** the worker MUST release the lock without querying Oracle and return False
#### Scenario: No recent refresh exists
- **WHEN** a worker acquires the distributed lock and the `equipment_status:meta:updated` timestamp is older than half the sync interval (or missing)
- **THEN** the worker MUST proceed with the full Oracle query and cache update
#### Scenario: Force refresh bypasses freshness gate
- **WHEN** `refresh_equipment_status_cache(force=True)` is called
- **THEN** the freshness gate MUST be skipped and the Oracle query MUST proceed regardless of `meta:updated` age
### Requirement: Sync Worker SHALL Not Duplicate Init Refresh
The background sync worker thread MUST wait for one full sync interval before its first refresh attempt, since `init_realtime_equipment_cache()` already performs an initial refresh at startup.
#### Scenario: Sync worker startup after init
- **WHEN** the sync worker thread starts after `init_realtime_equipment_cache()` completes the initial refresh
- **THEN** the worker MUST wait for the configured interval before attempting its first refresh
#### Scenario: Stop signal during wait
- **WHEN** a stop signal is received while the sync worker is waiting
- **THEN** the worker MUST exit without performing a refresh