syncthing/vendor/github.com/syndtr/goleveldb/leveldb/db_snapshot.go

184 lines
4.7 KiB
Go
Raw Normal View History

2014-07-06 12:46:48 +00:00
// 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 (
2014-11-04 04:00:11 +00:00
"container/list"
2014-12-29 11:22:58 +00:00
"fmt"
2014-07-06 12:46:48 +00:00
"runtime"
"sync"
2014-09-02 07:43:42 +00:00
"sync/atomic"
2014-07-06 12:46:48 +00:00
"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
2014-11-04 04:00:11 +00:00
e *list.Element
2014-07-06 12:46:48 +00:00
}
// Acquires a snapshot, based on latest sequence.
func (db *DB) acquireSnapshot() *snapshotElement {
db.snapsMu.Lock()
2014-11-04 04:00:11 +00:00
defer db.snapsMu.Unlock()
2014-07-06 12:46:48 +00:00
seq := db.getSeq()
2014-11-04 04:00:11 +00:00
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")
2014-07-06 12:46:48 +00:00
}
}
2014-11-04 04:00:11 +00:00
se := &snapshotElement{seq: seq, ref: 1}
se.e = db.snapsList.PushBack(se)
return se
2014-07-06 12:46:48 +00:00
}
// Releases given snapshot element.
2014-11-04 04:00:11 +00:00
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")
2014-07-06 12:46:48 +00:00
}
}
// Gets minimum sequence that not being snapshoted.
func (db *DB) minSeq() uint64 {
db.snapsMu.Lock()
defer db.snapsMu.Unlock()
2014-11-04 04:00:11 +00:00
if e := db.snapsList.Front(); e != nil {
return e.Value.(*snapshotElement).seq
2014-07-06 12:46:48 +00:00
}
2014-11-04 04:00:11 +00:00
2014-07-06 12:46:48 +00:00
return db.getSeq()
}
// Snapshot is a DB snapshot.
type Snapshot struct {
db *DB
elem *snapshotElement
2014-09-02 07:43:42 +00:00
mu sync.RWMutex
2014-07-06 12:46:48 +00:00
released bool
}
// Creates new snapshot object.
func (db *DB) newSnapshot() *Snapshot {
2014-07-23 06:31:36 +00:00
snap := &Snapshot{
2014-07-06 12:46:48 +00:00
db: db,
elem: db.acquireSnapshot(),
}
2014-09-02 07:43:42 +00:00
atomic.AddInt32(&db.aliveSnaps, 1)
2014-07-23 06:31:36 +00:00
runtime.SetFinalizer(snap, (*Snapshot).Release)
return snap
2014-07-06 12:46:48 +00:00
}
2014-12-29 11:22:58 +00:00
func (snap *Snapshot) String() string {
return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
}
2014-07-06 12:46:48 +00:00
// Get gets the value for the given key. It returns ErrNotFound if
2014-11-24 10:57:31 +00:00
// the DB does not contains the key.
2014-07-06 12:46:48 +00:00
//
// 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.
2014-07-23 06:31:36 +00:00
func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
err = snap.db.ok()
2014-07-06 12:46:48 +00:00
if err != nil {
return
}
2014-09-02 07:43:42 +00:00
snap.mu.RLock()
defer snap.mu.RUnlock()
2014-07-23 06:31:36 +00:00
if snap.released {
2014-07-06 12:46:48 +00:00
err = ErrSnapshotReleased
return
}
return snap.db.get(nil, nil, key, snap.elem.seq, ro)
2014-07-06 12:46:48 +00:00
}
2014-11-24 10:57:31 +00:00
// 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)
2014-11-24 10:57:31 +00:00
}
// NewIterator returns an iterator for the snapshot of the underlying DB.
2014-07-06 12:46:48 +00:00
// 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.
2014-07-23 06:31:36 +00:00
func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
if err := snap.db.ok(); err != nil {
2014-07-06 12:46:48 +00:00
return iterator.NewEmptyIterator(err)
}
2014-07-23 06:31:36 +00:00
snap.mu.Lock()
defer snap.mu.Unlock()
if snap.released {
2014-07-06 12:46:48 +00:00
return iterator.NewEmptyIterator(ErrSnapshotReleased)
}
2014-07-23 06:31:36 +00:00
// 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)
2014-07-06 12:46:48 +00:00
}
// 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.
2014-07-23 06:31:36 +00:00
func (snap *Snapshot) Release() {
snap.mu.Lock()
defer snap.mu.Unlock()
if !snap.released {
2014-07-06 12:46:48 +00:00
// Clear the finalizer.
2014-07-23 06:31:36 +00:00
runtime.SetFinalizer(snap, nil)
2014-07-06 12:46:48 +00:00
2014-07-23 06:31:36 +00:00
snap.released = true
snap.db.releaseSnapshot(snap.elem)
2014-09-02 07:43:42 +00:00
atomic.AddInt32(&snap.db.aliveSnaps, -1)
2014-07-23 06:31:36 +00:00
snap.db = nil
snap.elem = nil
2014-07-06 12:46:48 +00:00
}
}