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 }