diff --git a/third_party/github.com/cznic/kv/all_test.go b/third_party/github.com/cznic/kv/all_test.go index de489ba98..4c8e1c853 100644 --- a/third_party/github.com/cznic/kv/all_test.go +++ b/third_party/github.com/cznic/kv/all_test.go @@ -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 diff --git a/third_party/github.com/cznic/kv/doc.go b/third_party/github.com/cznic/kv/doc.go index 791553327..ba7cb9b96 100644 --- a/third_party/github.com/cznic/kv/doc.go +++ b/third_party/github.com/cznic/kv/doc.go @@ -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 diff --git a/third_party/github.com/cznic/kv/kv.go b/third_party/github.com/cznic/kv/kv.go index e7e636829..ec54105c9 100644 --- a/third_party/github.com/cznic/kv/kv.go +++ b/third_party/github.com/cznic/kv/kv.go @@ -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 } diff --git a/third_party/github.com/cznic/kv/v0.go b/third_party/github.com/cznic/kv/v0.go index 7d6202c78..39d1c22bb 100644 --- a/third_party/github.com/cznic/kv/v0.go +++ b/third_party/github.com/cznic/kv/v0.go @@ -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