feat(database): collect Sentinel topology (valkey#2158 groundwork) - #392
Conversation
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8385ee3. Configure here.
de2c6c3 to
bd8e1d7
Compare
8385ee3 to
49d4d96
Compare
bd8e1d7 to
26fe55a
Compare
49d4d96 to
bad30f9
Compare
26fe55a to
0324e81
Compare
bad30f9 to
936bbb5
Compare
0324e81 to
b32aec5
Compare
936bbb5 to
822fd2d
Compare
b32aec5 to
9e4aef4
Compare
822fd2d to
7443fdc
Compare
KIvanow
left a comment
There was a problem hiding this comment.
-
Nothing here has run against a live Sentinel. The modelled field names and the reply shape come from docs, not a real reply. If the shape is off, parseSentinelNodes returns [] with no error, and the detector built on it sees an empty topology and quietly does nothing. You called this out yourself, which I appreciate. But I'd want a sentinel service in a dedicated compose file (kept off the default CI path so it costs nothing for everyone else) landed before or with #393, so the detector isn't sitting on unverified parsing.
-
The safe-mode allowlist grows before any code uses it. You flagged this in the stack summary and I agree: CLI safe mode and the agent share the allowlist, so whitelisting the read-only SENTINEL views also opens them in betterdb cli (the SENTINEL MASTERS test flipped from reject to allow). It's a sound, reversible call, and FAILOVER/RESET/SET and friends stay blocked. Still, it widens a security surface in a groundwork PR with no consumer, so it wants an explicit yes from someone. Add a line saying the whole SENTINEL family is gated to three read-only subcommands.
Minor: port: Number(...) || 0 keeps a port-less entry at :0, which collides if the detector later keys on ip:port. And masterPort's isNaN guard reads an empty master-port as 0, not undefined, since Number('') === 0. Neither should show up in real Sentinel output, but worth a glance while there's no live test.
9e4aef4 to
ca2b56c
Compare
7443fdc to
4d2663c
Compare
|
Minor parsing issues fixed in Minor — both real, both fixed. 1. Nothing has run against a live Sentinel — agreed, and I have only partly closed it. On #393 I added tests that push a real-shaped It does not remove it for the field names: my fixture encodes the same modelled names the parser reads, so if upstream spells a field differently the test is wrong in the same direction as the code. Only a live Sentinel closes that, so your compose-file request stands. I have not added the compose service — where it lives and how it is kept off the default CI path is your and the team's call, and I would rather not guess at the CI cost trade-off. 2. Safe-mode allowlist — needs your explicit yes, so I have not touched it. You are asking for a sign-off, not a change, and it is not mine to give. Flagging one thing to make the decision concrete: the widening is to three read-only subcommands ( 60 parser tests pass, |
4d2663c to
1778d54
Compare
ca2b56c to
49b3974
Compare
KIvanow
left a comment
There was a problem hiding this comment.
-
Add the Sentinel compose service. Go ahead. Put it in its own compose file rather than docker-compose.test.yml, so the default CI run doesn't pay the startup cost, and gate a small Sentinel suite behind it. The test that earns its keep is a live SENTINEL MASTERS and REPLICAS reply run through parseSentinelNodes into detectSentinelDrift. That validates the field names against a real server, which is the gap the hand-built fixtures can't reach. If upstream spells a field differently from the modelled names, that test is where we find out instead of in production.
-
Safe-mode widening — approved. The framing is right: three read-only subcommands (MASTERS, REPLICAS, SENTINELS), shared by betterdb cli safe mode and the agent, with FAILOVER, RESET, and SET still blocked. Add that sentence to the PR body so the decision is on the record.
49b3974 to
d5e5361
Compare
|
Both points addressed. 2. Safe-mode allowlist — sentence added to the PR body, under a new "Safe-mode allowlist — scope of the widening" section, recording your sign-off and the exact scope: the 1. The Sentinel compose service is landed with #393, per your "before or with" — full detail in the reply there. Summary: It went on #393 rather than here because that is where the detector lives and the end-to-end assertion needs both halves. If you would rather the parser-level half sat in this PR so #392 is self-validating, say so and I will split it — the two Worth reporting: the live run validated the field names this PR models. A real The run also caught a real defect in my own first draft, which is the best argument for the harness: I asserted a healthy topology would be silent, and it was not. With |
- Add SentinelNodeInfo and MetricsParser.parseSentinelNodes for the flat field/value replies of SENTINEL MASTERS/REPLICAS/SENTINELS - Add getSentinelMasters/getSentinelReplicas/getSentinelPeers to the database port, the unified adapter and the agent adapter - Keep every returned field, model the stable ones, and drop entries with no usable ip - Handle RESP3 map replies as well as RESP2 flat arrays Refs #386
The new collection methods send SENTINEL over the agent transport, but SENTINEL was absent from the safe-mode allowlist, so in the default agent configuration every call was rejected at runtime and the layer could not work over an agent connection. Allow the three read-only topology subcommands; SENTINEL FAILOVER and friends stay blocked by the subcommand check. The allowlist is shared with CLI safe mode, so `SENTINEL MASTERS` is now accepted there too, and its test flips from reject to allow. A write subcommand takes its place as the rejection case. Also moves the CLUSTER SHARDS docblock back above its own function, where inserting the Sentinel parser had displaced it.
…hem as 0
Number('') is 0, so an empty port became :0 and an empty master-port became
0 rather than undefined — values that look real and collide once anything
keys on ip:port. Parse both through one guard that treats absent, empty and
non-finite alike, and drop an entry with no usable port.
1778d54 to
b9458b9
Compare

Part 1 of 2 for #386. Stacked on #391 (→ #390 → #389 → #388 → #387) — this
PR's own diff is the last commit. The detector itself is the follow-up PR; this
one is just the collection layer.
Why it is split
#386 is the only issue in the batch that needs plumbing that does not exist.
Before this PR there was no Sentinel support anywhere:
getRole()returns abare
{role: 'sentinel'}with no other fields,RoleInfohas no Sentinelmembers, and there is no
SENTINELcommand, parser, or type in the codebase.Splitting means the collection layer lands and is reviewable on its own — it
unlocks any future Sentinel work, not just this detector — and the detector PR
stays a detector PR instead of being half plumbing.
What is here
SentinelNodeInfoinmetrics.types.ts.MetricsParser.parseSentinelNodesfor the flat field/value replies thatSENTINEL MASTERS,SENTINEL REPLICAS <master>andSENTINEL SENTINELS <master>all share.getSentinelMasters/getSentinelReplicas/getSentinelPeersonDatabasePort, the unified adapter, and the agent adapter (which alsoimplements the port — the compiler caught that one).
Sentinel's exact field set varies by subcommand and version, so the modelled
fields are the stable ones (
name,ip,port,runid,flags,master-host,master-port) and every returned field is also kept infields.That way the detector can reach something like
slave-repl-offsetlater withoutanother round of plumbing. Entries with no usable
ipare dropped — an endpointis the entire point of this view. RESP3 map replies are handled alongside RESP2
flat arrays via the existing
flatReplyToMap.Safe-mode allowlist — scope of the widening
Reviewed and signed off by @KIvanow. The whole
SENTINELfamily is gated tothree read-only subcommands:
MASTERS,REPLICASandSENTINELS(
packages/shared/src/types/command-safety.ts). The allowlist is shared bybetterdb clisafe mode and the agent, so whitelisting these opens the samethree views in both. Every mutating subcommand —
FAILOVER,RESET,SET,MONITOR,REMOVE— stays blocked, andcli.service.spec.tsasserts bothsides:
SENTINEL MASTERSis allowed,SENTINEL FAILOVER mymasteris refused.Verification, honestly stated
8 parser unit tests cover masters, replicas with their configured master,
comma-separated flag splitting, unmodelled field retention, the RESP3 map shape,
ip-less entries, empty/non-array replies, and an absentmaster-port.This has not been exercised against a live Sentinel. There is no Sentinel
service in any of the six
docker-compose*.ymlfiles, so there is currently noway to integration-test it. I did not add one — that changes the local dev or CI
environment for everyone and is your call, not mine. If you want integration
coverage, the options are a
sentinelservice indocker-compose.test.yml(CIstartup cost) or a dedicated compose file used only by a Sentinel test suite.
Happy to add either.
Full API unit suite: 2591 passed. The 10 failing suites and 1 failing test are the
pre-existing
license.serviceand entitlement failures, unchanged by this PR.tsc --noEmitclean.Note
Low Risk
Additive read-only data path and narrowly scoped safe-mode allowlist; no auth or write paths, though SENTINEL commands are newly reachable in safe mode for three subcommands only.
Overview
Adds read-only Sentinel topology collection ahead of a follow-up drift detector: a
SentinelNodeInfomodel,MetricsParser.parseSentinelNodesfor sharedSENTINEL MASTERS/REPLICAS/SENTINELSreplies (RESP2 flat lists and RESP3 maps), andgetSentinelMasters/getSentinelReplicas/getSentinelPeersonDatabasePort, the unified adapter, and the agent adapter.Parser behavior drops entries without a usable
iporport(empty port is not treated as:0); extra Sentinel fields are kept infieldsfor later use without more plumbing.Safe mode widens the shared CLI/agent allowlist so only
SENTINEL MASTERS,REPLICAS, andSENTINELSare permitted; mutating subcommands (e.g.FAILOVER) stay blocked, with tests for both cases.Reviewed by Cursor Bugbot for commit b9458b9. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes