Repository navigation
perf(pg): read a range in one query, and only its bytes - #38
Merged
Merged
Conversation
PgReplicaClient.openLtxFile() read the file row, then the chunks covering the range, and cut the range out of whole 1 MiB chunks on the client. It now runs one query: the file row left-joined with the chunks of the range, each cut by substring() on the server, so a ranged read is one round trip and transfers only the bytes asked for. Chunks are stored uncompressed (LTX pages already are compressed), so Postgres reads only the slice of a chunk it returns. A missing file still rejects the call, and a range past the end reads nothing. A 128 KiB read, the size of a paged restore's read for a page, went from 9.7 ms to 1.4 ms against a local Postgres 18. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sfj4nDS4Mc5fAjmzSLYG7k
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.
Follow-up to #37: the Postgres counterpart of its S3 change. A paged restore reads each page a query waits for with one ranged read, so the latency of
openLtxFile()is time the query waits.Before
PgReplicaClient.openLtxFile({ offset, size }):SELECT sizefrom the files table, for the bounds and to detect a missing file;A read of 32 pages (≤ 128 KiB) took two round trips and transferred 1–2 MiB.
After
One query, through the same server-side cursor:
substring()cuts each chunk to the bytes of the range on the server. Chunks are stored uncompressed (pg_column_compression(data)is null, since LTX pages are already LZ4-compressed), so Postgres reads only the slice it returns.bigint, so files larger than 2 GiB work; the length passed tosubstring()is capped at the chunk size before itsintcast.Numbers
200 reads of 128 KiB at random offsets of a 16 MiB file, local Postgres 18: 9.7 ms → 1.4 ms per read.
Tests
New
openLtxFile reads ranges across chunks, in one query each: 64-byte chunks, 40 of them (more than one cursor batch), ranges inside a chunk, across chunks, from a boundary, to the end, past the end and from the end; each read counts the queries with thedebughook of postgres.js (the old code fails it with 2). Then a stream destroyed early, with a pool of one connection, followed by another read.Also ran Lavoratore's replicator tests on Postgres with this build, including its paged restore tests: 13/13.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Sfj4nDS4Mc5fAjmzSLYG7k