fix(conga): queue:clear reuses the app's published database (quick-261106-kt2)
Run in-process on an app that already published its pool, queue:clear failed with "backpack: duplicate provider for *sql.DB". It now goes through withAppDB; the CLI path is unchanged.
This commit is contained in:
@@ -120,7 +120,7 @@ asks first; --force skips the question. Off a terminal it never asks.`,
|
|||||||
if err := confirmProduction(app, in, out, fmt.Sprintf("Delete every pending job of queue %q in production?", queue)); err != nil {
|
if err := confirmProduction(app, in, out, fmt.Sprintf("Delete every pending job of queue %q in production?", queue)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return withDB(ctx, app, func() error {
|
return withAppDB(ctx, app, func() error {
|
||||||
return clearQueue(ctx, app, queue, out)
|
return clearQueue(ctx, app, queue, out)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -336,7 +336,9 @@ func dueAt(c pact.Cadence, t time.Time) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// withAppDB runs fn with the app's database: the published one when there is
|
// withAppDB runs fn with the app's database: the published one when there is
|
||||||
// one, otherwise one opened for the command the way withDB does.
|
// one, otherwise one opened for the command the way withDB does. It serves
|
||||||
|
// schedule:run --once and queue:clear, which may run in-process on an app
|
||||||
|
// that already published its pool.
|
||||||
func withAppDB(ctx context.Context, app *backpack.App, fn func() error) error {
|
func withAppDB(ctx context.Context, app *backpack.App, fn func() error) error {
|
||||||
if gdb, ok := app.Lookup[*gorm.DB](); ok && gdb != nil {
|
if gdb, ok := app.Lookup[*gorm.DB](); ok && gdb != nil {
|
||||||
return fn()
|
return fn()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
// none; a running job and other queues are left alone.
|
// none; a running job and other queues are left alone.
|
||||||
func TestQueueClear(t *testing.T) {
|
func TestQueueClear(t *testing.T) {
|
||||||
db, dsn := migratedDB(t)
|
db, dsn := migratedDB(t)
|
||||||
app, gdb := testApp(t, db, dsn, map[string]any{"queue.queues.default": 1})
|
app, gdb := testApp(t, db, dsn, map[string]any{"queue.queues.default": 1, "app.key": testAppKey})
|
||||||
m, err := From(app)
|
m, err := From(app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -73,6 +73,21 @@ func TestQueueClear(t *testing.T) {
|
|||||||
if !strings.Contains(out, `Clearing queue "default"`) || !strings.Contains(out, "Cleared 2 jobs") {
|
if !strings.Contains(out, `Clearing queue "default"`) || !strings.Contains(out, "Cleared 2 jobs") {
|
||||||
t.Fatalf("output = %q", out)
|
t.Fatalf("output = %q", out)
|
||||||
}
|
}
|
||||||
|
// In process, on the app that already published its pool (a plugin
|
||||||
|
// HasSchedule entry naming queue:clear): the block still holds the one
|
||||||
|
// worker slot, so these two stay pending.
|
||||||
|
for range 2 {
|
||||||
|
if err := m.Enqueue(ctx, nil, pingArgs{Tag: "pending-in-process"}, EnqueueOpts{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var inProcess bytes.Buffer
|
||||||
|
if err := bonfire.Call(ctx, RuntimeCommands(app, nil), "queue:clear", nil, &inProcess); err != nil {
|
||||||
|
t.Fatalf("in-process queue:clear: %v\n%s", err, inProcess.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(inProcess.String(), "Cleared 2 jobs") {
|
||||||
|
t.Fatalf("in-process output = %q", inProcess.String())
|
||||||
|
}
|
||||||
close(release)
|
close(release)
|
||||||
deadline := time.Now().Add(10 * time.Second)
|
deadline := time.Now().Add(10 * time.Second)
|
||||||
for countRows(t, gdb, `SELECT count(*) FROM river_job WHERE kind = ? AND state = 'completed'`, "conga_test_block") != 1 {
|
for countRows(t, gdb, `SELECT count(*) FROM river_job WHERE kind = ? AND state = 'completed'`, "conga_test_block") != 1 {
|
||||||
|
|||||||
Reference in New Issue
Block a user