Update cznic/kv to 1a1aa67018afcbe34b1448bae94661feab6c5ecd

Fixes camlistore.org/issue/332

Change-Id: Ie2de810b4572077263418fc6fdd17e2508ff7b24
This commit is contained in:
Brad Fitzpatrick 2014-01-10 09:22:04 -08:00
parent a78b2005d4
commit 2f022a4444
4 changed files with 35 additions and 16 deletions

View File

@ -123,6 +123,7 @@ func TestClose(t *testing.T) {
os.Remove(o._WAL)
}(db.Name())
go db.Close()
if err := db.Close(); err != nil {
t.Error(err)
return

View File

@ -9,12 +9,17 @@ Package kv implements a simple and easy to use persistent key/value (KV) store.
The stored KV pairs are sorted in the key collation order defined by an user
supplied 'compare' function (passed as a field in Options).
Keys and Values
Keys and Values Limits
Keys, as well as the values associated with them, are opaque []bytes. Maximum
size of a "native" key or value is 65787 bytes. Larger keys or values have to
be composed of the "native" ones in client code.
Database limits
The maximum DB size kv can handle is 2^60 bytes (1 exabyte). See also [4]:
"Block handles".
ACID and transactional properties
Transactions are resource limited. All changes made by a transaction are held
@ -71,6 +76,7 @@ Referenced from above:
[1]: http://en.wikipedia.org/wiki/ACID
[2]: http://en.wikipedia.org/wiki/2PC
[3]: http://en.wikipedia.org/wiki/Write_ahead_logging
[4]: http://godoc.org/github.com/cznic/exp/lldb#Allocator
*/
package kv

View File

@ -241,13 +241,14 @@ func Open(name string, opts *Options) (db *DB, err error) {
// any. Failing to call Close before exiting a program can lose the last open
// or being committed transaction.
//
// Close is idempotent.
// Successful Close is idempotent.
func (db *DB) Close() (err error) {
db.closeMu.Lock()
defer db.closeMu.Unlock()
if db.closed {
return nil
return
}
db.closed = true
if err = db.enter(); err != nil {
@ -301,6 +302,15 @@ func (db *DB) Close() (err error) {
}
func (db *DB) close() (err error) {
// We are safe to close due to locked db.closeMu, but not safe aginst
// any other goroutine concurrently calling other exported db methods,
// causing a race[0] in the db.enter() mechanism. So we must lock
// db.bkl.
//
// [0]: https://github.com/cznic/kv/issues/17#issuecomment-31960658
db.bkl.Lock()
defer db.bkl.Unlock()
if db.f == nil { // lldb.MemFiler
return
}
@ -424,6 +434,8 @@ func (db *DB) timeout() {
db.bkl.Lock()
defer db.bkl.Unlock()
db.closeMu.Lock()
defer db.closeMu.Unlock()
if db.closed {
return
}
@ -581,17 +593,17 @@ func (db *DB) Delete(key []byte) (err error) {
// Extract is a combination of Get and Delete. If the key exists in the DB, it
// is returned (like Get) and also deleted from the DB in a more efficient way
// which doesn't search for the key twice. The returned slice may be a
// sub-slice of dst if dst was large enough to hold the entire content.
// sub-slice of buf if buf was large enough to hold the entire content.
// Otherwise, a newly allocated slice will be returned. It is valid to pass a
// nil dst.
// nil buf.
//
// Extract is atomic and it is safe for concurrent use by multiple goroutines.
func (db *DB) Extract(dst, key []byte) (value []byte, err error) {
func (db *DB) Extract(buf, key []byte) (value []byte, err error) {
if err = db.enter(); err != nil {
return
}
value, err = db.root.Extract(dst, key)
value, err = db.root.Extract(buf, key)
db.leave(&err)
return
}
@ -611,17 +623,17 @@ func (db *DB) First() (key, value []byte, err error) {
}
// Get returns the value associated with key, or nil if no such value exists.
// The returned slice may be a sub-slice of dst if dst was large enough to hold
// The returned slice may be a sub-slice of buf if buf was large enough to hold
// the entire content. Otherwise, a newly allocated slice will be returned. It
// is valid to pass a nil dst.
// is valid to pass a nil buf.
//
// Get is atomic and it is safe for concurrent use by multiple goroutines.
func (db *DB) Get(dst, key []byte) (value []byte, err error) {
func (db *DB) Get(buf, key []byte) (value []byte, err error) {
if err = db.enter(); err != nil {
return
}
value, err = db.root.Get(dst, key)
value, err = db.root.Get(buf, key)
db.leave(&err)
return
}
@ -655,17 +667,17 @@ func (db *DB) Last() (key, value []byte, err error) {
//
// modulo the differing return values.
//
// The returned slice may be a sub-slice of dst if dst was large enough to hold
// The returned slice may be a sub-slice of buf if buf was large enough to hold
// the entire content. Otherwise, a newly allocated slice will be returned. It
// is valid to pass a nil dst.
// is valid to pass a nil buf.
//
// Put is atomic and it is safe for concurrent use by multiple goroutines.
func (db *DB) Put(dst, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) {
func (db *DB) Put(buf, key []byte, upd func(key, old []byte) (new []byte, write bool, err error)) (old []byte, written bool, err error) {
if err = db.enter(); err != nil {
return
}
old, written, err = db.root.Put(dst, key, upd)
old, written, err = db.root.Put(buf, key, upd)
db.leave(&err)
return
}

View File

@ -13,7 +13,7 @@ import (
func open00(name string, in *DB) (db *DB, err error) {
db = in
if db.alloc, err = lldb.NewAllocator(lldb.NewInnerFiler(db.filer, 16), &lldb.Options{}); err != nil {
return nil, &os.PathError{Op: "dbm.Open", Path: name, Err: err}
return nil, &os.PathError{Op: "kv.open00", Path: name, Err: err}
}
db.alloc.Compress = true