Use the transactor interface in metadata

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>
This commit is contained in:
Derek McGowan 2024-06-25 07:12:30 -07:00
parent 741c4bde51
commit 8f9607eed5
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
3 changed files with 16 additions and 13 deletions

View File

@ -33,14 +33,15 @@ func WithTransactionContext(ctx context.Context, tx *bolt.Tx) context.Context {
return context.WithValue(ctx, transactionKey{}, tx) return context.WithValue(ctx, transactionKey{}, tx)
} }
type transactor interface { // Transactor is the database interface for running transactions
type Transactor interface {
View(fn func(*bolt.Tx) error) error View(fn func(*bolt.Tx) error) error
Update(fn func(*bolt.Tx) error) error Update(fn func(*bolt.Tx) error) error
} }
// view gets a bolt db transaction either from the context // view gets a bolt db transaction either from the context
// or starts a new one with the provided bolt database. // or starts a new one with the provided bolt database.
func view(ctx context.Context, db transactor, fn func(*bolt.Tx) error) error { func view(ctx context.Context, db Transactor, fn func(*bolt.Tx) error) error {
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
if !ok { if !ok {
return db.View(fn) return db.View(fn)
@ -50,7 +51,7 @@ func view(ctx context.Context, db transactor, fn func(*bolt.Tx) error) error {
// update gets a writable bolt db transaction either from the context // update gets a writable bolt db transaction either from the context
// or starts a new one with the provided bolt database. // or starts a new one with the provided bolt database.
func update(ctx context.Context, db transactor, fn func(*bolt.Tx) error) error { func update(ctx context.Context, db Transactor, fn func(*bolt.Tx) error) error {
tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
if !ok { if !ok {
return db.Update(fn) return db.Update(fn)

View File

@ -25,16 +25,17 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/metadata/boltutil"
"github.com/containerd/containerd/v2/pkg/filters"
"github.com/containerd/containerd/v2/pkg/labels"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/errdefs" "github.com/containerd/errdefs"
"github.com/containerd/log" "github.com/containerd/log"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/metadata/boltutil"
"github.com/containerd/containerd/v2/pkg/filters"
"github.com/containerd/containerd/v2/pkg/labels"
"github.com/containerd/containerd/v2/pkg/namespaces"
) )
type contentStore struct { type contentStore struct {
@ -487,7 +488,7 @@ type namespacedWriter struct {
ctx context.Context ctx context.Context
ref string ref string
namespace string namespace string
db transactor db Transactor
provider interface { provider interface {
content.Provider content.Provider
content.Ingester content.Ingester

View File

@ -27,14 +27,15 @@ import (
"time" "time"
eventstypes "github.com/containerd/containerd/api/events" eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/log"
bolt "go.etcd.io/bbolt"
"github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/core/events" "github.com/containerd/containerd/v2/core/events"
"github.com/containerd/containerd/v2/core/snapshots" "github.com/containerd/containerd/v2/core/snapshots"
"github.com/containerd/containerd/v2/internal/cleanup" "github.com/containerd/containerd/v2/internal/cleanup"
"github.com/containerd/containerd/v2/pkg/gc" "github.com/containerd/containerd/v2/pkg/gc"
"github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/log"
bolt "go.etcd.io/bbolt"
) )
const ( const (
@ -80,7 +81,7 @@ type dbOptions struct {
// while proxying data shared across namespaces to backend // while proxying data shared across namespaces to backend
// datastores for content and snapshots. // datastores for content and snapshots.
type DB struct { type DB struct {
db *bolt.DB db Transactor
ss map[string]*snapshotter ss map[string]*snapshotter
cs *contentStore cs *contentStore
@ -115,7 +116,7 @@ type DB struct {
// NewDB creates a new metadata database using the provided // NewDB creates a new metadata database using the provided
// bolt database, content store, and snapshotters. // bolt database, content store, and snapshotters.
func NewDB(db *bolt.DB, cs content.Store, ss map[string]snapshots.Snapshotter, opts ...DBOpt) *DB { func NewDB(db Transactor, cs content.Store, ss map[string]snapshots.Snapshotter, opts ...DBOpt) *DB {
m := &DB{ m := &DB{
db: db, db: db,
ss: make(map[string]*snapshotter, len(ss)), ss: make(map[string]*snapshotter, len(ss)),