Reduce round trips for prepared bulk writes - #19
Merged
Merged
Conversation
This was referenced Sep 26, 2026
quinnj
force-pushed
the
perf/bounded-executemany
branch
from
September 26, 2026 18:02
e805207 to
dce518d
Compare
When the reader rejected a server reply, or a NOTICE log handler threw, while the background writer was still sending a large group, the reader aborted the connection and then reported the writer's resulting "use of closed network connection" instead of its own error. Each side now records its error before aborting, and the first one is reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shorten the manual section to what callers need, drop two eligibility checks that other checks already imply, and remove the Python proxy and benchmark README; the Julia benchmark carries its own usage line, like decoding.jl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
quinnj
referenced
this pull request
Sep 28, 2026
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.
Prepared
DBInterface.executemanycurrently waits for PostgreSQL after every row and materializes results that are discarded. Group ordinary typed, no-result bulk writes into bounded Bind/Execute groups while preserving the existing transaction/savepoint and parameter conversion behavior.The first row runs normally to confirm the actual command type; later groups contain at most 64 rows and stop after reaching 64 KiB, with a single large row allowed to exceed that target. A write-only task lets the caller drain responses concurrently, preventing bidirectional socket backpressure from deadlocking. Server errors drain through Sync before rollback, earlier errors retain precedence over later conversions, and uncertain protocol/transport state aborts and joins the writer before reuse.
Custom collections/values/styles, query logging, result-returning statements, and GSS encryption retain serial execution. The manual describes which statements and inputs are grouped. No new public API, export, or dependency.
Validation:
All 16 hosted checks pass on rebased head
dce518da05e4b44d8bb63a2c24bd985d8bf5ec55: runtime/platform, PostgreSQL 14–18, MD5/SCRAM authentication, docs and coverage.Full integration suites on the rebased head pass 8,377 checks on Julia 1.10 / DBInterface 2.5 and 8,403 on Julia 1.13 / DBInterface 2.7, including real PostgreSQL, TLS and Kerberos, with one thread. Strict docs build passes. The rebase includes main’s merged scalar-decoding and single-execute recovery changes; both incoming and bulk-execution test registrations are retained.
Protocol fixtures cover exact grouping, malformed/truncated responses, fatal/callback/write errors, reconnect, custom callback/input parity, statement lifetime, and error precedence. Live tests cover precision/nulls/arrays, nested and deferred failures, observed-state cancellation, and simultaneous 8 MiB notices/parameters. A synchronous-writer control deadlocks at the test deadline; the candidate completes.
Benchmarks on the original tested head
e805207, with nine alternating warmed pairs per workload: 128 small inserts use 3 Syncs instead of 128, with 130 → 5 ReadyForQuery messages. Current Julia takes 38.68 → 4.45 ms with no injected delay and 809.48 → 36.81 ms with a nominal 4 ms acknowledgment delay; allocations fall 69.5%. Minimum Julia shows comparable timing gains and 76.3% fewer bytes. The 64 KiB-per-row control is essentially unchanged. These loopback fixtures do not establish cloud throughput; unrelated host work and timing ranges are retained in the proof.benchmark/executemany.jlreproduces the serial-versus-grouped timing. The message counts and injected-delay figures above came from a loopback counting proxy that is not kept in the repository.Combined validation: the published SASLAuth #6 and #7 sources pass all 690 package checks together on Julia 1.9.4 and 1.13.0. Using that combined authentication source with the published Postgres #18 and #19 sources passes the full local integration suite: 8,375 checks on Julia 1.13.0 and 8,349 on 1.10.11. Production patches apply unchanged; only overlapping test-runner includes needed composition in the temporary fixture. Dependency versions and source hashes were verified. This adds compatibility evidence, not a new timing or native-compilation claim.
Review follow-up, pushed on top:
use of closed network connection(caused by the driver's own abort) instead of the real error. Each side now records its error before aborting, and the first one wins. The:unexpected_typeand:callbackfault tests now stall the fake server behind a 32 MiB row; both fail before the fix and pass after.Co-authored by Codex
🤖 Generated with Claude Code