github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/objstorage/objstorageprovider/objiotracing/obj_io_tracing_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 objiotracing_test 6 7 import ( 8 "io" 9 "strings" 10 "testing" 11 "unsafe" 12 13 "github.com/cockroachdb/pebble" 14 "github.com/cockroachdb/pebble/internal/base" 15 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing" 16 "github.com/cockroachdb/pebble/vfs" 17 "github.com/stretchr/testify/require" 18 ) 19 20 type Event = objiotracing.Event 21 22 const eventSize = int(unsafe.Sizeof(Event{})) 23 24 func TestTracing(t *testing.T) { 25 if !objiotracing.Enabled { 26 t.Skipf("test can only be run under pebble_obj_io_tracing build tag") 27 } 28 fs := vfs.NewMem() 29 d, err := pebble.Open("", &pebble.Options{FS: fs}) 30 require.NoError(t, err) 31 32 require.NoError(t, d.Set([]byte("a"), []byte("aaa"), nil)) 33 require.NoError(t, d.Set([]byte("b"), []byte("bbb"), nil)) 34 require.NoError(t, d.Flush()) 35 require.NoError(t, d.Set([]byte("c"), []byte("ccc"), nil)) 36 require.NoError(t, d.Flush()) 37 require.NoError(t, d.Compact([]byte("a"), []byte("z"), false /* parallelize */)) 38 require.NoError(t, d.Set([]byte("b"), []byte("bbb2"), nil)) 39 require.NoError(t, d.Set([]byte("c"), []byte("ccc2"), nil)) 40 require.NoError(t, d.Set([]byte("d"), []byte("ddd"), nil)) 41 require.NoError(t, d.Flush()) 42 require.NoError(t, d.Compact([]byte("a"), []byte("z"), false /* parallelize */)) 43 require.NoError(t, d.Close()) 44 45 collectEvents := func() []Event { 46 t.Helper() 47 48 list, err := fs.List("") 49 require.NoError(t, err) 50 51 var events []Event 52 for _, f := range list { 53 if strings.HasPrefix(f, "IOTRACES-") { 54 file, err := fs.Open(f) 55 require.NoError(t, err) 56 data, err := io.ReadAll(file) 57 file.Close() 58 require.NoError(t, err) 59 // Remove the file so we don't read these events again later. 60 fs.Remove(f) 61 if len(data) == 0 { 62 continue 63 } 64 require.Equal(t, len(data)%eventSize, 0) 65 p := unsafe.Pointer(&data[0]) 66 asEvents := unsafe.Slice((*Event)(p), len(data)/eventSize) 67 events = append(events, asEvents...) 68 } 69 } 70 if testing.Verbose() { 71 t.Logf("collected events:") 72 for _, e := range events { 73 t.Logf(" %#v", e) 74 } 75 } 76 return events 77 } 78 events := collectEvents() 79 num := func(check func(e Event) bool) int { 80 res := 0 81 for _, e := range events { 82 if check(e) { 83 res += 1 84 } 85 } 86 return res 87 } 88 // Check that we saw at least a few reads and writes. 89 // TODO(radu): check more fields when they are populated. 90 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.ReadOp }), 5) 91 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.WriteOp }), 5) 92 93 // We should see writes at L0 and L7. 94 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.WriteOp && e.LevelPlusOne == 1 }), 0) 95 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.WriteOp && e.LevelPlusOne == 7 }), 0) 96 97 // Check that we saw writes for flushing and for compaction. 98 require.Greater(t, num(func(e Event) bool { return e.Reason == objiotracing.ForFlush }), 0) 99 require.Greater(t, num(func(e Event) bool { return e.Reason == objiotracing.ForCompaction }), 0) 100 101 // Check that offset is set on reads & writes as expected. 102 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.ReadOp && e.Offset > 0 }), 0) 103 require.Greater(t, num(func(e Event) bool { return e.Op == objiotracing.WriteOp && e.Offset > 0 }), 0) 104 105 // Check that the FileNums are set and that we see at least two different files. 106 fileNums := make(map[base.FileNum]int) 107 for _, e := range events { 108 require.NotZero(t, e.FileNum) 109 fileNums[e.FileNum] += 1 110 } 111 require.GreaterOrEqual(t, len(fileNums), 2) 112 113 // Open again and do some reads. 114 d, err = pebble.Open("", &pebble.Options{FS: fs}) 115 require.NoError(t, err) 116 for _, k := range []string{"0", "a", "d", "ccc", "b"} { 117 _, closer, err := d.Get([]byte(k)) 118 if err == pebble.ErrNotFound { 119 continue 120 } 121 require.NoError(t, err) 122 closer.Close() 123 } 124 require.NoError(t, d.Close()) 125 events = collectEvents() 126 // Expect L6 data block reads. 127 require.Greater(t, num(func(e Event) bool { 128 return e.Op == objiotracing.ReadOp && e.BlockType == objiotracing.DataBlock && e.LevelPlusOne == 7 129 }), 0) 130 }