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  }