Feature serverselect#110
Open
ascottDI wants to merge 10 commits into
Open
Conversation
added 8 commits
June 16, 2026 16:53
…rgument validation, updates to test.csv and added integeration testing
…tly throughout. Fixed a couple errors
…han pretend, updated testing to cover this and added more robust logging to main script
DIReview Summary1 critical | 7 warning(s) | 0 suggestion(s)
|
DIReview Summary1 critical | 2 warning(s) | 0 suggestion(s)
|
DIReview Summary1 critical | 0 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.serverselect — TorQ Modularisation PR
Summary
Extracts the server-selection logic from TorQ's
.gwnamespace (gatewaylib.qandgateway.q) into a standalone kdb-x module:di.serverselect. The module maintains a registered pool of backend servers and selects from them by servertype or attribute requirements. It satisfies the di.* module contract: dependency injection viainit, a clean exported API, and no hard module dependencies beyond an injected logger.Background
TorQ's gateway uses a
.gwnamespace to track connected backend servers and route queries to them. Server registration, active-flag management, and selection logic were entangled with the gateway's query execution and connection-handling code. This PR extracts the server-selection layer — the pool of registered servers and the logic to pick from it — as an independently loadable and testable unit.This PR is part of the broader TorQ → kdb-x modularisation effort. The extracted module removes the dependency on TorQ's global process framework and can be used in any gateway or proxy that needs to maintain a backend server pool.
Changes
New files
di/serverselect/serverselect.qinit,addserverfull,addserverattr,addserver,setserveractive,getserverstable,addserversfromtable,getservers,selector,getserverbytype,gethandlebytype,gethpbytype,getserveridsand internal helpersdi/serverselect/init.qserverselect.qand declares the export listdi/serverselect/test.csvdi/serverselect/integration.qdi/serverselect/serverselect.mdDifferences from TorQ original
.gwdi.serverselect.lg.o/.lg.ecallslogdependency, required viainit(no fallback).gw.serverstable.z.m.serversmodule-local mutable statedi.serverselect:prefixusesingleton,init[deps]pattern,export:listattributematch(internal); exposed via thegetserversattribmatchcolumn.servers.SERVERShard-coded lookupaddserversfromtable[proctypes;conntable]accepting any connection tableLogging contract (injected dependency)
Logging is an injected dependency, wired via
init— there is no default logger and the module does not loadkx.logitself; initialising the logging framework is the job of the start-up script or the user.initmust be called before any other function.init[deps]takes a single dict carrying the requiredlogdependency (plus any future optional config). It errors immediately (plain signal) ifdepsis not a dict, is missing`log, orlogis not a dict exposing`info`warn`error.normlognormalises the injected logger to a binary`info`warn`error!{[c;m]}dict — each function takes a context symbolcand a message stringm. It accepts either:kx.loginstance ((use\kx.log)[`createLog][]) — detected by itsgetlvl/sinks/fmtskeys; its monadic functions are wrapped to{[c;m]}, folding context in as a"ctx: msg"` prefix; or`info`warn`error!({[c;m]};{[c;m]};{[c;m]})dict — passed through unchanged..z.m.log[\info][`ctx;"msg"](which maps 1:1 with TorQ's.lg.o[`ctx;"msg"]`).raiseerror[ctx;msg]helper that logs via.z.m.log[\error]**then** signals'"di.serverselect: ",ctx,": ",msg`, so every failure is observable in the log as well as thrown.Exported API
Hardening and fixes
updatestatskeyed onserverid, nothandle.handleis not unique (the table is keyed onserverid, and a freed handle can be reused after reconnect). Keying stats updates on the uniqueserveridprevents a single selection from skewinghits/lastpacross every server that happens to share a handle. Covered by a dedicated regression test.raiseerrorso the failure is logged:addserverfull/setserveractivecheck handle (int) and servertype/active-flag types;addserversfromtablechecks the connection table has the requiredw/proctype/attributescolumns.getserversvalidatesnameortype— it must be`servertypeor`procname(whenlookupsis not`); any other value errors rather than silently falling through to aprocnamelookup.selectorempty-table behaviour documented — it returns a null-valued row; the*bytypehelpers guard against this and return()when no active server matches.Test coverage
Two suites, both green.
Unit tests —
test.csv(k4unit), 128 assertionsinit— dependency injectiondeps, missinglog, non-dictlog, log dict missing a key;di.serverselect:error prefixinit— injected logger usedaddserver/addserverattr/addserverfull— row counts, handles, servertypes, procname/hpup population, serverid autoincrement, type-error rejectionsetserveractiveupdatestatsserverid, even when a handle is sharedgetserversattribmatch; servertype/procname filters; null lookups; per-attribute match scoring; invalidnameortyperejectedselectorroundrobin/last/anystrategies; single-row and empty-table behaviour; unknown strategy rejectedgetserverbytype/gethandlebytype/gethpbytype()for unknown type; round-robin rotation;hitsincrementgetserverids— symbol pathgetserverids— attribute pathbesteffortstrict modeaddserversfromtable`ALL; optionalprocname/hpupcolumns; missing-column rejectionIntegration test —
integration.q, 76 assertionsDrives every exported function through a realistic gateway lifecycle (register → activate/deactivate → query → select → bulk-register → error handling →
initre-wiring with a capturing logger), with aPASS/FAILsummary and a non-zero exit code equal to the number of failures.Running the tests
export QPATH=/path/to/kx/mod:/path/to/kdbx-modules/ integration test QPATH=/path/to/kx/mod:/path/to/kdbx-modules q integration.qIntegration notes
di.serverselecthas no hard module dependencies. The injectedlogis required —initthrows if it is not provided, and must be called before any other function.kx.logby passing the wholecreateLog[]instance (srvsel.init[enlist[\log]!enlist (use`kx.log)[`createLog][]]);normlogdetects and adapts it. A stripped 3-key dict of kx.log's monadic functions must **not** be passed (it would bypass detection and fail with'rank`).addserversfromtableaccepts a TorQ.servers.SERVERS-style table directly: columnsw(handle),proctype,attributesare required;procnameandhpupare optional.getserveridssupports the full TorQ gateway attribute-matching contract: cross-product matching (default), independent matching, per-servertype scoping, andbesteffortmode. Its result is consumed by the gateway viainter/:+first each(andrazefor emptiness checks), which is robust to its per-path return shape.di.serverselect:to identify the source in stack traces.Test results screenshot