blob: 099cac5120a9861108a74b90f74d4076e69f67c4 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package storage
import (
"fmt"
"github.com/jmoiron/sqlx"
"v.io/x/lib/dbutil"
)
var (
// Database handle with READ_COMMITTED transaction isolation.
// Used for non-transactional reads.
dbRead *sqlx.DB
// Database handle with SERIALIZABLE transaction isolation.
// Used for read-write transactions.
dbSeq *sqlx.DB
)
// connectDb is a helper method to connect a single database with the given
// isolation parameter.
func connectDb(sqlConfig *dbutil.ActiveSqlConfig, isolation string) (*sqlx.DB, error) {
// Open db connection from config,
conn, err := sqlConfig.NewSqlDBConn(isolation)
if err != nil {
return nil, err
}
// Create sqlx DB.
db := sqlx.NewDb(conn, "mysql")
// Ping db to check connection.
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("Error connecting to database: %v", err)
}
return db, nil
}
// Connect opens 2 connections to the database, one read-only, and one
// serializable.
func Connect(sqlConfig *dbutil.ActiveSqlConfig) (err error) {
// Data writes for the schema are complex enough to require transactions with
// SERIALIZABLE isolation. However, reads do not require SERIALIZABLE. Since
// database/sql only allows setting transaction isolation per connection,
// a separate connection with only READ-COMMITTED isolation is used for reads
// to reduce lock contention and deadlock frequency.
dbRead, err = connectDb(sqlConfig, "READ-COMMITTED")
if err != nil {
return err
}
dbSeq, err = connectDb(sqlConfig, "SERIALIZABLE")
if err != nil {
return err
}
return nil
}
// Close closes both databases.
func Close() error {
if err := dbRead.Close(); err != nil {
return err
}
if err := dbSeq.Close(); err != nil {
return err
}
return nil
}