github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/event_listener_test.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bitalosdb 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "os" 22 "reflect" 23 "runtime" 24 "strings" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/zuoyebang/bitalosdb/internal/base" 30 "github.com/zuoyebang/bitalosdb/internal/vfs" 31 32 "github.com/cockroachdb/errors" 33 "github.com/stretchr/testify/require" 34 ) 35 36 type syncedBuffer struct { 37 mu sync.Mutex 38 buf bytes.Buffer 39 } 40 41 func (b *syncedBuffer) Reset() { 42 b.mu.Lock() 43 defer b.mu.Unlock() 44 b.buf.Reset() 45 } 46 47 func (b *syncedBuffer) Write(p []byte) (n int, err error) { 48 b.mu.Lock() 49 defer b.mu.Unlock() 50 return b.buf.Write(p) 51 } 52 53 func (b *syncedBuffer) Cost(args ...interface{}) func() { 54 begin := time.Now() 55 return func() { 56 s := fmt.Sprint(args...) + base.FmtDuration(time.Since(begin)) 57 b.mu.Lock() 58 defer b.mu.Unlock() 59 b.buf.Write([]byte(s)) 60 if n := len(s); n == 0 || s[n-1] != '\n' { 61 b.buf.Write([]byte("\n")) 62 } 63 } 64 } 65 66 func (b *syncedBuffer) Info(args ...interface{}) { 67 s := fmt.Sprint(args...) 68 b.mu.Lock() 69 defer b.mu.Unlock() 70 b.buf.Write([]byte(s)) 71 if n := len(s); n == 0 || s[n-1] != '\n' { 72 b.buf.Write([]byte("\n")) 73 } 74 } 75 76 func (b *syncedBuffer) Warn(args ...interface{}) { 77 s := fmt.Sprint(args...) 78 b.mu.Lock() 79 defer b.mu.Unlock() 80 b.buf.Write([]byte(s)) 81 if n := len(s); n == 0 || s[n-1] != '\n' { 82 b.buf.Write([]byte("\n")) 83 } 84 } 85 86 func (b *syncedBuffer) Error(args ...interface{}) { 87 s := fmt.Sprint(args...) 88 b.mu.Lock() 89 defer b.mu.Unlock() 90 b.buf.Write([]byte(s)) 91 if n := len(s); n == 0 || s[n-1] != '\n' { 92 b.buf.Write([]byte("\n")) 93 } 94 } 95 96 func (b *syncedBuffer) Infof(format string, args ...interface{}) { 97 s := fmt.Sprintf(format, args...) 98 b.mu.Lock() 99 defer b.mu.Unlock() 100 b.buf.Write([]byte(s)) 101 if n := len(s); n == 0 || s[n-1] != '\n' { 102 b.buf.Write([]byte("\n")) 103 } 104 } 105 106 func (b *syncedBuffer) Warnf(format string, args ...interface{}) { 107 s := fmt.Sprintf(format, args...) 108 b.mu.Lock() 109 defer b.mu.Unlock() 110 b.buf.Write([]byte(s)) 111 if n := len(s); n == 0 || s[n-1] != '\n' { 112 b.buf.Write([]byte("\n")) 113 } 114 } 115 116 func (b *syncedBuffer) Errorf(format string, args ...interface{}) { 117 s := fmt.Sprintf(format, args...) 118 b.mu.Lock() 119 defer b.mu.Unlock() 120 b.buf.Write([]byte(s)) 121 if n := len(s); n == 0 || s[n-1] != '\n' { 122 b.buf.Write([]byte("\n")) 123 } 124 } 125 126 func (b *syncedBuffer) Fatalf(format string, args ...interface{}) { 127 b.Infof(format, args...) 128 runtime.Goexit() 129 } 130 131 func (b *syncedBuffer) String() string { 132 b.mu.Lock() 133 defer b.mu.Unlock() 134 return b.buf.String() 135 } 136 137 type loggingFS struct { 138 vfs.FS 139 w io.Writer 140 } 141 142 func (fs loggingFS) Create(name string) (vfs.File, error) { 143 fmt.Fprintf(fs.w, "create: %s\n", name) 144 f, err := fs.FS.Create(name) 145 if err != nil { 146 return nil, err 147 } 148 return loggingFile{f, name, fs.w}, nil 149 } 150 151 func (fs loggingFS) Link(oldname, newname string) error { 152 fmt.Fprintf(fs.w, "link: %s -> %s\n", oldname, newname) 153 return fs.FS.Link(oldname, newname) 154 } 155 156 func (fs loggingFS) OpenDir(name string) (vfs.File, error) { 157 fmt.Fprintf(fs.w, "open-dir: %s\n", name) 158 f, err := fs.FS.OpenDir(name) 159 if err != nil { 160 return nil, err 161 } 162 return loggingFile{f, name, fs.w}, nil 163 } 164 165 func (fs loggingFS) Rename(oldname, newname string) error { 166 fmt.Fprintf(fs.w, "rename: %s -> %s\n", oldname, newname) 167 return fs.FS.Rename(oldname, newname) 168 } 169 170 func (fs loggingFS) ReuseForWrite(oldname, newname string) (vfs.File, error) { 171 fmt.Fprintf(fs.w, "reuseForWrite: %s -> %s\n", oldname, newname) 172 f, err := fs.FS.ReuseForWrite(oldname, newname) 173 if err == nil { 174 f = loggingFile{f, newname, fs.w} 175 } 176 return f, err 177 } 178 179 func (fs loggingFS) MkdirAll(dir string, perm os.FileMode) error { 180 fmt.Fprintf(fs.w, "mkdir-all: %s %#o\n", dir, perm) 181 return fs.FS.MkdirAll(dir, perm) 182 } 183 184 func (fs loggingFS) Lock(name string) (io.Closer, error) { 185 fmt.Fprintf(fs.w, "lock: %s\n", name) 186 return fs.FS.Lock(name) 187 } 188 189 type loggingFile struct { 190 vfs.File 191 name string 192 w io.Writer 193 } 194 195 func (f loggingFile) Close() error { 196 fmt.Fprintf(f.w, "close: %s\n", f.name) 197 return f.File.Close() 198 } 199 200 func (f loggingFile) Sync() error { 201 fmt.Fprintf(f.w, "sync: %s\n", f.name) 202 return f.File.Sync() 203 } 204 205 func TestWriteStallEvents(t *testing.T) { 206 const flushCount = 10 207 const writeStallEnd = "write stall ending" 208 209 testCases := []struct { 210 delayFlush bool 211 expected string 212 }{ 213 {true, "memtable count limit reached"}, 214 } 215 216 for _, c := range testCases { 217 t.Run("", func(t *testing.T) { 218 stallEnded := make(chan struct{}, 1) 219 createReleased := make(chan struct{}, flushCount) 220 var buf syncedBuffer 221 listener := EventListener{ 222 WriteStallBegin: func(info WriteStallBeginInfo) { 223 fmt.Fprintln(&buf, info.String()) 224 createReleased <- struct{}{} 225 }, 226 WriteStallEnd: func() { 227 fmt.Fprintln(&buf, writeStallEnd) 228 select { 229 case stallEnded <- struct{}{}: 230 default: 231 } 232 }, 233 } 234 dir := testDirname 235 defer os.RemoveAll(dir) 236 os.RemoveAll(dir) 237 d, err := Open(dir, &Options{ 238 EventListener: listener, 239 FS: vfs.Default, 240 MemTableSize: 256 << 10, 241 MemTableStopWritesThreshold: 2, 242 }) 243 require.NoError(t, err) 244 defer d.Close() 245 246 for i := 0; i < flushCount; i++ { 247 require.NoError(t, d.Set([]byte("a"), nil, NoSync)) 248 249 ch, err := d.AsyncFlush() 250 require.NoError(t, err) 251 252 if !c.delayFlush { 253 <-ch 254 } 255 if strings.Contains(buf.String(), c.expected) { 256 break 257 } 258 } 259 <-stallEnded 260 261 events := buf.String() 262 require.Contains(t, events, c.expected) 263 require.Contains(t, events, writeStallEnd) 264 if testing.Verbose() { 265 t.Logf("\n%s", events) 266 } 267 }) 268 } 269 } 270 271 func TestEventListenerEnsureDefaultsBackgroundError(t *testing.T) { 272 e := EventListener{} 273 e.EnsureDefaults(nil) 274 e.BackgroundError(errors.New("an example error")) 275 } 276 277 func TestEventListenerEnsureDefaultsSetsAllCallbacks(t *testing.T) { 278 e := EventListener{} 279 e.EnsureDefaults(nil) 280 testAllCallbacksSetInEventListener(t, e) 281 } 282 283 func TestMakeLoggingEventListenerSetsAllCallbacks(t *testing.T) { 284 e := MakeLoggingEventListener(nil) 285 testAllCallbacksSetInEventListener(t, e) 286 } 287 288 func TestTeeEventListenerSetsAllCallbacks(t *testing.T) { 289 e := TeeEventListener(EventListener{}, EventListener{}) 290 testAllCallbacksSetInEventListener(t, e) 291 } 292 293 func testAllCallbacksSetInEventListener(t *testing.T, e EventListener) { 294 t.Helper() 295 v := reflect.ValueOf(e) 296 for i := 0; i < v.NumField(); i++ { 297 fType := v.Type().Field(i) 298 fVal := v.Field(i) 299 require.Equal(t, reflect.Func, fType.Type.Kind(), "unexpected non-func field: %s", fType.Name) 300 require.False(t, fVal.IsNil(), "unexpected nil field: %s", fType.Name) 301 } 302 }