github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/objstorage/objstorageprovider/remoteobjcat/catalog_test.go (about) 1 // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package remoteobjcat_test 6 7 import ( 8 "fmt" 9 "math/rand" 10 "sort" 11 "strconv" 12 "strings" 13 "testing" 14 15 "github.com/cockroachdb/datadriven" 16 "github.com/cockroachdb/pebble/internal/base" 17 "github.com/cockroachdb/pebble/objstorage" 18 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/remoteobjcat" 19 "github.com/cockroachdb/pebble/vfs" 20 ) 21 22 func TestCatalog(t *testing.T) { 23 mem := vfs.NewMem() 24 var memLog base.InMemLogger 25 26 var cat *remoteobjcat.Catalog 27 datadriven.RunTest(t, "testdata/catalog", func(t *testing.T, td *datadriven.TestData) string { 28 toUInt64 := func(args ...string) []uint64 { 29 t.Helper() 30 var res []uint64 31 for _, arg := range args { 32 n, err := strconv.Atoi(arg) 33 if err != nil { 34 td.Fatalf(t, "error parsing arg %s as integer: %v", arg, err) 35 } 36 res = append(res, uint64(n)) 37 } 38 return res 39 } 40 41 parseAdd := func(args []string) remoteobjcat.RemoteObjectMetadata { 42 t.Helper() 43 if len(args) != 3 { 44 td.Fatalf(t, "add <file-num> <creator-id> <creator-file-num>") 45 } 46 vals := toUInt64(args...) 47 return remoteobjcat.RemoteObjectMetadata{ 48 FileNum: base.FileNum(vals[0]).DiskFileNum(), 49 // When we support other file types, we should let the test determine this. 50 FileType: base.FileTypeTable, 51 CreatorID: objstorage.CreatorID(vals[1]), 52 CreatorFileNum: base.FileNum(vals[2]).DiskFileNum(), 53 } 54 } 55 56 parseDel := func(args []string) base.DiskFileNum { 57 t.Helper() 58 if len(args) != 1 { 59 td.Fatalf(t, "delete <file-num>") 60 } 61 return base.FileNum(toUInt64(args[0])[0]).DiskFileNum() 62 } 63 64 memLog.Reset() 65 switch td.Cmd { 66 case "open": 67 if len(td.CmdArgs) != 1 { 68 td.Fatalf(t, "open <dir>") 69 } 70 dirname := td.CmdArgs[0].String() 71 err := mem.MkdirAll(dirname, 0755) 72 if err != nil { 73 td.Fatalf(t, "%v", err) 74 } 75 var contents remoteobjcat.CatalogContents 76 cat, contents, err = remoteobjcat.Open(vfs.WithLogging(mem, memLog.Infof), dirname) 77 if err != nil { 78 return err.Error() 79 } 80 var buf strings.Builder 81 if contents.CreatorID.IsSet() { 82 fmt.Fprintf(&buf, "creator-id: %s\n", contents.CreatorID) 83 } 84 for _, meta := range contents.Objects { 85 fmt.Fprintf(&buf, "%s: %d/%s\n", meta.FileNum, meta.CreatorID, meta.CreatorFileNum) 86 } 87 88 return buf.String() 89 90 case "set-creator-id": 91 if len(td.CmdArgs) != 1 { 92 td.Fatalf(t, "set-creator-id <id>") 93 } 94 id := objstorage.CreatorID(toUInt64(td.CmdArgs[0].String())[0]) 95 if err := cat.SetCreatorID(id); err != nil { 96 return fmt.Sprintf("error setting creator ID: %v", err) 97 } 98 return memLog.String() 99 100 case "batch": 101 var b remoteobjcat.Batch 102 for _, cmd := range strings.Split(td.Input, "\n") { 103 tokens := strings.Split(cmd, " ") 104 if len(tokens) == 0 { 105 td.Fatalf(t, "empty batch line") 106 } 107 switch tokens[0] { 108 case "add": 109 b.AddObject(parseAdd(tokens[1:])) 110 case "delete": 111 b.DeleteObject(parseDel(tokens[1:])) 112 default: 113 td.Fatalf(t, "unknown batch command: %s", tokens[0]) 114 } 115 } 116 if err := cat.ApplyBatch(b); err != nil { 117 return fmt.Sprintf("error applying batch: %v", err) 118 } 119 b.Reset() 120 return memLog.String() 121 122 case "random-batches": 123 n := 1 124 size := 1000 125 for _, arg := range td.CmdArgs { 126 if len(arg.Vals) != 1 { 127 td.Fatalf(t, "random-batches n=<val> size=<val>") 128 } 129 val := toUInt64(arg.Vals[0])[0] 130 switch arg.Key { 131 case "n": 132 n = int(val) 133 case "size": 134 size = int(val) 135 default: 136 td.Fatalf(t, "random-batches n=<val> size=<val>") 137 } 138 } 139 var b remoteobjcat.Batch 140 for batchIdx := 0; batchIdx < n; batchIdx++ { 141 for i := 0; i < size; i++ { 142 b.AddObject(remoteobjcat.RemoteObjectMetadata{ 143 FileNum: base.FileNum(rand.Uint64()).DiskFileNum(), 144 // When we support other file types, we should let the test determine this. 145 FileType: base.FileTypeTable, 146 CreatorID: objstorage.CreatorID(rand.Uint64()), 147 CreatorFileNum: base.FileNum(rand.Uint64()).DiskFileNum(), 148 }) 149 } 150 if err := cat.ApplyBatch(b); err != nil { 151 td.Fatalf(t, "error applying batch: %v", err) 152 } 153 b.Reset() 154 } 155 return memLog.String() 156 157 case "close": 158 if cat == nil { 159 return "nil catalog" 160 } 161 err := cat.Close() 162 cat = nil 163 if err != nil { 164 return fmt.Sprintf("%v", err) 165 } 166 return memLog.String() 167 168 case "list": 169 if len(td.CmdArgs) != 1 { 170 td.Fatalf(t, "open <dir>") 171 } 172 paths, err := mem.List(td.CmdArgs[0].String()) 173 if err != nil { 174 return err.Error() 175 } 176 sort.Strings(paths) 177 return strings.Join(paths, "\n") 178 179 default: 180 return fmt.Sprintf("unknown command: %s", td.Cmd) 181 } 182 }) 183 }