github.com/matrixorigin/matrixone@v1.2.0/pkg/logservice/truncation_test.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     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 logservice
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/lni/goutils/leaktest"
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/hakeeper"
    25  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    26  	"github.com/stretchr/testify/assert"
    27  )
    28  
    29  func TestTruncationExportSnapshot(t *testing.T) {
    30  	fn := func(t *testing.T, s *Service) {
    31  		ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
    32  		defer cancel()
    33  
    34  		tnID := uint64(100)
    35  		req := pb.Request{
    36  			Method: pb.CONNECT_RO,
    37  			LogRequest: pb.LogRequest{
    38  				ShardID: 1,
    39  				TNID:    tnID,
    40  			},
    41  		}
    42  		resp := s.handleConnect(ctx, req)
    43  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
    44  
    45  		for i := 0; i < 10; i++ {
    46  			data := make([]byte, 8)
    47  			cmd := getTestAppendCmd(tnID, data)
    48  			req = pb.Request{
    49  				Method: pb.APPEND,
    50  				LogRequest: pb.LogRequest{
    51  					ShardID: 1,
    52  				},
    53  			}
    54  			resp = s.handleAppend(ctx, req, cmd)
    55  			assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
    56  			assert.Equal(t, uint64(4+i), resp.LogResponse.Lsn) // applied index is 4+i
    57  		}
    58  
    59  		req = pb.Request{
    60  			Method: pb.TRUNCATE,
    61  			LogRequest: pb.LogRequest{
    62  				ShardID: 1,
    63  				Lsn:     4,
    64  			},
    65  		}
    66  		resp = s.handleTruncate(ctx, req)
    67  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
    68  		assert.Equal(t, uint64(0), resp.LogResponse.Lsn)
    69  
    70  		req = pb.Request{
    71  			Method: pb.GET_TRUNCATE,
    72  			LogRequest: pb.LogRequest{
    73  				ShardID: 1,
    74  			},
    75  		}
    76  		resp = s.handleGetTruncatedIndex(ctx, req)
    77  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
    78  		assert.Equal(t, uint64(4), resp.LogResponse.Lsn)
    79  
    80  		err := s.store.processShardTruncateLog(ctx, 1)
    81  		assert.NoError(t, err)
    82  		assert.Equal(t, 1, s.store.snapshotMgr.Count(1, 1))
    83  
    84  		err = s.store.processShardTruncateLog(ctx, 1)
    85  		// truncate lsn not advanced, no error is returned.
    86  		assert.NoError(t, err)
    87  	}
    88  	runServiceTest(t, false, true, fn)
    89  }
    90  
    91  func TestTruncationImportSnapshot(t *testing.T) {
    92  	fn := func(t *testing.T, s *Service) {
    93  		ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
    94  		defer cancel()
    95  
    96  		tnID := uint64(100)
    97  		req := pb.Request{
    98  			Method: pb.CONNECT_RO,
    99  			LogRequest: pb.LogRequest{
   100  				ShardID: 1,
   101  				TNID:    tnID,
   102  			},
   103  		}
   104  		resp := s.handleConnect(ctx, req)
   105  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   106  
   107  		for i := 0; i < 10; i++ {
   108  			data := make([]byte, 8)
   109  			cmd := getTestAppendCmd(tnID, data)
   110  			req = pb.Request{
   111  				Method: pb.APPEND,
   112  				LogRequest: pb.LogRequest{
   113  					ShardID: 1,
   114  				},
   115  			}
   116  			resp = s.handleAppend(ctx, req, cmd)
   117  			assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   118  			assert.Equal(t, uint64(4+i), resp.LogResponse.Lsn) // applied index is 4+i
   119  		}
   120  
   121  		req = pb.Request{
   122  			Method: pb.TRUNCATE,
   123  			LogRequest: pb.LogRequest{
   124  				ShardID: 1,
   125  				Lsn:     4,
   126  			},
   127  		}
   128  		resp = s.handleTruncate(ctx, req)
   129  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   130  		assert.Equal(t, uint64(0), resp.LogResponse.Lsn)
   131  
   132  		req = pb.Request{
   133  			Method: pb.GET_TRUNCATE,
   134  			LogRequest: pb.LogRequest{
   135  				ShardID: 1,
   136  			},
   137  		}
   138  		resp = s.handleGetTruncatedIndex(ctx, req)
   139  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   140  		assert.Equal(t, uint64(4), resp.LogResponse.Lsn)
   141  
   142  		// after this, snapshot index 14 is exported.
   143  		err := s.store.processShardTruncateLog(ctx, 1)
   144  		assert.NoError(t, err)
   145  		assert.Equal(t, 1, s.store.snapshotMgr.Count(1, 1))
   146  
   147  		_, idx := s.store.snapshotMgr.EvalImportSnapshot(1, 1, 6)
   148  		assert.Equal(t, uint64(0), idx)
   149  
   150  		err = s.store.processShardTruncateLog(ctx, 1)
   151  		assert.NoError(t, err)
   152  		assert.Equal(t, 1, s.store.snapshotMgr.Count(1, 1))
   153  
   154  		req = pb.Request{
   155  			Method: pb.TRUNCATE,
   156  			LogRequest: pb.LogRequest{
   157  				ShardID: 1,
   158  				Lsn:     10,
   159  			},
   160  		}
   161  		resp = s.handleTruncate(ctx, req)
   162  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   163  		assert.Equal(t, uint64(0), resp.LogResponse.Lsn)
   164  
   165  		_, idx = s.store.snapshotMgr.EvalImportSnapshot(1, 1, 10)
   166  		assert.Equal(t, uint64(0), idx)
   167  		// index already advance to 15 because of truncate op.
   168  		err = s.store.processShardTruncateLog(ctx, 1)
   169  		assert.NoError(t, err)
   170  		assert.Equal(t, 2, s.store.snapshotMgr.Count(1, 1))
   171  
   172  		for i := 0; i < 10; i++ {
   173  			data := make([]byte, 8)
   174  			cmd := getTestAppendCmd(tnID, data)
   175  			req = pb.Request{
   176  				Method: pb.APPEND,
   177  				LogRequest: pb.LogRequest{
   178  					ShardID: 1,
   179  				},
   180  			}
   181  			resp = s.handleAppend(ctx, req, cmd)
   182  			assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   183  			assert.Equal(t, uint64(16+i), resp.LogResponse.Lsn) // applied index is 16+i
   184  		}
   185  
   186  		req = pb.Request{
   187  			Method: pb.TRUNCATE,
   188  			LogRequest: pb.LogRequest{
   189  				ShardID: 1,
   190  				Lsn:     15,
   191  			},
   192  		}
   193  		resp = s.handleTruncate(ctx, req)
   194  		assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   195  		assert.Equal(t, uint64(0), resp.LogResponse.Lsn)
   196  
   197  		_, idx = s.store.snapshotMgr.EvalImportSnapshot(1, 1, 14)
   198  		assert.Equal(t, uint64(14), idx)
   199  		err = s.store.processShardTruncateLog(ctx, 1)
   200  		assert.NoError(t, err)
   201  		assert.Equal(t, 0, s.store.snapshotMgr.Count(1, 1))
   202  	}
   203  	runServiceTest(t, false, true, fn)
   204  }
   205  
   206  func TestHAKeeperTruncation(t *testing.T) {
   207  	defer leaktest.AfterTest(t)()
   208  
   209  	fn := func(t *testing.T, s *Service) {
   210  		ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
   211  		defer cancel()
   212  		req := pb.Request{
   213  			Method: pb.LOG_HEARTBEAT,
   214  			LogHeartbeat: &pb.LogStoreHeartbeat{
   215  				UUID: "uuid1",
   216  			},
   217  		}
   218  		for i := 0; i < 10; i++ {
   219  			resp := s.handleLogHeartbeat(ctx, req)
   220  			assert.Equal(t, uint32(moerr.Ok), resp.ErrorCode)
   221  		}
   222  		v, err := s.store.read(ctx, hakeeper.DefaultHAKeeperShardID, &hakeeper.IndexQuery{})
   223  		assert.NoError(t, err)
   224  		assert.Equal(t, uint64(12), v.(uint64))
   225  
   226  		err = s.store.processHAKeeperTruncation(ctx)
   227  		assert.NoError(t, err)
   228  
   229  		checkTick := time.NewTicker(time.Millisecond * 100)
   230  		defer checkTick.Stop()
   231  		for {
   232  			select {
   233  			case <-ctx.Done():
   234  				panic("failed to truncate logs")
   235  			case <-checkTick.C:
   236  				rs, err := s.store.nh.QueryRaftLog(hakeeper.DefaultHAKeeperShardID,
   237  					1, 100, 1024*100)
   238  				assert.NoError(t, err)
   239  				select {
   240  				case v := <-rs.ResultC():
   241  					// We cannot fetch the logs because they are truncated.
   242  					if v.RequestOutOfRange() {
   243  						return
   244  					}
   245  				case <-ctx.Done():
   246  					panic("failed to truncate logs")
   247  				}
   248  
   249  			}
   250  		}
   251  	}
   252  	runServiceTest(t, true, true, fn)
   253  }