cog-api's paginate() sliced an already-fully-materialized result: every page of a deep sweep re-ran the whole cog_spending()/cog_revenue() query and re-listified every row, just to keep up to 1000 and discard the rest. A 193,105-row/194-page fleet-wide sweep (cog_explorer's Southern guide, corpus summary build) repeated that full cost 194 times and wedged the production server for hours on 2026-08-06 -- single request, CPU-bound, single-threaded plumber process, no other request could get through, not even /health. cog_spending()/cog_revenue() gain optional limit/offset, pushed into .build_verb_sql() as SQL LIMIT/OFFSET behind the existing (already deterministic) ORDER BY. The full unpaginated row count rides along via COUNT(*) OVER() in the same scan -- exposed as a total_rows attribute -- so a caller walking pages never needs a second round trip to ask how many there are. A page now costs O(limit), not O(full result). Mutually exclusive with complete = TRUE (which fills a grid over the FULL requested (year, category) space -- pagination over a partial slice of already-grouped rows has no defined meaning for the cells it would fill) and with recipe (whose result comes from a separate, not-yet-wired query path). Both abort with a clear classed condition rather than silently ignoring the parameter. limit/offset default to NULL; every existing call site is unaffected.
30 lines
1.2 KiB
R
30 lines
1.2 KiB
R
# Mirror of test-spending-pagination.R for cog_revenue(), which shares the
|
|
# same .verb_spendrev()/.build_verb_sql() pushdown -- see that file for the
|
|
# incident this fixes.
|
|
|
|
test_that("cog_revenue limit/offset page correctly and report total_rows", {
|
|
skip_if_no_corpus()
|
|
full <- cog_revenue("121011212191", years = 2019:2020, category = NULL)
|
|
page <- cog_revenue("121011212191", years = 2019:2020, category = NULL,
|
|
limit = 5L, offset = 3L)
|
|
expect_equal(nrow(page), 5L)
|
|
expect_equal(page[c("year", "canonical_govid", "revenue_subtype", "category")],
|
|
full[4:8, c("year", "canonical_govid", "revenue_subtype", "category")],
|
|
ignore_attr = TRUE)
|
|
expect_equal(attr(page, "total_rows"), nrow(full))
|
|
})
|
|
|
|
test_that("cog_revenue limit unset by default leaves total_rows absent", {
|
|
skip_if_no_corpus()
|
|
r <- cog_revenue("121011212191", 2020L, "Property Tax")
|
|
expect_null(attr(r, "total_rows"))
|
|
})
|
|
|
|
test_that("cog_revenue complete + limit conflict aborts the same way as cog_spending", {
|
|
skip_if_no_corpus()
|
|
expect_error(
|
|
cog_revenue("121011212191", 2020L, "Property Tax", complete = TRUE, limit = 5L),
|
|
class = "uscogdata_complete_pagination_conflict"
|
|
)
|
|
})
|