github.com/ethersphere/bee/v2@v2.2.0/pkg/sharky/recovery_test.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sharky_test 6 7 import ( 8 "bytes" 9 "context" 10 "encoding/binary" 11 "errors" 12 "math/rand" 13 "testing" 14 "time" 15 16 "github.com/ethersphere/bee/v2/pkg/sharky" 17 ) 18 19 func TestMissingShard(t *testing.T) { 20 t.Parallel() 21 22 _, err := sharky.NewRecovery(t.TempDir(), 1, 8) 23 if !errors.Is(err, sharky.ErrShardNotFound) { 24 t.Fatalf("want %v, got %v", sharky.ErrShardNotFound, err) 25 } 26 } 27 28 // nolint:paralleltest 29 func TestRecovery(t *testing.T) { 30 datasize := 4 31 shards := 8 32 shardSize := uint32(16) 33 limitInChunks := shards * int(shardSize) 34 35 dir := t.TempDir() 36 ctx := context.Background() 37 size := limitInChunks / 2 38 data := make([]byte, 4) 39 locs := make([]sharky.Location, size) 40 preserved := make(map[uint32]bool) 41 42 s := newSharky(t, dir, shards, datasize) 43 for i := range locs { 44 binary.BigEndian.PutUint32(data, uint32(i)) 45 loc, err := s.Write(ctx, data) 46 if err != nil { 47 t.Fatal(err) 48 } 49 locs[i] = loc 50 } 51 // extract locations to preserve / free in map 52 indexes := make([]uint32, size) 53 for i := range indexes { 54 indexes[i] = uint32(i) 55 } 56 rest := indexes[:] 57 for n := size; n > size/2; n-- { 58 i := rand.Intn(n) 59 preserved[rest[i]] = false 60 rest = append(rest[:i], rest[i+1:]...) 61 } 62 if len(rest) != len(preserved) { 63 t.Fatalf("incorrect set sizes: %d <> %d", len(rest), len(preserved)) 64 } 65 for _, i := range rest { 66 preserved[i] = true 67 } 68 69 t.Run("recover based on preserved map", func(t *testing.T) { 70 r, err := sharky.NewRecovery(dir, shards, datasize) 71 if err != nil { 72 t.Fatal(err) 73 } 74 t.Cleanup(func() { 75 if err := r.Close(); err != nil { 76 t.Fatal(err) 77 } 78 }) 79 for i, add := range preserved { 80 if add { 81 if err := r.Add(locs[i]); err != nil { 82 t.Fatal(err) 83 } 84 } 85 } 86 if err := r.Save(); err != nil { 87 t.Fatal(err) 88 } 89 }) 90 91 payload := []byte{0xff} 92 93 t.Run("check integrity of recovered sharky", func(t *testing.T) { 94 s := newSharky(t, dir, shards, datasize) 95 buf := make([]byte, datasize) 96 t.Run("preserved are found", func(t *testing.T) { 97 for i := range preserved { 98 loc := locs[i] 99 if err := s.Read(ctx, loc, buf); err != nil { 100 t.Fatal(err) 101 } 102 j := binary.BigEndian.Uint32(buf) 103 if i != j { 104 t.Fatalf("data not preserved at location %v: want %d; got %d", loc, i, j) 105 } 106 } 107 }) 108 109 var freelocs []sharky.Location 110 111 t.Run("correct number of free slots", func(t *testing.T) { 112 s := newSharky(t, dir, 1, datasize) 113 cctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond) 114 defer cancel() 115 116 runs := 96 117 for i := 0; i < runs; i++ { 118 loc, err := s.Write(cctx, payload) 119 if err != nil { 120 if errors.Is(err, context.DeadlineExceeded) { 121 break 122 } 123 t.Fatal(err) 124 } 125 freelocs = append(freelocs, loc) 126 } 127 if len(freelocs) != runs { 128 t.Fatalf("incorrect number of free slots: wanted %d; got %d", runs, len(freelocs)) 129 } 130 }) 131 t.Run("added locs are still preserved", func(t *testing.T) { 132 for i, added := range preserved { 133 if !added { 134 continue 135 } 136 if err := s.Read(ctx, locs[int(i)], buf); err != nil { 137 t.Fatal(err) 138 } 139 j := binary.BigEndian.Uint32(buf) 140 if i != j { 141 t.Fatalf("data not preserved at location %v: want %d; got %d", locs[int(j)], i, j) 142 } 143 } 144 }) 145 t.Run("all other slots also overwritten", func(t *testing.T) { 146 for _, loc := range freelocs { 147 if err := s.Read(ctx, loc, buf); err != nil { 148 t.Fatal(err) 149 } 150 data := buf[:len(payload)] 151 if !bytes.Equal(data, payload) { 152 t.Fatalf("incorrect data on freed location %v: want %x; got %x", loc, payload, data) 153 } 154 } 155 }) 156 }) 157 } 158 159 func newSharky(t *testing.T, dir string, shards, datasize int) *sharky.Store { 160 t.Helper() 161 s, err := sharky.New(&dirFS{basedir: dir}, shards, datasize) 162 if err != nil { 163 t.Fatal(err) 164 } 165 t.Cleanup(func() { 166 if err := s.Close(); err != nil { 167 t.Fatal(err) 168 } 169 }) 170 171 return s 172 }