You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
7.6 KiB

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