Feature tickerplant - #125
Open
ascottDI wants to merge 5 commits into
Open
Conversation
added 3 commits
August 13, 2026 09:44
…plant are merged to the same branch
DIReview Summary1 critical | 2 warning(s) | 0 suggestion(s)
|
DIReview Summary0 critical | 3 warning(s) | 0 suggestion(s)
|
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.
di.tickerplant
Summary
Adds
di.tickerplant, the core tick-capture process for the modular TorQ world: it receives updatesfrom feeds, stamps them, writes them to a tickerplant log for recovery, and publishes them to
subscribers, rolling the log at end of day. It is the modular replacement for TorQ's
code/processes/tickerplant.q.It orchestrates three hard dependencies —
di.pubsub(subscribe/publish),di.eodtime(roll timing)and
di.tplog(log check/repair) — with an injected logger and timer.Motivation
TorQ's
tickerplant.qhand-rolls the whole capture loop —.z.*handling, the.upublish/subscribemachinery, log writing and rolling, and end-of-day scheduling — in one process file. The modular
framework already provides those pieces as standalone modules (
di.pubsub,di.eodtime,di.tplog,di.timer), so this extraction re-expresses the tickerplant as an orchestration layer over them:capture, stamp, log, publish, roll — delegating pub/sub, roll timing and log recovery to the modules
that own them.
Design
Root tables and the upd contract
The captured tables live at root, not in
.z.m: a tickerplant owns its tables, feeds insert intothem, and
di.pubsubreads them by name, so they cannot be module-local. This is the one deliberateroot-state exception; all other mutable state is module-local.
di.torqwires the process's rootupdtotickerplant.updso feeds publish to it.Capture modes
updinserts into the root table; a timer job publishes the accumulated rowsevery
batchperiodand clears them.updpublishes each update immediately and does not buffer.Both modes stamp the update (prepending a data timestamp unless one is already present) and log every
message.
End of day
The roll fires when the current time passes
di.eodtime's next roll timestamp, checked on everyupdand every timer tick.
endofdayflushes the buffer, notifies subscribers, rolls the log to the nextday, and refreshes the roll time and data-timestamp offset from
di.eodtime.Use of
di.tplogdi.tplogis used only for check/repair on recovery: whenopenlogfinds a pre-existing log it runsit through
tplog.check, repairing a corrupt one. The tickerplant opens for append and rolls thelog itself, rather than using
di.tplog'sopen/roll, because those replay the log throughupd—which a tickerplant must not do to its own log. (This module was updated to the new
di.tplogcontract: it now calls
tplog.initduring its owninit, andtplog.checkis 1-arg.)No
di.handlersdependencySubscriber-disconnect cleanup is handled by
di.pubsub's own.z.pc, so this module takes no handlerdependency.
di.pubsubshould migrate todi.handlersso.z.*is not assigned outside the centralregistry — tracked separately, out of scope here.
Dependencies
di.pubsubuse)di.eodtimeuse)di.tploguse)log`info`warn`errordict of{[ctx;msg]}functionstimerdi.timer's exports (must exposeaddjob)Hard deps are declared in
deps.q; the injectedlogandtimerare validated bydi.depcheck'score-contract check, not declared there.
initvalidates every dependency strictly and signalsimmediately if any is missing or malformed.
Public API
init[deps]`g#onsym), open today's log, and schedule the batch/roll timer job. Idempotent.upd[table;data]subscribe[tables;filters]di.pubsub).endofday[]getcounts[]`i`j`d— messages published, messages logged, trading date.gettables[]version"0.1.0").updvalidates its arguments and routes failures through a log-then-signal helper.getapimeta[]exposes the callable API for central registration with
di.api; the framework plumbing (init,getapimeta,version) is intentionally excluded.initalso accepts optional config keys:batch(1b),batchperiod(timespan),logdir(string,""disables logging),logname(prefix, default"tp"),subtables(symbol list), and thedi.eodtimekeys (rolltimezone/datatimezone/rolltimeoffset) forwarded verbatim.Testing
test.csv/test.q(k4unit), 18 checks, run against the realdi.pubsub,di.eodtime,di.tploganddi.timer— no dependencies are mocked. The timer is used withoutinit(so no live.z.ts; its job is exercised throughendofday), and a capturing logger is shared across the modulesso their output is assertable.
Coverage: the metadata/version contract; strict
initdependency validation (afailrow per guard);init materialising the root tables and scheduling the timer job; batch and zero-latency
upd;endofdayflushing and rolling;updinput validation; and the twodi.tplogintegration points — atickerplant-written log replaying through
di.tplog, and rolling into a corrupt log repairing it viatplog.check.Because two tests replay/repair through
di.tplog, the suite passes oncedi.tploganddi.tickerplantare present together (as they merge) — it is not runnable against an olderdi.tplog.Files
The module version lives in a plain-text
VERSIONfile, read ininit.q(version:trim first read0\:::VERSION) and exported fordi.depcheck, matching the convention used bydi.servers,di.clienttrackinganddi.tplog`.