github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/db_transaction.go (about) 1 // Copyright (c) 2016, Suryandaru Triandana <syndtr@gmail.com> 2 // All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 package leveldb 8 9 import ( 10 "errors" 11 "sync" 12 "time" 13 14 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/iterator" 15 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/opt" 16 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/util" 17 ) 18 19 var errTransactionDone = errors.New("leveldb: transaction already closed") 20 21 // Transaction is the transaction handle. 22 type Transaction struct { 23 db *DB 24 lk sync.RWMutex 25 seq uint64 26 mem *memDB 27 tables tFiles 28 ikScratch []byte 29 rec sessionRecord 30 stats cStatStaging 31 closed bool 32 } 33 34 // Get gets the value for the given key. It returns ErrNotFound if the 35 // DB does not contains the key. 36 // 37 // The returned slice is its own copy, it is safe to modify the contents 38 // of the returned slice. 39 // It is safe to modify the contents of the argument after Get returns. 40 func (tr *Transaction) Get(key []byte, ro *opt.ReadOptions) ([]byte, error) { 41 tr.lk.RLock() 42 defer tr.lk.RUnlock() 43 if tr.closed { 44 return nil, errTransactionDone 45 } 46 return tr.db.get(tr.mem.DB, tr.tables, key, tr.seq, ro) 47 } 48 49 // Has returns true if the DB does contains the given key. 50 // 51 // It is safe to modify the contents of the argument after Has returns. 52 func (tr *Transaction) Has(key []byte, ro *opt.ReadOptions) (bool, error) { 53 tr.lk.RLock() 54 defer tr.lk.RUnlock() 55 if tr.closed { 56 return false, errTransactionDone 57 } 58 return tr.db.has(tr.mem.DB, tr.tables, key, tr.seq, ro) 59 } 60 61 // NewIterator returns an iterator for the latest snapshot of the transaction. 62 // The returned iterator is not goroutine-safe, but it is safe to use multiple 63 // iterators concurrently, with each in a dedicated goroutine. 64 // It is also safe to use an iterator concurrently while writes to the 65 // transaction. The resultant key/value pairs are guaranteed to be consistent. 66 // 67 // Slice allows slicing the iterator to only contains keys in the given 68 // range. A nil Range.Start is treated as a key before all keys in the 69 // DB. And a nil Range.Limit is treated as a key after all keys in 70 // the DB. 71 // 72 // The iterator must be released after use, by calling Release method. 73 // 74 // Also read Iterator documentation of the leveldb/iterator package. 75 func (tr *Transaction) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator { 76 tr.lk.RLock() 77 defer tr.lk.RUnlock() 78 if tr.closed { 79 return iterator.NewEmptyIterator(errTransactionDone) 80 } 81 tr.mem.incref() 82 return tr.db.newIterator(tr.mem, tr.tables, tr.seq, slice, ro) 83 } 84 85 func (tr *Transaction) flush() error { 86 // Flush memdb. 87 if tr.mem.Len() != 0 { 88 tr.stats.startTimer() 89 iter := tr.mem.NewIterator(nil) 90 t, n, err := tr.db.s.tops.createFrom(iter) 91 iter.Release() 92 tr.stats.stopTimer() 93 if err != nil { 94 return err 95 } 96 if tr.mem.getref() == 1 { 97 tr.mem.Reset() 98 } else { 99 tr.mem.decref() 100 tr.mem = tr.db.mpoolGet(0) 101 tr.mem.incref() 102 } 103 tr.tables = append(tr.tables, t) 104 tr.rec.addTableFile(0, t) 105 tr.stats.write += t.size 106 tr.db.logf("transaction@flush created L0@%d N·%d S·%s %q:%q", t.fd.Num, n, shortenb(int(t.size)), t.imin, t.imax) 107 } 108 return nil 109 } 110 111 func (tr *Transaction) put(kt keyType, key, value []byte) error { 112 tr.ikScratch = makeInternalKey(tr.ikScratch, key, tr.seq+1, kt) 113 if tr.mem.Free() < len(tr.ikScratch)+len(value) { 114 if err := tr.flush(); err != nil { 115 return err 116 } 117 } 118 if err := tr.mem.Put(tr.ikScratch, value); err != nil { 119 return err 120 } 121 tr.seq++ 122 return nil 123 } 124 125 // Put sets the value for the given key. It overwrites any previous value 126 // for that key; a DB is not a multi-map. 127 // Please note that the transaction is not compacted until committed, so if you 128 // writes 10 same keys, then those 10 same keys are in the transaction. 129 // 130 // It is safe to modify the contents of the arguments after Put returns. 131 func (tr *Transaction) Put(key, value []byte, wo *opt.WriteOptions) error { 132 tr.lk.Lock() 133 defer tr.lk.Unlock() 134 if tr.closed { 135 return errTransactionDone 136 } 137 return tr.put(keyTypeVal, key, value) 138 } 139 140 // Delete deletes the value for the given key. 141 // Please note that the transaction is not compacted until committed, so if you 142 // writes 10 same keys, then those 10 same keys are in the transaction. 143 // 144 // It is safe to modify the contents of the arguments after Delete returns. 145 func (tr *Transaction) Delete(key []byte, wo *opt.WriteOptions) error { 146 tr.lk.Lock() 147 defer tr.lk.Unlock() 148 if tr.closed { 149 return errTransactionDone 150 } 151 return tr.put(keyTypeDel, key, nil) 152 } 153 154 // Write apply the given batch to the transaction. The batch will be applied 155 // sequentially. 156 // Please note that the transaction is not compacted until committed, so if you 157 // writes 10 same keys, then those 10 same keys are in the transaction. 158 // 159 // It is safe to modify the contents of the arguments after Write returns. 160 func (tr *Transaction) Write(b *Batch, wo *opt.WriteOptions) error { 161 if b == nil || b.Len() == 0 { 162 return nil 163 } 164 165 tr.lk.Lock() 166 defer tr.lk.Unlock() 167 if tr.closed { 168 return errTransactionDone 169 } 170 return b.decodeRec(func(i int, kt keyType, key, value []byte) error { 171 return tr.put(kt, key, value) 172 }) 173 } 174 175 func (tr *Transaction) setDone() { 176 tr.closed = true 177 tr.db.tr = nil 178 tr.mem.decref() 179 <-tr.db.writeLockC 180 } 181 182 // Commit commits the transaction. 183 // 184 // Other methods should not be called after transaction has been committed. 185 func (tr *Transaction) Commit() error { 186 if err := tr.db.ok(); err != nil { 187 return err 188 } 189 190 tr.lk.Lock() 191 defer tr.lk.Unlock() 192 if tr.closed { 193 return errTransactionDone 194 } 195 defer tr.setDone() 196 if err := tr.flush(); err != nil { 197 tr.discard() 198 return err 199 } 200 if len(tr.tables) != 0 { 201 // Committing transaction. 202 tr.rec.setSeqNum(tr.seq) 203 tr.db.compCommitLk.Lock() 204 defer tr.db.compCommitLk.Unlock() 205 for retry := 0; retry < 3; retry++ { 206 if err := tr.db.s.commit(&tr.rec); err != nil { 207 tr.db.logf("transaction@commit error R·%d %q", retry, err) 208 select { 209 case <-time.After(time.Second): 210 case _, _ = <-tr.db.closeC: 211 tr.db.logf("transaction@commit exiting") 212 return err 213 } 214 } else { 215 // Success. Set db.seq. 216 tr.db.setSeq(tr.seq) 217 break 218 } 219 } 220 // Trigger table auto-compaction. 221 tr.db.compTrigger(tr.db.tcompCmdC) 222 } 223 return nil 224 } 225 226 func (tr *Transaction) discard() { 227 // Discard transaction. 228 for _, t := range tr.tables { 229 tr.db.logf("transaction@discard @%d", t.fd.Num) 230 if err1 := tr.db.s.stor.Remove(t.fd); err1 == nil { 231 tr.db.s.reuseFileNum(t.fd.Num) 232 } 233 } 234 } 235 236 // Discard discards the transaction. 237 // 238 // Other methods should not be called after transaction has been discarded. 239 func (tr *Transaction) Discard() { 240 tr.lk.Lock() 241 if !tr.closed { 242 tr.discard() 243 tr.setDone() 244 } 245 tr.lk.Unlock() 246 } 247 248 // OpenTransaction opens an atomic DB transaction. Only one transaction can be 249 // opened at a time. Write will be blocked until the transaction is committed or 250 // discarded. 251 // The returned transaction handle is goroutine-safe. 252 // 253 // The transaction must be closed once done, either by committing or discarding 254 // the transaction. 255 // Closing the DB will discard open transaction. 256 func (db *DB) OpenTransaction() (*Transaction, error) { 257 if err := db.ok(); err != nil { 258 return nil, err 259 } 260 261 // The write happen synchronously. 262 select { 263 case db.writeLockC <- struct{}{}: 264 case err := <-db.compPerErrC: 265 return nil, err 266 case _, _ = <-db.closeC: 267 return nil, ErrClosed 268 } 269 270 if db.tr != nil { 271 panic("leveldb: has open transaction") 272 } 273 274 // Flush current memdb. 275 if db.mem != nil && db.mem.Len() != 0 { 276 if _, err := db.rotateMem(0, true); err != nil { 277 return nil, err 278 } 279 } 280 281 tr := &Transaction{ 282 db: db, 283 seq: db.seq, 284 mem: db.mpoolGet(0), 285 } 286 tr.mem.incref() 287 db.tr = tr 288 return tr, nil 289 }