fix(ac-0024): address 4 blockers: NUL byte in C identity, instance_key isolation, clear_warned gate, production wiring
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
153a563f6d
commit
f1864a6aa8
@@ -105,8 +105,12 @@ def _normalize_legacy_cost_basis(value: object) -> tuple[float | None, str, str
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _c_identity_key(instance_key: str, symbol: str) -> str:
|
||||
"""Stable C-case notification identity key (null-byte separated to avoid collisions)."""
|
||||
return f"{instance_key}\x00C\x00{symbol}"
|
||||
"""Stable C-case notification identity key (PostgreSQL TEXT-safe, collision-resistant).
|
||||
|
||||
Uses ASCII Unit Separator (\\x1f) which PostgreSQL TEXT accepts (only \\x00 is
|
||||
forbidden) and which cannot appear in well-formed instance_key / symbol values.
|
||||
"""
|
||||
return f"{instance_key}\x1fC\x1f{symbol}"
|
||||
|
||||
|
||||
def _build_alarm_fields(
|
||||
@@ -286,12 +290,17 @@ def _retry_pending_resolved(
|
||||
notification_store,
|
||||
*,
|
||||
case_code: str,
|
||||
instance_key: str,
|
||||
active_identity_keys: set[str],
|
||||
) -> None:
|
||||
"""Retry pending_resolved publishes for identities no longer actively mismatched.
|
||||
|
||||
Called at the end of each reconcile pass to drain the pending_resolved queue
|
||||
for identities that have since been cleared by operator sync/verify.
|
||||
|
||||
Only processes states belonging to ``instance_key`` so that a shared
|
||||
PostgreSQL store used by multiple scheduler instances does not let instance A
|
||||
re-publish resolved events for instance B's pending states.
|
||||
"""
|
||||
try:
|
||||
pending = notification_store.list_states_by_lifecycle("pending_resolved")
|
||||
@@ -304,6 +313,8 @@ def _retry_pending_resolved(
|
||||
for state in pending:
|
||||
if state.get("case_code") != case_code:
|
||||
continue
|
||||
if state.get("instance_key") != instance_key:
|
||||
continue # belongs to a different scheduler instance — do not touch
|
||||
identity_key = state["identity_key"]
|
||||
if identity_key in active_identity_keys:
|
||||
continue # still mismatched — do not retry resolved yet
|
||||
@@ -446,6 +457,7 @@ def reconcile_core_holdings(
|
||||
alarm_emitter,
|
||||
notification_store,
|
||||
case_code="B",
|
||||
instance_key=instance_key,
|
||||
active_identity_keys=active_b_identity_keys,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
@@ -568,6 +580,7 @@ def reconcile_positions(
|
||||
alarm_emitter,
|
||||
notification_store,
|
||||
case_code="C",
|
||||
instance_key=instance_key,
|
||||
active_identity_keys=active_c_identity_keys,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
@@ -857,8 +870,8 @@ def sync_slot_to_broker(
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
_log.exception("sync: failed to clear_warned for slot=%s", slot_id)
|
||||
# req-0007 / ac-0024: publish B-case resolved alarm (best-effort, warning clear kept).
|
||||
if alarm_emitter is not None and notification_store is not None:
|
||||
# req-0007 / ac-0024: publish B-case resolved alarm only after warning is cleared.
|
||||
if warning_cleared and alarm_emitter is not None and notification_store is not None:
|
||||
try:
|
||||
_notify_resolved(
|
||||
alarm_emitter,
|
||||
|
||||
Reference in New Issue
Block a user