github.com/hdt3213/godis@v1.2.9/database/aof_test.go (about)

     1  package database
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hdt3213/godis/aof"
    12  
    13  	"github.com/hdt3213/godis/config"
    14  	"github.com/hdt3213/godis/interface/database"
    15  	"github.com/hdt3213/godis/interface/redis"
    16  	"github.com/hdt3213/godis/lib/utils"
    17  	"github.com/hdt3213/godis/redis/connection"
    18  	"github.com/hdt3213/godis/redis/protocol"
    19  	"github.com/hdt3213/godis/redis/protocol/asserts"
    20  )
    21  
    22  func makeTestData(db database.DB, dbIndex int, prefix string, size int) {
    23  	conn := connection.NewFakeConn()
    24  	conn.SelectDB(dbIndex)
    25  	db.Exec(conn, utils.ToCmdLine("FlushDB"))
    26  	cursor := 0
    27  	for i := 0; i < size; i++ {
    28  		key := prefix + strconv.Itoa(cursor)
    29  		cursor++
    30  		db.Exec(conn, utils.ToCmdLine("SET", key, key, "EX", "10000"))
    31  	}
    32  	for i := 0; i < size; i++ {
    33  		key := prefix + strconv.Itoa(cursor)
    34  		cursor++
    35  		db.Exec(conn, utils.ToCmdLine("RPUSH", key, key))
    36  	}
    37  	for i := 0; i < size; i++ {
    38  		key := prefix + strconv.Itoa(cursor)
    39  		cursor++
    40  		db.Exec(conn, utils.ToCmdLine("HSET", key, key, key))
    41  	}
    42  	for i := 0; i < size; i++ {
    43  		key := prefix + strconv.Itoa(cursor)
    44  		cursor++
    45  		db.Exec(conn, utils.ToCmdLine("SADD", key, key))
    46  	}
    47  	for i := 0; i < size; i++ {
    48  		key := prefix + strconv.Itoa(cursor)
    49  		cursor++
    50  		db.Exec(conn, utils.ToCmdLine("ZADD", key, "10", key))
    51  	}
    52  }
    53  
    54  func validateTestData(t *testing.T, db database.DB, dbIndex int, prefix string, size int) {
    55  	conn := connection.NewFakeConn()
    56  	conn.SelectDB(dbIndex)
    57  	cursor := 0
    58  	var ret redis.Reply
    59  	for i := 0; i < size; i++ {
    60  		key := prefix + strconv.Itoa(cursor)
    61  		cursor++
    62  		ret = db.Exec(conn, utils.ToCmdLine("GET", key))
    63  		asserts.AssertBulkReply(t, ret, key)
    64  		ret = db.Exec(conn, utils.ToCmdLine("TTL", key))
    65  		intResult, ok := ret.(*protocol.IntReply)
    66  		if !ok {
    67  			t.Errorf("expected int protocol, actually %s", ret.ToBytes())
    68  			return
    69  		}
    70  		if intResult.Code <= 0 || intResult.Code > 10000 {
    71  			t.Error("wrong ttl")
    72  		}
    73  	}
    74  	for i := 0; i < size; i++ {
    75  		key := prefix + strconv.Itoa(cursor)
    76  		cursor++
    77  		ret = db.Exec(conn, utils.ToCmdLine("LRANGE", key, "0", "-1"))
    78  		asserts.AssertMultiBulkReply(t, ret, []string{key})
    79  	}
    80  	for i := 0; i < size; i++ {
    81  		key := prefix + strconv.Itoa(cursor)
    82  		cursor++
    83  		ret = db.Exec(conn, utils.ToCmdLine("HGET", key, key))
    84  		asserts.AssertBulkReply(t, ret, key)
    85  	}
    86  	for i := 0; i < size; i++ {
    87  		key := prefix + strconv.Itoa(cursor)
    88  		cursor++
    89  		ret = db.Exec(conn, utils.ToCmdLine("SIsMember", key, key))
    90  		asserts.AssertIntReply(t, ret, 1)
    91  	}
    92  	for i := 0; i < size; i++ {
    93  		key := prefix + strconv.Itoa(cursor)
    94  		cursor++
    95  		ret = db.Exec(conn, utils.ToCmdLine("ZRANGE", key, "0", "-1"))
    96  		asserts.AssertMultiBulkReply(t, ret, []string{key})
    97  	}
    98  }
    99  
   100  func TestAof(t *testing.T) {
   101  	tmpDir, err := ioutil.TempDir("", "godis")
   102  	if err != nil {
   103  		t.Error(err)
   104  		return
   105  	}
   106  	aofFilename := path.Join(tmpDir, "a.aof")
   107  	defer func() {
   108  		_ = os.Remove(aofFilename)
   109  	}()
   110  	config.Properties = &config.ServerProperties{
   111  		AppendOnly:        true,
   112  		AppendFilename:    aofFilename,
   113  		AofUseRdbPreamble: false,
   114  		AppendFsync:       aof.FsyncEverySec,
   115  	}
   116  	dbNum := 4
   117  	size := 10
   118  	var prefixes []string
   119  	aofWriteDB := NewStandaloneServer()
   120  	// generate test data
   121  	for i := 0; i < dbNum; i++ {
   122  		prefix := utils.RandString(8)
   123  		prefixes = append(prefixes, prefix)
   124  		makeTestData(aofWriteDB, i, prefix, size)
   125  	}
   126  	aofWriteDB.Close()                 // wait for aof finished
   127  	aofReadDB := NewStandaloneServer() // start new db and read aof file
   128  	for i := 0; i < dbNum; i++ {
   129  		prefix := prefixes[i]
   130  		validateTestData(t, aofReadDB, i, prefix, size)
   131  	}
   132  	aofReadDB.Close()
   133  }
   134  
   135  func TestRDB(t *testing.T) {
   136  	tmpDir, err := ioutil.TempDir("", "godis")
   137  	if err != nil {
   138  		t.Error(err)
   139  		return
   140  	}
   141  	aofFilename := path.Join(tmpDir, "a.aof")
   142  	rdbFilename := path.Join(tmpDir, "dump.rdb")
   143  	defer func() {
   144  		_ = os.Remove(aofFilename)
   145  		_ = os.Remove(rdbFilename)
   146  	}()
   147  	config.Properties = &config.ServerProperties{
   148  		AppendOnly:     true,
   149  		AppendFilename: aofFilename,
   150  		RDBFilename:    rdbFilename,
   151  	}
   152  	dbNum := 4
   153  	size := 10
   154  	var prefixes []string
   155  	conn := connection.NewFakeConn()
   156  	writeDB := NewStandaloneServer()
   157  	for i := 0; i < dbNum; i++ {
   158  		prefix := utils.RandString(8)
   159  		prefixes = append(prefixes, prefix)
   160  		makeTestData(writeDB, i, prefix, size)
   161  	}
   162  	time.Sleep(time.Second) // wait for aof finished
   163  	writeDB.Exec(conn, utils.ToCmdLine("save"))
   164  	writeDB.Close()
   165  	readDB := NewStandaloneServer() // start new db and read aof file
   166  	for i := 0; i < dbNum; i++ {
   167  		prefix := prefixes[i]
   168  		validateTestData(t, readDB, i, prefix, size)
   169  	}
   170  	readDB.Close()
   171  }
   172  
   173  func TestRewriteAOF(t *testing.T) {
   174  	tmpFile, err := os.CreateTemp(config.GetTmpDir(), "*.aof")
   175  	if err != nil {
   176  		t.Error(err)
   177  		return
   178  	}
   179  	aofFilename := tmpFile.Name()
   180  	defer func() {
   181  		_ = os.Remove(aofFilename)
   182  	}()
   183  	config.Properties = &config.ServerProperties{
   184  		AppendOnly:        true,
   185  		AppendFilename:    aofFilename,
   186  		AofUseRdbPreamble: false,
   187  		AppendFsync:       aof.FsyncEverySec,
   188  	}
   189  	aofWriteDB := NewStandaloneServer()
   190  	size := 1
   191  	dbNum := 4
   192  	var prefixes []string
   193  	for i := 0; i < dbNum; i++ {
   194  		prefix := "" // utils.RandString(8)
   195  		prefixes = append(prefixes, prefix)
   196  		makeTestData(aofWriteDB, i, prefix, size)
   197  	}
   198  	//time.Sleep(2 * time.Second)
   199  	aofWriteDB.Exec(nil, utils.ToCmdLine("rewriteaof"))
   200  	time.Sleep(2 * time.Second)        // wait for async goroutine finish its job
   201  	aofWriteDB.Close()                 // wait for aof finished
   202  	aofReadDB := NewStandaloneServer() // start new db and read aof file
   203  	for i := 0; i < dbNum; i++ {
   204  		prefix := prefixes[i]
   205  		validateTestData(t, aofReadDB, i, prefix, size)
   206  	}
   207  	aofReadDB.Close()
   208  }
   209  
   210  // TestRewriteAOF2 tests execute commands during rewrite procedure
   211  func TestRewriteAOF2(t *testing.T) {
   212  	/* prepare */
   213  	tmpFile, err := os.CreateTemp(config.GetTmpDir(), "*.aof")
   214  	if err != nil {
   215  		t.Error(err)
   216  		return
   217  	}
   218  	aofFilename := tmpFile.Name()
   219  	config.Properties = &config.ServerProperties{
   220  		AppendOnly:     true,
   221  		AppendFilename: aofFilename,
   222  		// set Aof-use-rdb-preamble to true to make sure rewrite procedure
   223  		AppendFsync:       aof.FsyncAlways,
   224  		AofUseRdbPreamble: true,
   225  	}
   226  
   227  	keySize1 := 100
   228  	keySize2 := 250
   229  	/* write data */
   230  	aofWriteDB := NewStandaloneServer()
   231  	dbNum := 4
   232  	conn := connection.NewFakeConn()
   233  	for i := 0; i < dbNum; i++ {
   234  		conn.SelectDB(i)
   235  		for j := 0; j < keySize1; j++ {
   236  			key := strconv.Itoa(j)
   237  			aofWriteDB.Exec(conn, utils.ToCmdLine("SET", key, key))
   238  		}
   239  	}
   240  
   241  	/* rewrite */
   242  	ctx, err := aofWriteDB.persister.StartRewrite()
   243  	if err != nil {
   244  		t.Error(err, "start rewrite failed")
   245  		return
   246  	}
   247  
   248  	/* add data during rewrite */
   249  	ch := make(chan struct{})
   250  	go func() {
   251  		for i := 0; i < dbNum; i++ {
   252  			conn.SelectDB(i)
   253  			for j := 0; j < keySize2; j++ {
   254  				key := "a" + strconv.Itoa(j)
   255  				aofWriteDB.Exec(conn, utils.ToCmdLine("SET", key, key))
   256  			}
   257  		}
   258  		ch <- struct{}{}
   259  	}()
   260  
   261  	doRewriteErr := aofWriteDB.persister.DoRewrite(ctx)
   262  	if doRewriteErr != nil {
   263  		t.Error(doRewriteErr, "do rewrite failed")
   264  		return
   265  	}
   266  	aofWriteDB.persister.FinishRewrite(ctx)
   267  	<-ch
   268  	aofWriteDB.Close() // wait for aof finished
   269  
   270  	// start new db and read aof file
   271  	aofReadDB := NewStandaloneServer()
   272  	for i := 0; i < dbNum; i++ {
   273  		conn.SelectDB(i)
   274  
   275  		for j := 0; j < keySize1; j++ {
   276  			key := strconv.Itoa(j)
   277  			ret := aofReadDB.Exec(conn, utils.ToCmdLine("GET", key))
   278  			asserts.AssertBulkReply(t, ret, key)
   279  		}
   280  		for j := 0; j < keySize2; j++ {
   281  			key := "a" + strconv.Itoa(j)
   282  			ret := aofReadDB.Exec(conn, utils.ToCmdLine("GET", key))
   283  			asserts.AssertBulkReply(t, ret, key)
   284  		}
   285  	}
   286  	aofReadDB.Close()
   287  }