Skip to content

Commit 7742250

Browse files
Add option to disable concurrent index build/drops (#242)
* Add --disable-concurrent-index-ops command-line flag --------- Co-authored-by: geeknonerd <[email protected]>
1 parent 3efa9b9 commit 7742250

File tree

6 files changed

+182
-78
lines changed

6 files changed

+182
-78
lines changed

cmd/pg-schema-diff/plan_cmd.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ type (
114114

115115
dataPackNewTables bool
116116
disablePlanValidation bool
117+
noConcurrentIndexOps bool
117118

118119
statementTimeoutModifiers []string
119120
lockTimeoutModifiers []string
@@ -222,6 +223,8 @@ func createPlanOptionsFlags(cmd *cobra.Command) *planOptionsFlags {
222223
cmd.Flags().BoolVar(&flags.dataPackNewTables, "data-pack-new-tables", true, "If set, will data pack new tables in the plan to minimize table size (re-arranges columns).")
223224
cmd.Flags().BoolVar(&flags.disablePlanValidation, "disable-plan-validation", false, "If set, will disable plan validation. Plan validation runs the migration against a temporary"+
224225
"database with an identical schema to the original, asserting that the generated plan actually migrates the schema to the desired target.")
226+
cmd.Flags().BoolVar(&flags.noConcurrentIndexOps, "no-concurrent-index-ops", false, "If set, will disable the use of CONCURRENTLY in CREATE INDEX and DROP INDEX statements. "+
227+
"This may result in longer lock times and potential downtime during migrations.")
225228

226229
timeoutModifierFlagVar(cmd, &flags.statementTimeoutModifiers, "statement", "t")
227230
timeoutModifierFlagVar(cmd, &flags.lockTimeoutModifiers, "lock", "l")
@@ -321,6 +324,9 @@ func parsePlanOptions(p planOptionsFlags) (planOptions, error) {
321324
if p.disablePlanValidation {
322325
opts = append(opts, diff.WithDoNotValidatePlan())
323326
}
327+
if p.noConcurrentIndexOps {
328+
opts = append(opts, diff.WithNoConcurrentIndexOps())
329+
}
324330

325331
var statementTimeoutModifiers []timeoutModifier
326332
for _, s := range p.statementTimeoutModifiers {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package migration_acceptance_tests
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stripe/pg-schema-diff/pkg/diff"
7+
)
8+
9+
var indexNoConcurrentAcceptanceTestCases = []acceptanceTestCase{
10+
{
11+
name: "Add",
12+
oldSchemaDDL: []string{
13+
`
14+
CREATE TABLE "Foobar"(
15+
id INT PRIMARY KEY,
16+
"Foo" VARCHAR(255)
17+
);
18+
`,
19+
},
20+
newSchemaDDL: []string{
21+
`
22+
CREATE TABLE "Foobar"(
23+
id INT PRIMARY KEY,
24+
"Foo" VARCHAR(255)
25+
);
26+
CREATE INDEX "Some_idx" ON "Foobar"(id, "Foo");
27+
`,
28+
},
29+
expectedHazardTypes: []diff.MigrationHazardType{
30+
diff.MigrationHazardTypeAcquiresShareLock,
31+
},
32+
planOpts: []diff.PlanOpt{diff.WithNoConcurrentIndexOps()},
33+
},
34+
{
35+
name: "Delete",
36+
oldSchemaDDL: []string{
37+
`
38+
CREATE TABLE "Foobar"(
39+
id INT PRIMARY KEY,
40+
"Foo" VARCHAR(255)
41+
);
42+
CREATE INDEX "Some_idx" ON "Foobar"(id, "Foo");
43+
`,
44+
},
45+
newSchemaDDL: []string{
46+
`
47+
CREATE TABLE "Foobar"(
48+
id INT PRIMARY KEY,
49+
"Foo" VARCHAR(255) NOT NULL
50+
);
51+
`,
52+
},
53+
expectedHazardTypes: []diff.MigrationHazardType{
54+
diff.MigrationHazardTypeAcquiresAccessExclusiveLock,
55+
diff.MigrationHazardTypeIndexDropped,
56+
},
57+
planOpts: []diff.PlanOpt{diff.WithNoConcurrentIndexOps()},
58+
},
59+
}
60+
61+
func TestIndexNoConcurrentTestCases(t *testing.T) {
62+
runTestCases(t, indexNoConcurrentAcceptanceTestCases)
63+
}

internal/pgengine/engine.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ const (
6464
var (
6565
defaultServerConfiguration = map[string]string{
6666
"log_checkpoints": "false",
67+
"max_connections": "1000",
6768
}
6869
)
6970

pkg/diff/plan_generator.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type (
3535
validatePlan bool
3636
getSchemaOpts []schema.GetSchemaOpt
3737
randReader io.Reader
38+
noConcurrentIndexOps bool
3839
}
3940

4041
PlanOpt func(opts *planOptions)
@@ -102,6 +103,16 @@ func WithRandReader(randReader io.Reader) PlanOpt {
102103
}
103104
}
104105

106+
// WithNoConcurrentIndexOps disables the use of CONCURRENTLY in CREATE INDEX and DROP INDEX statements.
107+
// This can be useful when you need simpler DDL statements or when working in environments that don't support
108+
// concurrent index operations. Note that disabling concurrent operations may result in longer lock times
109+
// and potential downtime during migrations.
110+
func WithNoConcurrentIndexOps() PlanOpt {
111+
return func(opts *planOptions) {
112+
opts.noConcurrentIndexOps = true
113+
}
114+
}
115+
105116
// Generate generates a migration plan to migrate the database to the target schema
106117
//
107118
// Parameters:
@@ -187,7 +198,7 @@ func generateMigrationStatements(oldSchema, newSchema schema.Schema, planOptions
187198
diff = removeChangesToColumnOrdering(diff)
188199
}
189200

190-
statements, err := newSchemaSQLGenerator(planOptions.randReader).Alter(diff)
201+
statements, err := newSchemaSQLGenerator(planOptions.randReader, planOptions).Alter(diff)
191202
if err != nil {
192203
return nil, fmt.Errorf("generating migration statements: %w", err)
193204
}

pkg/diff/schema_migration_plan_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ func TestSchemaMigrationPlanTest(t *testing.T) {
370370
} else {
371371
require.NoError(t, err)
372372
}
373-
stmts, err := newSchemaSQLGenerator(randReader).Alter(schemaDiff)
373+
stmts, err := newSchemaSQLGenerator(randReader, &planOptions{}).Alter(schemaDiff)
374374
require.NoError(t, err)
375375
assert.Equal(t, testCase.expectedStatements, stmts, "actual:\n %# v", pretty.Formatter(stmts))
376376
})

0 commit comments

Comments
 (0)