Repository navigation
iceberg REST: the pages of one list share the one-response cap - #2047
Merged
Merged
Conversation
rest_get_list keeps every page's document until the list is built, because each item points into its page. Each response was capped at 64 MB, but a catalog that kept sending new page tokens could make the backend hold up to REST_MAX_PAGES (100000) of them. The pages of one list now share the one-response cap, and a larger list is refused with 54000. The fixture catalog gains --padded-pages N:BYTES. test_iceberg_rest_scan.py lists 15 pages of 4 MiB (60 MiB in all) and is refused at 17 (68 MiB). On main, the 17-page list was listed in full. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
@OffgridwithJD Ready for review. This one comes from the 2026-11-07 security audit and was written test-first; the red and green runs are in the description. |
OffgridwithJD
approved these changes
Nov 9, 2026
OffgridwithJD
left a comment
Collaborator
There was a problem hiding this comment.
Approved at 1d0a87b.
What I ran
- PR head:
test_iceberg_rest_scan.pyon PG18.6 (private prefix): 130 passed, 130 checks. - Mutant: the
held > REST_MAX_RESPONSEcheck disabled. Onlytest_paging_pages_over_the_response_cap_together_are_refusedfails (1 failed, 129 passed). On main the 17-page list was held in full. - Boundary: 15 × 4 MiB lists and 17 × 4 MiB is refused, so the test bounds the cap from both sides. Doubling the cap would let 17 pages through. Halving it would refuse 15. Counting only the last page would let 17 pages through.
Code
heldsumsVARSIZE(doc)across pages. That is the parsed size kept per page, not the bytes on the wire, which is the right thing to bound, since the parsed documents are what stays in memory.- The check sits right after each page is parsed. Peak memory is at most the cap plus one page.
Non-blocking
- The message says "64 MB", but the cap is 64 MiB. The per-response refusal it pairs with reports bytes (
exceeded 67108864 bytes). Either unit works; matching the other message would make the two easier to tell apart in a log. - User docs: my docs audit PR (opening shortly) adds the 64 MiB per-response cap to sql-reference.md's REST catalog section. Once both land, I'll add one sentence there saying a paged list shares that cap, unless you'd rather add it here.
- Registries:
cluster_tests 5350 -> 5352is on 17d9287. Main has since moved (#2043). The diff merges cleanly, and CI collection is what checks the count.
🤖 Generated with Claude Code
jdatcmd
added a commit
that referenced
this pull request
Nov 9, 2026
Brings in #2047. No conflicts; the registries were recounted by collection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jdatcmd
added a commit
that referenced
this pull request
Nov 9, 2026
Brings in #2047. No conflicts; the registries were recounted by collection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OffgridwithJD
pushed a commit
that referenced
this pull request
Nov 9, 2026
test_logical_decoding_cdc_recipe runs the user-guide recipe and queries events by name, so the rename to events_live broke it. Only the parallel_copy example keeps its new name. #2047 caps a REST list's pages together; say so beside the single-reply cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #2046.
What was wrong
rest_get_list(src/columnar_iceberg_rest.c) followsnext-page-tokenand keeps every page's JSON document until the list is built, because each list item points into its page. Each response was capped at 64 MB (REST_MAX_RESPONSE). The only bound across pages wasREST_MAX_PAGES(100000) and the repeated-token check. A catalog that kept sending fresh tokens could therefore make one backend hold up to 100000 × 64 MB.This reaches
iceberg_rest_namespaces,iceberg_rest_tablesand the namespace walk.The fix
rest_get_listadds upVARSIZEof each page's document and refuses the list once the total passesREST_MAX_RESPONSE. The error isERRCODE_PROGRAM_LIMIT_EXCEEDED(54000):iceberg: the REST catalog <what> is larger than 64 MB across its pages. A list may now hold as much as one response could, and no more.Out of scope: the namespace walk calls
rest_get_listonce per namespace, so the cap applies to each list, not to the whole walk. The walk is still bounded by depth (64) and by its per-list caps. Say if you want a per-call total as well.Test
The fixture catalog (
test/iceberg_rest_server.py) gains--padded-pages N:BYTES. It serves N pages, each padded with an unread string BYTES long and carrying a fresh token until the last. Two checks join the paging checks intest_iceberg_rest_scan.py(§274), so this PR adds no new TESTS.md section:test_paging_pages_within_the_response_cap_together_still_listeventstest_paging_pages_over_the_response_cap_together_are_refusedRed, on main's code (PG18)
The 15-page control passed on main, as it should.
Said plainly: my first red run failed for the wrong reason, a bug in my own test. With
SET statement_timeout ...; SELECT ...as one string, psycopg'sfetchall()read theSET's result, so both checks were refused as failed queries. I removed theSET; 17 local pages need no timeout. The red above is from the corrected test.Green
Registries
expected_tests.txt: cluster_tests 5350 → 5352, by collection (6230 collected, less guard_tests 878).Found by
The security audit of 2026-11-07. It was a suspected finding until the red run above proved it.
🤖 Generated with Claude Code