github.com/bhojpur/cache@v0.0.4/pkg/memory/compact.go (about) 1 package memory 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 // Compact will create a copy of the source DB and in the destination DB. This may 24 // reclaim space that the source database no longer has use for. txMaxSize can be 25 // used to limit the transactions size of this process and may trigger intermittent 26 // commits. A value of zero will ignore transaction sizes. 27 func Compact(dst, src *DB, txMaxSize int64) error { 28 // commit regularly, or we'll run out of memory for large datasets if using 29 // one transaction. 30 var size int64 31 tx, err := dst.Begin(true) 32 if err != nil { 33 return err 34 } 35 defer tx.Rollback() 36 37 if err := walk(src, func(keys [][]byte, k, v []byte, seq uint64) error { 38 // On each key/value, check if we have exceeded tx size. 39 sz := int64(len(k) + len(v)) 40 if size+sz > txMaxSize && txMaxSize != 0 { 41 // Commit previous transaction. 42 if err := tx.Commit(); err != nil { 43 return err 44 } 45 46 // Start new transaction. 47 tx, err = dst.Begin(true) 48 if err != nil { 49 return err 50 } 51 size = 0 52 } 53 size += sz 54 55 // Create bucket on the root transaction if this is the first level. 56 nk := len(keys) 57 if nk == 0 { 58 bkt, err := tx.CreateBucket(k) 59 if err != nil { 60 return err 61 } 62 if err := bkt.SetSequence(seq); err != nil { 63 return err 64 } 65 return nil 66 } 67 68 // Create buckets on subsequent levels, if necessary. 69 b := tx.Bucket(keys[0]) 70 if nk > 1 { 71 for _, k := range keys[1:] { 72 b = b.Bucket(k) 73 } 74 } 75 76 // Fill the entire page for best compaction. 77 b.FillPercent = 1.0 78 79 // If there is no value then this is a bucket call. 80 if v == nil { 81 bkt, err := b.CreateBucket(k) 82 if err != nil { 83 return err 84 } 85 if err := bkt.SetSequence(seq); err != nil { 86 return err 87 } 88 return nil 89 } 90 91 // Otherwise treat it as a key/value pair. 92 return b.Put(k, v) 93 }); err != nil { 94 return err 95 } 96 97 return tx.Commit() 98 } 99 100 // walkFunc is the type of the function called for keys (buckets and "normal" 101 // values) discovered by Walk. keys is the list of keys to descend to the bucket 102 // owning the discovered key/value pair k/v. 103 type walkFunc func(keys [][]byte, k, v []byte, seq uint64) error 104 105 // walk walks recursively the Bhojpur Cache in-memory database db, calling walkFn 106 // for each key it finds. 107 func walk(db *DB, walkFn walkFunc) error { 108 return db.View(func(tx *Tx) error { 109 return tx.ForEach(func(name []byte, b *Bucket) error { 110 return walkBucket(b, nil, name, nil, b.Sequence(), walkFn) 111 }) 112 }) 113 } 114 115 func walkBucket(b *Bucket, keypath [][]byte, k, v []byte, seq uint64, fn walkFunc) error { 116 // Execute callback. 117 if err := fn(keypath, k, v, seq); err != nil { 118 return err 119 } 120 121 // If this is not a Bucket then stop. 122 if v != nil { 123 return nil 124 } 125 126 // Iterate over each child key/value. 127 keypath = append(keypath, k) 128 return b.ForEach(func(k, v []byte) error { 129 if v == nil { 130 bkt := b.Bucket(k) 131 return walkBucket(bkt, keypath, k, nil, bkt.Sequence(), fn) 132 } 133 return walkBucket(b, keypath, k, v, b.Sequence(), fn) 134 }) 135 }