184 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
 | |
| // All rights reserved.
 | |
| //
 | |
| // Use of this source code is governed by a BSD-style license that can be
 | |
| // found in the LICENSE file.
 | |
| 
 | |
| package leveldb
 | |
| 
 | |
| import (
 | |
| 	"container/list"
 | |
| 	"fmt"
 | |
| 	"runtime"
 | |
| 	"sync"
 | |
| 	"sync/atomic"
 | |
| 
 | |
| 	"github.com/syndtr/goleveldb/leveldb/iterator"
 | |
| 	"github.com/syndtr/goleveldb/leveldb/opt"
 | |
| 	"github.com/syndtr/goleveldb/leveldb/util"
 | |
| )
 | |
| 
 | |
| type snapshotElement struct {
 | |
| 	seq uint64
 | |
| 	ref int
 | |
| 	e   *list.Element
 | |
| }
 | |
| 
 | |
| // Acquires a snapshot, based on latest sequence.
 | |
| func (db *DB) acquireSnapshot() *snapshotElement {
 | |
| 	db.snapsMu.Lock()
 | |
| 	defer db.snapsMu.Unlock()
 | |
| 
 | |
| 	seq := db.getSeq()
 | |
| 
 | |
| 	if e := db.snapsList.Back(); e != nil {
 | |
| 		se := e.Value.(*snapshotElement)
 | |
| 		if se.seq == seq {
 | |
| 			se.ref++
 | |
| 			return se
 | |
| 		} else if seq < se.seq {
 | |
| 			panic("leveldb: sequence number is not increasing")
 | |
| 		}
 | |
| 	}
 | |
| 	se := &snapshotElement{seq: seq, ref: 1}
 | |
| 	se.e = db.snapsList.PushBack(se)
 | |
| 	return se
 | |
| }
 | |
| 
 | |
| // Releases given snapshot element.
 | |
| func (db *DB) releaseSnapshot(se *snapshotElement) {
 | |
| 	db.snapsMu.Lock()
 | |
| 	defer db.snapsMu.Unlock()
 | |
| 
 | |
| 	se.ref--
 | |
| 	if se.ref == 0 {
 | |
| 		db.snapsList.Remove(se.e)
 | |
| 		se.e = nil
 | |
| 	} else if se.ref < 0 {
 | |
| 		panic("leveldb: Snapshot: negative element reference")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Gets minimum sequence that not being snapshotted.
 | |
| func (db *DB) minSeq() uint64 {
 | |
| 	db.snapsMu.Lock()
 | |
| 	defer db.snapsMu.Unlock()
 | |
| 
 | |
| 	if e := db.snapsList.Front(); e != nil {
 | |
| 		return e.Value.(*snapshotElement).seq
 | |
| 	}
 | |
| 
 | |
| 	return db.getSeq()
 | |
| }
 | |
| 
 | |
| // Snapshot is a DB snapshot.
 | |
| type Snapshot struct {
 | |
| 	db       *DB
 | |
| 	elem     *snapshotElement
 | |
| 	mu       sync.RWMutex
 | |
| 	released bool
 | |
| }
 | |
| 
 | |
| // Creates new snapshot object.
 | |
| func (db *DB) newSnapshot() *Snapshot {
 | |
| 	snap := &Snapshot{
 | |
| 		db:   db,
 | |
| 		elem: db.acquireSnapshot(),
 | |
| 	}
 | |
| 	atomic.AddInt32(&db.aliveSnaps, 1)
 | |
| 	runtime.SetFinalizer(snap, (*Snapshot).Release)
 | |
| 	return snap
 | |
| }
 | |
| 
 | |
| func (snap *Snapshot) String() string {
 | |
| 	return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
 | |
| }
 | |
| 
 | |
| // Get gets the value for the given key. It returns ErrNotFound if
 | |
| // the DB does not contains the key.
 | |
| //
 | |
| // The caller should not modify the contents of the returned slice, but
 | |
| // it is safe to modify the contents of the argument after Get returns.
 | |
| func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
 | |
| 	err = snap.db.ok()
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	snap.mu.RLock()
 | |
| 	defer snap.mu.RUnlock()
 | |
| 	if snap.released {
 | |
| 		err = ErrSnapshotReleased
 | |
| 		return
 | |
| 	}
 | |
| 	return snap.db.get(nil, nil, key, snap.elem.seq, ro)
 | |
| }
 | |
| 
 | |
| // Has returns true if the DB does contains the given key.
 | |
| //
 | |
| // It is safe to modify the contents of the argument after Get returns.
 | |
| func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
 | |
| 	err = snap.db.ok()
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	snap.mu.RLock()
 | |
| 	defer snap.mu.RUnlock()
 | |
| 	if snap.released {
 | |
| 		err = ErrSnapshotReleased
 | |
| 		return
 | |
| 	}
 | |
| 	return snap.db.has(nil, nil, key, snap.elem.seq, ro)
 | |
| }
 | |
| 
 | |
| // NewIterator returns an iterator for the snapshot of the underlying DB.
 | |
| // The returned iterator is not safe for concurrent use, but it is safe to use
 | |
| // multiple iterators concurrently, with each in a dedicated goroutine.
 | |
| // It is also safe to use an iterator concurrently with modifying its
 | |
| // underlying DB. The resultant key/value pairs are guaranteed to be
 | |
| // consistent.
 | |
| //
 | |
| // Slice allows slicing the iterator to only contains keys in the given
 | |
| // range. A nil Range.Start is treated as a key before all keys in the
 | |
| // DB. And a nil Range.Limit is treated as a key after all keys in
 | |
| // the DB.
 | |
| //
 | |
| // The iterator must be released after use, by calling Release method.
 | |
| // Releasing the snapshot doesn't mean releasing the iterator too, the
 | |
| // iterator would be still valid until released.
 | |
| //
 | |
| // Also read Iterator documentation of the leveldb/iterator package.
 | |
| func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
 | |
| 	if err := snap.db.ok(); err != nil {
 | |
| 		return iterator.NewEmptyIterator(err)
 | |
| 	}
 | |
| 	snap.mu.Lock()
 | |
| 	defer snap.mu.Unlock()
 | |
| 	if snap.released {
 | |
| 		return iterator.NewEmptyIterator(ErrSnapshotReleased)
 | |
| 	}
 | |
| 	// Since iterator already hold version ref, it doesn't need to
 | |
| 	// hold snapshot ref.
 | |
| 	return snap.db.newIterator(nil, nil, snap.elem.seq, slice, ro)
 | |
| }
 | |
| 
 | |
| // Release releases the snapshot. This will not release any returned
 | |
| // iterators, the iterators would still be valid until released or the
 | |
| // underlying DB is closed.
 | |
| //
 | |
| // Other methods should not be called after the snapshot has been released.
 | |
| func (snap *Snapshot) Release() {
 | |
| 	snap.mu.Lock()
 | |
| 	defer snap.mu.Unlock()
 | |
| 
 | |
| 	if !snap.released {
 | |
| 		// Clear the finalizer.
 | |
| 		runtime.SetFinalizer(snap, nil)
 | |
| 
 | |
| 		snap.released = true
 | |
| 		snap.db.releaseSnapshot(snap.elem)
 | |
| 		atomic.AddInt32(&snap.db.aliveSnaps, -1)
 | |
| 		snap.db = nil
 | |
| 		snap.elem = nil
 | |
| 	}
 | |
| }
 |