-
Notifications
You must be signed in to change notification settings - Fork 485
fix(pgdelta): force close event loop on success path #5714
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
Open
avallete
wants to merge
3
commits into
develop
Choose a base branch
from
claude/issue-312-investigation-mec6f6
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package diff | ||
|
|
||
| import ( | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // lastCodeLine returns the final non-blank, non-comment line of a script. | ||
| func lastCodeLine(script string) string { | ||
| lines := strings.Split(script, "\n") | ||
| for i := len(lines) - 1; i >= 0; i-- { | ||
| line := strings.TrimSpace(lines[i]) | ||
| if line == "" || strings.HasPrefix(line, "//") { | ||
| continue | ||
| } | ||
| return line | ||
| } | ||
| return "" | ||
| } | ||
|
|
||
| // Every pg-delta edge-runtime script must force the worker's event loop closed | ||
| // once its output has been written. The pg connection pool can leave keepalive | ||
| // handles registered even after close() resolves; if the worker never exits, | ||
| // the container never stops and the CLI — which streams the container logs with | ||
| // Follow:true — blocks forever following them, hanging declarative sync at 0% | ||
| // CPU (supabase/pg-toolbelt#312). The success path must terminate | ||
| // unconditionally rather than rely on the event loop draining on its own, so | ||
| // guard against the force-close being dropped from any template's success path. | ||
| func TestPgDeltaScriptsForceCloseOnSuccess(t *testing.T) { | ||
| scripts := map[string]string{ | ||
| "pgdelta.ts": pgDeltaScript, | ||
| "pgdelta_declarative_export.ts": pgDeltaDeclarativeExportScript, | ||
| "pgdelta_catalog_export.ts": pgDeltaCatalogExportScript, | ||
| } | ||
| for name, script := range scripts { | ||
| t.Run(name, func(t *testing.T) { | ||
| require.NotEmpty(t, script) | ||
| // The terminating statement runs on the success path (the catch | ||
| // branch no longer re-throws), so the worker is torn down whether | ||
| // or not the body succeeded. | ||
| assert.Equal(t, `throw new Error("");`, lastCodeLine(script), | ||
| "success path must force the Edge Runtime worker to exit so the container stops") | ||
| }) | ||
| } | ||
| } | ||
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
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
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,33 @@ | ||
| package pgcache | ||
|
|
||
| import ( | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // The migrations-catalog cache script (db start / db push with pg-delta caching) | ||
| // opens a connection pool and must force the worker's event loop closed once it | ||
| // has written its snapshot. If a keepalive handle lingers after close() resolves | ||
| // the worker never exits, so the container never stops and the CLI — which | ||
| // follows the container logs with Follow:true — hangs indefinitely at 0% CPU | ||
| // (supabase/pg-toolbelt#312). Guard against the success-path force-close being | ||
| // dropped. | ||
| func TestPgDeltaCatalogExportScriptForceClosesOnSuccess(t *testing.T) { | ||
| require.NotEmpty(t, pgDeltaCatalogExportTS) | ||
|
|
||
| lines := strings.Split(pgDeltaCatalogExportTS, "\n") | ||
| last := "" | ||
| for i := len(lines) - 1; i >= 0; i-- { | ||
| line := strings.TrimSpace(lines[i]) | ||
| if line == "" || strings.HasPrefix(line, "//") { | ||
| continue | ||
| } | ||
| last = line | ||
| break | ||
| } | ||
| assert.Equal(t, `throw new Error("");`, last, | ||
| "success path must force the Edge Runtime worker to exit so the container stops") | ||
| } |
33 changes: 33 additions & 0 deletions
33
apps/cli-go/internal/pgdelta/pgdelta_apply_template_test.go
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,33 @@ | ||
| package pgdelta | ||
|
|
||
| import ( | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // The declarative-apply script connects to TARGET and must force the worker's | ||
| // event loop closed once it has written its result JSON. applyDeclarativeSchema | ||
| // can leave connection keepalive handles registered, and if the worker never | ||
| // exits the container never stops — the CLI, which follows the container logs | ||
| // with Follow:true, then hangs indefinitely at 0% CPU (supabase/pg-toolbelt#312). | ||
| // The success path must terminate unconditionally, so guard against the | ||
| // force-close being dropped. | ||
| func TestDeclarativeApplyScriptForceClosesOnSuccess(t *testing.T) { | ||
| require.NotEmpty(t, pgDeltaDeclarativeApplyScript) | ||
|
|
||
| lines := strings.Split(pgDeltaDeclarativeApplyScript, "\n") | ||
| last := "" | ||
| for i := len(lines) - 1; i >= 0; i-- { | ||
| line := strings.TrimSpace(lines[i]) | ||
| if line == "" || strings.HasPrefix(line, "//") { | ||
| continue | ||
| } | ||
| last = line | ||
| break | ||
| } | ||
| assert.Equal(t, `throw new Error("");`, last, | ||
| "success path must force the Edge Runtime worker to exit so the container stops") | ||
| } |
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When pg-delta catalog caching is enabled,
pgcache.TryCacheMigrationsCatalogstill runs the separatepgDeltaCatalogExportTSscript inapps/cli-go/internal/db/pgcache/cache.gothroughRunEdgeRuntimeScript; that script uses the samecreateManagedPool/extractCatalog/close()pattern but still ends afterawait close()with no final force-close throw. Because this new coverage only checks the three diff-package templates, the hang fixed here can still occur on the migrations-catalog cache path, such asdb start/db pushwith pg-delta caching enabled. Please update that template and cover it as well.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch — fixed.
pgDeltaCatalogExportTSininternal/db/pgcache/cache.go(thedb start/db pushmigrations-catalog cache path) had the samecreateManagedPool/close()pattern with no success-path force-close. Added the force-close there plus acache_template_test.goguard, matching the other four scripts. Pushed in 7908b63.Generated by Claude Code