mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-17 02:48:57 -07:00
169 lines
3.0 KiB
Go
169 lines
3.0 KiB
Go
package files_test
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
|
)
|
|
|
|
var items map[string][]byte
|
|
var keys map[string]string
|
|
|
|
const nItems = 10000
|
|
|
|
func setupMaps() {
|
|
|
|
// Set up two simple maps, one "key" => data and one "indirect key" =>
|
|
// "key".
|
|
|
|
items = make(map[string][]byte, nItems)
|
|
keys = make(map[string]string, nItems)
|
|
|
|
for i := 0; i < nItems; i++ {
|
|
k1 := fmt.Sprintf("key%d", i)
|
|
data := make([]byte, 87)
|
|
_, err := rand.Reader.Read(data)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
items[k1] = data
|
|
|
|
k2 := fmt.Sprintf("indirect%d", i)
|
|
keys[k2] = k1
|
|
}
|
|
}
|
|
|
|
func makeK1(s string) []byte {
|
|
k1 := make([]byte, 1+len(s))
|
|
k1[0] = 1
|
|
copy(k1[1:], []byte(s))
|
|
return k1
|
|
}
|
|
|
|
func makeK2(s string) []byte {
|
|
k2 := make([]byte, 1+len(s))
|
|
k2[0] = 2 // Only difference from makeK1
|
|
copy(k2[1:], []byte(s))
|
|
return k2
|
|
}
|
|
|
|
func setItems(db *leveldb.DB) error {
|
|
snap, err := db.GetSnapshot()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer snap.Release()
|
|
|
|
batch := &leveldb.Batch{}
|
|
for k2, k1 := range keys {
|
|
// Create k1 => item mapping first
|
|
batch.Put(makeK1(k1), items[k1])
|
|
// Then the k2 => k1 mapping
|
|
batch.Put(makeK2(k2), makeK1(k1))
|
|
}
|
|
return db.Write(batch, nil)
|
|
}
|
|
|
|
func clearItems(db *leveldb.DB) error {
|
|
snap, err := db.GetSnapshot()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer snap.Release()
|
|
|
|
// Iterate from the start of k2 space to the end
|
|
it := snap.NewIterator(&util.Range{Start: []byte{2}, Limit: []byte{2, 0xff, 0xff, 0xff, 0xff}}, nil)
|
|
defer it.Release()
|
|
|
|
batch := &leveldb.Batch{}
|
|
for it.Next() {
|
|
k2 := it.Key()
|
|
k1 := it.Value()
|
|
|
|
// k1 should exist
|
|
_, err := snap.Get(k1, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Delete the k2 => k1 mapping first
|
|
batch.Delete(k2)
|
|
// Then the k1 => key mapping
|
|
batch.Delete(k1)
|
|
}
|
|
return db.Write(batch, nil)
|
|
}
|
|
|
|
func scanItems(db *leveldb.DB) error {
|
|
snap, err := db.GetSnapshot()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer snap.Release()
|
|
|
|
// Iterate from the start of k2 space to the end
|
|
it := snap.NewIterator(&util.Range{Start: []byte{2}, Limit: []byte{2, 0xff, 0xff, 0xff, 0xff}}, nil)
|
|
defer it.Release()
|
|
|
|
for it.Next() {
|
|
// k2 => k1 => data
|
|
k2 := it.Key()
|
|
k1 := it.Value()
|
|
_, err := snap.Get(k1, nil)
|
|
if err != nil {
|
|
log.Printf("k1: %q (%x)", k1, k1)
|
|
log.Printf("k2: %q (%x)", k2, k2)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestConcurrent(t *testing.T) {
|
|
setupMaps()
|
|
|
|
dur := 2 * time.Second
|
|
t0 := time.Now()
|
|
var wg sync.WaitGroup
|
|
|
|
os.RemoveAll("testdata/global.db")
|
|
db, err := leveldb.OpenFile("testdata/global.db", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll("testdata/global.db")
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for time.Since(t0) < dur {
|
|
if err := setItems(db); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := clearItems(db); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for time.Since(t0) < dur {
|
|
if err := scanItems(db); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
db.Close()
|
|
}
|