github.com/EgonCoin/EgonChain@v1.10.16/core/rawdb/freezer_test.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rawdb 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "math/rand" 26 "os" 27 "sync" 28 "testing" 29 30 "github.com/EgonCoin/EgonChain/ethdb" 31 "github.com/EgonCoin/EgonChain/rlp" 32 "github.com/stretchr/testify/require" 33 ) 34 35 var freezerTestTableDef = map[string]bool{"test": true} 36 37 func TestFreezerModify(t *testing.T) { 38 t.Parallel() 39 40 // Create test data. 41 var valuesRaw [][]byte 42 var valuesRLP []*big.Int 43 for x := 0; x < 100; x++ { 44 v := getChunk(256, x) 45 valuesRaw = append(valuesRaw, v) 46 iv := big.NewInt(int64(x)) 47 iv = iv.Exp(iv, iv, nil) 48 valuesRLP = append(valuesRLP, iv) 49 } 50 51 tables := map[string]bool{"raw": true, "rlp": false} 52 f, dir := newFreezerForTesting(t, tables) 53 defer os.RemoveAll(dir) 54 defer f.Close() 55 56 // Commit test data. 57 _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 58 for i := range valuesRaw { 59 if err := op.AppendRaw("raw", uint64(i), valuesRaw[i]); err != nil { 60 return err 61 } 62 if err := op.Append("rlp", uint64(i), valuesRLP[i]); err != nil { 63 return err 64 } 65 } 66 return nil 67 }) 68 if err != nil { 69 t.Fatal("ModifyAncients failed:", err) 70 } 71 72 // Dump indexes. 73 for _, table := range f.tables { 74 t.Log(table.name, "index:", table.dumpIndexString(0, int64(len(valuesRaw)))) 75 } 76 77 // Read back test data. 78 checkAncientCount(t, f, "raw", uint64(len(valuesRaw))) 79 checkAncientCount(t, f, "rlp", uint64(len(valuesRLP))) 80 for i := range valuesRaw { 81 v, _ := f.Ancient("raw", uint64(i)) 82 if !bytes.Equal(v, valuesRaw[i]) { 83 t.Fatalf("wrong raw value at %d: %x", i, v) 84 } 85 ivEnc, _ := f.Ancient("rlp", uint64(i)) 86 want, _ := rlp.EncodeToBytes(valuesRLP[i]) 87 if !bytes.Equal(ivEnc, want) { 88 t.Fatalf("wrong RLP value at %d: %x", i, ivEnc) 89 } 90 } 91 } 92 93 // This checks that ModifyAncients rolls back freezer updates 94 // when the function passed to it returns an error. 95 func TestFreezerModifyRollback(t *testing.T) { 96 t.Parallel() 97 98 f, dir := newFreezerForTesting(t, freezerTestTableDef) 99 defer os.RemoveAll(dir) 100 101 theError := errors.New("oops") 102 _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 103 // Append three items. This creates two files immediately, 104 // because the table size limit of the test freezer is 2048. 105 require.NoError(t, op.AppendRaw("test", 0, make([]byte, 2048))) 106 require.NoError(t, op.AppendRaw("test", 1, make([]byte, 2048))) 107 require.NoError(t, op.AppendRaw("test", 2, make([]byte, 2048))) 108 return theError 109 }) 110 if err != theError { 111 t.Errorf("ModifyAncients returned wrong error %q", err) 112 } 113 checkAncientCount(t, f, "test", 0) 114 f.Close() 115 116 // Reopen and check that the rolled-back data doesn't reappear. 117 tables := map[string]bool{"test": true} 118 f2, err := newFreezer(dir, "", false, 2049, tables) 119 if err != nil { 120 t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err) 121 } 122 defer f2.Close() 123 checkAncientCount(t, f2, "test", 0) 124 } 125 126 // This test runs ModifyAncients and Ancient concurrently with each other. 127 func TestFreezerConcurrentModifyRetrieve(t *testing.T) { 128 t.Parallel() 129 130 f, dir := newFreezerForTesting(t, freezerTestTableDef) 131 defer os.RemoveAll(dir) 132 defer f.Close() 133 134 var ( 135 numReaders = 5 136 writeBatchSize = uint64(50) 137 written = make(chan uint64, numReaders*6) 138 wg sync.WaitGroup 139 ) 140 wg.Add(numReaders + 1) 141 142 // Launch the writer. It appends 10000 items in batches. 143 go func() { 144 defer wg.Done() 145 defer close(written) 146 for item := uint64(0); item < 10000; item += writeBatchSize { 147 _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 148 for i := uint64(0); i < writeBatchSize; i++ { 149 item := item + i 150 value := getChunk(32, int(item)) 151 if err := op.AppendRaw("test", item, value); err != nil { 152 return err 153 } 154 } 155 return nil 156 }) 157 if err != nil { 158 panic(err) 159 } 160 for i := 0; i < numReaders; i++ { 161 written <- item + writeBatchSize 162 } 163 } 164 }() 165 166 // Launch the readers. They read random items from the freezer up to the 167 // current frozen item count. 168 for i := 0; i < numReaders; i++ { 169 go func() { 170 defer wg.Done() 171 for frozen := range written { 172 for rc := 0; rc < 80; rc++ { 173 num := uint64(rand.Intn(int(frozen))) 174 value, err := f.Ancient("test", num) 175 if err != nil { 176 panic(fmt.Errorf("error reading %d (frozen %d): %v", num, frozen, err)) 177 } 178 if !bytes.Equal(value, getChunk(32, int(num))) { 179 panic(fmt.Errorf("wrong value at %d", num)) 180 } 181 } 182 } 183 }() 184 } 185 186 wg.Wait() 187 } 188 189 // This test runs ModifyAncients and TruncateAncients concurrently with each other. 190 func TestFreezerConcurrentModifyTruncate(t *testing.T) { 191 f, dir := newFreezerForTesting(t, freezerTestTableDef) 192 defer os.RemoveAll(dir) 193 defer f.Close() 194 195 var item = make([]byte, 256) 196 197 for i := 0; i < 1000; i++ { 198 // First reset and write 100 items. 199 if err := f.TruncateAncients(0); err != nil { 200 t.Fatal("truncate failed:", err) 201 } 202 _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 203 for i := uint64(0); i < 100; i++ { 204 if err := op.AppendRaw("test", i, item); err != nil { 205 return err 206 } 207 } 208 return nil 209 }) 210 if err != nil { 211 t.Fatal("modify failed:", err) 212 } 213 checkAncientCount(t, f, "test", 100) 214 215 // Now append 100 more items and truncate concurrently. 216 var ( 217 wg sync.WaitGroup 218 truncateErr error 219 modifyErr error 220 ) 221 wg.Add(3) 222 go func() { 223 _, modifyErr = f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 224 for i := uint64(100); i < 200; i++ { 225 if err := op.AppendRaw("test", i, item); err != nil { 226 return err 227 } 228 } 229 return nil 230 }) 231 wg.Done() 232 }() 233 go func() { 234 truncateErr = f.TruncateAncients(10) 235 wg.Done() 236 }() 237 go func() { 238 f.AncientSize("test") 239 wg.Done() 240 }() 241 wg.Wait() 242 243 // Now check the outcome. If the truncate operation went through first, the append 244 // fails, otherwise it succeeds. In either case, the freezer should be positioned 245 // at 10 after both operations are done. 246 if truncateErr != nil { 247 t.Fatal("concurrent truncate failed:", err) 248 } 249 if !(errors.Is(modifyErr, nil) || errors.Is(modifyErr, errOutOrderInsertion)) { 250 t.Fatal("wrong error from concurrent modify:", modifyErr) 251 } 252 checkAncientCount(t, f, "test", 10) 253 } 254 } 255 256 func TestFreezerReadonlyValidate(t *testing.T) { 257 tables := map[string]bool{"a": true, "b": true} 258 dir, err := ioutil.TempDir("", "freezer") 259 if err != nil { 260 t.Fatal(err) 261 } 262 defer os.RemoveAll(dir) 263 // Open non-readonly freezer and fill individual tables 264 // with different amount of data. 265 f, err := newFreezer(dir, "", false, 2049, tables) 266 if err != nil { 267 t.Fatal("can't open freezer", err) 268 } 269 var item = make([]byte, 1024) 270 aBatch := f.tables["a"].newBatch() 271 require.NoError(t, aBatch.AppendRaw(0, item)) 272 require.NoError(t, aBatch.AppendRaw(1, item)) 273 require.NoError(t, aBatch.AppendRaw(2, item)) 274 require.NoError(t, aBatch.commit()) 275 bBatch := f.tables["b"].newBatch() 276 require.NoError(t, bBatch.AppendRaw(0, item)) 277 require.NoError(t, bBatch.commit()) 278 if f.tables["a"].items != 3 { 279 t.Fatalf("unexpected number of items in table") 280 } 281 if f.tables["b"].items != 1 { 282 t.Fatalf("unexpected number of items in table") 283 } 284 require.NoError(t, f.Close()) 285 286 // Re-openening as readonly should fail when validating 287 // table lengths. 288 f, err = newFreezer(dir, "", true, 2049, tables) 289 if err == nil { 290 t.Fatal("readonly freezer should fail with differing table lengths") 291 } 292 } 293 294 func newFreezerForTesting(t *testing.T, tables map[string]bool) (*freezer, string) { 295 t.Helper() 296 297 dir, err := ioutil.TempDir("", "freezer") 298 if err != nil { 299 t.Fatal(err) 300 } 301 // note: using low max table size here to ensure the tests actually 302 // switch between multiple files. 303 f, err := newFreezer(dir, "", false, 2049, tables) 304 if err != nil { 305 t.Fatal("can't open freezer", err) 306 } 307 return f, dir 308 } 309 310 // checkAncientCount verifies that the freezer contains n items. 311 func checkAncientCount(t *testing.T, f *freezer, kind string, n uint64) { 312 t.Helper() 313 314 if frozen, _ := f.Ancients(); frozen != n { 315 t.Fatalf("Ancients() returned %d, want %d", frozen, n) 316 } 317 318 // Check at index n-1. 319 if n > 0 { 320 index := n - 1 321 if ok, _ := f.HasAncient(kind, index); !ok { 322 t.Errorf("HasAncient(%q, %d) returned false unexpectedly", kind, index) 323 } 324 if _, err := f.Ancient(kind, index); err != nil { 325 t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err) 326 } 327 } 328 329 // Check at index n. 330 index := n 331 if ok, _ := f.HasAncient(kind, index); ok { 332 t.Errorf("HasAncient(%q, %d) returned true unexpectedly", kind, index) 333 } 334 if _, err := f.Ancient(kind, index); err == nil { 335 t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index) 336 } else if err != errOutOfBounds { 337 t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err) 338 } 339 }