
The boltdb instance in metadata is only used for getting transactions and can also be overriden via the context to have a wider control of the transaction boundary. Using the transactor interface allows callers of metadata to have more control of the transaction lifecycle. Since boltdb must be fsync'ed on commit, operations which perform many database operations can be costly and slow. While providing transactor via context can be used to group together operations, it does not provide a way to manage the commit fsyncs more globally. Signed-off-by: Derek McGowan <derek@mcg.dev>
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package metadata
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
bolt "go.etcd.io/bbolt"
|
|
)
|
|
|
|
type transactionKey struct{}
|
|
|
|
// WithTransactionContext returns a new context holding the provided
|
|
// bolt transaction. Functions which require a bolt transaction will
|
|
// first check to see if a transaction is already created on the
|
|
// context before creating their own.
|
|
func WithTransactionContext(ctx context.Context, tx *bolt.Tx) context.Context {
|
|
return context.WithValue(ctx, transactionKey{}, tx)
|
|
}
|
|
|
|
// Transactor is the database interface for running transactions
|
|
type Transactor interface {
|
|
View(fn func(*bolt.Tx) error) error
|
|
Update(fn func(*bolt.Tx) error) error
|
|
}
|
|
|
|
// view gets a bolt db transaction either from the context
|
|
// or starts a new one with the provided bolt database.
|
|
func view(ctx context.Context, db Transactor, fn func(*bolt.Tx) error) error {
|
|
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
|
|
if !ok {
|
|
return db.View(fn)
|
|
}
|
|
return fn(tx)
|
|
}
|
|
|
|
// update gets a writable bolt db transaction either from the context
|
|
// or starts a new one with the provided bolt database.
|
|
func update(ctx context.Context, db Transactor, fn func(*bolt.Tx) error) error {
|
|
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
|
|
if !ok {
|
|
return db.Update(fn)
|
|
} else if !tx.Writable() {
|
|
return fmt.Errorf("unable to use transaction from context: %w", bolt.ErrTxNotWritable)
|
|
}
|
|
return fn(tx)
|
|
}
|