Skip to content

Commit c761393

Browse files
committed
Let lock function be pluggable.
Allows for more involved locking methods, lock cleanup policy, and disabling locking altogether.
1 parent 4baa7fd commit c761393

File tree

3 files changed

+74
-27
lines changed

3 files changed

+74
-27
lines changed

kv.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package kv
77
import (
88
"encoding/binary"
99
"fmt"
10+
"io"
1011
"os"
1112
"os/signal"
1213
"sync"
@@ -52,7 +53,7 @@ type DB struct {
5253
gracePeriod time.Duration // WAL grace period
5354
isMem bool // No signal capture
5455
lastCommitErr error // from failed EndUpdate
55-
lock *os.File // The DB file lock
56+
lock io.Closer // The DB file lock
5657
root *lldb.BTree // The KV layer
5758
wal *os.File // WAL if any
5859
}
@@ -78,9 +79,7 @@ func create(f *os.File, filer lldb.Filer, opts *Options, isMem bool) (db *DB, er
7879
defer func() {
7980
lock := opts.lock
8081
if err != nil && lock != nil {
81-
n := lock.Name()
8282
lock.Close()
83-
os.Remove(n)
8483
db = nil
8584
}
8685
}()
@@ -178,9 +177,7 @@ func Open(name string, opts *Options) (db *DB, err error) {
178177
defer func() {
179178
lock := opts.lock
180179
if err != nil && lock != nil {
181-
n := lock.Name()
182180
lock.Close()
183-
os.Remove(n)
184181
db = nil
185182
}
186183
if err != nil {
@@ -289,15 +286,10 @@ func (db *DB) Close() (err error) {
289286
}
290287

291288
if lock := db.lock; lock != nil {
292-
n := lock.Name()
293289
e1 := lock.Close()
294-
e2 := os.Remove(n)
295290
if err == nil {
296291
err = e1
297292
}
298-
if err == nil {
299-
err = e2
300-
}
301293
}
302294
return
303295
}

lock.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2013 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package kv
6+
7+
import (
8+
"crypto/sha1"
9+
"fmt"
10+
"io"
11+
"os"
12+
"path/filepath"
13+
"sync"
14+
)
15+
16+
func lockName(dbname string) string {
17+
base := filepath.Base(filepath.Clean(dbname)) + "lockfile"
18+
h := sha1.New()
19+
io.WriteString(h, base)
20+
return filepath.Join(filepath.Dir(dbname), fmt.Sprintf(".%x", h.Sum(nil)))
21+
}
22+
23+
func defaultLocker(dbname string) (io.Closer, error) {
24+
lname := lockName(dbname)
25+
abs, err := filepath.Abs(lname)
26+
if err != nil {
27+
return nil, err
28+
}
29+
f, err := os.OpenFile(abs, os.O_CREATE|os.O_EXCL|os.O_RDONLY, 0666)
30+
if os.IsExist(err) {
31+
return nil, fmt.Errorf("cannot access DB %q: lock file %q exists", dbname, abs)
32+
}
33+
if err != nil {
34+
return nil, err
35+
}
36+
return &lockCloser{f: f, abs: abs}, nil
37+
}
38+
39+
type lockCloser struct {
40+
f *os.File
41+
abs string
42+
once sync.Once
43+
err error
44+
}
45+
46+
func (lc *lockCloser) Close() error {
47+
lc.once.Do(lc.close)
48+
return lc.err
49+
}
50+
51+
func (lc *lockCloser) close() {
52+
if err := lc.f.Close(); err != nil {
53+
lc.err = err
54+
}
55+
if err := os.Remove(lc.abs); err != nil {
56+
lc.err = err
57+
}
58+
}

options.go

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ type Options struct {
6565
// +1 if x > y
6666
Compare func(x, y []byte) int
6767

68+
// Locker specifies a function to lock a named file.
69+
// On success it returns an io.Closer to release the lock.
70+
// If nil, a default implementation is used.
71+
Locker func(name string) (io.Closer, error)
72+
6873
// See the ACID* constants documentation.
6974
_ACID int
7075

@@ -98,11 +103,18 @@ type Options struct {
98103
// and they may not be always honored.
99104
_GracePeriod time.Duration
100105
wal *os.File
101-
lock *os.File
106+
lock io.Closer
102107

103108
noClone bool // test hook
104109
}
105110

111+
func (o *Options) locker(dbname string) (io.Closer, error) {
112+
if o == nil || o.Locker == nil {
113+
return defaultLocker(dbname)
114+
}
115+
return o.Locker(dbname)
116+
}
117+
106118
func (o *Options) clone() *Options {
107119
if o.noClone {
108120
return o
@@ -112,13 +124,8 @@ func (o *Options) clone() *Options {
112124
}
113125

114126
func (o *Options) check(dbname string, new, lock bool) (err error) {
115-
var lname string
116127
if lock {
117-
lname = o.lockName(dbname)
118-
if o.lock, err = os.OpenFile(lname, os.O_CREATE|os.O_EXCL|os.O_RDONLY, 0666); err != nil {
119-
if os.IsExist(err) {
120-
err = fmt.Errorf("cannot access DB %q: lock file %q exists", dbname, lname)
121-
}
128+
if o.lock, err = o.locker(dbname); err != nil {
122129
return
123130
}
124131
}
@@ -130,9 +137,6 @@ func (o *Options) check(dbname string, new, lock bool) (err error) {
130137
case _ACIDFull:
131138
o._GracePeriod = time.Second
132139
o._WAL = o.walName(dbname, o._WAL)
133-
if lname == o._WAL {
134-
panic("internal error")
135-
}
136140

137141
switch new {
138142
case true:
@@ -155,13 +159,6 @@ func (o *Options) check(dbname string, new, lock bool) (err error) {
155159
return
156160
}
157161

158-
func (o *Options) lockName(dbname string) (r string) {
159-
base := filepath.Base(filepath.Clean(dbname)) + "lockfile"
160-
h := sha1.New()
161-
io.WriteString(h, base)
162-
return filepath.Join(filepath.Dir(dbname), fmt.Sprintf(".%x", h.Sum(nil)))
163-
}
164-
165162
func (o *Options) walName(dbname, wal string) (r string) {
166163
if wal != "" {
167164
return filepath.Clean(wal)

0 commit comments

Comments
 (0)