github.com/tonyhb/nomad@v0.11.8/helper/boltdd/boltdd.go (about)

     1  // BOLTdd contains a wrapper around BoltDB to deduplicate writes and encode
     2  // values using mgspack.  (dd stands for DeDuplicate)
     3  package boltdd
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"os"
     9  	"sync"
    10  
    11  	"github.com/boltdb/bolt"
    12  	"github.com/hashicorp/go-msgpack/codec"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"golang.org/x/crypto/blake2b"
    15  )
    16  
    17  // ErrNotFound is returned when a key is not found.
    18  type ErrNotFound struct {
    19  	name string
    20  }
    21  
    22  func (e *ErrNotFound) Error() string {
    23  	return fmt.Sprintf("key not found: %s", e.name)
    24  }
    25  
    26  // NotFound returns a new error for a key that was not found.
    27  func NotFound(name string) error {
    28  	return &ErrNotFound{name}
    29  }
    30  
    31  // IsErrNotFound returns true if the error is an ErrNotFound error.
    32  func IsErrNotFound(e error) bool {
    33  	if e == nil {
    34  		return false
    35  	}
    36  	_, ok := e.(*ErrNotFound)
    37  	return ok
    38  }
    39  
    40  // DB wraps an underlying bolt.DB to create write deduplicating buckets and
    41  // msgpack encoded values.
    42  type DB struct {
    43  	rootBuckets     map[string]*bucketMeta
    44  	rootBucketsLock sync.Mutex
    45  
    46  	bdb *bolt.DB
    47  }
    48  
    49  // Open a bolt.DB and wrap it in a write-deduplicating msgpack-encoding
    50  // implementation.
    51  func Open(path string, mode os.FileMode, options *bolt.Options) (*DB, error) {
    52  	bdb, err := bolt.Open(path, mode, options)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	return New(bdb), nil
    58  }
    59  
    60  // New deduplicating wrapper for the given boltdb.
    61  func New(bdb *bolt.DB) *DB {
    62  	return &DB{
    63  		rootBuckets: make(map[string]*bucketMeta),
    64  		bdb:         bdb,
    65  	}
    66  }
    67  
    68  func (db *DB) bucket(btx *bolt.Tx, name []byte) *Bucket {
    69  	bb := btx.Bucket(name)
    70  	if bb == nil {
    71  		return nil
    72  	}
    73  
    74  	db.rootBucketsLock.Lock()
    75  	defer db.rootBucketsLock.Unlock()
    76  
    77  	if db.isClosed() {
    78  		return nil
    79  	}
    80  
    81  	b, ok := db.rootBuckets[string(name)]
    82  	if !ok {
    83  		b = newBucketMeta()
    84  		db.rootBuckets[string(name)] = b
    85  	}
    86  
    87  	return newBucket(b, bb)
    88  }
    89  
    90  func (db *DB) createBucket(btx *bolt.Tx, name []byte) (*Bucket, error) {
    91  	bb, err := btx.CreateBucket(name)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	db.rootBucketsLock.Lock()
    97  	defer db.rootBucketsLock.Unlock()
    98  
    99  	// While creating a bucket on a closed db would error, we must recheck
   100  	// after acquiring the lock to avoid races.
   101  	if db.isClosed() {
   102  		return nil, bolt.ErrDatabaseNotOpen
   103  	}
   104  
   105  	// Always create a new Bucket since CreateBucket above fails if the
   106  	// bucket already exists.
   107  	b := newBucketMeta()
   108  	db.rootBuckets[string(name)] = b
   109  
   110  	return newBucket(b, bb), nil
   111  }
   112  
   113  func (db *DB) createBucketIfNotExists(btx *bolt.Tx, name []byte) (*Bucket, error) {
   114  	bb, err := btx.CreateBucketIfNotExists(name)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	db.rootBucketsLock.Lock()
   120  	defer db.rootBucketsLock.Unlock()
   121  
   122  	// While creating a bucket on a closed db would error, we must recheck
   123  	// after acquiring the lock to avoid races.
   124  	if db.isClosed() {
   125  		return nil, bolt.ErrDatabaseNotOpen
   126  	}
   127  
   128  	b, ok := db.rootBuckets[string(name)]
   129  	if !ok {
   130  		b = newBucketMeta()
   131  		db.rootBuckets[string(name)] = b
   132  	}
   133  
   134  	return newBucket(b, bb), nil
   135  }
   136  
   137  func (db *DB) Update(fn func(*Tx) error) error {
   138  	return db.bdb.Update(func(btx *bolt.Tx) error {
   139  		tx := newTx(db, btx)
   140  		return fn(tx)
   141  	})
   142  }
   143  
   144  func (db *DB) View(fn func(*Tx) error) error {
   145  	return db.bdb.View(func(btx *bolt.Tx) error {
   146  		tx := newTx(db, btx)
   147  		return fn(tx)
   148  	})
   149  }
   150  
   151  // isClosed returns true if the database is closed and must be called while
   152  // db.rootBucketsLock is acquired.
   153  func (db *DB) isClosed() bool {
   154  	return db.rootBuckets == nil
   155  }
   156  
   157  // Close closes the underlying bolt.DB and clears all bucket hashes. DB is
   158  // unusable after closing.
   159  func (db *DB) Close() error {
   160  	db.rootBucketsLock.Lock()
   161  	db.rootBuckets = nil
   162  	db.rootBucketsLock.Unlock()
   163  	return db.bdb.Close()
   164  }
   165  
   166  // BoltDB returns the underlying bolt.DB.
   167  func (db *DB) BoltDB() *bolt.DB {
   168  	return db.bdb
   169  }
   170  
   171  type Tx struct {
   172  	db  *DB
   173  	btx *bolt.Tx
   174  }
   175  
   176  func newTx(db *DB, btx *bolt.Tx) *Tx {
   177  	return &Tx{
   178  		db:  db,
   179  		btx: btx,
   180  	}
   181  }
   182  
   183  // Bucket returns a root bucket or nil if it doesn't exist.
   184  func (tx *Tx) Bucket(name []byte) *Bucket {
   185  	return tx.db.bucket(tx.btx, name)
   186  }
   187  
   188  func (tx *Tx) CreateBucket(name []byte) (*Bucket, error) {
   189  	return tx.db.createBucket(tx.btx, name)
   190  }
   191  
   192  // CreateBucketIfNotExists returns a root bucket or creates a new one if it
   193  // doesn't already exist.
   194  func (tx *Tx) CreateBucketIfNotExists(name []byte) (*Bucket, error) {
   195  	return tx.db.createBucketIfNotExists(tx.btx, name)
   196  }
   197  
   198  // Writable wraps boltdb Tx.Writable.
   199  func (tx *Tx) Writable() bool {
   200  	return tx.btx.Writable()
   201  }
   202  
   203  // BoltTx returns the underlying bolt.Tx.
   204  func (tx *Tx) BoltTx() *bolt.Tx {
   205  	return tx.btx
   206  }
   207  
   208  // bucketMeta persists metadata -- such as key hashes and child buckets --
   209  // about boltdb Buckets across transactions.
   210  type bucketMeta struct {
   211  	// hashes holds all of the value hashes for keys in this bucket
   212  	hashes     map[string][]byte
   213  	hashesLock sync.Mutex
   214  
   215  	// buckets holds all of the child buckets
   216  	buckets     map[string]*bucketMeta
   217  	bucketsLock sync.Mutex
   218  }
   219  
   220  func newBucketMeta() *bucketMeta {
   221  	return &bucketMeta{
   222  		hashes:  make(map[string][]byte),
   223  		buckets: make(map[string]*bucketMeta),
   224  	}
   225  }
   226  
   227  // getHash of last value written to a key or nil if no hash exists.
   228  func (bm *bucketMeta) getHash(hashKey string) []byte {
   229  	bm.hashesLock.Lock()
   230  	lastHash := bm.hashes[hashKey]
   231  	bm.hashesLock.Unlock()
   232  	return lastHash
   233  }
   234  
   235  // setHash of last value written to key.
   236  func (bm *bucketMeta) setHash(hashKey string, hashVal []byte) {
   237  	bm.hashesLock.Lock()
   238  	bm.hashes[hashKey] = hashVal
   239  	bm.hashesLock.Unlock()
   240  }
   241  
   242  // delHash deletes a hash value or does nothing if the hash key does not exist.
   243  func (bm *bucketMeta) delHash(hashKey string) {
   244  	bm.hashesLock.Lock()
   245  	delete(bm.hashes, hashKey)
   246  	bm.hashesLock.Unlock()
   247  }
   248  
   249  // createBucket metadata entry for the given nested bucket. Overwrites any
   250  // existing entry so caller should ensure bucket does not already exist.
   251  func (bm *bucketMeta) createBucket(name []byte) *bucketMeta {
   252  	bm.bucketsLock.Lock()
   253  	defer bm.bucketsLock.Unlock()
   254  
   255  	// Always create a new Bucket since CreateBucket above fails if the
   256  	// bucket already exists.
   257  	b := newBucketMeta()
   258  	bm.buckets[string(name)] = b
   259  	return b
   260  }
   261  
   262  // deleteBucket metadata entry for the given nested bucket. Does nothing if
   263  // nested bucket metadata does not exist.
   264  func (bm *bucketMeta) deleteBucket(name []byte) {
   265  	bm.bucketsLock.Lock()
   266  	delete(bm.buckets, string(name))
   267  	bm.bucketsLock.Unlock()
   268  
   269  }
   270  
   271  // getOrCreateBucket metadata entry for the given nested bucket.
   272  func (bm *bucketMeta) getOrCreateBucket(name []byte) *bucketMeta {
   273  	bm.bucketsLock.Lock()
   274  	defer bm.bucketsLock.Unlock()
   275  
   276  	b, ok := bm.buckets[string(name)]
   277  	if !ok {
   278  		b = newBucketMeta()
   279  		bm.buckets[string(name)] = b
   280  	}
   281  	return b
   282  }
   283  
   284  type Bucket struct {
   285  	bm         *bucketMeta
   286  	boltBucket *bolt.Bucket
   287  }
   288  
   289  // newBucket creates a new view into a bucket backed by a boltdb
   290  // transaction.
   291  func newBucket(b *bucketMeta, bb *bolt.Bucket) *Bucket {
   292  	return &Bucket{
   293  		bm:         b,
   294  		boltBucket: bb,
   295  	}
   296  }
   297  
   298  // Put into boltdb iff it has changed since the last write.
   299  func (b *Bucket) Put(key []byte, val interface{}) error {
   300  	// buffer for writing serialized state to
   301  	var buf bytes.Buffer
   302  
   303  	// Serialize the object
   304  	if err := codec.NewEncoder(&buf, structs.MsgpackHandle).Encode(val); err != nil {
   305  		return fmt.Errorf("failed to encode passed object: %v", err)
   306  	}
   307  
   308  	// Hash for skipping unnecessary writes
   309  	hashKey := string(key)
   310  	hashVal := blake2b.Sum256(buf.Bytes())
   311  
   312  	// lastHash value or nil if it hasn't been hashed yet
   313  	lastHash := b.bm.getHash(hashKey)
   314  
   315  	// If the hashes are equal, skip the write
   316  	if bytes.Equal(hashVal[:], lastHash) {
   317  		return nil
   318  	}
   319  
   320  	// New value: write it to the underlying boltdb
   321  	if err := b.boltBucket.Put(key, buf.Bytes()); err != nil {
   322  		return fmt.Errorf("failed to write data at key %s: %v", key, err)
   323  	}
   324  
   325  	// New value written, store hash (bucket path map was created above)
   326  	b.bm.setHash(hashKey, hashVal[:])
   327  
   328  	return nil
   329  
   330  }
   331  
   332  // Get value by key from boltdb or return an ErrNotFound error if key not
   333  // found.
   334  func (b *Bucket) Get(key []byte, obj interface{}) error {
   335  	// Get the raw data from the underlying boltdb
   336  	data := b.boltBucket.Get(key)
   337  	if data == nil {
   338  		return NotFound(string(key))
   339  	}
   340  
   341  	// Deserialize the object
   342  	if err := codec.NewDecoderBytes(data, structs.MsgpackHandle).Decode(obj); err != nil {
   343  		return fmt.Errorf("failed to decode data into passed object: %v", err)
   344  	}
   345  
   346  	return nil
   347  }
   348  
   349  // Delete removes a key from the bucket. If the key does not exist then nothing
   350  // is done and a nil error is returned. Returns an error if the bucket was
   351  // created from a read-only transaction.
   352  func (b *Bucket) Delete(key []byte) error {
   353  	err := b.boltBucket.Delete(key)
   354  	b.bm.delHash(string(key))
   355  	return err
   356  }
   357  
   358  // Bucket represents a boltdb Bucket and its associated metadata necessary for
   359  // write deduplication. Like bolt.Buckets it is only valid for the duration of
   360  // the transaction that created it.
   361  func (b *Bucket) Bucket(name []byte) *Bucket {
   362  	bb := b.boltBucket.Bucket(name)
   363  	if bb == nil {
   364  		return nil
   365  	}
   366  
   367  	bmeta := b.bm.getOrCreateBucket(name)
   368  	return newBucket(bmeta, bb)
   369  }
   370  
   371  // CreateBucket creates a new bucket at the given key and returns the new
   372  // bucket. Returns an error if the key already exists, if the bucket name is
   373  // blank, or if the bucket name is too long. The bucket instance is only valid
   374  // for the lifetime of the transaction.
   375  func (b *Bucket) CreateBucket(name []byte) (*Bucket, error) {
   376  	bb, err := b.boltBucket.CreateBucket(name)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  
   381  	bmeta := b.bm.createBucket(name)
   382  	return newBucket(bmeta, bb), nil
   383  }
   384  
   385  // CreateBucketIfNotExists creates a new bucket if it doesn't already exist and
   386  // returns a reference to it. The bucket instance is only valid for the
   387  // lifetime of the transaction.
   388  func (b *Bucket) CreateBucketIfNotExists(name []byte) (*Bucket, error) {
   389  	bb, err := b.boltBucket.CreateBucketIfNotExists(name)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  
   394  	bmeta := b.bm.getOrCreateBucket(name)
   395  	return newBucket(bmeta, bb), nil
   396  }
   397  
   398  // DeleteBucket deletes a child bucket. Returns an error if the bucket
   399  // corresponds to a non-bucket key or another error is encountered. No error is
   400  // returned if the bucket does not exist.
   401  func (b *Bucket) DeleteBucket(name []byte) error {
   402  	// Delete the bucket from the underlying boltdb
   403  	err := b.boltBucket.DeleteBucket(name)
   404  	if err == bolt.ErrBucketNotFound {
   405  		err = nil
   406  	}
   407  
   408  	// Remove reference to child bucket
   409  	b.bm.deleteBucket(name)
   410  	return err
   411  }
   412  
   413  // BoltBucket returns the internal bolt.Bucket for this Bucket. Only valid
   414  // for the duration of the current transaction.
   415  func (b *Bucket) BoltBucket() *bolt.Bucket {
   416  	return b.boltBucket
   417  }