github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/log_recycler_test.go (about)

     1  // Copyright 2019 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 pebble
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/petermattis/pebble/vfs"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func (r *logRecycler) logNums() []uint64 {
    15  	r.mu.Lock()
    16  	defer r.mu.Unlock()
    17  	return append([]uint64(nil), r.mu.logNums...)
    18  }
    19  
    20  func (r *logRecycler) maxLogNum() uint64 {
    21  	r.mu.Lock()
    22  	defer r.mu.Unlock()
    23  	return r.mu.maxLogNum
    24  }
    25  
    26  func TestLogRecycler(t *testing.T) {
    27  	r := logRecycler{limit: 3}
    28  
    29  	// Logs are recycled up to the limit.
    30  	require.True(t, r.add(1))
    31  	require.EqualValues(t, []uint64{1}, r.logNums())
    32  	require.EqualValues(t, 1, r.maxLogNum())
    33  	require.EqualValues(t, 1, r.peek())
    34  	require.True(t, r.add(2))
    35  	require.EqualValues(t, []uint64{1, 2}, r.logNums())
    36  	require.EqualValues(t, 2, r.maxLogNum())
    37  	require.True(t, r.add(3))
    38  	require.EqualValues(t, []uint64{1, 2, 3}, r.logNums())
    39  	require.EqualValues(t, 3, r.maxLogNum())
    40  
    41  	// Trying to add a file past the limit fails.
    42  	require.False(t, r.add(4))
    43  	require.EqualValues(t, []uint64{1, 2, 3}, r.logNums())
    44  	require.EqualValues(t, 4, r.maxLogNum())
    45  
    46  	// Trying to add a previously recycled file returns success, but the internal
    47  	// state is unchanged.
    48  	require.True(t, r.add(1))
    49  	require.EqualValues(t, []uint64{1, 2, 3}, r.logNums())
    50  	require.EqualValues(t, 4, r.maxLogNum())
    51  
    52  	// An error is returned if we try to pop an element other than the first.
    53  	require.Regexp(t, `invalid 2 vs \[1 2 3\]`, r.pop(2))
    54  
    55  	require.NoError(t, r.pop(1))
    56  	require.EqualValues(t, []uint64{2, 3}, r.logNums())
    57  
    58  	// Log number 4 was already considered, so it won't be recycled.
    59  	require.True(t, r.add(4))
    60  	require.EqualValues(t, []uint64{2, 3}, r.logNums())
    61  
    62  	require.True(t, r.add(5))
    63  	require.EqualValues(t, []uint64{2, 3, 5}, r.logNums())
    64  	require.EqualValues(t, 5, r.maxLogNum())
    65  
    66  	require.NoError(t, r.pop(2))
    67  	require.EqualValues(t, []uint64{3, 5}, r.logNums())
    68  	require.NoError(t, r.pop(3))
    69  	require.EqualValues(t, []uint64{5}, r.logNums())
    70  	require.NoError(t, r.pop(5))
    71  	require.EqualValues(t, []uint64(nil), r.logNums())
    72  
    73  	require.Regexp(t, `empty`, r.pop(6))
    74  }
    75  
    76  func TestRecycleLogs(t *testing.T) {
    77  	mem := vfs.NewMem()
    78  	d, err := Open("", &Options{
    79  		FS: mem,
    80  	})
    81  	if err != nil {
    82  		t.Fatal(err)
    83  	}
    84  
    85  	logNum := func() uint64 {
    86  		d.mu.Lock()
    87  		defer d.mu.Unlock()
    88  		return d.mu.log.queue[len(d.mu.log.queue)-1]
    89  	}
    90  	logCount := func() int {
    91  		d.mu.Lock()
    92  		defer d.mu.Unlock()
    93  		return len(d.mu.log.queue)
    94  	}
    95  
    96  	// Flush the memtable a few times, forcing rotation of the WAL. We should see
    97  	// the recycled logs change as expected.
    98  	require.EqualValues(t, []uint64(nil), d.logRecycler.logNums())
    99  	curLog := logNum()
   100  
   101  	if err := d.Flush(); err != nil {
   102  		t.Fatal(err)
   103  	}
   104  
   105  	require.EqualValues(t, []uint64{curLog}, d.logRecycler.logNums())
   106  	curLog = logNum()
   107  
   108  	if err := d.Flush(); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	require.EqualValues(t, []uint64{curLog}, d.logRecycler.logNums())
   113  
   114  	if err := d.Close(); err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	d, err = Open("", &Options{
   119  		FS: mem,
   120  	})
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	metrics := d.Metrics()
   125  	if n := logCount(); n != int(metrics.WAL.Files) {
   126  		t.Fatalf("expected %d WAL files, but found %d", n, metrics.WAL.Files)
   127  	}
   128  	if n := d.logRecycler.count(); n != int(metrics.WAL.ObsoleteFiles) {
   129  		t.Fatalf("expected %d obsolete WAL files, but found %d", n, metrics.WAL.ObsoleteFiles)
   130  	}
   131  	if err := d.Close(); err != nil {
   132  		t.Fatal(err)
   133  	}
   134  }