Feature server - #120
Open
ascottDI wants to merge 10 commits into
Open
Conversation
added 7 commits
July 28, 2026 16:19
…ill mocked awaiting merge
DIReview Summary0 critical | 6 warning(s) | 0 suggestion(s)
|
DIReview Summary0 critical | 1 warning(s) | 0 suggestion(s)
|
…ore getservers ALL/null contract to couple correctly with di.heartbeat and prevent self-connections
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.servers — TorQ Modularisation PR
Summary
Extracts TorQ's
.serversconnection management (code/handlers/trackservers.q+servers.q) into a standalone kdb-x module:di.servers. The module dials a staticprocess.csv"phone book", maintains a pool of live handles to peer processes, hands them out by proctype via a selection algorithm, and recovers dropped connections. It satisfies the di.* module contract: one-arginit[deps]dependency injection, strict validation with no fallback, a conservative exported API,getapimetafordi.api, and no harddi.*dependencies (log,timerandhandlersare all injected).Background
TorQ's
.serverstracks connected backend processes and lets a gateway (or any process) look up a handle to a peer by type. In TorQ this was entangled with the discovery service, password/access-list files, non-TorQ process tracking, environment reads, and the global process framework.This PR is part of the broader TorQ → kdb-x modularisation effort.
di.serversis a FRAMEWORK-tier module scoped down for v1: no discovery service, no password/access-list files, no non-TorQ process tracking, no FinSpace, and no environment reads.process.csvis treated as a static phone book (who to dial), not an identity source — a process's self-identity arrives via config, injected bydi.torq. This makes connection management independently loadable, testable, and drivable from resolved config rather than TorQ globals.Changes
New files
di/servers/servers.qinit,startup,getservers,gethandlebytype,waitfortype,getapimeta, plus internal helpers (retry,cleanup,opencon,formathp,readprocesscsv,retryrows,selector,updatestats,signalfound,raiseerror)di/servers/init.qservers.qand declares the export listdi/servers/test.csvdi/servers/test.qdi/servers/servers.mdDifferences from TorQ original
.serversdi.servers.lg.o/.lg.ecallslogdependency, three flat vars (.z.m.loginfo/logwarn/logerr), binary{[c;m]}.z.pcvia.dotz.sethandlersdep;.z.pcregistered as a simple/observer event viaregister[event;phase;nm;pri;func](fan-out, side-effect only).timerframeworktimerdep;timer[`addjob][`custom]6-arg variant, a 10s (mode-1, seconds)serversretryjob.servers.SERVERStable.z.m.SERVERSmodule-local mutable stateprocess.csv/ discoveryproctype/procnameindeps);process.csvis a dial-only phone bookprocess.csvpath arrives viaconfigprocesscsv` (resolved by di.torq)process.csvparsinghost,port,proctype,procname4-column layout; header validated and rejected loudly (positional read would otherwise misparse silently)startupdepcycleswaitfortype[proctype;timeoutms;pollms]— "fail fast, but wait for a hard dependency"; caller decides if timeout is fatalusesingleton, strictinit[deps](no fallback),raiseerrorlog-then-signal,export:list,getapimetaExported API
init/getapimetaare exported as plumbing (di.torq calls them by convention) but are deliberately not listed ingetapimeta[]— the registry describes the callable API, not plumbing.deps(injectables + config, assembled by di.torq)inittakes a singledepsdict carrying both the injectable dependencies and this process's resolved config slice (the one-arg convention shared withdi.config):log`info`warn`error{[c;m]}logger dict —di.logsatisfies it directlytimerdi.timerexport; callstimer[`addjob][`custom](6-arg variant)handlersdi.handlerscontract;register[event;phase;nm;pri;func]proctype/procnameprocess.csvconnections.tomlcascade — normalised). Default: noneprocesscsvprocess.csv. Required only onceconnectionsis non-emptyinitwires the deps, records self-identity, and installs two one-time process-global side effects — the.z.pccleanup observer and the 10s retry job — guarded by an internalregisteredflag soinitis idempotent (a duplicatedi.timer.addjobid would otherwise throw).initdoes not open connections; that isstartup's job.process.csv format (v1)
Strict 4-column layout — header validated up front and rejected loudly if reordered or wider:
startupreads this phone book, drops this process's own row (matched onproctype+procname), connects to every row whose proctype is inconnections, and records each as aSERVERSrow. A failed connect is logged, not raised, and left asw:0Niforretryto reopen.startupis idempotent — it skips procs already tracked, so a repeat call (or a grownprocess.csv) adds only new rows, never a duplicate or a leaked second handle.Connection recovery
.z.pcobserver marks the closed handle's row disconnected (w:0Ni,endpstamped).serversretryjob (10s) runscleanupto sweep handles that vanished fromkey .z.Wwithout a clean.z.pc, then reopens every dead (null) handle viaretry.waitfortypereusesretryto block at startup until a hard-dependency proctype comes up (or a timeout elapses).Test coverage
Tests are in
test.csvand run viak4unit(the live-handle tests spin up real peer processes viatest.q):Areas covered
init— dependency + config validationlog/timer/handlers, non-dict / partiallog, badtimershape (missingaddjob/customvariant), non-dicthandlers, missing/non-symbolproctype/procname— all with the"di.servers"error prefix (plain signal; logger not wired yet)init— wiring and idempotencyinitrefreshes refs without re-registering the.z.pchandler or the retry jobstartup— live + dead peergethandlebytype— live remote handlecleanup+retrykey .z.W)waitfortype1b; timeout case returns0bstartupidempotencyreadprocesscsvfail-loudgetapimetagetapimeta[]lists exactly the callable exports (plumbing omitted) with the registry columnsdi.logintegrationdi.loglogger itself, not just the recording mockAll green in local KDB-X runs (live-peer suite).
Notes
di.servershas no harddi.*module dependencies —log,timerandhandlersare injected and all required;initsignals immediately (plain', logger not yet wired) if any is missing.formathpbuilds a`:host:porthandle); a futureSOCKETTYPEconfig would reintroducetcps/unixwhen there is a real requirement and a test — no unexercised branches ship.hopenuses the single 2-item timeout formhopen (handle;timeoutms)(the dyadic form throws'rank); defaultHOPENTIMEOUTis 2000ms.10000would have been ~2.8h, the latent typo that made dead-handle recovery never fire in early POCs.SERVERSis mutated as a source-level.z.m.SERVERS(catenate-and-reassign) so it picks up the compile-time module-local rewrite; a symbol-based`.z.m.SERVERS`insert would silently target the wrong table.di.torqowns centraldi.apiregistration of thegetapimeta[]rows.