diff --git a/cmd/roaring-migrate/main.go b/cmd/roaring-migrate/main.go index 72e6fa94a..129159f2b 100644 --- a/cmd/roaring-migrate/main.go +++ b/cmd/roaring-migrate/main.go @@ -4,6 +4,7 @@ package main import ( "encoding/json" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -13,25 +14,42 @@ import ( "syscall" pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/rbf" "github.com/molecula/featurebase/v3/rbf/cfg" "github.com/molecula/featurebase/v3/roaring" txkey "github.com/molecula/featurebase/v3/short_txkey" + "github.com/molecula/featurebase/v3/vprint" "github.com/spf13/cobra" ) +var visited map[string]int64 +var glogger = logger.NewStandardLogger(os.Stdout) + +const ( + Version = "1.0" +) + func main() { + os.Exit(realMain()) +} +func realMain() int { + visited = make(map[string]int64) var dataDir, backupPath string + var verbose bool cmdMigrate := &cobra.Command{ Use: "roaring-migrate", Short: "convert roaring pilosa backup to rbf", Long: `roaring-migrate uses the pilosa data-dir for each node, and produces a new backup that is able to be restored from utilizing the new pilosa restore tool.`, Run: func(cmd *cobra.Command, args []string) { + if verbose { + glogger.Infof("Version: %v", Version) + } nodes := strings.Split(dataDir, ",") for _, nodePath := range nodes { - err := Migrate(nodePath, backupPath) + err := Migrate(nodePath, backupPath, verbose) if err != nil { - fmt.Println("Error", err) + glogger.Errorf("%v", Version) return } @@ -40,24 +58,24 @@ func main() { } cmdMigrate.Flags().StringVarP(&dataDir, "data-dir", "d", "", "source directories for each node seperated by commas") cmdMigrate.Flags().StringVarP(&backupPath, "backup-dir", "b", "", "location of backup directory") + cmdMigrate.Flags().BoolVar(&verbose, "verbose", false, "additional progress information") err := cmdMigrate.MarkFlagRequired("data-dir") if err != nil { - fmt.Println("Error setting flag data-dir") - os.Exit(1) - return + glogger.Errorf("Error setting flag data-dir") + return 1 } err = cmdMigrate.MarkFlagRequired("backup-dir") if err != nil { - fmt.Println("Error setting flag backup-dir") - os.Exit(1) - return + glogger.Errorf("Error setting flag backup-dir") + return 1 } err = cmdMigrate.Execute() if err != nil { - fmt.Println("exec error", err) - os.Exit(1) + glogger.Errorf("exec error %v", err) + return 1 } + return 0 } func FetchFragments(base string) []string { @@ -67,7 +85,7 @@ func FetchFragments(base string) []string { // first thing to do, check error. and decide what to do about it if errX != nil { - fmt.Printf("error 「%v」 at a path 「%q」\n", errX, pathX) + glogger.Errorf("error 「%v」 at a path 「%q」\n", errX, pathX) return errX } pathX = pathX[len(base):] @@ -82,7 +100,7 @@ func FetchFragments(base string) []string { err := filepath.Walk(base, ff) if err != nil { - fmt.Printf("error walking the path %q: %v\n", base, err) + glogger.Errorf("error walking the path %q: %v\n", base, err) } return fragments } @@ -94,6 +112,14 @@ type local struct { Fields []*pilosa.FieldInfo `json:"fields,omitempty"` } +func fileExists(filename string) (bool, int64) { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false, 0 + } + return !info.IsDir(), info.Size() +} + func BuildSchema(dataDir string) ([]byte, error) { //need to find all the ".meta" files and load as field options @@ -105,7 +131,7 @@ func BuildSchema(dataDir string) ([]byte, error) { // first thing to do, check error. and decide what to do about it if errX != nil { - fmt.Printf("error 「%v」 at a path 「%q」\n", errX, pathX) + glogger.Infof("error 「%v」 at a path 「%q」\n", errX, pathX) return errX } pathX = pathX[len(dataDir):] @@ -115,7 +141,7 @@ func BuildSchema(dataDir string) ([]byte, error) { if strings.Contains(pathX, ".meta") { //convert the file to a fieldOptions // ex: metaPath /trait_store/aba/.meta - fmt.Println("PATHX", pathX) + glogger.Infof("PATHX %v", pathX) t := strings.Split(pathX, "/") index := t[1] src := dataDir + pathX @@ -160,7 +186,7 @@ func BuildSchema(dataDir string) ([]byte, error) { err := filepath.Walk(dataDir, ff) if err != nil { - fmt.Printf("error walking the path %q: %v\n", dataDir, err) + glogger.Errorf("error walking the path %q: %v\n", dataDir, err) } return json.MarshalIndent(schemaSerializer, "", " ") } @@ -183,7 +209,7 @@ func (d *rbfFile) getDB(path, index string, shard uint64) (*rbf.DB, error) { if d.last != src { d.Close() d.last = src - fmt.Println("RBF:", src) + glogger.Infof("RBF: %v", src) c := cfg.NewDefaultConfig() c.FsyncEnabled = false c.MinWALCheckpointSize = 0 @@ -197,20 +223,34 @@ func (d *rbfFile) getDB(path, index string, shard uint64) (*rbf.DB, error) { return d.working, nil } func (d *rbfFile) Close() error { + defer func() error { + // clean up the temp directory + err := os.RemoveAll(d.temp) + if err != nil { + return err + } + return nil + }() + if d.last != "" { d.working.Close() + //if d.last exists only keep the biggest - err := os.MkdirAll(filepath.Dir(d.last), 0777) - if err != nil { - return err + exists, sz := fileExists(d.last) + src := filepath.Join(d.temp, "data") + if !exists { + err := os.MkdirAll(filepath.Dir(d.last), 0777) + if err != nil { + return err + } + } else { + _, sz2 := fileExists(src) + if sz > sz2 { + return nil + } } // move the datafile backup shard - err = os.Rename(filepath.Join(d.temp, "data"), d.last) - if err != nil { - return err - } - //cleanup the tempdirectory - err = os.RemoveAll(d.temp) + err := os.Rename(src, d.last) if err != nil { return err } @@ -218,19 +258,26 @@ func (d *rbfFile) Close() error { return nil } func copyFile(src, dest string) error { - input, err := ioutil.ReadFile(src) + from, err := os.Open(src) if err != nil { return err } + defer from.Close() - err = ioutil.WriteFile(dest, input, 0644) + to, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + return err + } + defer to.Close() + + _, err = io.Copy(to, from) if err != nil { return err } return nil } -func Migrate(dataDir, backupPath string) error { +func Migrate(dataDir, backupPath string, verbose bool) error { dataDir = strings.TrimSuffix(dataDir, "/") err := os.MkdirAll(backupPath, 0777) @@ -279,7 +326,21 @@ func Migrate(dataDir, backupPath string) error { bm := roaring.NewSliceBitmap() for _, filename := range raw { index, field, view, shard := Extract(filename) - + sz, before := visited[filename] + fi, _ := os.Stat(dataDir + filename) + if field != "_exists" { + if !before { + visited[filename] = fi.Size() + } else { + if fi.Size() <= sz { + continue // skip it + } + visited[filename] = fi.Size() + } + } + if verbose { + glogger.Infof("processing: %v", dataDir+filename) + } content, err := ioutil.ReadFile(dataDir + filename) if err != nil { return err @@ -293,35 +354,19 @@ func Migrate(dataDir, backupPath string) error { if err != nil { return err } - tx, err := db.Begin(true) - if err != nil { - return err - } key := string(txkey.Prefix(index, field, view, shard)) - itr, ok := bm.Containers.Iterator(0) - if ok { - for itr.Next() { - k, v := itr.Value() - tx.PutContainer(key, k, v) - - } - } + tx, err := db.Begin(true) + tx.AddRoaring(key, bm) err = tx.Commit() - if err != nil { - return err - } } cache.Close() keys := FetchIndexKeys(dataDir) for _, filename := range keys { - fmt.Println("index keys", filename) - content, err := ioutil.ReadFile(filepath.Join(dataDir, filename)) - if err != nil { - return err - } + glogger.Infof("index keys %v", filename) + srcFile := filepath.Join(dataDir, filename) parts := strings.Split(filename, "/") destFile := filepath.Join(backupPath, "indexes", parts[1], "translate", parts[3]) - err = writeIfBigger(destFile, content) + err = writeIfBigger(destFile, srcFile) if err != nil { return err } @@ -330,14 +375,11 @@ func Migrate(dataDir, backupPath string) error { //deal with index field(row)keys keys = FetchRowkeys(dataDir) for _, filename := range keys { - fmt.Println("field", filename) - content, err := ioutil.ReadFile(dataDir + filename) - if err != nil { - return err - } + glogger.Infof("field %v", filename) + srcFile := dataDir + filename parts := strings.Split(filename, "/") destFile := filepath.Join(backupPath, "indexes", parts[1], "fields", parts[2], "translate") - err = writeIfBigger(destFile, content) + err = writeIfBigger(destFile, srcFile) if err != nil { return err } @@ -345,16 +387,21 @@ func Migrate(dataDir, backupPath string) error { return nil } -func writeIfBigger(dst string, content []byte) error { +func writeIfBigger(dst string, srcFile string) error { if stats, err := os.Stat(dst); os.IsNotExist(err) { err = os.MkdirAll(filepath.Dir(dst), 0777) if err != nil { return err } - return ioutil.WriteFile(dst, content, 0644) + return copyFile(srcFile, dst) } else { - if stats.Size() < int64(len(content)) { - return ioutil.WriteFile(dst, content, 0644) + stats2, err := os.Stat(srcFile) + if err != nil { + return err + } + if stats.Size() < stats2.Size() { + vprint.VV("Bigger %v %v", stats.Size(), stats2.Size()) + return copyFile(srcFile, dst) } } return nil //simply skip it @@ -376,7 +423,7 @@ func FetchIndexKeys(base string) []string { // first thing to do, check error. and decide what to do about it if errX != nil { - fmt.Printf("error 「%v」 at a path 「%q」\n", errX, pathX) + glogger.Errorf("error 「%v」 at a path 「%q」\n", errX, pathX) return errX } pathX = pathX[len(base):] @@ -393,7 +440,7 @@ func FetchIndexKeys(base string) []string { err := filepath.Walk(base, ff) if err != nil { - fmt.Printf("error walking the path %q: %v\n", base, err) + glogger.Errorf("error walking the path %q: %v\n", base, err) } return directory } @@ -405,7 +452,7 @@ func FetchRowkeys(base string) []string { // first thing to do, check error. and decide what to do about it if errX != nil { - fmt.Printf("error 「%v」 at a path 「%q」\n", errX, pathX) + glogger.Errorf("error 「%v」 at a path 「%q」\n", errX, pathX) return errX } pathX = pathX[len(base):] @@ -427,7 +474,7 @@ func FetchRowkeys(base string) []string { err := filepath.Walk(base, ff) if err != nil { - fmt.Printf("error walking the path %q: %v\n", base, err) + glogger.Errorf("error walking the path %q: %v\n", base, err) } return directory } diff --git a/cmd/roaring-migrate/main_test.go b/cmd/roaring-migrate/main_test.go new file mode 100644 index 000000000..8f452c178 --- /dev/null +++ b/cmd/roaring-migrate/main_test.go @@ -0,0 +1,56 @@ +package main + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestFileExists(t *testing.T) { + fileName := "missing" + if x, _ := fileExists(fileName); x { + t.Fatalf("file %v doesn't exist", fileName) + } + file, err := os.Create(fileName) + if err != nil { + t.Fatal(err) + } + file.Close() + + if x, _ := fileExists(fileName); !x { + t.Fatalf("file %v doesn't exist", fileName) + } + + t.Cleanup(func() { + os.Remove(fileName) + }) +} + +func TestMainProgram(t *testing.T) { + os.Args = []string{"roaring-migrate", + "--verbose", + } + if realMain() == 0 { + t.Fatal("should fail and it succeeded") + } + os.Args = []string{"roaring-migrate", + "--verbose", + } + if realMain() == 0 { + t.Fatal("should fail and it succeeded") + } + dir, err := ioutil.TempDir("", "backup") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) // clean up + os.Args = []string{"roaring-migrate", + "--verbose=true", + "--data-dir=testdata/data-dir/", + "--backup-dir=" + dir, + } + if realMain() == 1 { + t.Fatal("shouldn't fail") + } + +} diff --git a/cmd/roaring-migrate/testdata/data-dir/.id b/cmd/roaring-migrate/testdata/data-dir/.id new file mode 100644 index 000000000..8b78590a2 --- /dev/null +++ b/cmd/roaring-migrate/testdata/data-dir/.id @@ -0,0 +1 @@ +6fc20f49-edf3-4211-8f6d-c670258ee6ea \ No newline at end of file diff --git a/cmd/roaring-migrate/testdata/data-dir/.startup.log b/cmd/roaring-migrate/testdata/data-dir/.startup.log new file mode 100644 index 000000000..704761102 --- /dev/null +++ b/cmd/roaring-migrate/testdata/data-dir/.startup.log @@ -0,0 +1 @@ +2022-02-14T11:49:34.20065623-06:00 v2.7.0 diff --git a/cmd/roaring-migrate/testdata/data-dir/.topology b/cmd/roaring-migrate/testdata/data-dir/.topology new file mode 100644 index 000000000..c434c3203 --- /dev/null +++ b/cmd/roaring-migrate/testdata/data-dir/.topology @@ -0,0 +1,2 @@ + +$a317bd70-60ed-4723-99fa-3067563a708e$6fc20f49-edf3-4211-8f6d-c670258ee6ea \ No newline at end of file diff --git a/cmd/roaring-migrate/testdata/data-dir/idalloc.db b/cmd/roaring-migrate/testdata/data-dir/idalloc.db new file mode 100644 index 000000000..e449c2898 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/idalloc.db differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/.data b/cmd/roaring-migrate/testdata/data-dir/repository/.data new file mode 100644 index 000000000..efe3a38f1 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/.data differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/.meta b/cmd/roaring-migrate/testdata/data-dir/repository/.meta new file mode 100644 index 000000000..af4e17a1c --- /dev/null +++ b/cmd/roaring-migrate/testdata/data-dir/repository/.meta @@ -0,0 +1 @@ +  \ No newline at end of file diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.data b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.data new file mode 100644 index 000000000..efe3a38f1 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.data differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.meta b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.meta new file mode 100644 index 000000000..2267b8ec2 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/.meta differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_exists/keys b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/keys new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/keys differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222 b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222 new file mode 100644 index 000000000..ed0e9ad30 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222.cache b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222.cache new file mode 100644 index 000000000..742c749a9 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_exists/views/standard/fragments/222.cache differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/0 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/0 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/0 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/1 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/1 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/1 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/10 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/10 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/10 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/100 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/100 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/100 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/101 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/101 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/101 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/102 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/102 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/102 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/103 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/103 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/103 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/104 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/104 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/104 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/105 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/105 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/105 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/106 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/106 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/106 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/107 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/107 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/107 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/108 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/108 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/108 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/109 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/109 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/109 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/11 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/11 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/11 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/110 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/110 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/110 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/111 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/111 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/111 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/112 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/112 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/112 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/113 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/113 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/113 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/114 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/114 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/114 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/115 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/115 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/115 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/116 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/116 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/116 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/117 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/117 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/117 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/118 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/118 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/118 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/119 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/119 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/119 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/12 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/12 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/12 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/120 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/120 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/120 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/121 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/121 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/121 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/122 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/122 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/122 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/123 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/123 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/123 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/124 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/124 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/124 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/125 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/125 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/125 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/126 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/126 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/126 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/127 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/127 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/127 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/128 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/128 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/128 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/129 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/129 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/129 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/13 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/13 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/13 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/130 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/130 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/130 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/131 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/131 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/131 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/132 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/132 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/132 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/133 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/133 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/133 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/134 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/134 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/134 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/135 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/135 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/135 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/136 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/136 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/136 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/137 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/137 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/137 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/138 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/138 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/138 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/139 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/139 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/139 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/14 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/14 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/14 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/140 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/140 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/140 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/141 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/141 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/141 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/142 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/142 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/142 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/143 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/143 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/143 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/144 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/144 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/144 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/145 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/145 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/145 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/146 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/146 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/146 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/147 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/147 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/147 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/148 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/148 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/148 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/149 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/149 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/149 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/15 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/15 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/15 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/150 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/150 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/150 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/151 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/151 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/151 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/152 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/152 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/152 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/153 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/153 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/153 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/154 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/154 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/154 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/155 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/155 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/155 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/156 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/156 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/156 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/157 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/157 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/157 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/158 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/158 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/158 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/159 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/159 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/159 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/16 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/16 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/16 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/160 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/160 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/160 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/161 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/161 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/161 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/162 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/162 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/162 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/163 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/163 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/163 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/164 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/164 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/164 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/165 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/165 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/165 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/166 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/166 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/166 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/167 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/167 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/167 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/168 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/168 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/168 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/169 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/169 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/169 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/17 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/17 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/17 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/170 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/170 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/170 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/171 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/171 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/171 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/172 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/172 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/172 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/173 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/173 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/173 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/174 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/174 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/174 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/175 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/175 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/175 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/176 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/176 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/176 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/177 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/177 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/177 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/178 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/178 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/178 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/179 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/179 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/179 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/18 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/18 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/18 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/180 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/180 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/180 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/181 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/181 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/181 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/182 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/182 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/182 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/183 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/183 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/183 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/184 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/184 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/184 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/185 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/185 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/185 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/186 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/186 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/186 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/187 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/187 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/187 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/188 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/188 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/188 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/189 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/189 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/189 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/19 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/19 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/19 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/190 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/190 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/190 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/191 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/191 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/191 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/192 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/192 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/192 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/193 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/193 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/193 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/194 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/194 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/194 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/195 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/195 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/195 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/196 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/196 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/196 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/197 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/197 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/197 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/198 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/198 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/198 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/199 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/199 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/199 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/2 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/2 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/2 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/20 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/20 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/20 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/200 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/200 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/200 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/201 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/201 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/201 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/202 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/202 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/202 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/203 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/203 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/203 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/204 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/204 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/204 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/205 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/205 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/205 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/206 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/206 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/206 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/207 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/207 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/207 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/208 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/208 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/208 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/209 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/209 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/209 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/21 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/21 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/21 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/210 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/210 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/210 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/211 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/211 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/211 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/212 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/212 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/212 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/213 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/213 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/213 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/214 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/214 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/214 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/215 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/215 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/215 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/216 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/216 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/216 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/217 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/217 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/217 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/218 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/218 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/218 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/219 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/219 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/219 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/22 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/22 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/22 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/220 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/220 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/220 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/221 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/221 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/221 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/222 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/222 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/222 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/223 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/223 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/223 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/224 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/224 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/224 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/225 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/225 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/225 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/226 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/226 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/226 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/227 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/227 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/227 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/228 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/228 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/228 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/229 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/229 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/229 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/23 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/23 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/23 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/230 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/230 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/230 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/231 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/231 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/231 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/232 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/232 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/232 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/233 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/233 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/233 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/234 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/234 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/234 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/235 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/235 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/235 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/236 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/236 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/236 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/237 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/237 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/237 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/238 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/238 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/238 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/239 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/239 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/239 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/24 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/24 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/24 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/240 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/240 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/240 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/241 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/241 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/241 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/242 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/242 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/242 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/243 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/243 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/243 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/244 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/244 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/244 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/245 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/245 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/245 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/246 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/246 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/246 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/247 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/247 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/247 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/248 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/248 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/248 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/249 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/249 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/249 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/25 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/25 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/25 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/250 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/250 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/250 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/251 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/251 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/251 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/252 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/252 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/252 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/253 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/253 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/253 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/254 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/254 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/254 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/255 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/255 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/255 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/26 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/26 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/26 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/27 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/27 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/27 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/28 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/28 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/28 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/29 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/29 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/29 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/3 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/3 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/3 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/30 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/30 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/30 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/31 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/31 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/31 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/32 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/32 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/32 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/33 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/33 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/33 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/34 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/34 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/34 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/35 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/35 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/35 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/36 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/36 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/36 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/37 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/37 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/37 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/38 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/38 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/38 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/39 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/39 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/39 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/4 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/4 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/4 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/40 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/40 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/40 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/41 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/41 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/41 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/42 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/42 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/42 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/43 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/43 new file mode 100644 index 000000000..2deca814d Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/43 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/44 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/44 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/44 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/45 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/45 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/45 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/46 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/46 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/46 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/47 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/47 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/47 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/48 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/48 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/48 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/49 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/49 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/49 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/5 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/5 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/5 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/50 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/50 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/50 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/51 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/51 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/51 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/52 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/52 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/52 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/53 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/53 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/53 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/54 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/54 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/54 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/55 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/55 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/55 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/56 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/56 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/56 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/57 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/57 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/57 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/58 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/58 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/58 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/59 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/59 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/59 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/6 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/6 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/6 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/60 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/60 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/60 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/61 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/61 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/61 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/62 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/62 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/62 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/63 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/63 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/63 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/64 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/64 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/64 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/65 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/65 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/65 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/66 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/66 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/66 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/67 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/67 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/67 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/68 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/68 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/68 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/69 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/69 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/69 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/7 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/7 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/7 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/70 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/70 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/70 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/71 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/71 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/71 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/72 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/72 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/72 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/73 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/73 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/73 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/74 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/74 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/74 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/75 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/75 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/75 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/76 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/76 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/76 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/77 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/77 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/77 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/78 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/78 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/78 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/79 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/79 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/79 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/8 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/8 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/8 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/80 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/80 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/80 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/81 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/81 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/81 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/82 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/82 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/82 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/83 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/83 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/83 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/84 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/84 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/84 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/85 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/85 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/85 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/86 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/86 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/86 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/87 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/87 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/87 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/88 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/88 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/88 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/89 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/89 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/89 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/9 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/9 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/9 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/90 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/90 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/90 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/91 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/91 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/91 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/92 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/92 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/92 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/93 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/93 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/93 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/94 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/94 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/94 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/95 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/95 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/95 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/96 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/96 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/96 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/97 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/97 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/97 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/98 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/98 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/98 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/_keys/99 b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/99 new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/_keys/99 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/language/.data b/cmd/roaring-migrate/testdata/data-dir/repository/language/.data new file mode 100644 index 000000000..efe3a38f1 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/language/.data differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/language/.meta b/cmd/roaring-migrate/testdata/data-dir/repository/language/.meta new file mode 100644 index 000000000..2267b8ec2 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/language/.meta differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/language/keys b/cmd/roaring-migrate/testdata/data-dir/repository/language/keys new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/language/keys differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222 b/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222 new file mode 100644 index 000000000..f854440ff Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222 differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222.cache b/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222.cache new file mode 100644 index 000000000..160742634 --- /dev/null +++ b/cmd/roaring-migrate/testdata/data-dir/repository/language/views/standard/fragments/222.cache @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.data b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.data new file mode 100644 index 000000000..efe3a38f1 Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.data differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.meta b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.meta new file mode 100644 index 000000000..38cad5ccb Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/.meta differ diff --git a/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/keys b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/keys new file mode 100644 index 000000000..37e586e0e Binary files /dev/null and b/cmd/roaring-migrate/testdata/data-dir/repository/stargazer/keys differ diff --git a/disco/disco.go b/disco/disco.go index 7f4519f13..8ad815c28 100644 --- a/disco/disco.go +++ b/disco/disco.go @@ -312,6 +312,14 @@ type inMemSchemator struct { schema Schema } +// NewInMemSchemator instantiates an InMemSchemator +// this allows new holders to have thier own, and not rely on a shared instance +func NewInMemSchemator() *inMemSchemator { + return &inMemSchemator{ + schema: make(Schema), + } +} + // Schema is an in-memory implementation of the Schemator Schema method. func (s *inMemSchemator) Schema(ctx context.Context) (Schema, error) { s.mu.RLock() diff --git a/executor.go b/executor.go index 3b09cd92c..065dfaf0a 100644 --- a/executor.go +++ b/executor.go @@ -22,6 +22,7 @@ import ( "github.com/molecula/featurebase/v3/proto" "github.com/molecula/featurebase/v3/roaring" "github.com/molecula/featurebase/v3/shardwidth" + "github.com/molecula/featurebase/v3/task" "github.com/molecula/featurebase/v3/testhook" "github.com/molecula/featurebase/v3/topology" "github.com/molecula/featurebase/v3/tracing" @@ -51,9 +52,6 @@ type executor struct { Node *topology.Node Cluster *cluster - // how many jobs the work queue has seen - workCounter uint64 - // Client used for remote requests. client *InternalClient @@ -61,10 +59,9 @@ type executor struct { MaxWritesPerRequest int shutdown bool - workMu sync.RWMutex - workersWG sync.WaitGroup + workers *task.Pool + workerPoolMu sync.Mutex workerPoolSize int - currentWorkers int64 work chan job // Maximum per-request memory usage (Extract() only) @@ -130,72 +127,11 @@ func newExecutor(opts ...executorOption) *executor { // workloads. Possible that it could be smaller. e.work = make(chan job, e.workerPoolSize) _ = testhook.Opened(NewAuditor(), e, nil) - for i := 0; i < e.workerPoolSize; i++ { - e.addWorker() - } - go func() { - // background task: every so often, check to see whether we have - // work in the queue but none has been taken for a while. if so, we - // need more workers. - prev := atomic.LoadUint64(&e.workCounter) - periodic := time.NewTicker(50 * time.Millisecond) - defer periodic.Stop() - running := true - idle := 0 - for running { - <-periodic.C - func() { - e.workMu.RLock() - defer e.workMu.RUnlock() - if e.shutdown { - running = false - return - } - if len(e.work) == 0 { - idle++ - if idle > 10 && atomic.LoadInt64(&e.currentWorkers) > int64(e.workerPoolSize*2) { - select { - case e.work <- job{idleHands: true}: - // we closed an excess worker - default: - // somehow between our test above and now the work - // queue FILLED UP and we stoically accept this - } - idle = 0 - } - return - } - next := atomic.LoadUint64(&e.workCounter) - if next == prev { - e.addWorker() - } - prev = next - }() - } - }() + e.workers = task.NewPool(e.workerPoolSize, e.doOneJob, e) return e } -func (e *executor) addWorker() { - e.workersWG.Add(1) - n := atomic.AddInt64(&e.currentWorkers, 1) - if e.Holder != nil { - e.Holder.Stats.Gauge("worker_total", float64(n), 0) - } - - go func() { - defer e.workersWG.Done() - e.worker(e.work) - n := atomic.AddInt64(&e.currentWorkers, -1) - if e.Holder != nil { - e.Holder.Stats.Gauge("worker_total", float64(n), 0) - } - }() -} - func (e *executor) Close() error { - e.workMu.Lock() - defer e.workMu.Unlock() if e.shutdown { // otherwise close(e.work) can result in // panic: close of closed channel. @@ -206,15 +142,23 @@ func (e *executor) Close() error { e.shutdown = true _ = testhook.Closed(NewAuditor(), e, nil) close(e.work) - e.workersWG.Wait() + e.workers.Close() return nil } +// PoolSize is exported to let the task pool update us +func (e *executor) PoolSize(n int) { + if e.Holder != nil { + e.Holder.Stats.Gauge("worker_total", float64(n), 0) + } +} + // InitStats initializes stats counters. Must be called after Holder set. func (e *executor) InitStats() { if e.Holder != nil { e.Holder.Stats.Count("job_total", 0, 0) - e.Holder.Stats.Gauge("worker_total", float64(atomic.LoadInt64(&e.currentWorkers)), 0) + l, _, _ := e.workers.Stats() + e.Holder.Stats.Gauge("worker_total", float64(l), 0) } } @@ -5781,7 +5725,7 @@ loop: continue loop } } - return nil, errors.Wrapf(errShardUnavailable, "%s:%d:%v:%v", index, shard, shards, nodes) + return nil, errors.Wrapf(errShardUnavailable, "%s:%d:%v", index, shard, nodes) } return m, nil } @@ -6068,9 +6012,25 @@ type job struct { idleHands bool } +// doOneJob had one job. *disappointed sigh* +func (e *executor) doOneJob() { + j, ok := <-e.work + if !ok { + return + } + // Skip out early if the context is done, but still send + // an ack so mapperLocal can be sure we aren't about to + // work on something it sent us. + if err := j.ctx.Err(); err != nil { + j.resultChan <- mapResponse{result: nil, err: err} + return + } + result, err := j.mapFn(j.ctx, j.shard, &mapOptions{memoryAvailable: j.memoryAvailable}) + j.resultChan <- mapResponse{result: result, err: err} +} + func (e *executor) worker(work chan job) { for j := range work { - atomic.AddUint64(&e.workCounter, 1) e.Holder.Stats.Count("job_total", 1, 0) if j.idleHands { return @@ -6096,9 +6056,8 @@ func (e *executor) mapperLocal(ctx context.Context, shards []uint64, mapFn mapFu ctx, cancel := context.WithCancel(ctx) defer cancel() done := ctx.Done() - e.workMu.RLock() - defer e.workMu.RUnlock() - + e.workerPoolMu.Lock() + defer e.workerPoolMu.Unlock() if e.shutdown { return nil, errShutdown } diff --git a/holder.go b/holder.go index 18cd7154c..db38a6db7 100644 --- a/holder.go +++ b/holder.go @@ -9,7 +9,6 @@ import ( "path/filepath" "runtime" "sort" - "strings" "sync" "time" @@ -70,6 +69,9 @@ type Holder struct { sharder disco.Sharder serializer Serializer + // executor, which we use only to get access to its worker pool + executor *executor + // Close management wg sync.WaitGroup closing chan struct{} @@ -216,7 +218,7 @@ func DefaultHolderConfig() *HolderConfig { OpenIDAllocator: func(string, bool) (*idAllocator, error) { return &idAllocator{}, nil }, TranslationSyncer: NopTranslationSyncer, Serializer: GobSerializer, - Schemator: disco.InMemSchemator, + Schemator: disco.NewInMemSchemator(), Sharder: disco.InMemSharder, CacheFlushInterval: defaultCacheFlushInterval, StatsClient: stats.NopStatsClient, @@ -332,23 +334,7 @@ func (h *Holder) Open() error { } defer f.Close() - fis, err := f.Readdir(0) - if err != nil { - return errors.Wrap(err, "reading directory") - } - - for _, fi := range fis { - // Skip files or hidden directories. - if !fi.IsDir() || strings.HasPrefix(fi.Name(), ".") { - continue - } - - // Only continue with indexes which are present in schema. - idx, ok := schema[fi.Name()] - if !ok { - continue - } - + for idxKey, idx := range schema { // decode the CreateIndexMessage from the schema data in order to // get its metadata, such as CreateAt. cim, err := decodeCreateIndexMessage(h.serializer, idx.Data) @@ -356,11 +342,11 @@ func (h *Holder) Open() error { return errors.Wrap(err, "decoding create index message") } - h.Logger.Printf("opening index: %s", filepath.Base(fi.Name())) + h.Logger.Printf("opening index: %s", idxKey) - index, err := h.newIndex(h.IndexPath(filepath.Base(fi.Name())), filepath.Base(fi.Name())) + index, err := h.newIndex(h.IndexPath(idxKey), idxKey) if errors.Cause(err) == ErrName { - h.Logger.Errorf("opening index: %s, err=%s", fi.Name(), err) + h.Logger.Errorf("opening index: %s, err=%s", idxKey, err) continue } else if err != nil { return errors.Wrap(err, "opening index") diff --git a/holder_internal_test.go b/holder_internal_test.go index e18704ac8..c9e164f27 100644 --- a/holder_internal_test.go +++ b/holder_internal_test.go @@ -10,7 +10,7 @@ func mustHolderConfig() *HolderConfig { cfg := DefaultHolderConfig() cfg.StorageConfig.FsyncEnabled = false cfg.RBFConfig.FsyncEnabled = false - cfg.Schemator = disco.InMemSchemator + cfg.Schemator = disco.NewInMemSchemator() cfg.Sharder = disco.InMemSharder return cfg } diff --git a/index.go b/index.go index ba3901d67..0e900cb05 100644 --- a/index.go +++ b/index.go @@ -77,7 +77,7 @@ func NewIndex(holder *Holder, path, name string) (*Index, error) { holder: holder, trackExistence: true, - Schemator: disco.InMemSchemator, + Schemator: disco.NewInMemSchemator(), serializer: NopSerializer, translateStores: make(map[int]TranslateStore), @@ -264,43 +264,26 @@ func (i *Index) openFields(idx *disco.Index) error { } defer f.Close() - fis, err := f.Readdir(0) - if err != nil { - return errors.Wrap(err, "reading directory") - } eg, ctx := errgroup.WithContext(context.Background()) var mu sync.Mutex + if idx == nil { + return nil + } fileLoop: - for _, loopFi := range fis { + for fname, fld := range idx.Fields { select { case <-ctx.Done(): break fileLoop default: - fi := loopFi - if !fi.IsDir() { - continue - } - var cfm *CreateFieldMessage = &CreateFieldMessage{} var err error - // Only continue with fields which are present in the provided, - // non-nil index schema. The reason we have to check for idx != nil - // here is because there are tests which call index.Open without - // having a disco.Index available. - if idx != nil { - fld, ok := idx.Fields[fi.Name()] - if !ok { - continue - } - - // Decode the CreateFieldMessage from the schema data in order to - // get its metadata. - cfm, err = decodeCreateFieldMessage(i.holder.serializer, fld.Data) - if err != nil { - return errors.Wrap(err, "decoding create field message") - } + // Decode the CreateFieldMessage from the schema data in order to + // get its metadata. + cfm, err = decodeCreateFieldMessage(i.holder.serializer, fld.Data) + if err != nil { + return errors.Wrap(err, "decoding create field message") } indexQueue <- struct{}{} @@ -308,9 +291,9 @@ fileLoop: defer func() { <-indexQueue }() - i.holder.Logger.Debugf("open field: %s", fi.Name()) + i.holder.Logger.Debugf("open field: %s", fname) - _, err := i.openField(&mu, cfm, fi.Name()) + _, err := i.openField(&mu, cfm, fname) if err != nil { return errors.Wrap(err, "opening field") } @@ -319,6 +302,7 @@ fileLoop: }) } } + err = eg.Wait() if err != nil { // Close any fields which got opened, since the overall diff --git a/server.go b/server.go index 3475b670a..531e56737 100644 --- a/server.go +++ b/server.go @@ -494,6 +494,7 @@ func NewServer(opts ...ServerOption) (*Server, error) { s.holder.Stats = s.holder.Stats.WithTags(fmt.Sprintf("node_id:%s", s.nodeID)) s.executor.Holder = s.holder + s.holder.executor = s.executor s.executor.Cluster = s.cluster s.executor.MaxWritesPerRequest = s.maxWritesPerRequest s.cluster.broadcaster = s diff --git a/task/doc.go b/task/doc.go new file mode 100644 index 000000000..ccfbea653 --- /dev/null +++ b/task/doc.go @@ -0,0 +1,42 @@ +// Copyright 2022 Molecula Corp. All rights reserved. + +// Package task provides an interface for indicating when an operation has +// been blocked, so that a worker pool which wants to be doing N things at +// a time can start trying new things when some things are blocked. +// +// To understand this, you have to start with the original context: We have +// a worker pool, which can handle up to N tasks at once. Tasks come in +// in batches, asynchronously. At most one write task can be active on a given +// database at a time, but many read tasks can be active on the database, +// with or without a write task. Each read task completes only when its entire +// containing operation completes. Write tasks can *partially* complete +// immediately, but in some cases, must wait for read tasks to finish before +// they can do crucial bookkeeping work. +// +// Regardless of the workload, we always have tasks which can progress +// available, and if we do them, eventually everything will complete. However, +// for some workloads, it is possible to pick N tasks *all of which are +// blocked*. In this case, the worker pool becomes useless. Furthermore, +// even if we don't hit that state, we can hit a state where nearly all worker +// pool tasks are blocked. +// +// To address this, we need a way for a worker pool to recognize that a worker +// has become blocked, and *start another worker*. This can result in running +// more than N workers at once. However, it rarely results in running *many* +// more. The typical case would be that we have a worker pool of N, and M of +// them are blocked waiting for write access to a given database. If one of them +// becomes unblocked, we may end up with N+1 active workers, but the other M-1 +// waiting on that database are still blocked. +// +// It might seem like the simplest thing to do is use a buffered channel as a +// semaphore, this being a standard Go idiom for pools. It's a great idiom, but +// in our case, it runs into a problem. When each worker starts, it writes into +// a buffered channel. When it becomes blocked, it reads from the channel to +// free up a slot. When it becomes unblocked, then, it has to write to the +// channel to indicate that it's taking up a slot again. But writes to the +// channel are contested, and usually only become possible when something else +// either blocks or exits... Meaning that, precisely at the moment that we have +// gained a highly contested lock and are able to proceed, we block for an +// indeterminate period of time *while holding that lock*. This is the opposite +// of what we want. +package task diff --git a/task/pool.go b/task/pool.go new file mode 100644 index 000000000..2102b468b --- /dev/null +++ b/task/pool.go @@ -0,0 +1,151 @@ +// Copyright 2022 Molecula Corp. All rights reserved. + +package task + +import ( + "sync" + "sync/atomic" +) + +// Pool represents a worker-pool type thing, which will call a given +// function in parallel aiming for a given level of concurrency. +// To use a pool, you create it, passing in a worker function; it +// then spawns goroutines to run that function in a loop. If the Pool's +// Block method is called, this marks one instance of the worker goroutine +// as blocked; the Unblock method marks it as unblocked. When there are +// insufficient unblocked goroutines, more are spawned. When there are +// excess goroutines, they exit. +// +// The pool can be shut down by calling Close(), setting its target number +// of workers to 0. +type Pool struct { + mu sync.Mutex // locker used for cond + cond *sync.Cond // notify of exiting workers + step func() + targetN int32 // desired number + unblocked int32 // currently active and unblocked + live int32 // currently active including blocked + stats PoolStats +} + +type PoolStats interface { + PoolSize(int) // reports current pool size +} + +// NewPool creates a pool that attempts to keep targetN goroutines +// active, executing step() repeatedly. It updates poolSize with the +// current size of the pool when that changes. +func NewPool(targetN int, step func(), stats PoolStats) *Pool { + p := &Pool{targetN: int32(targetN), step: step, stats: stats} + p.cond = sync.NewCond(&p.mu) + p.mu.Lock() + defer p.mu.Unlock() + for i := 0; i < targetN; i++ { + p.addWorker() + } + return p +} + +// Block marks a worker as blocked, indicating that we may need a new worker +// spawned because the caller is about to be blocked for an indeterminate +// period of time. If a new worker is needed, it's spawned immediately before +// Block returns. +func (p *Pool) Block() { + p.mu.Lock() + defer p.mu.Unlock() + unblocked := atomic.AddInt32(&p.unblocked, -1) + target := atomic.LoadInt32(&p.targetN) + if unblocked < target { + p.addWorker() + } +} + +// Unblock marks a worker as unblocked, potentially allowing the pool to +// retire a worker thread at some point in the future. +func (p *Pool) Unblock() { + atomic.AddInt32(&p.unblocked, 1) +} + +// Shutdown tells a pool to terminate by setting its desired pool size +// to zero, but does not wait for the jobs in it to stop. It is safe to +// call this before calling Close. +func (p *Pool) Shutdown() { + atomic.StoreInt32(&p.targetN, 0) +} + +// Stats reports on the pool's current state -- total live workers it +// has, how many it thinks are unblocked, and what its target is. +// These numbers are sampled individually, and there's no locking, so they +// are not guaranteed to be consistent. This is useful for approximate +// monitoring. +func (p *Pool) Stats() (live, unblocked, target int) { + return int(atomic.LoadInt32(&p.live)), int(atomic.LoadInt32(&p.unblocked)), int(atomic.LoadInt32(&p.targetN)) +} + +// Close is a Shutdown followed by waiting for all jobs to exit. +func (p *Pool) Close() { + p.mu.Lock() + p.Shutdown() + live := atomic.LoadInt32(&p.live) + for live > 0 { + p.cond.Wait() + // This line occurs while we hold p.mu. addWorker can't be called + // except from inside something that would also hold the lock. + // So, if the value can't be stale and increasing, and it can't + // increase anyway once targetN is 0. + live = atomic.LoadInt32(&p.live) + } +} + +// addWorker increments the number of unblocked things, and starts a worker. +// The unblocked count is technically wrong until the worker gets running, but +// it's right "soon". The live count maintenance is done inside the worker. +func (p *Pool) addWorker() { + // update worker count. we don't notify the condition variable because + // increasing workers can't make us more-closed. + live := atomic.AddInt32(&p.live, 1) + if p.stats != nil { + p.stats.PoolSize(int(live)) + } + atomic.AddInt32(&p.unblocked, 1) + go p.work() +} + +// work runs the provided work function in a loop as long as there's not +// too many unblocked goroutines, otherwise it exits. +func (p *Pool) work() { + defer func() { + live := atomic.AddInt32(&p.live, -1) + if p.stats != nil { + p.stats.PoolSize(int(live)) + } + // notify any waiters that we're done + if live == 0 { + p.cond.Broadcast() + } + }() + for { + unblocked := atomic.LoadInt32(&p.unblocked) + target := atomic.LoadInt32(&p.targetN) + for unblocked > target { + // Might have too many! + swapped := atomic.CompareAndSwapInt32(&p.unblocked, unblocked, unblocked-1) + if swapped { + // we've successfully removed ourselves from the unblocked count. + // now return, letting the deferred add above remove us from the live + // count as well. + return + } + // If the swap failed, unblocked increased or decreased. We + // re-extract it, and try the loop again. If it's no longer higher + // than the target, this loop ends and we continue running. + // If it's higher than the target, we'll try again with this new + // value. + // We also reload target because someone could have told us to + // terminate. + unblocked = atomic.LoadInt32(&p.unblocked) + target = atomic.LoadInt32(&p.targetN) + } + p.step() + } +} diff --git a/task/pool_test.go b/task/pool_test.go new file mode 100644 index 000000000..dbdf5f210 --- /dev/null +++ b/task/pool_test.go @@ -0,0 +1,430 @@ +// Copyright 2021 Molecula Corp. All rights reserved. + +package task + +import ( + "fmt" + "golang.org/x/sync/errgroup" + "math/rand" + "sync" + "sync/atomic" + "testing" + "time" +) + +// db represents a thing which can be locked, and which can +// perform read and write operations, which are modeled as channels which +// a workload can wait on writes to, and which embeds a lockable RWMutex. +// The RWMutex actually makes this slightly stricter than the semantics +// of RBF, which usually allows writes and reads to coexist, but fairly +// accurately represents the specific issue that RBF can't *finish* a write +// while an older read is active. Not the same, but has similar impact. +type db struct { + read, write chan struct{} + sync.Mutex +} + +// server represents a set of dbs, which jobs can be run against. They're +// [26] because they're denoted by lowercase/uppercase letters. +type server struct { + dbs [26]db + mu sync.Mutex // mutex to govern access to readers + readers [26][]workload // a list of readers associated with each db + waiters [26]struct { + mu sync.Mutex + cond *sync.Cond + } + pool *Pool + jobs chan *job + tb testing.TB +} + +// a job represents a single operation on a server, and a receiver +// waiting to hear back when it's done. It also has a reference to the +// bitmasks of read/write locks so that the parent operation can clean +// them all up when it's done. This is roughly parallel to the Qcx/Tx +// locking behavior in featurebase. +type job struct { + descr workload // the workload that generated this job, used to identify them + id int + write bool + locked *uint32 // bitmask of read-locked jobs + ch chan<- struct{} +} + +// workload represents a series of jobs as letters; +// lowercase letters read from the read channel of a component, uppercase +// letters read from the write channel of the corresponding lowercase +// component. each operation locks components as it reaches them for +// the first time, then unlocks all of them at the end of the string. +type workload string + +// runJob grabs a single job from the server's job queue, does it, and +// notifies the waiter. To "do" a job is to acquire the appropriate +// lock (for read or write), mark the appropriate bit in a bitmap of +// active locks, and then read from either a read or write channel, which +// then corresponds to values being passed to Satisfy. +func (s *server) runJob() { + j, ok := <-s.jobs + if !ok { + return + } + if j.write { + s.pool.Block() + s.dbs[j.id].Lock() + s.pool.Unblock() + s.mu.Lock() + // obtain list of existing readers + waiting := make([]workload, len(s.readers[j.id])) + copy(waiting, s.readers[j.id]) + s.mu.Unlock() + <-s.dbs[j.id].write + // In RBF, the write lock can't be released until the last outstanding + // reader predating this write terminates, but that's asynchronous + // from the actual request processing. So, similarly, we launch a thing + // that will unlock this slot in the database, once it's done waiting + // for any readers. We do that without the pool marked as blocked. + go func() { + // but the write can't actually complete until any pending readers + // that were already in play complete + if len(waiting) > 0 { + // We might need to wait for things. We need to be sure, + // though, that the server's list of readers for this isn't + // changing while we're checking it. So, we grab the specific + // lock, then check the reader list, and if we think we need + // to wait, we wait on a condition variable which then + // releases that lock so something else can update the reader + // list and notify us. + func() { + s.waiters[j.id].mu.Lock() + defer s.waiters[j.id].mu.Unlock() + // we have to check this with the specific lock held, so if + // anything were to change the list, it'd have to wait + // until we're done or waiting on the cond. + stillWaiting := s.stillWaiting(j.id, waiting) + for stillWaiting { + s.waiters[j.id].cond.Wait() + stillWaiting = s.stillWaiting(j.id, waiting) + } + }() + } + s.dbs[j.id].Unlock() + }() + } else { + s.pool.Block() + // attach us to the list of known readers, which must exit before + // any writers starting after them can exit + s.mu.Lock() + s.readers[j.id] = append(s.readers[j.id], j.descr) + s.mu.Unlock() + s.pool.Unblock() + cur := atomic.LoadUint32(j.locked) + // mask this bit in + for (cur>>j.id)&1 == 0 { + added := cur | (1 << j.id) + atomic.CompareAndSwapUint32(j.locked, cur, added) + cur = atomic.LoadUint32(j.locked) + } + <-s.dbs[j.id].read + } + j.ch <- struct{}{} +} + +// stillWaiting determines whether we're still waiting on anything in +// a given list terminating. +func (s *server) stillWaiting(id int, waitingOn []workload) bool { + s.mu.Lock() + readers := s.readers[id] + s.mu.Unlock() + for _, waiter := range waitingOn { + for _, reader := range readers { + if waiter == reader { + return true + } + } + } + return false +} + +// runWorkload runs the tasks within a workload, passing them to the worker +// queue, and then waiting for them all to complete. When it's done waiting +// for them, it releases any locks they obtained. +func (s *server) runWorkload(w workload) { + var locked uint32 + defer func() { + // unlock everything marked as locked + read := atomic.LoadUint32(&locked) + for i := 0; i < 32; i++ { + if (read>>i)&1 != 0 { + s.waiters[i].mu.Lock() + s.mu.Lock() + // remove us from readers list + for j := range s.readers[i] { + if s.readers[i][j] == w { + copy(s.readers[i][j:], s.readers[i][j+1:]) + s.readers[i] = s.readers[i][:len(s.readers[i])-1] + break + } + } + s.mu.Unlock() + s.waiters[i].mu.Unlock() + // and wake up anything that was waiting for this. + s.waiters[i].cond.Broadcast() + } + } + }() + ch := make(chan struct{}) + eg := &errgroup.Group{} + j := job{ch: ch, locked: &locked, descr: w} + for _, c := range w { + switch { + case c >= 'a' && c <= 'z': + j.id = int(c - 'a') + j.write = false + case c >= 'A' && c <= 'Z': + j.id = int(c - 'A') + j.write = true + default: + s.tb.Logf("unhandled character '%c'", c) + continue + } + j := j + eg.Go(func() error { + s.jobs <- &j + <-ch + return nil + }) + } + _ = eg.Wait() +} + +// newServer creates a server associated with the given testing.TB, +// allowing us to log things. +func newServer(tb testing.TB) *server { + s := &server{tb: tb, jobs: make(chan *job)} + for i := range s.dbs { + s.dbs[i].read = make(chan struct{}) + s.dbs[i].write = make(chan struct{}) + s.waiters[i].cond = sync.NewCond(&s.waiters[i].mu) + } + return s +} + +// close shuts the server down by closing all of its channels, and may +// not really be necessary. +func (s *server) close() { + for i := range s.dbs { + db := &s.dbs[i] + db.Lock() + close(db.read) + close(db.write) + db.Unlock() + } + close(s.jobs) +} + +// Satisfy satisfies the given read or write operations asynchronously, +// but waits for all of them in this batch to complete before returning. +func (s *server) satisfy(w workload) { + var eg errgroup.Group + for _, c := range w { + var id int + var write bool + switch { + case c >= 'a' && c <= 'z': + id = int(c - 'a') + write = false + case c >= 'A' && c <= 'Z': + id = int(c - 'A') + write = true + default: + s.tb.Logf("unhandled character '%c'", c) + continue + } + eg.Go(func() error { + if write { + s.dbs[id].write <- struct{}{} + } else { + s.dbs[id].read <- struct{}{} + } + return nil + }) + } + _ = eg.Wait() +} + +// makeWorkload generates a sequence of letters, some of which may be +// capitalized, in order +func makeWorkload() workload { + var letters [26]byte + var n int + write := rand.Intn(8) == 0 + for i := 0; i < 26; i++ { + if rand.Intn(4) == 0 { + if write { + letters[n] = 'A' + byte(i) + } else { + letters[n] = 'a' + byte(i) + } + n++ + } + } + return workload(letters[:n]) +} + +// testRandomWorkload makes up an arbitrary workload and tries to run +// the server against it. +func testRandomWorkload(t *testing.T) { + s := newServer(t) + eg := &errgroup.Group{} + p := NewPool(2, s.runJob, nil) + s.pool = p + defer p.Close() + defer s.close() + var workloads []workload // the requests we make + var quick []workload // the requests that get satisfied soon + var slow []workload // the requests that don't get satisfied until later + for i := 0; i < 10; i++ { + w := makeWorkload() + if len(w) == 0 { + continue + } + workloads = append(workloads, w) + partial := rand.Intn(26) + // possibly truncate and postpone some + if partial < len(w) { + quick = append(quick, w[:partial]) + slow = append(slow, w[partial:]) + } else { + quick = append(quick, w) + } + } + for _, w := range workloads { + w := w + eg.Go(func() error { + s.runWorkload(w) + return nil + }) + } + for _, w := range quick { + w := w + eg.Go(func() error { + s.satisfy(w) + return nil + }) + } + l, u, target := p.Stats() + // Only one worker at a time can be invoking the mark-as-blocked logic, + // so you can run after it marks that, but before the new worker is spawned, + // but the next worker can't invoke the blocked logic until that completes. + // + // Live count always decreases after unblocked count on the exit path, and + // increases before unblocked count on the startup path. So even if the + // samples are interrupted, I think it should be impossible for live + // to be less than unblocked. + if u < target-1 || l < u { + t.Fatalf("inconsistent pool stats: %d live, %d unblocked, %d target", l, u, target) + } + for _, w := range slow { + w := w + eg.Go(func() error { + s.satisfy(w) + return nil + }) + } + _ = eg.Wait() +} + +// TestRandomWorkloads makes up some arbitrary workloads, then tries to +// satisfy them out of order. +// In theory, this should work for any sequence of operations as long as +// no operation has the same letter for both read and write ops, and +// ops always occur in order. +func TestRandomWorkloads(t *testing.T) { + for i := 0; i < 10; i++ { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + testRandomWorkload(t) + }) + } +} + +func TestServer(t *testing.T) { + s := newServer(t) + eg := &errgroup.Group{} + p := NewPool(3, s.runJob, nil) + s.pool = p + defer p.Close() + defer s.close() + request := func(w workload) { + eg.Go(func() error { + s.runWorkload(w) + return nil + }) + } + // requesting "abcd" means that the reader "abcd" will still be active on + // a until all the other letters show up. + request("abcd") + // satisfy won't complete until at least two of the jobs have happened, + // so there's a decent chance that we've marked ourselves as a reader on + // a. + s.satisfy("abc") + // so we request a write on A. we get the write lock, but we can't + // relinquish it until "d" shows up. + request("A") + // satisfy that request immediately, but to no avail. + s.satisfy("A") + // three more requests come in. if they get pool slots, they definitely + // block; that request on A can't have finished yet. so they could fully + // block our work pool. + request("A") + request("A") + request("A") + // spawn something to provide "efg" + go s.satisfy("efg") + // runWorkload means we actually block waiting for it. if all the worker + // pool is blocked waiting on A, we can't do that. + s.runWorkload("efg") + // now we provide the missing d, which should allow the first request to + // finally complete, and then the next three A, which should finish + // the rest. + s.satisfy("dAAA") + // If we spawned new jobs, this should complete. Otherwise it should hang + // because the requests can't be satisfied because the queue is full + // of blocked operations. + _ = eg.Wait() +} + +func TestPoolStartup(t *testing.T) { + var counter int32 + started := make(chan struct{}) + done := make(chan struct{}) + addAndWait := func() { + <-started + atomic.AddInt32(&counter, 1) + <-done + } + // we expect this to spawn three counters + p := NewPool(3, addAndWait, nil) + time.Sleep(50 * time.Millisecond) + v := atomic.LoadInt32(&counter) + if v != 0 { + t.Fatalf("expected no adds yet, got %d", v) + } + close(started) + time.Sleep(50 * time.Millisecond) + v = atomic.LoadInt32(&counter) + if v != 3 { + t.Fatalf("expected 3 adds, got %d", v) + } + // Tell the pool to stop processing jobs + p.Shutdown() + // Allow the jobs to complete. Since this happens after the + // shutdown has set desired pool size to zero, they should now all exit. + close(done) + p.Close() + time.Sleep(50 * time.Millisecond) + v = atomic.LoadInt32(&counter) + if v != 3 { + t.Fatalf("expected no more adds, got %d including previous 3", v) + } +} diff --git a/txfactory.go b/txfactory.go index 58134a6bb..54dfa4390 100644 --- a/txfactory.go +++ b/txfactory.go @@ -8,6 +8,7 @@ import ( "strings" "sync" + "github.com/molecula/featurebase/v3/task" "github.com/molecula/featurebase/v3/testhook" "github.com/molecula/featurebase/v3/vprint" "github.com/pkg/errors" @@ -83,8 +84,9 @@ var sep = string(os.PathSeparator) // See also the Qcx.GetTx() example and the TxGroup description below. // type Qcx struct { - Grp *TxGroup - Txf *TxFactory + Grp *TxGroup + Txf *TxFactory + workers *task.Pool // if we go back to using Qcx values, this must become a pointer, // or otherwise be dealt with because copies of Mutex are a no-no. @@ -178,6 +180,9 @@ func (f *TxFactory) NewQcx() (qcx *Qcx) { Grp: f.NewTxGroup(), Txf: f, } + if f.holder != nil && f.holder.executor != nil { + qcx.workers = f.holder.executor.workers + } if f.typeOfTx == "roaring" { qcx.isRoaring = true } @@ -223,6 +228,10 @@ var ErrQcxDone = fmt.Errorf("Qcx already Aborted or Finished, so must call reset // to make it clear we are referring to the first and final error. // func (qcx *Qcx) GetTx(o Txo) (tx Tx, finisher func(perr *error), err error) { + if qcx.workers != nil { + qcx.workers.Block() + defer qcx.workers.Unblock() + } qcx.mu.Lock() defer qcx.mu.Unlock()