SIENTIAPDE-1974: fix(slots): address the code review of the unsubscribe fix

Code review (code-review skill, xhigh) of 057c1fd: 6 findings, all fixed.

- unsubscribe_slot handles a failing reader: the legacy OpcManager's delete()
  raises when the server session is gone, which still stopped the main loop.
  The failure is notified (OPC_UNSUBSCRIBE_ERROR_<slot>:<server>) and logged,
  the slot is dropped from that reader's books (as the v2 engine already does)
  so it can be subscribed again, and the other readers are still unsubscribed.
  Repo convention: a single server failure must not kill the pod.
- update_slot_config unsubscribes a slot deleted from Redis right away
  (_unsubscribe_removed_slots) instead of leaving it to the snapshot diff in
  update_ingestor_manager, which leaked the subscription if anything in between
  failed. The later diff call is now a no-op.
- manage_leases unsubscribes the extra slots before giving their leases back,
  so another pod never takes a slot this pod is still reading.
- unsubscribe_slot iterates a snapshot of the readers (list(...) # NOSONAR, as
  in pool.close_all / ServerReader.shutdown): the loop awaits, so a reader
  added or removed meanwhile must not break the iteration.
- Contract test with the real readers (ServerReader over the real
  SubscriptionEngine, and the legacy OpcManager): both expose `subscriptions`
  keyed by slot and both are unsubscribed (subscription deleted, pool unpinned).
- The reader test double moves to tests/unit/conftest.py (reader_double).

283 unit tests; the 3 new behavior tests fail on 057c1fd.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Eduardo Rios
2026-10-07 16:04:56 -03:00
parent 057c1fd1ce
commit 085e6090c8
5 changed files with 168 additions and 36 deletions

View File

@@ -268,12 +268,14 @@ class Ingestor(SientiaMonitoring):
overleases = list(self.ingestor_manager.managed_tags.keys())[1:]
await self.ingestor_manager.drop_slot_leases(overleases)
# Unsubscribe before giving the leases back: another pod may take a released
# slot right away, and must not find this pod still reading it
for lease in overleases:
await self.ingestor_manager.unsubscribe_slot(lease)
self.ingestor_manager.managed_tags.pop(lease)
await self.ingestor_manager.drop_slot_leases(overleases)
await self.emit_metric(
metric_object=metrics.SLOTS_MANAGED,
method='set',