feat: allow seeding the monitor resume position#530
Merged
Conversation
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.
cpswan
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The monitor verb is
monitor:<epochMillis> <regex>: the server replays notifications received afterepochMillis.last_received_timewas a class attribute with no way to set it at construction, so a caller that rebuilds itsAtClientafter a dropped monitor (a common resilience pattern) always sentmonitor:0— replaying the entire retained backlog or, depending on server retention, missing notifications that arrived while disconnected.Changes
last_received_timeis now anAtMonitorConnectionconstructor parameter (default0, behaviour unchanged) and is passed throughAtClient.start_monitor(regex, last_received_time=...)— same construction-time-only semantics as the existingregexparameter._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).__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()fetchesgetLastNotificationTime()on every reconnect and issues the monitor command viaMonitorVerbBuilder..lastNotificationTime(packages/at_client/lib/src/manager/monitor.dart). Dart additionally persists the last received notification in the local keystore (local:lastreceivednotification..., gated byfetchOfflineNotifications); 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 whoselast_received_timeis updated per notification in_run); this parameter covers the rebuild-the-client path.Tests (
test/monitor_resume_test.py, network-free): default emitsmonitor:0; a seeded position appears in the monitor command;AtClient.start_monitorpasses the seed through to the connection.Independent of #529 (no shared hunks; the test mocks
start_heart_beatso it is thread-free either way).