freezer_test.go 11 KB

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