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  }