github.com/adityamillind98/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 }