-
Notifications
You must be signed in to change notification settings - Fork 5k
sql(postgres): stop sending a redundant Sync after a simple Query #32772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
e179cdb
sql(postgres): stop sending a redundant Sync after a simple Query
robobun 0bd7af5
test: cover the sql.unsafe() entry point to the simple query protocol
robobun b78fd1a
comment: rephrase the Sync note as a forward-looking invariant
robobun c019ee5
ci: retrigger
robobun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // A simple-protocol Query ('Q') is its own sync point: the backend answers it | ||
| // with exactly one ReadyForQuery. Bun also appended an extended-protocol Sync | ||
| // after every 'Q', so the server replied with a second, unaccounted | ||
| // ReadyForQuery per simple query. That spurious ReadyForQuery re-armed the | ||
| // connection's "ready" state while the next query's Parse+Describe round trip | ||
| // was still in flight, and advance() then pipelined a third query into that | ||
| // window, so its replies were delivered to the wrong query. | ||
| // | ||
| // The simple protocol is used for query.simple(), for sql.unsafe(text) with | ||
| // no parameters, and for the BEGIN/COMMIT/ROLLBACK of sql.begin(), so every | ||
| // one of those emitted the spurious ReadyForQuery. | ||
| import { SQL } from "bun"; | ||
| import { expect, test } from "bun:test"; | ||
| import { describeWithContainer } from "harness"; | ||
|
|
||
| describeWithContainer("postgres", { image: "postgres_plain" }, container => { | ||
| const url = () => `postgres://bun_sql_test@${container.host}:${container.port}/bun_sql_test`; | ||
|
|
||
| // Before the fix: the second ReadyForQuery from A's redundant Sync lets C's | ||
| // 'Q' go out inside B's Parse+Describe window. C's result set then arrives | ||
| // while B is still the current query, so B resolves with C's row and C | ||
| // resolves with B's (field-less) Bind+Execute row: b = [{v:"CCCC"}], c = [{}]. | ||
| test("a simple query does not steal the rows of an in-flight prepare", async () => { | ||
| await container.ready; | ||
| await using sql = new SQL({ url: url(), max: 1, idleTimeout: 5, connectionTimeout: 5 }); | ||
|
|
||
| const [a, b, c] = await Promise.all([ | ||
| sql`SELECT 'AAAA'::text AS v`.simple(), | ||
| sql`SELECT ${"BBBB"}::text AS v`, | ||
| sql`SELECT 'CCCC'::text AS v`.simple(), | ||
| ]); | ||
|
|
||
| expect({ a, b, c }).toEqual({ | ||
| a: [{ v: "AAAA" }], | ||
| b: [{ v: "BBBB" }], | ||
| c: [{ v: "CCCC" }], | ||
| }); | ||
| }); | ||
|
|
||
| // sql.unsafe(text) with no parameters routes through the same simple ('Q') | ||
| // protocol, so the same misattribution happens without the caller ever | ||
| // opting into simple mode. | ||
| test("unsafe() with no parameters does not steal the rows of an in-flight prepare", async () => { | ||
| await container.ready; | ||
| await using sql = new SQL({ url: url(), max: 1, idleTimeout: 5, connectionTimeout: 5 }); | ||
|
|
||
| const [a, b, c] = await Promise.all([ | ||
| sql.unsafe(`SELECT 'AAAA'::text AS v`), | ||
| sql.unsafe(`SELECT $1::text AS v`, ["BBBB"]), | ||
| sql.unsafe(`SELECT 'CCCC'::text AS v`), | ||
| ]); | ||
|
|
||
| expect({ a, b, c }).toEqual({ | ||
| a: [{ v: "AAAA" }], | ||
| b: [{ v: "BBBB" }], | ||
| c: [{ v: "CCCC" }], | ||
| }); | ||
| }); | ||
|
|
||
| // Same root, different symptom: when the third query needs its own Parse, the | ||
| // spurious ReadyForQuery also clears the waiting-to-prepare state, so C's | ||
| // Parse+Describe is pipelined inside B's. C's describe reply is then consumed | ||
| // under B, C's statement never leaves the Parsing state, and C never settles. | ||
| test("a second prepare queued behind an in-flight prepare still settles", async () => { | ||
| await container.ready; | ||
| await using sql = new SQL({ url: url(), max: 1, idleTimeout: 5, connectionTimeout: 5 }); | ||
|
|
||
| const [a, b, c] = await Promise.all([ | ||
| sql`SELECT 'AAAA'::text AS v`.simple(), | ||
| sql`SELECT ${"BBBB"}::text AS v`, | ||
| sql`SELECT ${"CCCC"}::text AS x`, | ||
| ]); | ||
|
|
||
| expect({ a, b, c }).toEqual({ | ||
| a: [{ v: "AAAA" }], | ||
| b: [{ v: "BBBB" }], | ||
| c: [{ x: "CCCC" }], | ||
| }); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.