Render the begin statement once per standard option combination - #2653
Open
timofurrer wants to merge 1 commit into
Open
timofurrer wants to merge 1 commit into
timofurrer wants to merge 1 commit into
Conversation
TxOptions.beginSQL built the statement with a strings.Builder on every call, so every Conn.BeginTx and Batch.BeginTx paid a 64 byte allocation for options whose statement never changes. The statements for all combinations of the standard isolation levels, access modes and deferrable modes are now rendered once at package initialization and looked up by option value. Non-standard values and BeginQuery behave as before. goos: darwin, goarch: arm64, Apple M1 Pro: BeginSQL/default 8.3ns 0 allocs -> 7.7ns 0 allocs BeginSQL/isolation_level 25.0ns 1 allocs -> 10.0ns 0 allocs BeginSQL/every_option 32.5ns 1 allocs -> 10.6ns 0 allocs BeginSQL/custom_isolation_level 25.0ns 1 allocs -> 29.5ns 1 allocs
timofurrer
marked this pull request as ready for review
September 13, 2026 10:14
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.
TxOptions.beginSQLbuilds the begin statement with astrings.Builderon every call, so everyConn.BeginTx,Batch.BeginTx,pgxpool.Pool.BeginTxandstdlibBeginTxpays a 64 byte allocation for a statement that never changes for a given set of options. Only the defaultTxOptions{}avoided it, through theemptyTxOptionsfast path.What this changes
The statements for every combination of the standard isolation levels, access modes and deferrable modes (5 x 3 x 3) are rendered once at package initialization into a small array, and
beginSQLlooks the statement up by the position of each option value. That covers every transaction begun with the exported constants. Non-standard values, such as a customTxIsoLevelstring, still render on demand exactly as before, andBeginQuerystill overrides everything.emptyTxOptionsis gone: the default options are the[0][0][0]entry.Benchmark
BenchmarkTxOptionsBeginSQLis new intx_internal_test.go. goos: darwin, goarch: arm64, Apple M1 Pro,-count 3:The custom-value path is about 4 ns slower because it scans the three option lists before falling back to rendering. Per transaction this is noise compared with the round trip, but the allocation it removes is on the path of every transaction a pgx application begins.
Testing
TestTxOptionsBeginSQLpins the rendered statement for representative options, including theBeginQueryoverride and non-standard values.TestTxOptionsBeginSQLEveryStandardCombinationchecks the lookup against rendering for all 45 combinations, so a table indexing mistake cannot pass. The root package,pgxpoolandstdlibsuites pass against PostgreSQL 17.AI disclosure
Per CONTRIBUTING.md: this change was developed with AI assistance (Claude Code, Fable 5.1), including the implementation, the tests and the benchmark. I understand the change and can answer questions about it.