github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/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  }