Skip to content

Commit 23aad5f

Browse files
committed
MVCC API.
1 parent e5bd10a commit 23aad5f

File tree

6 files changed

+97
-41
lines changed

6 files changed

+97
-41
lines changed

tests/parallel/parallel_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ func Test_mvcc(t *testing.T) {
107107
iter = 5000
108108
}
109109

110-
mvcc.Create("test.db", "")
111-
name := "file:/test.db?vfs=mvcc" +
112-
"&_pragma=busy_timeout(10000)"
110+
name := mvcc.TestDB(t, mvcc.Snapshot{}, url.Values{
111+
"_pragma": {"busy_timeout(10000)"},
112+
})
113113
createDB(t, name)
114114
testParallel(t, name, iter)
115115
testIntegrity(t, name)
@@ -330,9 +330,9 @@ func Benchmark_memdb(b *testing.B) {
330330
}
331331

332332
func Benchmark_mvcc(b *testing.B) {
333-
mvcc.Create("test.db", "")
334-
name := "file:/test.db?vfs=mvcc" +
335-
"&_pragma=busy_timeout(10000)"
333+
name := mvcc.TestDB(b, mvcc.Snapshot{}, url.Values{
334+
"_pragma": {"busy_timeout(10000)"},
335+
})
336336
createDB(b, name)
337337

338338
b.ResetTimer()

vfs/mvcc/api.go

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
package mvcc
1111

1212
import (
13+
"crypto/rand"
14+
"fmt"
15+
"net/url"
16+
"strings"
1317
"sync"
18+
"testing"
1419

1520
"github.com/ncruces/go-sqlite3/vfs"
21+
"github.com/ncruces/wbt"
1622
)
1723

1824
func init() {
@@ -26,42 +32,103 @@ var (
2632
)
2733

2834
// Create creates a shared memory database,
29-
// using data as its initial contents.
30-
func Create(name string, data string) {
35+
// using a snapshot as its initial contents.
36+
func Create(name string, snapshot Snapshot) {
3137
memoryMtx.Lock()
3238
defer memoryMtx.Unlock()
3339

34-
db := &mvccDB{
40+
memoryDBs[name] = &mvccDB{
3541
refs: 1,
3642
name: name,
43+
data: snapshot.Tree,
3744
}
38-
memoryDBs[name] = db
39-
if len(data) == 0 {
40-
return
41-
}
45+
}
46+
47+
// Delete deletes a shared memory database.
48+
func Delete(name string) {
49+
name = getName(name)
50+
51+
memoryMtx.Lock()
52+
defer memoryMtx.Unlock()
53+
delete(memoryDBs, name)
54+
}
55+
56+
// Snapshot represents a database snapshot.
57+
type Snapshot struct {
58+
*wbt.Tree[int64, string]
59+
}
60+
61+
// NewSnapshot creates a snapshot from data.
62+
func NewSnapshot(data string) Snapshot {
63+
var tree *wbt.Tree[int64, string]
64+
4265
// Convert data from WAL/2 to rollback journal.
4366
if len(data) >= 20 && (false ||
4467
data[18] == 2 && data[19] == 2 ||
4568
data[18] == 3 && data[19] == 3) {
46-
db.data = db.data.
69+
tree = tree.
4770
Put(0, data[:18]).
4871
Put(18, "\001\001").
4972
Put(20, data[20:])
50-
} else {
51-
db.data = db.data.Put(0, data)
73+
} else if len(data) > 0 {
74+
tree = tree.Put(0, data)
5275
}
76+
77+
return Snapshot{tree}
5378
}
5479

55-
// Delete deletes a shared memory database.
56-
func Delete(name string) {
80+
// TakeSnapshot takes a snapshot of a database.
81+
// Name may be a URI filename.
82+
func TakeSnapshot(name string) Snapshot {
83+
name = getName(name)
84+
5785
memoryMtx.Lock()
5886
defer memoryMtx.Unlock()
59-
delete(memoryDBs, name)
87+
db := memoryDBs[name]
88+
if db == nil {
89+
return Snapshot{}
90+
}
91+
92+
db.mtx.Lock()
93+
defer db.mtx.Unlock()
94+
return Snapshot{db.data}
6095
}
6196

62-
// Snapshot stores a snapshot of database src into dst.
63-
func Snapshot(dst, src string) {
64-
memoryMtx.Lock()
65-
defer memoryMtx.Unlock()
66-
memoryDBs[dst] = memoryDBs[src].fork()
97+
// TestDB creates a shared database from a snapshot for the test to use.
98+
// The database is automatically deleted when the test and all its subtests complete.
99+
// Returns a URI filename appropriate to call Open with.
100+
// Each subsequent call to TestDB returns a unique database.
101+
func TestDB(tb testing.TB, snapshot Snapshot, params ...url.Values) string {
102+
tb.Helper()
103+
104+
name := fmt.Sprintf("%s_%s", tb.Name(), rand.Text())
105+
tb.Cleanup(func() { Delete(name) })
106+
Create(name, snapshot)
107+
108+
p := url.Values{"vfs": {"mvcc"}}
109+
for _, v := range params {
110+
for k, v := range v {
111+
for _, v := range v {
112+
p.Add(k, v)
113+
}
114+
}
115+
}
116+
117+
return (&url.URL{
118+
Scheme: "file",
119+
OmitHost: true,
120+
Path: "/" + name,
121+
RawQuery: p.Encode(),
122+
}).String()
123+
}
124+
125+
func getName(dsn string) string {
126+
u, err := url.Parse(dsn)
127+
if err == nil &&
128+
u.Scheme == "file" &&
129+
strings.HasPrefix(u.Path, "/") &&
130+
u.Query().Get("vfs") == "mvcc" {
131+
return u.Path[1:]
132+
}
133+
return dsn
67134
}

vfs/mvcc/example_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
var testDB string
1616

1717
func Example() {
18-
mvcc.Create("test.db", testDB)
18+
mvcc.Create("test.db", mvcc.NewSnapshot(testDB))
1919

2020
db, err := sql.Open("sqlite3", "file:/test.db?vfs=mvcc")
2121
if err != nil {

vfs/mvcc/mvcc.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,6 @@ func (m *mvccDB) release() {
8585
}
8686
}
8787

88-
func (m *mvccDB) fork() *mvccDB {
89-
m.mtx.Lock()
90-
defer m.mtx.Unlock()
91-
return &mvccDB{
92-
refs: 1,
93-
name: m.name,
94-
data: m.data,
95-
}
96-
}
97-
9888
type mvccFile struct {
9989
*mvccDB
10090
data *wbt.Tree[int64, string]

vfs/mvcc/mvcc_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ var walDB string
1414

1515
func Test_wal(t *testing.T) {
1616
t.Parallel()
17+
dsn := TestDB(t, NewSnapshot(walDB))
1718

18-
Create("test.db", walDB)
19-
20-
db, err := sqlite3.Open("file:/test.db?vfs=mvcc")
19+
db, err := sqlite3.Open(dsn)
2120
if err != nil {
2221
t.Fatal(err)
2322
}

vfs/tests/mptest/mptest_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func Test_multiwrite01_memory(t *testing.T) {
197197
}
198198

199199
func Test_config01_mvcc(t *testing.T) {
200-
mvcc.Create("test.db", "")
200+
mvcc.Create("test.db", mvcc.Snapshot{})
201201
ctx := util.NewContext(newContext(t))
202202
cfg := config(ctx).WithArgs("mptest", "/test.db", "config01.test",
203203
"--vfs", "mvcc")
@@ -213,7 +213,7 @@ func Test_crash01_mvcc(t *testing.T) {
213213
t.Skip("skipping in short mode")
214214
}
215215

216-
mvcc.Create("test.db", "")
216+
mvcc.Create("test.db", mvcc.Snapshot{})
217217
ctx := util.NewContext(newContext(t))
218218
cfg := config(ctx).WithArgs("mptest", "/test.db", "crash01.test",
219219
"--vfs", "mvcc")
@@ -229,7 +229,7 @@ func Test_multiwrite01_mvcc(t *testing.T) {
229229
t.Skip("skipping in slow CI")
230230
}
231231

232-
mvcc.Create("test.db", "")
232+
mvcc.Create("test.db", mvcc.Snapshot{})
233233
ctx := util.NewContext(newContext(t))
234234
cfg := config(ctx).WithArgs("mptest", "/test.db", "multiwrite01.test",
235235
"--vfs", "mvcc")

0 commit comments

Comments
 (0)