github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/tool/remotecat.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 tool 6 7 import ( 8 "fmt" 9 "io" 10 "slices" 11 12 "github.com/cockroachdb/pebble" 13 "github.com/cockroachdb/pebble/internal/base" 14 "github.com/cockroachdb/pebble/objstorage" 15 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/remoteobjcat" 16 "github.com/cockroachdb/pebble/record" 17 "github.com/spf13/cobra" 18 ) 19 20 // remoteCatalogT implements tools for the remote object catalog. 21 type remoteCatalogT struct { 22 Root *cobra.Command 23 Dump *cobra.Command 24 25 verbose bool 26 opts *pebble.Options 27 } 28 29 func newRemoteCatalog(opts *pebble.Options) *remoteCatalogT { 30 m := &remoteCatalogT{ 31 opts: opts, 32 } 33 34 m.Root = &cobra.Command{ 35 Use: "remotecat", 36 Short: "remote object catalog introspection tools", 37 } 38 39 // Add dump command 40 m.Dump = &cobra.Command{ 41 Use: "dump <remote-catalog-files>", 42 Short: "print remote object catalog contents", 43 Long: ` 44 Print the contents of the REMOTE-OBJ-CATALOG files. 45 `, 46 Args: cobra.MinimumNArgs(1), 47 Run: m.runDump, 48 } 49 m.Dump.Flags().BoolVarP(&m.verbose, "verbose", "v", false, "show each record in the catalog") 50 m.Root.AddCommand(m.Dump) 51 52 return m 53 } 54 55 func (m *remoteCatalogT) runDump(cmd *cobra.Command, args []string) { 56 for _, arg := range args { 57 err := m.runDumpOne(cmd.OutOrStdout(), arg) 58 if err != nil { 59 fmt.Fprintf(cmd.OutOrStderr(), "%s\n", err) 60 } 61 } 62 } 63 64 func (m *remoteCatalogT) runDumpOne(stdout io.Writer, filename string) error { 65 f, err := m.opts.FS.Open(filename) 66 if err != nil { 67 return err 68 } 69 70 var creatorID objstorage.CreatorID 71 objects := make(map[base.DiskFileNum]remoteobjcat.RemoteObjectMetadata) 72 73 fmt.Fprintf(stdout, "%s\n", filename) 74 var editIdx int 75 rr := record.NewReader(f, 0 /* logNum */) 76 for { 77 offset := rr.Offset() 78 r, err := rr.Next() 79 if err == io.EOF { 80 break 81 } else if err != nil { 82 return err 83 } 84 85 var ve remoteobjcat.VersionEdit 86 err = ve.Decode(r) 87 if err != nil { 88 return err 89 } 90 91 if m.verbose { 92 fmt.Fprintf(stdout, "%d/%d\n", offset, editIdx) 93 if ve.CreatorID.IsSet() { 94 fmt.Fprintf(stdout, " CreatorID: %s\n", ve.CreatorID) 95 } 96 if len(ve.NewObjects) > 0 { 97 fmt.Fprintf(stdout, " NewObjects:\n") 98 for _, m := range ve.NewObjects { 99 fmt.Fprintf( 100 stdout, " %s CreatorID: %s CreatorFileNum: %s Locator: %q CustomObjectName: %q\n", 101 m.FileNum, m.CreatorID, m.CreatorFileNum, m.Locator, m.CustomObjectName, 102 ) 103 } 104 } 105 if len(ve.DeletedObjects) > 0 { 106 fmt.Fprintf(stdout, " DeletedObjects:\n") 107 for _, n := range ve.DeletedObjects { 108 fmt.Fprintf(stdout, " %s\n", n) 109 } 110 } 111 } 112 editIdx++ 113 if err := ve.Apply(&creatorID, objects); err != nil { 114 return err 115 } 116 } 117 fmt.Fprintf(stdout, "CreatorID: %v\n", creatorID) 118 var filenums []base.DiskFileNum 119 for n := range objects { 120 filenums = append(filenums, n) 121 } 122 slices.Sort(filenums) 123 fmt.Fprintf(stdout, "Objects:\n") 124 for _, n := range filenums { 125 m := objects[n] 126 fmt.Fprintf( 127 stdout, " %s CreatorID: %s CreatorFileNum: %s Locator: %q CustomObjectName: %q\n", 128 n, m.CreatorID, m.CreatorFileNum, m.Locator, m.CustomObjectName, 129 ) 130 } 131 return nil 132 }