Skip to content

Commit f6d624c

Browse files
authored
Merge pull request #150 from tsenart/atomic-migrations-table-creation
postgres: Make `ensureVersionTable` atomic
2 parents 1ca4da9 + 5ad19c5 commit f6d624c

File tree

6 files changed

+82
-16
lines changed

6 files changed

+82
-16
lines changed

Gopkg.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,7 @@
104104
[[constraint]]
105105
name = "github.com/dhui/dktest"
106106
version = "0.3.0"
107+
108+
[[constraint]]
109+
name = "github.com/hashicorp/go-multierror"
110+
version = "1.0.0"

database/postgres/postgres.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/golang-migrate/migrate/v4"
1616
"github.com/golang-migrate/migrate/v4/database"
17+
multierror "github.com/hashicorp/go-multierror"
1718
"github.com/lib/pq"
1819
)
1920

@@ -332,21 +333,25 @@ func (p *Postgres) Drop() error {
332333
return nil
333334
}
334335

335-
func (p *Postgres) ensureVersionTable() error {
336-
// check if migration table exists
337-
var count int
338-
query := `SELECT COUNT(1) FROM information_schema.tables WHERE table_name = $1 AND table_schema = (SELECT current_schema()) LIMIT 1`
339-
if err := p.conn.QueryRowContext(context.Background(), query, p.config.MigrationsTable).Scan(&count); err != nil {
340-
return &database.Error{OrigErr: err, Query: []byte(query)}
341-
}
342-
if count == 1 {
343-
return nil
336+
func (p *Postgres) ensureVersionTable() (err error) {
337+
if err = p.Lock(); err != nil {
338+
return err
344339
}
345340

346-
// if not, create the empty migration table
347-
query = `CREATE TABLE "` + p.config.MigrationsTable + `" (version bigint not null primary key, dirty boolean not null)`
348-
if _, err := p.conn.ExecContext(context.Background(), query); err != nil {
341+
defer func() {
342+
if e := p.Unlock(); e != nil {
343+
if err == nil {
344+
err = e
345+
} else {
346+
err = multierror.Append(err, e)
347+
}
348+
}
349+
}()
350+
351+
query := `CREATE TABLE IF NOT EXISTS "` + p.config.MigrationsTable + `" (version bigint not null primary key, dirty boolean not null)`
352+
if _, err = p.conn.ExecContext(context.Background(), query); err != nil {
349353
return &database.Error{OrigErr: err, Query: []byte(query)}
350354
}
355+
351356
return nil
352357
}

database/postgres/postgres_test.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ import (
1010
"io"
1111
"strconv"
1212
"strings"
13+
"sync"
1314
"testing"
14-
)
1515

16-
import (
1716
"github.com/dhui/dktest"
18-
)
1917

20-
import (
2118
dt "github.com/golang-migrate/migrate/v4/database/testing"
2219
"github.com/golang-migrate/migrate/v4/dktesting"
2320
)
@@ -310,6 +307,44 @@ func TestPostgres_Lock(t *testing.T) {
310307
})
311308
}
312309

310+
func TestWithInstance_Concurrent(t *testing.T) {
311+
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
312+
ip, port, err := c.FirstPort()
313+
if err != nil {
314+
t.Fatal(err)
315+
}
316+
317+
// The number of concurrent processes running WithInstance
318+
const concurrency = 30
319+
320+
// We can instantiate a single database handle because it is
321+
// actually a connection pool, and so, each of the below go
322+
// routines will have a high probability of using a separate
323+
// connection, which is something we want to exercise.
324+
db, err := sql.Open("postgres", pgConnectionString(ip, port))
325+
if err != nil {
326+
t.Fatal(err)
327+
}
328+
defer db.Close()
329+
330+
db.SetMaxIdleConns(concurrency)
331+
db.SetMaxOpenConns(concurrency)
332+
333+
var wg sync.WaitGroup
334+
defer wg.Wait()
335+
336+
wg.Add(concurrency)
337+
for i := 0; i < concurrency; i++ {
338+
go func(i int) {
339+
defer wg.Done()
340+
_, err := WithInstance(db, &Config{})
341+
if err != nil {
342+
t.Errorf("process %d error: %s", i, err)
343+
}
344+
}(i)
345+
}
346+
})
347+
}
313348
func Test_computeLineFromPos(t *testing.T) {
314349
testcases := []struct {
315350
pos int

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/google/go-querystring v1.0.0 // indirect
2020
github.com/google/martian v2.1.0+incompatible // indirect
2121
github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f // indirect
22+
github.com/hashicorp/go-multierror v1.0.0
2223
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
2324
github.com/jackc/pgx v3.2.0+incompatible // indirect
2425
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
100100
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
101101
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
102102
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
103+
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
104+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
105+
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
106+
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
103107
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
104108
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
105109
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=

0 commit comments

Comments
 (0)