github.com/ethersphere/bee/v2@v2.2.0/pkg/sharky/shard_slots_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 package sharky 5 6 import ( 7 "bytes" 8 "context" 9 "fmt" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "sync" 14 "testing" 15 "time" 16 ) 17 18 // TestShard ensures that released slots eventually become available for writes 19 func TestShard(t *testing.T) { 20 t.Parallel() 21 22 shard := newShard(t) 23 24 payload := write{buf: []byte{0xff}, res: make(chan entry)} 25 loc := writePayload(t, shard, payload) 26 buf := readFromLocation(t, shard, loc) 27 28 if !bytes.Equal(buf, payload.buf) { 29 t.Fatalf("want %x, got %x", buf, payload.buf) 30 } 31 32 if loc.Slot != 0 { 33 t.Fatalf("expected to write to slot 0, got %d", loc.Slot) 34 } 35 36 // in order for the test to succeed this slot is expected to become available before test finishes 37 releaseSlot(t, shard, loc) 38 39 payload = write{buf: []byte{0xff >> 1}, res: make(chan entry)} 40 loc = writePayload(t, shard, payload) 41 42 // immediate write should pick the next slot 43 if loc.Slot != 1 { 44 t.Fatalf("expected to write to slot 1, got %d", loc.Slot) 45 } 46 47 releaseSlot(t, shard, loc) 48 49 // we make ten writes expecting that slot 0 is released and becomes available for writing eventually 50 i, runs := 0, 10 51 for ; i < runs; i++ { 52 payload = write{buf: []byte{0x01 << i}, res: make(chan entry)} 53 loc = writePayload(t, shard, payload) 54 releaseSlot(t, shard, loc) 55 if loc.Slot == 0 { 56 break 57 } 58 } 59 60 if i == runs { 61 t.Errorf("expected to write to slot 0 within %d runs, write did not occur", runs) 62 } 63 } 64 65 func writePayload(t *testing.T, shard *shard, payload write) (loc Location) { 66 t.Helper() 67 68 select { 69 case shard.writes <- payload: 70 e := <-payload.res 71 if e.err != nil { 72 t.Fatal("write entry", e.err) 73 } 74 loc = e.loc 75 case <-time.After(100 * time.Millisecond): 76 t.Fatal("write timeout") 77 } 78 79 return loc 80 } 81 82 func readFromLocation(t *testing.T, shard *shard, loc Location) []byte { 83 t.Helper() 84 buf := make([]byte, loc.Length) 85 86 select { 87 case shard.reads <- read{ctx: context.Background(), buf: buf[:loc.Length], slot: loc.Slot}: 88 if err := <-shard.errc; err != nil { 89 t.Fatal("read", err) 90 } 91 case <-time.After(100 * time.Millisecond): 92 t.Fatal("timeout reading") 93 } 94 95 return buf 96 } 97 98 func releaseSlot(t *testing.T, shard *shard, loc Location) { 99 t.Helper() 100 ctx := context.Background() 101 cctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond) 102 if err := shard.release(cctx, loc.Slot); err != nil { 103 t.Fatal("release slot", loc.Slot, "err", err) 104 } 105 cancel() 106 } 107 108 type dirFS string 109 110 func (d *dirFS) Open(path string) (fs.File, error) { 111 return os.OpenFile(filepath.Join(string(*d), path), os.O_RDWR|os.O_CREATE, 0644) 112 } 113 114 func newShard(t *testing.T) *shard { 115 t.Helper() 116 117 basedir := dirFS(t.TempDir()) 118 index := 1 119 120 file, err := basedir.Open(fmt.Sprintf("shard_%03d", index)) 121 if err != nil { 122 t.Fatal(err) 123 } 124 125 ffile, err := basedir.Open(fmt.Sprintf("free_%03d", index)) 126 if err != nil { 127 t.Fatal(err) 128 } 129 130 var wg sync.WaitGroup 131 132 slots := newSlots(ffile.(sharkyFile), &wg) 133 err = slots.load() 134 if err != nil { 135 t.Fatal(err) 136 } 137 138 quit := make(chan struct{}) 139 shard := &shard{ 140 reads: make(chan read), 141 errc: make(chan error), 142 writes: make(chan write), 143 index: uint8(index), 144 maxDataSize: 1, 145 file: file.(sharkyFile), 146 slots: slots, 147 quit: quit, 148 } 149 150 t.Cleanup(func() { 151 close(quit) 152 if err := shard.close(); err != nil { 153 t.Fatal("close shard", err) 154 } 155 }) 156 157 terminated := make(chan struct{}) 158 159 wg.Add(1) 160 go func() { 161 defer wg.Done() 162 shard.process() 163 close(terminated) 164 }() 165 166 wg.Add(1) 167 go func() { 168 defer wg.Done() 169 slots.process(terminated) 170 }() 171 172 return shard 173 }