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.
183 lines
4.7 KiB
183 lines
4.7 KiB
// Copyright (c) 2012, Suryandaru Triandana <[email protected]> |
|
// 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 |
|
} |
|
}
|
|
|