github.com/defanghe/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/storage_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package etcdraft 8 9 import ( 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "sort" 15 "strings" 16 "testing" 17 18 "github.com/hyperledger/fabric/common/flogging" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "go.etcd.io/etcd/pkg/fileutil" 22 "go.etcd.io/etcd/raft" 23 "go.etcd.io/etcd/raft/raftpb" 24 "go.etcd.io/etcd/wal" 25 "go.uber.org/zap" 26 ) 27 28 var ( 29 err error 30 logger *flogging.FabricLogger 31 32 dataDir, walDir, snapDir string 33 34 ram *raft.MemoryStorage 35 store *RaftStorage 36 ) 37 38 func setup(t *testing.T) { 39 logger = flogging.NewFabricLogger(zap.NewExample()) 40 ram = raft.NewMemoryStorage() 41 dataDir, err = ioutil.TempDir("", "etcdraft-") 42 assert.NoError(t, err) 43 walDir, snapDir = path.Join(dataDir, "wal"), path.Join(dataDir, "snapshot") 44 store, err = CreateStorage(logger, walDir, snapDir, ram) 45 assert.NoError(t, err) 46 } 47 48 func clean(t *testing.T) { 49 err = store.Close() 50 assert.NoError(t, err) 51 err = os.RemoveAll(dataDir) 52 assert.NoError(t, err) 53 } 54 55 func fileCount(files []string, suffix string) (c int) { 56 for _, f := range files { 57 if strings.HasSuffix(f, suffix) { 58 c++ 59 } 60 } 61 return 62 } 63 64 func assertFileCount(t *testing.T, wal, snap int) { 65 files, err := fileutil.ReadDir(walDir) 66 assert.NoError(t, err) 67 assert.Equal(t, wal, fileCount(files, ".wal"), "WAL file count mismatch") 68 69 files, err = fileutil.ReadDir(snapDir) 70 assert.NoError(t, err) 71 assert.Equal(t, snap, fileCount(files, ".snap"), "Snap file count mismatch") 72 } 73 74 func TestOpenWAL(t *testing.T) { 75 t.Run("Last WAL file is broken", func(t *testing.T) { 76 setup(t) 77 defer clean(t) 78 79 // create 10 new wal files 80 for i := 0; i < 10; i++ { 81 store.Store( 82 []raftpb.Entry{{Index: uint64(i), Data: make([]byte, 10)}}, 83 raftpb.HardState{}, 84 raftpb.Snapshot{}, 85 ) 86 } 87 assertFileCount(t, 1, 0) 88 lasti, _ := store.ram.LastIndex() // it never returns err 89 90 // close current storage 91 err = store.Close() 92 assert.NoError(t, err) 93 94 // truncate wal file 95 w := func() string { 96 files, err := fileutil.ReadDir(walDir) 97 assert.NoError(t, err) 98 for _, f := range files { 99 if strings.HasSuffix(f, ".wal") { 100 return path.Join(walDir, f) 101 } 102 } 103 t.FailNow() 104 return "" 105 }() 106 err = os.Truncate(w, 200) 107 assert.NoError(t, err) 108 109 // create new storage 110 ram = raft.NewMemoryStorage() 111 store, err = CreateStorage(logger, walDir, snapDir, ram) 112 require.NoError(t, err) 113 lastI, _ := store.ram.LastIndex() 114 assert.True(t, lastI > 0) // we are still able to read some entries 115 assert.True(t, lasti > lastI) // but less than before because some are broken 116 }) 117 } 118 119 func TestTakeSnapshot(t *testing.T) { 120 // To make this test more understandable, here's a list 121 // of expected wal files: 122 // (wal file name format: seq-index.wal, index is the first index in this file) 123 // 124 // 0000000000000000-0000000000000000.wal (this is created initially by etcd/wal) 125 // 0000000000000001-0000000000000001.wal 126 // 0000000000000002-0000000000000002.wal 127 // 0000000000000003-0000000000000003.wal 128 // 0000000000000004-0000000000000004.wal 129 // 0000000000000005-0000000000000005.wal 130 // 0000000000000006-0000000000000006.wal 131 // 0000000000000007-0000000000000007.wal 132 // 0000000000000008-0000000000000008.wal 133 // 0000000000000009-0000000000000009.wal 134 // 000000000000000a-000000000000000a.wal 135 136 t.Run("Good", func(t *testing.T) { 137 t.Run("MaxSnapshotFiles==1", func(t *testing.T) { 138 backup := MaxSnapshotFiles 139 MaxSnapshotFiles = 1 140 defer func() { MaxSnapshotFiles = backup }() 141 142 setup(t) 143 defer clean(t) 144 145 // set SegmentSizeBytes to a small value so that 146 // every entry persisted to wal would result in 147 // a new wal being created. 148 oldSegmentSizeBytes := wal.SegmentSizeBytes 149 wal.SegmentSizeBytes = 10 150 defer func() { 151 wal.SegmentSizeBytes = oldSegmentSizeBytes 152 }() 153 154 // create 10 new wal files 155 for i := 0; i < 10; i++ { 156 store.Store( 157 []raftpb.Entry{{Index: uint64(i), Data: make([]byte, 100)}}, 158 raftpb.HardState{}, 159 raftpb.Snapshot{}, 160 ) 161 } 162 163 assertFileCount(t, 11, 0) 164 165 err = store.TakeSnapshot(uint64(3), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 166 assert.NoError(t, err) 167 // Snapshot is taken at index 3, which releases lock up to 2 (excl.). 168 // This results in wal files with index [0, 1] being purged (2 files) 169 assertFileCount(t, 9, 1) 170 171 err = store.TakeSnapshot(uint64(5), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 172 assert.NoError(t, err) 173 // Snapshot is taken at index 5, which releases lock up to 4 (excl.). 174 // This results in wal files with index [2, 3] being purged (2 files) 175 assertFileCount(t, 7, 1) 176 177 t.Logf("Close the storage and create a new one based on existing files") 178 179 err = store.Close() 180 assert.NoError(t, err) 181 ram := raft.NewMemoryStorage() 182 store, err = CreateStorage(logger, walDir, snapDir, ram) 183 assert.NoError(t, err) 184 185 err = store.TakeSnapshot(uint64(7), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 186 assert.NoError(t, err) 187 // Snapshot is taken at index 7, which releases lock up to 6 (excl.). 188 // This results in wal files with index [4, 5] being purged (2 file) 189 assertFileCount(t, 5, 1) 190 191 err = store.TakeSnapshot(uint64(9), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 192 assert.NoError(t, err) 193 // Snapshot is taken at index 9, which releases lock up to 8 (excl.). 194 // This results in wal files with index [6, 7] being purged (2 file) 195 assertFileCount(t, 3, 1) 196 }) 197 198 t.Run("MaxSnapshotFiles==2", func(t *testing.T) { 199 backup := MaxSnapshotFiles 200 MaxSnapshotFiles = 2 201 defer func() { MaxSnapshotFiles = backup }() 202 203 setup(t) 204 defer clean(t) 205 206 // set SegmentSizeBytes to a small value so that 207 // every entry persisted to wal would result in 208 // a new wal being created. 209 oldSegmentSizeBytes := wal.SegmentSizeBytes 210 wal.SegmentSizeBytes = 10 211 defer func() { 212 wal.SegmentSizeBytes = oldSegmentSizeBytes 213 }() 214 215 // create 10 new wal files 216 for i := 0; i < 10; i++ { 217 store.Store( 218 []raftpb.Entry{{Index: uint64(i), Data: make([]byte, 100)}}, 219 raftpb.HardState{}, 220 raftpb.Snapshot{}, 221 ) 222 } 223 224 assertFileCount(t, 11, 0) 225 226 // Only one snapshot is taken, no wal pruning happened 227 err = store.TakeSnapshot(uint64(3), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 228 assert.NoError(t, err) 229 assertFileCount(t, 11, 1) 230 231 // Two snapshots at index 3, 5. And we keep one extra wal file prior to oldest snapshot. 232 // So we should have pruned wal file with index [0, 1] 233 err = store.TakeSnapshot(uint64(5), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 234 assert.NoError(t, err) 235 assertFileCount(t, 9, 2) 236 237 t.Logf("Close the storage and create a new one based on existing files") 238 239 err = store.Close() 240 assert.NoError(t, err) 241 ram := raft.NewMemoryStorage() 242 store, err = CreateStorage(logger, walDir, snapDir, ram) 243 assert.NoError(t, err) 244 245 // Two snapshots at index 5, 7. And we keep one extra wal file prior to oldest snapshot. 246 // So we should have pruned wal file with index [2, 3] 247 err = store.TakeSnapshot(uint64(7), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 248 assert.NoError(t, err) 249 assertFileCount(t, 7, 2) 250 251 // Two snapshots at index 7, 9. And we keep one extra wal file prior to oldest snapshot. 252 // So we should have pruned wal file with index [4, 5] 253 err = store.TakeSnapshot(uint64(9), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 254 assert.NoError(t, err) 255 assertFileCount(t, 5, 2) 256 }) 257 }) 258 259 t.Run("Bad", func(t *testing.T) { 260 261 t.Run("MaxSnapshotFiles==2", func(t *testing.T) { 262 // If latest snapshot file is corrupted, storage should be able 263 // to recover from an older one. 264 265 backup := MaxSnapshotFiles 266 MaxSnapshotFiles = 2 267 defer func() { MaxSnapshotFiles = backup }() 268 269 setup(t) 270 defer clean(t) 271 272 // set SegmentSizeBytes to a small value so that 273 // every entry persisted to wal would result in 274 // a new wal being created. 275 oldSegmentSizeBytes := wal.SegmentSizeBytes 276 wal.SegmentSizeBytes = 10 277 defer func() { 278 wal.SegmentSizeBytes = oldSegmentSizeBytes 279 }() 280 281 // create 10 new wal files 282 for i := 0; i < 10; i++ { 283 store.Store( 284 []raftpb.Entry{{Index: uint64(i), Data: make([]byte, 100)}}, 285 raftpb.HardState{}, 286 raftpb.Snapshot{}, 287 ) 288 } 289 290 assertFileCount(t, 11, 0) 291 292 // Only one snapshot is taken, no wal pruning happened 293 err = store.TakeSnapshot(uint64(3), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 294 assert.NoError(t, err) 295 assertFileCount(t, 11, 1) 296 297 // Two snapshots at index 3, 5. And we keep one extra wal file prior to oldest snapshot. 298 // So we should have pruned wal file with index [0, 1] 299 err = store.TakeSnapshot(uint64(5), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 300 assert.NoError(t, err) 301 assertFileCount(t, 9, 2) 302 303 d, err := os.Open(snapDir) 304 assert.NoError(t, err) 305 defer d.Close() 306 names, err := d.Readdirnames(-1) 307 assert.NoError(t, err) 308 sort.Sort(sort.Reverse(sort.StringSlice(names))) 309 310 corrupted := filepath.Join(snapDir, names[0]) 311 t.Logf("Corrupt latest snapshot file: %s", corrupted) 312 f, err := os.OpenFile(corrupted, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 313 assert.NoError(t, err) 314 _, err = f.WriteString("Corrupted Snapshot") 315 assert.NoError(t, err) 316 f.Close() 317 318 // Corrupted snapshot file should've been renamed by ListSnapshots 319 _ = ListSnapshots(logger, snapDir) 320 assertFileCount(t, 9, 1) 321 322 // Rollback the rename 323 broken := corrupted + ".broken" 324 err = os.Rename(broken, corrupted) 325 assert.NoError(t, err) 326 assertFileCount(t, 9, 2) 327 328 t.Logf("Close the storage and create a new one based on existing files") 329 330 err = store.Close() 331 assert.NoError(t, err) 332 ram := raft.NewMemoryStorage() 333 store, err = CreateStorage(logger, walDir, snapDir, ram) 334 assert.NoError(t, err) 335 336 // Corrupted snapshot file should've been renamed by CreateStorage 337 assertFileCount(t, 9, 1) 338 339 files, err := fileutil.ReadDir(snapDir) 340 assert.NoError(t, err) 341 assert.Equal(t, 1, fileCount(files, ".broken")) 342 343 err = store.TakeSnapshot(uint64(7), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 344 assert.NoError(t, err) 345 assertFileCount(t, 9, 2) 346 347 err = store.TakeSnapshot(uint64(9), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 348 assert.NoError(t, err) 349 assertFileCount(t, 5, 2) 350 }) 351 }) 352 } 353 354 func TestApplyOutOfDateSnapshot(t *testing.T) { 355 t.Run("Apply out of date snapshot", func(t *testing.T) { 356 setup(t) 357 defer clean(t) 358 359 // set SegmentSizeBytes to a small value so that 360 // every entry persisted to wal would result in 361 // a new wal being created. 362 oldSegmentSizeBytes := wal.SegmentSizeBytes 363 wal.SegmentSizeBytes = 10 364 defer func() { 365 wal.SegmentSizeBytes = oldSegmentSizeBytes 366 }() 367 368 // create 10 new wal files 369 for i := 0; i < 10; i++ { 370 store.Store( 371 []raftpb.Entry{{Index: uint64(i), Data: make([]byte, 100)}}, 372 raftpb.HardState{}, 373 raftpb.Snapshot{}, 374 ) 375 } 376 assertFileCount(t, 11, 0) 377 378 err = store.TakeSnapshot(uint64(3), raftpb.ConfState{Nodes: []uint64{1}}, make([]byte, 10)) 379 assert.NoError(t, err) 380 assertFileCount(t, 11, 1) 381 382 snapshot := store.Snapshot() 383 assert.NotNil(t, snapshot) 384 385 // Applying old snapshot should have no effect 386 store.ApplySnapshot(snapshot) 387 388 // Storing old snapshot gets no error 389 err := store.Store( 390 []raftpb.Entry{{Index: uint64(10), Data: make([]byte, 100)}}, 391 raftpb.HardState{}, 392 snapshot, 393 ) 394 assert.NoError(t, err) 395 assertFileCount(t, 12, 1) 396 }) 397 }