Add the keygrabber control keywords and a testable scheduler - #34
Merged
Merged
Conversation
mikelangmayr
force-pushed
the
mike/keygrabber-control
branch
from
September 17, 2026 20:57
c145885 to
3eb22bd
Compare
mikelangmayr
force-pushed
the
mike/keygrabber-control
branch
from
September 17, 2026 22:32
3eb22bd to
a886005
Compare
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.
Stacked on #33. Gives the keygrabber a runtime control surface, and extracts the scheduler so its logic is testable and the daemon's attribute-count disable can go.
Scheduler: a due-time heap with the in-flight set, deliberately free of threads, pools and sleeping. It answers "what should run now" and nothing else, so the ordering and the skip rule are tested against an injected clock instead of by racing real threads. Twelve unit tests, none of which waitmax(due_at + interval, now)always resolved todue_at + interval, so the "never schedule into the past" comment described behaviour the code did not have. Rescheduling now measures from the claim, so a sixty-second stall on a one-second cadence yields one tick rather than sixty that can only skipenabled,isconnected,pointswritten,readerrors,writeerrors,queuedepth,skippedticks,droppedbatches,reloadandshutdown, plus<collection>.enabled,.interval,.lastsampleand.lagisconnectedreports a flag the writer thread maintains rather than pinging the database, and writing it only requests a reconnect that the writer thread performs. A getter that blocked would time out every read in flight, which is why there is a test asserting the control keywords answer while ticks are running<collection>.intervalre-applies the same headroom rule the config loader enforces, and reschedules only that collection rather than re-seeding every due time and setting the whole fleet reading at oncereload: re-reads the config file and applies it, forcing a re-resolve so a changed keyword selection is picked up without waiting out the old refresh window. A file that fails to parse leaves the running collections untouched and reports why. It refuses to add or remove collections rather than half-applying, because libby cannot withdraw a keyword and a new collection's control keywords could not appear without a restart%does not match across the dot in<collection>.enabled, so listing the control surface needshispec.keygrabber.%.%-Wdocs build verified. The control keywords are exercised over both transports, thirteen end-to-end cases eachSeparately, these tests surfaced a pre-existing socket leak in
ZmqTransport, unrelated to this change.Discovery.stop()only sets a flag and never joins its thread, so up todiscover_interval_safterLibby.stop()has closed every socket, that thread can still broadcast, find_dealersempty but_bookpopulated, and create a DEALER nothing will close. Worth its own small fix: havesendreturn early once_stopis set.