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.

325 lines
8.8 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 safe for concurrent use, but it is safe to use
  55. // multiple 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.replayInternal(func(i int, kt keyType, k, v []byte) error {
  157. return tr.put(kt, k, v)
  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. If error is not nil, then the transaction is
  167. // not committed, it can then either be retried or discarded.
  168. //
  169. // Other methods should not be called after transaction has been committed.
  170. func (tr *Transaction) Commit() error {
  171. if err := tr.db.ok(); err != nil {
  172. return err
  173. }
  174. tr.lk.Lock()
  175. defer tr.lk.Unlock()
  176. if tr.closed {
  177. return errTransactionDone
  178. }
  179. if err := tr.flush(); err != nil {
  180. // Return error, lets user decide either to retry or discard
  181. // transaction.
  182. return err
  183. }
  184. if len(tr.tables) != 0 {
  185. // Committing transaction.
  186. tr.rec.setSeqNum(tr.seq)
  187. tr.db.compCommitLk.Lock()
  188. tr.stats.startTimer()
  189. var cerr error
  190. for retry := 0; retry < 3; retry++ {
  191. cerr = tr.db.s.commit(&tr.rec)
  192. if cerr != nil {
  193. tr.db.logf("transaction@commit error R·%d %q", retry, cerr)
  194. select {
  195. case <-time.After(time.Second):
  196. case <-tr.db.closeC:
  197. tr.db.logf("transaction@commit exiting")
  198. tr.db.compCommitLk.Unlock()
  199. return cerr
  200. }
  201. } else {
  202. // Success. Set db.seq.
  203. tr.db.setSeq(tr.seq)
  204. break
  205. }
  206. }
  207. tr.stats.stopTimer()
  208. if cerr != nil {
  209. // Return error, lets user decide either to retry or discard
  210. // transaction.
  211. return cerr
  212. }
  213. // Update compaction stats. This is safe as long as we hold compCommitLk.
  214. tr.db.compStats.addStat(0, &tr.stats)
  215. // Trigger table auto-compaction.
  216. tr.db.compTrigger(tr.db.tcompCmdC)
  217. tr.db.compCommitLk.Unlock()
  218. // Additionally, wait compaction when certain threshold reached.
  219. // Ignore error, returns error only if transaction can't be committed.
  220. tr.db.waitCompaction()
  221. }
  222. // Only mark as done if transaction committed successfully.
  223. tr.setDone()
  224. return nil
  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. // Discard discards the transaction.
  236. //
  237. // Other methods should not be called after transaction has been discarded.
  238. func (tr *Transaction) Discard() {
  239. tr.lk.Lock()
  240. if !tr.closed {
  241. tr.discard()
  242. tr.setDone()
  243. }
  244. tr.lk.Unlock()
  245. }
  246. func (db *DB) waitCompaction() error {
  247. if db.s.tLen(0) >= db.s.o.GetWriteL0PauseTrigger() {
  248. return db.compTriggerWait(db.tcompCmdC)
  249. }
  250. return nil
  251. }
  252. // OpenTransaction opens an atomic DB transaction. Only one transaction can be
  253. // opened at a time. Subsequent call to Write and OpenTransaction will be blocked
  254. // until in-flight transaction is committed or discarded.
  255. // The returned transaction handle is safe for concurrent use.
  256. //
  257. // Transaction is expensive and can overwhelm compaction, especially if
  258. // transaction size is small. Use with caution.
  259. //
  260. // The transaction must be closed once done, either by committing or discarding
  261. // the transaction.
  262. // Closing the DB will discard open transaction.
  263. func (db *DB) OpenTransaction() (*Transaction, error) {
  264. if err := db.ok(); err != nil {
  265. return nil, err
  266. }
  267. // The write happen synchronously.
  268. select {
  269. case db.writeLockC <- struct{}{}:
  270. case err := <-db.compPerErrC:
  271. return nil, err
  272. case <-db.closeC:
  273. return nil, ErrClosed
  274. }
  275. if db.tr != nil {
  276. panic("leveldb: has open transaction")
  277. }
  278. // Flush current memdb.
  279. if db.mem != nil && db.mem.Len() != 0 {
  280. if _, err := db.rotateMem(0, true); err != nil {
  281. return nil, err
  282. }
  283. }
  284. // Wait compaction when certain threshold reached.
  285. if err := db.waitCompaction(); err != nil {
  286. return nil, err
  287. }
  288. tr := &Transaction{
  289. db: db,
  290. seq: db.seq,
  291. mem: db.mpoolGet(0),
  292. }
  293. tr.mem.incref()
  294. db.tr = tr
  295. return tr, nil
  296. }