| // 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 snapshoted. |
| 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 goroutine-safe, 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 |
| } |
| } |