github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/xlog/buffer_syncer_test.go (about)

     1  package xlog
     2  
     3  import (
     4  	"archive/zip"
     5  	"context"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/require"
    14  	"go.uber.org/zap/zapcore"
    15  
    16  	"github.com/benz9527/xboot/lib/id"
    17  )
    18  
    19  type syncerOutWriter struct {
    20  	data [][]byte
    21  }
    22  
    23  func (w *syncerOutWriter) Write(data []byte) (n int, err error) {
    24  	l := len(data)
    25  	tmp := make([]byte, l)
    26  	copy(tmp, data)
    27  	w.data = append(w.data, tmp)
    28  	return l, nil
    29  }
    30  
    31  func (w *syncerOutWriter) Close() error {
    32  	return nil
    33  }
    34  
    35  func genLog(strLen, count int) (keys []string) {
    36  	nanoID, err := id.ClassicNanoID(strLen)
    37  	if err != nil {
    38  		panic(err)
    39  	}
    40  	keys = make([]string, count)
    41  	for i := range keys {
    42  		keys[i] = nanoID()
    43  	}
    44  	return
    45  }
    46  
    47  func TestXLogBufferSyncer_Console(t *testing.T) {
    48  	ctx, cancel := context.WithCancel(context.TODO())
    49  	w := &syncerOutWriter{}
    50  	syncer := &xLogBufferSyncer{
    51  		ctx:       ctx,
    52  		outWriter: w,
    53  		arena: &xLogArena{
    54  			size: 1 << 10,
    55  		},
    56  		flushInterval: 500 * time.Millisecond,
    57  	}
    58  	syncer.initialize()
    59  
    60  	logs := genLog(100, 200)
    61  	for _, log := range logs {
    62  		_, err := syncer.Write([]byte(log))
    63  		require.NoError(t, err)
    64  	}
    65  	time.Sleep(1 * time.Second)
    66  	err := syncer.Sync()
    67  	require.NoError(t, err)
    68  	require.NotZero(t, len(w.data))
    69  	for i, log := range logs {
    70  		require.Equal(t, w.data[i], []byte(log))
    71  	}
    72  	cancel()
    73  }
    74  
    75  func TestXLogBufferSyncer_Console_DataRace(t *testing.T) {
    76  	ctx, cancel := context.WithCancel(context.TODO())
    77  	w := &syncerOutWriter{}
    78  	syncer := &xLogBufferSyncer{
    79  		ctx:       ctx,
    80  		outWriter: w,
    81  		arena: &xLogArena{
    82  			size: 1 << 10,
    83  		},
    84  		flushInterval: 500 * time.Millisecond,
    85  	}
    86  	syncer.initialize()
    87  
    88  	wg := sync.WaitGroup{}
    89  	wg.Add(2)
    90  	logs := genLog(100, 200)
    91  	go func() {
    92  		for i := 0; i < len(logs)>>1; i++ {
    93  			_, err := syncer.Write([]byte(logs[i]))
    94  			require.NoError(t, err)
    95  		}
    96  		wg.Done()
    97  	}()
    98  	go func() {
    99  		for i := len(logs) >> 1; i < len(logs); i++ {
   100  			_, err := syncer.Write([]byte(logs[i]))
   101  			require.NoError(t, err)
   102  		}
   103  		wg.Done()
   104  	}()
   105  	wg.Wait()
   106  	time.Sleep(1 * time.Second)
   107  	err := syncer.Sync()
   108  	require.NoError(t, err)
   109  	require.NotZero(t, len(w.data))
   110  	set := make(map[string]struct{}, len(logs))
   111  	for _, log := range logs {
   112  		set[log] = struct{}{}
   113  	}
   114  	for _, log := range w.data {
   115  		_, ok := set[string(log)]
   116  		require.True(t, ok)
   117  	}
   118  	cancel()
   119  }
   120  
   121  func testBufferSyncerRotateLogWriteRunCore(t *testing.T, syncer zapcore.WriteSyncer) {
   122  	var err error
   123  	for i := 0; i < 100; i++ {
   124  		data := []byte(strconv.Itoa(i) + " " + time.Now().UTC().Format(backupDateTimeFormat) + " xlog rolling log write test!\n")
   125  		_, err = syncer.Write(data)
   126  		require.NoError(t, err)
   127  	}
   128  	time.Sleep(1 * time.Second)
   129  }
   130  
   131  func testBufferSyncerRotateLogWriteDataRaceRunCore(t *testing.T, syncer zapcore.WriteSyncer) {
   132  	wg := sync.WaitGroup{}
   133  	wg.Add(2)
   134  	go func() {
   135  		for i := 0; i < 50; i++ {
   136  			data := []byte(strconv.Itoa(i) + " " + time.Now().UTC().Format(backupDateTimeFormat) + " xlog rolling log write test!\n")
   137  			_, err := syncer.Write(data)
   138  			require.NoError(t, err)
   139  		}
   140  		wg.Done()
   141  	}()
   142  	go func() {
   143  		for i := 50; i < 100; i++ {
   144  			data := []byte(strconv.Itoa(i) + " " + time.Now().UTC().Format(backupDateTimeFormat) + " xlog rolling log write test!\n")
   145  			_, err := syncer.Write(data)
   146  			require.NoError(t, err)
   147  		}
   148  		wg.Done()
   149  	}()
   150  	wg.Wait()
   151  	time.Sleep(1 * time.Second)
   152  }
   153  
   154  func TestXLogBufferSyncer_RotateLog(t *testing.T) {
   155  	nano, err := id.ClassicNanoID(6)
   156  	require.NoError(t, err)
   157  	rngLogSuffix := "_" + nano() + "_xlog"
   158  	rngLogZipSuffix := rngLogSuffix + "s"
   159  	ctx, cancel := context.WithCancel(context.TODO())
   160  	cfg := &FileCoreConfig{
   161  		FileMaxSize:       "1KB",
   162  		Filename:          filepath.Base(os.Args[0]) + rngLogSuffix + ".log",
   163  		FileCompressible:  true,
   164  		FileMaxBackups:    4,
   165  		FileMaxAge:        "3day",
   166  		FileCompressBatch: 2,
   167  		FileZipName:       filepath.Base(os.Args[0]) + rngLogZipSuffix + ".zip",
   168  		FilePath:          os.TempDir(),
   169  	}
   170  	log := RotateLog(ctx, cfg)
   171  
   172  	size, err := parseFileSize(log.(*rotateLog).fileMaxSize)
   173  	require.NoError(t, err)
   174  	require.Equal(t, uint64(1024), size)
   175  
   176  	syncer := XLogBufferSyncer(ctx, log, 1<<10, 500)
   177  
   178  	loop := 2
   179  	for i := 0; i < loop; i++ {
   180  		testBufferSyncerRotateLogWriteRunCore(t, syncer)
   181  		require.NoError(t, log.Close())
   182  	}
   183  	cancel()
   184  
   185  	reader, err := zip.OpenReader(filepath.Join(cfg.FilePath, cfg.FileZipName))
   186  	require.NoError(t, err)
   187  	require.LessOrEqual(t, int((loop-1)*cfg.FileMaxBackups), len(reader.File))
   188  	require.NoError(t, reader.Close())
   189  	testCleanLogFiles(t, os.TempDir(), filepath.Base(os.Args[0])+rngLogSuffix, ".log")
   190  	testCleanLogFiles(t, os.TempDir(), filepath.Base(os.Args[0])+rngLogZipSuffix, ".zip")
   191  }
   192  
   193  func TestXLogBufferSyncer_RotateLog_DataRace(t *testing.T) {
   194  	nano, err := id.ClassicNanoID(6)
   195  	require.NoError(t, err)
   196  	rngLogSuffix := "_" + nano() + "_xlog"
   197  	rngLogZipSuffix := rngLogSuffix + "s"
   198  	ctx, cancel := context.WithCancel(context.TODO())
   199  	cfg := &FileCoreConfig{
   200  		FileMaxSize:       "1KB",
   201  		Filename:          filepath.Base(os.Args[0]) + rngLogSuffix + ".log",
   202  		FileCompressible:  true,
   203  		FileMaxBackups:    4,
   204  		FileMaxAge:        "3day",
   205  		FileCompressBatch: 2,
   206  		FileZipName:       filepath.Base(os.Args[0]) + rngLogZipSuffix + ".zip",
   207  		FilePath:          os.TempDir(),
   208  	}
   209  	log := RotateLog(ctx, cfg)
   210  
   211  	size, err := parseFileSize(log.(*rotateLog).fileMaxSize)
   212  	require.NoError(t, err)
   213  	require.Equal(t, uint64(1024), size)
   214  
   215  	syncer := XLogBufferSyncer(ctx, log, 1<<10, 500)
   216  
   217  	loop := 2
   218  	for i := 0; i < loop; i++ {
   219  		testBufferSyncerRotateLogWriteDataRaceRunCore(t, syncer)
   220  	}
   221  	cancel()
   222  
   223  	reader, err := zip.OpenReader(filepath.Join(cfg.FilePath, cfg.FileZipName))
   224  	require.NoError(t, err)
   225  	require.LessOrEqual(t, int((loop-1)*cfg.FileMaxBackups), len(reader.File))
   226  	require.NoError(t, reader.Close())
   227  	testCleanLogFiles(t, os.TempDir(), filepath.Base(os.Args[0])+rngLogSuffix, ".log")
   228  	testCleanLogFiles(t, os.TempDir(), filepath.Base(os.Args[0])+rngLogZipSuffix, ".zip")
   229  }