Стабилизация восстановления Bridge и Quick Connect

This commit is contained in:
DCCONSTRUCTIONS
2026-08-23 10:45:53 +03:00
parent 71c8b043c9
commit 7d911b4b1f
6 changed files with 381 additions and 17 deletions
+150 -17
View File
@@ -12885,6 +12885,8 @@ class XgridsK1CompatibilityService:
def _validate_active_acquisition_checkpoint_lineage(
self,
*,
allow_untrusted_terminal_settlement: bool = False,
) -> _ActiveAcquisitionCheckpointTrustToken | None:
"""Recompute the current unceased checkpoint/ledger lineage locally.
@@ -12892,13 +12894,23 @@ class XgridsK1CompatibilityService:
ledger and immutable archive retain its proof material. This method is
deliberately read-only: a mismatch revokes local START/resume admission
for the process lifetime and never repairs either durable document.
``allow_untrusted_terminal_settlement`` is reserved for one narrower
caller which has already durably committed an exact, non-retained
READY/SCAN_OVER reconciliation. It may recompute a token only to cease
the old checkpoint; process-local START/resume trust stays revoked until
that terminal CAS has succeeded.
"""
if getattr(
current_trust = getattr(
self,
"_active_acquisition_checkpoint_trust",
"trusted",
) != "trusted":
)
if current_trust != "trusted" and not (
allow_untrusted_terminal_settlement
and current_trust == "unavailable"
):
return None
store = self._active_acquisition_checkpoint
if store is None:
@@ -13023,6 +13035,110 @@ class XgridsK1CompatibilityService:
)
return None
def _settle_untrusted_checkpoint_after_verified_standby(
self,
*,
reconciliation_id: str,
reconciled_record: Mapping[str, Any],
) -> bool:
"""Consume a new durable standby proof without reviving START authority.
A process can reject an ACTIVE checkpoint while an older STOP is still
unresolved. If the same process later appends an exact read-only
READY/SCAN_OVER reconciliation, that durable change is new evidence.
Recompute the lineage only for terminal checkpoint cessation; no K1
command, network mutation, runtime rehydration, or acquisition authority
is permitted by this path.
"""
if getattr(
self,
"_active_acquisition_checkpoint_trust",
"trusted",
) != "unavailable":
return False
store = self._active_acquisition_checkpoint
if store is None:
return False
try:
checkpoint_snapshot = store.snapshot()
ledger_snapshot = self._physical_command_ledger.snapshot()
record = ledger_snapshot.record
reconciliation = (
record.reconciliations[-1]
if record is not None and record.reconciliations
else None
)
if not (
checkpoint_snapshot.status in {"prepared", "active"}
and checkpoint_snapshot.checkpoint is not None
and ledger_snapshot.status == "resolved"
and record is not None
and record.as_dict() == dict(reconciled_record)
and reconciliation is not None
and reconciliation.reconciliation_id == reconciliation_id
and reconciliation.resolution == "physical-standby-observed"
and reconciliation.kind
in {
"ambiguous-outcome",
"prepared-stop-classification",
"resolved-active-cessation",
}
and reconciliation.observation.source
== "explicit-read-only-reconciliation"
and reconciliation.observation.session_state
in {"ready", "scan_over"}
and not reconciliation.observation.project_bound
and not reconciliation.observation.init_ready
and not reconciliation.observation.mqtt_retained
):
return False
token = self._validate_active_acquisition_checkpoint_lineage(
allow_untrusted_terminal_settlement=True,
)
if token is None:
return False
settled = self._settle_restart_checkpoint_after_verified_standby(
token=token,
reconciliation_id=reconciliation_id,
reconciled_record=reconciled_record,
)
if not settled:
return False
committed = store.snapshot().checkpoint
if not (
committed is not None
and committed.state == "ceased"
and committed.acquisition_id == token.acquisition_id
and committed.original_start_operation_id
== token.root_start_operation_id
):
return False
with self._lock:
if self._active_acquisition_checkpoint_trust == "unavailable":
self._active_acquisition_checkpoint_trust = "trusted"
self._active_acquisition_checkpoint_reason = None
logger.info(
"untrusted K1 checkpoint closed from new durable standby proof",
extra={
"event_code": (
"active_acquisition_checkpoint_untrusted_standby_settled"
),
"acquisition_id": token.acquisition_id,
"reconciliation_id": reconciliation_id,
"device_write_performed": False,
"automatic_retry": False,
},
)
return True
except (
ActiveAcquisitionRecoveryCheckpointError,
PhysicalCommandLedgerError,
OSError,
ValueError,
):
return False
def _require_active_acquisition_checkpoint_store(
self,
) -> ActiveAcquisitionRecoveryCheckpointStore:
@@ -16105,8 +16221,7 @@ class XgridsK1CompatibilityService:
else None
)
checkpoint_settlement_required = bool(
checkpoint_trust_token is not None
and ledger_snapshot.status == "resolved"
ledger_snapshot.status == "resolved"
and record is not None
and latest_reconciliation is not None
and latest_reconciliation.resolution
@@ -16128,14 +16243,24 @@ class XgridsK1CompatibilityService:
if checkpoint_settlement_required:
assert record is not None
assert latest_reconciliation is not None
settled = self._settle_restart_checkpoint_after_verified_standby(
token=checkpoint_trust_token,
reconciliation_id=(
latest_reconciliation.reconciliation_id
),
reconciled_record=record.as_dict(),
)
if not settled:
if checkpoint_trust_token is not None:
settled = self._settle_restart_checkpoint_after_verified_standby(
token=checkpoint_trust_token,
reconciliation_id=(
latest_reconciliation.reconciliation_id
),
reconciled_record=record.as_dict(),
)
else:
settled = (
self._settle_untrusted_checkpoint_after_verified_standby(
reconciliation_id=(
latest_reconciliation.reconciliation_id
),
reconciled_record=record.as_dict(),
)
)
if checkpoint_trust_token is not None and not settled:
raise ConnectionVerificationError(
"Подтверждённый READY не закрыл старую локальную START-сессию",
reason_code=(
@@ -16369,11 +16494,19 @@ class XgridsK1CompatibilityService:
checkpoint_lineage=checkpoint_lineage,
)
elif reconciled_record is not None:
settled = self._settle_restart_checkpoint_after_verified_standby(
token=checkpoint_trust_token,
reconciliation_id=reconciliation_id,
reconciled_record=reconciled_record,
)
if checkpoint_trust_token is not None:
settled = self._settle_restart_checkpoint_after_verified_standby(
token=checkpoint_trust_token,
reconciliation_id=reconciliation_id,
reconciled_record=reconciled_record,
)
else:
settled = (
self._settle_untrusted_checkpoint_after_verified_standby(
reconciliation_id=reconciliation_id,
reconciled_record=reconciled_record,
)
)
if checkpoint_trust_token is not None and not settled:
raise ActiveAcquisitionRecoveryCheckpointError(
"restart standby checkpoint settlement failed closed"
+1
View File
@@ -535,6 +535,7 @@ _CLOSED_WEBSOCKET_SEND_ERRORS = frozenset(
"Unexpected ASGI message 'websocket.send', after sending "
"'websocket.close' or response already completed."
),
"Unexpected ASGI message 'websocket.send', after sending 'websocket.close'.",
}
)