Open snapshot database only once

Prevents having multiple database connections open to a connection,
preventing bugs with locking by relying on the boltdb database
object to handle synchronization rather than file locking.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan
2017-11-01 15:03:40 -07:00
parent c933088381
commit 68d3c77eae
5 changed files with 117 additions and 32 deletions

View File

@@ -30,21 +30,6 @@ var (
ErrNoTransaction = errors.New("no transaction in context")
)
type boltFileTransactor struct {
db *bolt.DB
tx *bolt.Tx
}
func (bft *boltFileTransactor) Rollback() error {
defer bft.db.Close()
return bft.tx.Rollback()
}
func (bft *boltFileTransactor) Commit() error {
defer bft.db.Close()
return bft.tx.Commit()
}
// parentKey returns a composite key of the parent and child identifiers. The
// parts of the key are separated by a zero byte.
func parentKey(parent, child uint64) []byte {
@@ -405,11 +390,11 @@ func CommitActive(ctx context.Context, key, name string, usage snapshot.Usage, o
}
func withSnapshotBucket(ctx context.Context, key string, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
if !ok {
return ErrNoTransaction
}
bkt := t.tx.Bucket(bucketKeyStorageVersion)
bkt := tx.Bucket(bucketKeyStorageVersion)
if bkt == nil {
return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
}
@@ -426,11 +411,11 @@ func withSnapshotBucket(ctx context.Context, key string, fn func(context.Context
}
func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
if !ok {
return ErrNoTransaction
}
bkt := t.tx.Bucket(bucketKeyStorageVersion)
bkt := tx.Bucket(bucketKeyStorageVersion)
if bkt == nil {
return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
}
@@ -438,12 +423,12 @@ func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bol
}
func createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
if !ok {
return ErrNoTransaction
}
bkt, err := t.tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
bkt, err := tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
if err != nil {
return errors.Wrap(err, "failed to create version bucket")
}

View File

@@ -7,6 +7,7 @@ package storage
import (
"context"
"sync"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/snapshot"
@@ -46,6 +47,9 @@ type Snapshot struct {
// complexities of a driver implementation.
type MetaStore struct {
dbfile string
dbL sync.Mutex
db *bolt.DB
}
// NewMetaStore returns a snapshot MetaStore for storage of metadata related to
@@ -63,22 +67,33 @@ type transactionKey struct{}
// TransactionContext creates a new transaction context. The writable value
// should be set to true for transactions which are expected to mutate data.
func (ms *MetaStore) TransactionContext(ctx context.Context, writable bool) (context.Context, Transactor, error) {
db, err := bolt.Open(ms.dbfile, 0600, nil)
if err != nil {
return ctx, nil, errors.Wrap(err, "failed to open database file")
ms.dbL.Lock()
if ms.db == nil {
db, err := bolt.Open(ms.dbfile, 0600, nil)
if err != nil {
ms.dbL.Unlock()
return ctx, nil, errors.Wrap(err, "failed to open database file")
}
ms.db = db
}
ms.dbL.Unlock()
tx, err := db.Begin(writable)
tx, err := ms.db.Begin(writable)
if err != nil {
return ctx, nil, errors.Wrap(err, "failed to start transaction")
}
t := &boltFileTransactor{
db: db,
tx: tx,
}
ctx = context.WithValue(ctx, transactionKey{}, tx)
ctx = context.WithValue(ctx, transactionKey{}, t)
return ctx, t, nil
return ctx, tx, nil
}
// Close closes the metastore and any underlying database connections
func (ms *MetaStore) Close() error {
ms.dbL.Lock()
defer ms.dbL.Unlock()
if ms.db == nil {
return nil
}
return ms.db.Close()
}