Repository navigation
feat(databases): count databases in the workspace - #254
Conversation
`hotdata databases count` reports how many managed databases a workspace
holds. Prints a bare integer by default so it composes in scripts;
--output json/yaml render {"count": N} / count: N.
`databases list` shows one page on purpose, and the API's per-page
`count` field is a page size, not a total, so there was no way to ask
the question without paging by hand and adding up rows.
Backed by GET /v1/databases/count (runtimedb #1053, SDK 0.11.0), which
answers from a single indexed aggregate. The alternative — draining the
paged listing client-side — costs one sequential round trip per 100
databases and discards every row on arrival.
Also in this change, both forced by the 0.11.0 upgrade:
- `databases().list` takes `search` and `batch` filters now, so the
three call sites pass `None, None`. Behaviour is unchanged: the client
omits absent query params and the server applies no filter without
them.
- `TableInfo` gained required `partition_by` / `sorted_by` fields, so
the information-schema mocks in the databases and indexes tests
declare them. Without this the mocks fail to deserialize and the test
process aborts rather than failing an assertion.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
3c3cd12 to
a21fab1
Compare
| /// One request. The list endpoint's `count` field is a page size, not a | ||
| /// workspace total, so this used to drain every page and take the length — | ||
| /// which cost a round trip per 100 databases and threw every row away on | ||
| /// arrival. `GET /v1/databases/count` answers it in a single indexed query. |
There was a problem hiding this comment.
super nit: "this used to drain every page" reads as CLI history, but no released version of this command ever drained — the drain only existed in an earlier iteration of this PR (not blocking). Stating it in the present tense keeps the useful part (why the endpoint is used) without implying a past behaviour a reader might go looking for in the changelog, e.g. "the list endpoint's count field is a page size, not a workspace total, so a client-side total would mean a round trip per 100 databases with every row discarded on arrival."
| the caller (`src/commands/databases.rs:930`), so counting today means paging by | ||
| hand and adding up rows. | ||
|
|
||
| The API offers no total-count endpoint. `GET /v1/databases` returns |
There was a problem hiding this comment.
nit: this spec is committed alongside an implementation that contradicts it (not blocking). "The API offers no total-count endpoint" is false as of this change — the code calls GET /v1/databases/count (src/commands/databases.rs:1041). The ## Counting section ("reuses the existing cursor-drain helper unchanged", plus the ### Cost paragraph accepting the drain) and testing item 1 ("Drains and sums across pages") all describe the approach that was rejected. This is the only file under docs/superpowers/specs/, so there is no established convention marking these as frozen historical snapshots — a reader will take it as the design of record. Either update the Problem/Counting/Cost/Testing sections to the endpoint-based design, or add a note at the top recording that the drain approach was superseded by the 0.11.0 count endpoint.
There was a problem hiding this comment.
Cycle 3: the spec doc that contradicted the implementation is no longer part of this PR, so the prior nit is moot. No blocking issues in the count command, the 0.11.0 call-site updates, or the information-schema mock changes. Note that CI / test and Integration Tests / integration were still pending when this review ran — I did not run anything myself, so the SDK signature changes (list(.., None, None), databases().count(None, None), resp.total) are unverified against a real compile.
Why
There was no way to ask how many managed databases a workspace holds.
databases listshows one page on purpose, and the API's per-pagecountfield is a page size rather than a total — so counting meant paging by hand and adding up rows.What
tableprints a bare integer and nothing else, so$(hotdata databases count)drops straight into a script. The spinner writes to stderr and suppresses itself when stderr isn't a TTY, so stdout stays clean when piped. An empty workspace prints0in every format — no "No databases found." hint, because zero is the answer, not an empty result needing explanation.No
--limit/--cursor: a count is inherently whole-workspace, and paging flags would let a caller ask for a "count" that silently described one page — the exact confusion this command removes.How
Backed by
GET /v1/databases/count(runtimedb #1053, shipped in SDK 0.11.0), which answers from one indexed aggregate. The first implementation of this command drained the paged listing client-side; that costs a sequential round trip per 100 databases (DATABASE_LIST_MAX_LIMIT) and throws every row away on arrival — roughly 8–25 minutes at a million databases, since cursor paging can't be parallelized. The endpoint exists so the CLI doesn't have to do that.The output field stays
countwhile the API answers withtotal. That's deliberate:{"count": N}is what reads naturally from a command calledcount, whereas the API needed the other name because its list response already spendscounton a page size.Also here — both forced by the 0.11.0 upgrade
databases().listgainedsearchandbatch, so the three call sites passNone, None. Behaviour is unchanged — the client omits absent query params, and the server applies no join and no predicate without them.TableInfogained requiredpartition_by/sorted_by, so ten information-schema mocks across the databases and indexes tests now declare them. Worth knowing how this failed: the mocks stopped deserializing, which hit an error path that calls.exit(), so the test process aborted instead of a test failing. The harness reported "test exited abnormally" with no failing test named.Tests
count_databases_reads_the_total_from_the_count_endpoint— mocks the endpoint withexpect(1)and registers the listing withexpect(0), so a regression back to draining pages fails on the wrong request being made, not just a wrong number.count_databases_empty_workspace_is_zerodatabase_count_serialization_shapes—{"count":7}/count: 7databases --helplistscount;databases count --helpdocuments--output308 unit + 16 integration tests pass.
cargo fmtandcargo clippy --all-targetsclean.Docs
skills/hotdata/SKILL.mdgains the command and a bullet noting it spans all pages and prints a bare integer for scripting.README.mdis a narrative quickstart that doesn't enumerate subcommands — unchanged.