storage/sql: use isolation level "serializable" for transactions

This commit is contained in:
Eric Chiang 2016-10-31 23:00:55 -07:00
parent 651b406cfd
commit 52e2a1668c
2 changed files with 45 additions and 7 deletions

View File

@ -36,6 +36,7 @@ func cleanDB(c *conn) error {
delete from auth_code; delete from auth_code;
delete from refresh_token; delete from refresh_token;
delete from keys; delete from keys;
delete from password;
`) `)
return err return err
} }
@ -48,6 +49,7 @@ func TestSQLite3(t *testing.T) {
s := &SQLite3{":memory:"} s := &SQLite3{":memory:"}
conn, err := s.open() conn, err := s.open()
if err != nil { if err != nil {
fmt.Fprintln(os.Stdout, err)
t.Fatal(err) t.Fatal(err)
} }
return conn return conn
@ -58,15 +60,25 @@ func TestSQLite3(t *testing.T) {
}) })
} }
func getenv(key, defaultVal string) string {
if val := os.Getenv(key); val != "" {
return val
}
return defaultVal
}
const testPostgresEnv = "DEX_POSTGRES_HOST"
func TestPostgres(t *testing.T) { func TestPostgres(t *testing.T) {
if os.Getenv("DEX_POSTGRES_HOST") == "" { host := os.Getenv(testPostgresEnv)
t.Skip("postgres envs not set, skipping tests") if host == "" {
t.Skipf("test environment variable %q not set, skipping", testPostgresEnv)
} }
p := Postgres{ p := Postgres{
Database: os.Getenv("DEX_POSTGRES_DATABASE"), Database: getenv("DEX_POSTGRES_DATABASE", "postgres"),
User: os.Getenv("DEX_POSTGRES_USER"), User: getenv("DEX_POSTGRES_USER", "postgres"),
Password: os.Getenv("DEX_POSTGRES_PASSWORD"), Password: getenv("DEX_POSTGRES_PASSWORD", "postgres"),
Host: os.Getenv("DEX_POSTGRES_HOST"), Host: host,
SSL: PostgresSSL{ SSL: PostgresSSL{
Mode: sslDisable, // Postgres container doesn't support SSL. Mode: sslDisable, // Postgres container doesn't support SSL.
}, },
@ -92,4 +104,7 @@ func TestPostgres(t *testing.T) {
withTimeout(time.Minute*1, func() { withTimeout(time.Minute*1, func() {
conformance.RunTests(t, newStorage) conformance.RunTests(t, newStorage)
}) })
withTimeout(time.Minute*1, func() {
conformance.RunTransactionTests(t, newStorage)
})
} }

View File

@ -45,7 +45,30 @@ func matchLiteral(s string) *regexp.Regexp {
var ( var (
// The "github.com/lib/pq" driver is the default flavor. All others are // The "github.com/lib/pq" driver is the default flavor. All others are
// translations of this. // translations of this.
flavorPostgres = flavor{} flavorPostgres = flavor{
// The default behavior for Postgres transactions is consistent reads, not consistent writes.
// For each transaction opened, ensure it has the correct isolation level.
//
// See: https://www.postgresql.org/docs/9.3/static/sql-set-transaction.html
//
// NOTE(ericchiang): For some reason using `SET SESSION CHARACTERISTICS AS TRANSACTION` at a
// session level didn't work for some edge cases. Might be something worth exploring.
executeTx: func(db *sql.DB, fn func(sqlTx *sql.Tx) error) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec(`SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;`); err != nil {
return err
}
if err := fn(tx); err != nil {
return err
}
return tx.Commit()
},
}
flavorSQLite3 = flavor{ flavorSQLite3 = flavor{
queryReplacers: []replacer{ queryReplacers: []replacer{