Skip to content

feat: allow seeding the monitor resume position#530

Merged
cconstab merged 1 commit into
trunkfrom
feat/monitor-resume-seed
Jul 8, 2026
Merged

feat: allow seeding the monitor resume position#530
cconstab merged 1 commit into
trunkfrom
feat/monitor-resume-seed

Conversation

@cconstab

@cconstab cconstab commented Jul 7, 2026

Copy link
Copy Markdown
Member

The monitor verb is monitor:<epochMillis> <regex>: the server replays notifications received after epochMillis. last_received_time was a class attribute with no way to set it at construction, so a caller that rebuilds its AtClient after a dropped monitor (a common resilience pattern) always sent monitor:0 — replaying the entire retained backlog or, depending on server retention, missing notifications that arrived while disconnected.

Changes

  • last_received_time is now an AtMonitorConnection constructor parameter (default 0, behaviour unchanged) and is passed through AtClient.start_monitor(regex, last_received_time=...) — same construction-time-only semantics as the existing regex parameter.
  • Monitor command construction extracted into _build_monitor_command() so the resume position is testable without a connection (same pattern as _is_shared_key_notification, fix: detect shared-key notifications (call to_string()) #524).
  • The touched __init__ signature is wrapped and PEP8-spaced so the changed lines are flake8-clean.

Reference Dart SDK parity: Dart's monitor does exactly this — stayConnected() fetches getLastNotificationTime() on every reconnect and issues the monitor command via MonitorVerbBuilder..lastNotificationTime (packages/at_client/lib/src/manager/monitor.dart). Dart additionally persists the last received notification in the local keystore (local:lastreceivednotification..., gated by fetchOfflineNotifications); in at_python persistence is left to the caller, which this parameter enables. The internal heartbeat-driven restart already resumes correctly (it reuses the connection whose last_received_time is updated per notification in _run); this parameter covers the rebuild-the-client path.

Tests (test/monitor_resume_test.py, network-free): default emits monitor:0; a seeded position appears in the monitor command; AtClient.start_monitor passes the seed through to the connection.

Independent of #529 (no shared hunks; the test mocks start_heart_beat so it is thread-free either way).

The monitor verb is 'monitor:<epochMillis> <regex>': the server replays
notifications received after epochMillis. last_received_time was a class
attribute with no way to set it at construction, so a caller that rebuilds its
AtClient after a dropped monitor (a common resilience pattern, since the monitor
does not always self-recover) always sent 'monitor:0' — replaying the entire
retained backlog or, depending on server retention, missing notifications that
arrived while disconnected.

last_received_time is now a constructor parameter (default 0, unchanged
behaviour), passed through AtClient.start_monitor(), so a caller can resume from
the last notification it processed. The monitor command construction is
extracted into _build_monitor_command() so the resume position is testable
without a connection.

Adds network-free tests: default is monitor:0, a seeded position appears in the
monitor command, and AtClient.start_monitor passes the seed through.
@cconstab cconstab merged commit 81bdace into trunk Jul 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants