physicalslotmanager_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package slotting
  11. import (
  12. "bufio"
  13. "bytes"
  14. "fmt"
  15. "testing"
  16. "devt.de/krotik/eliasdb/storage/file"
  17. "devt.de/krotik/eliasdb/storage/paging"
  18. "devt.de/krotik/eliasdb/storage/paging/view"
  19. "devt.de/krotik/eliasdb/storage/slotting/pageview"
  20. "devt.de/krotik/eliasdb/storage/util"
  21. )
  22. func TestPhysicalSlotManager(t *testing.T) {
  23. sf, err := file.NewDefaultStorageFile(DBDIR+"/test5_data", false)
  24. if err != nil {
  25. t.Error(err.Error())
  26. return
  27. }
  28. psf, err := paging.NewPagedStorageFile(sf)
  29. if err != nil {
  30. t.Error(err)
  31. return
  32. }
  33. fsf, err := file.NewDefaultStorageFile(DBDIR+"/test5_free", false)
  34. if err != nil {
  35. t.Error(err.Error())
  36. return
  37. }
  38. fpsf, err := paging.NewPagedStorageFile(fsf)
  39. if err != nil {
  40. t.Error(err)
  41. return
  42. }
  43. psm := NewPhysicalSlotManager(psf, fpsf, false)
  44. // Build up a data array
  45. arr := make([]byte, 9000)
  46. for i := 0; i < 9000; i++ {
  47. arr[i] = byte(i%5) + 1
  48. }
  49. arr2 := make([]byte, 9000)
  50. for i := 0; i < 9000; i++ {
  51. arr2[i] = byte(i%5) + 5
  52. }
  53. loc, err := psm.Insert(arr, 1, 8999)
  54. if err != nil {
  55. t.Error(err)
  56. return
  57. }
  58. // Location should be beginning of the first record
  59. //checkLocation(t, loc, 1, 20)
  60. // Read back the written data
  61. var b bytes.Buffer
  62. buf := bufio.NewWriter(&b)
  63. if err := psm.Fetch(loc, buf); err != nil {
  64. t.Error("Unexpected read result:", err)
  65. return
  66. }
  67. buf.Flush()
  68. str1 := fmt.Sprint(b.Bytes())
  69. str2 := fmt.Sprint(arr[1:])
  70. if str1 != str2 {
  71. t.Error("Unexpected result reading back what was written")
  72. return
  73. }
  74. loc, err = psm.Update(loc, arr2, 0, 9000)
  75. if err != nil {
  76. t.Error(err)
  77. return
  78. }
  79. // Location should have changed now
  80. checkLocation(t, loc, 3, 871)
  81. // Make sure the new free slots are known
  82. psm.Flush()
  83. // Insert new data - the manager should reuse the previous location
  84. loc, err = psm.Insert(arr2, 1, 8999)
  85. if err != nil {
  86. t.Error(err)
  87. return
  88. }
  89. checkLocation(t, loc, 1, 20)
  90. if err := psm.Free(loc); err != nil {
  91. t.Error(err)
  92. return
  93. }
  94. if err := psm.Flush(); err != nil {
  95. t.Error(err)
  96. return
  97. }
  98. // Test error cases
  99. testInsertPanic(t, psm)
  100. record, err := fsf.Get(1)
  101. if err != nil {
  102. t.Error(err)
  103. return
  104. }
  105. _, err = psm.Insert(make([]byte, 1), 0, 1)
  106. if err != file.ErrAlreadyInUse {
  107. t.Error("Unexpected insert result:", err)
  108. return
  109. }
  110. fsf.ReleaseInUse(record)
  111. if err := psm.Free(util.PackLocation(0, 20)); err != file.ErrAlreadyInUse {
  112. t.Error("Unexpected free result:", err)
  113. return
  114. }
  115. record, err = sf.Get(5)
  116. if err != nil {
  117. t.Error(err)
  118. return
  119. }
  120. _, err = psm.allocate(10)
  121. if err != file.ErrAlreadyInUse {
  122. t.Error("Unexpected allocate result:", err)
  123. return
  124. }
  125. sf.ReleaseInUse(record)
  126. record, err = sf.Get(1)
  127. if err != nil {
  128. t.Error(err)
  129. return
  130. }
  131. // This slot shot be free on page 1
  132. _, err = psm.Insert(arr2, 1, 8999)
  133. // The insert should have failed. The allocated space
  134. // for it should have been send back to the free manager
  135. if err != file.ErrAlreadyInUse {
  136. t.Error("Unexpected insert result:", err)
  137. return
  138. }
  139. sf.ReleaseInUse(record)
  140. // This should write the recovered free location
  141. // back to the free manager
  142. psm.Flush()
  143. record, err = sf.Get(2)
  144. if err != nil {
  145. t.Error(err)
  146. return
  147. }
  148. _, err = psm.Insert(arr2, 1, 8999)
  149. if err != file.ErrAlreadyInUse {
  150. t.Error("Unexpected update result:", err)
  151. return
  152. }
  153. sf.ReleaseInUse(record)
  154. checkLocation(t, loc, 1, 20)
  155. // Write the free data which has been declared during the
  156. // last failed call to disk
  157. psm.Flush()
  158. loc, err = psm.Insert(arr2, 1, 8999)
  159. if err != nil {
  160. t.Error("Unexpected insert result:", err)
  161. return
  162. }
  163. checkLocation(t, loc, 1, 20)
  164. record, err = sf.Get(1)
  165. if err != nil {
  166. t.Error(err)
  167. return
  168. }
  169. _, err = psm.Update(loc, arr2, 1, 8999)
  170. if err != file.ErrAlreadyInUse {
  171. t.Error("Unexpected update result:", err)
  172. return
  173. }
  174. sf.ReleaseInUse(record)
  175. record, err = sf.Get(2)
  176. if err != nil {
  177. t.Error(err)
  178. return
  179. }
  180. _, err = psm.Update(loc, arr2, 1, 8999)
  181. if err != file.ErrAlreadyInUse {
  182. t.Error("Unexpected update result:", err)
  183. return
  184. }
  185. sf.ReleaseInUse(record)
  186. record, err = sf.Get(5)
  187. if err != nil {
  188. t.Error(err)
  189. return
  190. }
  191. _, err = psm.Update(loc, arr2, 0, 9000)
  192. if err != file.ErrAlreadyInUse {
  193. t.Error("Unexpected update result:", err)
  194. return
  195. }
  196. sf.ReleaseInUse(record)
  197. record, err = sf.Get(5)
  198. if err != nil {
  199. t.Error(err)
  200. return
  201. }
  202. _, err = psm.Update(loc, arr2, 0, 9000)
  203. if err != file.ErrAlreadyInUse {
  204. t.Error("Unexpected update result:", err)
  205. return
  206. }
  207. sf.ReleaseInUse(record)
  208. if err := psf.Close(); err != nil {
  209. t.Error(err)
  210. return
  211. }
  212. if err := fpsf.Close(); err != nil {
  213. t.Error(err)
  214. return
  215. }
  216. }
  217. func testInsertPanic(t *testing.T, psm *PhysicalSlotManager) {
  218. defer func() {
  219. if r := recover(); r == nil {
  220. t.Error("Inserting 0 bytes did not cause a panic.")
  221. }
  222. }()
  223. psm.Insert(make([]byte, 0), 0, 0)
  224. }
  225. func TestPhysicalSlotManagerReadWrite(t *testing.T) {
  226. sf, err := file.NewDefaultStorageFile(DBDIR+"/test4_data", false)
  227. if err != nil {
  228. t.Error(err.Error())
  229. return
  230. }
  231. psf, err := paging.NewPagedStorageFile(sf)
  232. if err != nil {
  233. t.Error(err)
  234. return
  235. }
  236. fsf, err := file.NewDefaultStorageFile(DBDIR+"/test4_free", false)
  237. if err != nil {
  238. t.Error(err.Error())
  239. return
  240. }
  241. fpsf, err := paging.NewPagedStorageFile(fsf)
  242. if err != nil {
  243. t.Error(err)
  244. return
  245. }
  246. psm := NewPhysicalSlotManager(psf, fpsf, false)
  247. // Allocate some space
  248. loc1, err := psm.allocateNew(10000, 0)
  249. if err != nil {
  250. t.Error(err)
  251. return
  252. }
  253. // Expected offset is the beginning of page 1
  254. checkLocation(t, loc1, 1, 20)
  255. // Allocate some more space
  256. loc2, err := psm.allocateNew(10, 3)
  257. if err != nil {
  258. t.Error(err)
  259. return
  260. }
  261. // Expected offset is on page 3
  262. checkLocation(t, loc2, 3, 1872)
  263. // Build up a data array
  264. arr := make([]byte, 9000)
  265. for i := 0; i < 9000; i++ {
  266. arr[i] = byte(i%5) + 1
  267. }
  268. // Now write the data array in the allocated space
  269. if err := psm.write(loc1, arr, 1, 8999); err != nil {
  270. t.Error("Unexpected write result:", err)
  271. return
  272. }
  273. // Now check the actual written data
  274. record, err := sf.Get(1)
  275. if err != nil {
  276. t.Error(err)
  277. return
  278. }
  279. sf.ReleaseInUse(record)
  280. // Slot size header should be 10000 available and 8999 current
  281. if asize := util.AvailableSize(record, 20); asize != 10000 {
  282. t.Error("Unexpected available size:", asize)
  283. return
  284. }
  285. if csize := util.CurrentSize(record, 20); csize != 8999 {
  286. t.Error("Unexpected current size:", csize)
  287. return
  288. }
  289. // Check the beginning of the written data
  290. if wdata := record.ReadUInt16(24); wdata != 0x0203 {
  291. t.Error("Unexpected beginning of written data:", wdata)
  292. return
  293. }
  294. record, err = sf.Get(2)
  295. if err != nil {
  296. t.Error(err)
  297. return
  298. }
  299. sf.ReleaseInUse(record)
  300. // Check that the second page is a full data page
  301. pv := pageview.NewDataPage(record)
  302. if of := pv.OffsetFirst(); of != 0 {
  303. t.Error("Unexpected first offset:", of)
  304. return
  305. }
  306. if record.ReadSingleByte(20) != 0x04 || record.ReadSingleByte(4095) != 0x04 {
  307. t.Error("Unexpected record data:", record)
  308. return
  309. }
  310. record, err = sf.Get(3)
  311. if err != nil {
  312. t.Error(err)
  313. return
  314. }
  315. sf.ReleaseInUse(record)
  316. // Check that the last page is partially written
  317. // Offset should be the location of the second allocated data block
  318. pv = pageview.NewDataPage(record)
  319. if of := pv.OffsetFirst(); of != 1872 {
  320. t.Error("Unexpected first offset:", of)
  321. return
  322. }
  323. // Data should end with 5 on the following location
  324. // 8999 data written + 4 byte header = 9003 bytes written
  325. // 9003 - 4076 page1 - 4076 page2 = 851 bytes for the last page
  326. // 20 bytes header + 851 written bytes = 871 bytes (offset 870)
  327. if lastByte := record.ReadSingleByte(870); lastByte != 5 {
  328. t.Error("Unexpected last byte:", lastByte)
  329. return
  330. }
  331. if lastByteAfter := record.ReadSingleByte(871); lastByteAfter != 0 {
  332. t.Error("Unexpected byte after last byte:", lastByteAfter)
  333. return
  334. }
  335. // Read back the written data
  336. var b bytes.Buffer
  337. buf := bufio.NewWriter(&b)
  338. if err := psm.Fetch(loc1, buf); err != nil {
  339. t.Error("Unexpected read result:", err)
  340. return
  341. }
  342. buf.Flush()
  343. str1 := fmt.Sprint(b.Bytes())
  344. str2 := fmt.Sprint(arr[1:])
  345. if str1 != str2 {
  346. t.Error("Unexpected result reading back what was written")
  347. return
  348. }
  349. // Test some special cases
  350. record, err = sf.Get(util.LocationRecord(loc2))
  351. if err != nil {
  352. t.Error(err)
  353. return
  354. }
  355. if err := psm.write(loc2, make([]byte, 0), 0, 0); err != file.ErrAlreadyInUse {
  356. t.Error("Unexpected write result:", err)
  357. }
  358. if err := psm.Fetch(loc2, buf); err != file.ErrAlreadyInUse {
  359. t.Error("Unexpected read result:", err)
  360. return
  361. }
  362. sf.ReleaseInUse(record)
  363. if err := psm.write(loc2, make([]byte, 0), 0, 0); err != nil {
  364. t.Error("Unexpected write result:", err)
  365. }
  366. var b2 bytes.Buffer
  367. buf = bufio.NewWriter(&b2)
  368. if err := psm.Fetch(loc2, buf); err != nil {
  369. t.Error("Unexpected read result:", err)
  370. return
  371. }
  372. buf.Flush()
  373. if len(b2.Bytes()) != 0 {
  374. t.Error("Nothing should have been read back")
  375. return
  376. }
  377. if asize := util.AvailableSize(record, int(util.LocationOffset(loc2))); asize != 10 {
  378. t.Error("Unexpected available size:", asize)
  379. return
  380. }
  381. if csize := util.CurrentSize(record, int(util.LocationOffset(loc2))); csize != 0 {
  382. t.Error("Unexpected current size:", csize)
  383. return
  384. }
  385. record, err = sf.Get(3)
  386. if err != nil {
  387. t.Error(err)
  388. return
  389. }
  390. if err := psm.write(loc2, make([]byte, 10000), 0, 9999); err != file.ErrAlreadyInUse {
  391. t.Error("Unexpected write result:", err)
  392. }
  393. if err := psm.Fetch(loc2, buf); err != file.ErrAlreadyInUse {
  394. t.Error("Unexpected read result:", err)
  395. return
  396. }
  397. sf.ReleaseInUse(record)
  398. loc3, err := psm.allocateNew(10000, 3)
  399. if err != nil {
  400. t.Error(err)
  401. return
  402. }
  403. record, err = sf.Get(5)
  404. if err != nil {
  405. t.Error(err)
  406. return
  407. }
  408. if err := psm.write(loc3, make([]byte, 10000), 0, 9999); err != file.ErrAlreadyInUse {
  409. t.Error("Unexpected write result:", err)
  410. }
  411. if err := psm.Fetch(loc3, buf); err != file.ErrAlreadyInUse {
  412. t.Error("Unexpected read result:", err)
  413. return
  414. }
  415. sf.ReleaseInUse(record)
  416. if err := psf.Close(); err != nil {
  417. t.Error(err)
  418. return
  419. }
  420. if err := fpsf.Close(); err != nil {
  421. t.Error(err)
  422. return
  423. }
  424. }
  425. func TestPhysicalSlotManagerAllocateNew(t *testing.T) {
  426. sf, err := file.NewDefaultStorageFile(DBDIR+"/test3_data", false)
  427. if err != nil {
  428. t.Error(err.Error())
  429. return
  430. }
  431. psf, err := paging.NewPagedStorageFile(sf)
  432. if err != nil {
  433. t.Error(err)
  434. return
  435. }
  436. fsf, err := file.NewDefaultStorageFile(DBDIR+"/test3_free", false)
  437. if err != nil {
  438. t.Error(err.Error())
  439. return
  440. }
  441. fpsf, err := paging.NewPagedStorageFile(fsf)
  442. if err != nil {
  443. t.Error(err)
  444. return
  445. }
  446. psm := NewPhysicalSlotManager(psf, fpsf, false)
  447. // Check the simple case
  448. size := util.NormalizeSlotSize(500)
  449. // Error case new allocated page is already in use
  450. record, err := sf.Get(1)
  451. if err != nil {
  452. t.Error(err)
  453. return
  454. }
  455. loc, err := psm.allocateNew(size, 0)
  456. if err != file.ErrAlreadyInUse {
  457. t.Error(err)
  458. return
  459. }
  460. sf.ReleaseInUse(record)
  461. // Test first allocation
  462. loc, err = psm.allocateNew(size, 0)
  463. if err != nil {
  464. t.Error(err)
  465. return
  466. }
  467. checkLocation(t, loc, 1, pageview.OffsetData)
  468. // Error case existing page is already in use
  469. record, err = sf.Get(1)
  470. if err != nil {
  471. t.Error(err)
  472. return
  473. }
  474. loc, err = psm.allocateNew(10, 1)
  475. if err != file.ErrAlreadyInUse {
  476. t.Error(err)
  477. return
  478. }
  479. sf.ReleaseInUse(record)
  480. loc, err = psm.allocateNew(10, 1)
  481. if err != nil {
  482. t.Error(err)
  483. return
  484. }
  485. // Expected offset is 524
  486. // Page header (20) + prev. allocated data (500) + SizeInfoSize header (4)
  487. exploc := pageview.OffsetData + 500 + util.SizeInfoSize
  488. if exploc != 524 {
  489. t.Error("Expected location should be 532 but is:", exploc)
  490. return
  491. }
  492. checkLocation(t, loc, 1, uint16(exploc))
  493. loc, err = psm.allocateNew(7000, 1)
  494. if err != nil {
  495. t.Error(err)
  496. return
  497. }
  498. // Expected offset is 538
  499. // Last offset (524) + prev. allocated data (10) + SizeInfoSize header (4)
  500. checkLocation(t, loc, 1, 538)
  501. // Last page is now page 2
  502. loc, err = psm.allocateNew(10, 2)
  503. if err != nil {
  504. t.Error(err)
  505. return
  506. }
  507. // Expected offset is 3466 (+ 1 page)
  508. // Last offset (538) + prev. allocated data (7000) + SizeInfoSize header (4)
  509. // Default size for one record is 4096 - 20 bytes header = 4076
  510. // 7542 - 4076 = 3466
  511. checkLocation(t, loc, 2, 3466)
  512. loc, err = psm.allocateNew(10000, 2)
  513. if err != nil {
  514. t.Error(err)
  515. return
  516. }
  517. // Expected offset is 3480
  518. // Last offset (3466) + prev. allocated data (10) + SizeInfoSize header (4)
  519. checkLocation(t, loc, 2, 3480)
  520. // Last page is now page 5 - This allocation should fill up page 5 exacly
  521. // - allocation should be rounded up by 6
  522. loc, err = psm.allocateNew(2830, 5)
  523. if err != nil {
  524. t.Error(err)
  525. return
  526. }
  527. // Expected offset is 1256 (+ 3 pages)
  528. // Last offset (3480) + prev. allocated data (10000) + SizeInfoSize header (4)
  529. // Default size for one record is 4096 - 20 bytes header = 4076
  530. // 13484 - 4076 - 4076 - 4076 = 1256
  531. checkLocation(t, loc, 5, 1256)
  532. // Since page 5 was filled up we should be now allocated to page 6 at
  533. // the beginning - the next allocation should take up page 6 and 7
  534. loc, err = psm.allocateNew(8147, 5)
  535. if err != nil {
  536. t.Error(err)
  537. return
  538. }
  539. // Expected offset is the beginning of page 6
  540. checkLocation(t, loc, 6, 20)
  541. // With the allocated space we should fill page 7
  542. // Rounded up by 1
  543. if lap := psm.pager.Last(view.TypeDataPage); lap != 7 {
  544. t.Error("Unexpected last allocated page", lap)
  545. return
  546. }
  547. // Since page 7 was filled up completely and its first offset is 0
  548. // the algorithm should allocate a new page.
  549. loc, err = psm.allocateNew(10, 7)
  550. if err != nil {
  551. t.Error(err)
  552. }
  553. // Expected offset is the beginning of page 8
  554. checkLocation(t, loc, 8, 20)
  555. // Construct a page where not enough space is free for an allocation
  556. page, err := psm.pager.AllocatePage(view.TypeDataPage)
  557. if err != nil {
  558. t.Error(err)
  559. return
  560. }
  561. record, err = psm.storagefile.Get(page)
  562. if err != nil {
  563. t.Error(err)
  564. return
  565. }
  566. pv := pageview.NewDataPage(record)
  567. pv.SetOffsetFirst(uint16(4093))
  568. psm.storagefile.ReleaseInUseID(page, true)
  569. loc, err = psm.allocateNew(10, 9)
  570. if err != nil {
  571. t.Error(err)
  572. }
  573. // Expected offset is the beginning of page 10
  574. checkLocation(t, loc, 10, 20)
  575. // Now a two error tests which will cause the page pointers get out of sync
  576. record, err = sf.Get(12)
  577. if err != nil {
  578. t.Error(err)
  579. return
  580. }
  581. loc, err = psm.allocateNew(8147, 5)
  582. if err != file.ErrAlreadyInUse {
  583. t.Error(err)
  584. return
  585. }
  586. sf.ReleaseInUse(record)
  587. // Page 11 was now allocated but not written to
  588. loc, err = psm.allocateNew(10, 9)
  589. if err != nil {
  590. t.Error(err)
  591. }
  592. // Expected offset is the beginning of page 12
  593. checkLocation(t, loc, 12, 20)
  594. record, err = sf.Get(14)
  595. if err != nil {
  596. t.Error(err)
  597. return
  598. }
  599. loc, err = psm.allocateNew(8147, 12)
  600. if err != file.ErrAlreadyInUse {
  601. t.Error(err)
  602. return
  603. }
  604. sf.ReleaseInUse(record)
  605. // Page 13 was now allocated but not written to
  606. loc, err = psm.allocateNew(10, 9)
  607. if err != nil {
  608. t.Error(err)
  609. }
  610. // Expected offset is the beginning of page 14
  611. checkLocation(t, loc, 14, 20)
  612. if err := psf.Close(); err != nil {
  613. t.Error(err)
  614. return
  615. }
  616. if err := fpsf.Close(); err != nil {
  617. t.Error(err)
  618. return
  619. }
  620. }
  621. func checkLocation(t *testing.T, loc uint64, record uint64, offset uint16) {
  622. lrecord := util.LocationRecord(loc)
  623. loffset := util.LocationOffset(loc)
  624. if lrecord != record || loffset != offset {
  625. t.Error("Unexpected location. Expected:", record, offset, "Got:", lrecord, loffset)
  626. }
  627. }