freelogicalslotmanager_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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. "testing"
  13. "devt.de/krotik/eliasdb/storage/file"
  14. "devt.de/krotik/eliasdb/storage/paging"
  15. "devt.de/krotik/eliasdb/storage/paging/view"
  16. "devt.de/krotik/eliasdb/storage/slotting/pageview"
  17. "devt.de/krotik/eliasdb/storage/util"
  18. )
  19. func TestFreeLogicalSlotManager(t *testing.T) {
  20. sf, err := file.NewDefaultStorageFile(DBDIR+"/test6", false)
  21. if err != nil {
  22. t.Error(err.Error())
  23. return
  24. }
  25. psf, err := paging.NewPagedStorageFile(sf)
  26. if err != nil {
  27. t.Error(err)
  28. return
  29. }
  30. flsm := NewFreeLogicalSlotManager(psf)
  31. testAddPanic(t, flsm)
  32. // Add some locations
  33. flsm.Add(util.PackLocation(5, 22))
  34. flsm.Add(util.PackLocation(6, 23))
  35. out := flsm.String()
  36. if out != "FreeLogicalSlotManager: buckettest/test6\n"+
  37. "Ids :[327702 393239]\n" {
  38. t.Error("Unexpected output of FreeLogicalSlotManager:", out)
  39. }
  40. if err = flsm.Flush(); err != nil {
  41. t.Error(err)
  42. return
  43. }
  44. if len(flsm.slots) != 0 {
  45. t.Error("Nothing should be left in the slot cache after a flush")
  46. return
  47. }
  48. // Check pages are allocated
  49. cursor := paging.NewPageCursor(flsm.pager, view.TypeFreeLogicalSlotPage, 0)
  50. if page, err := cursor.Next(); page != 1 || err != nil {
  51. t.Error("Unexpected free logical slot page:", page, err)
  52. return
  53. }
  54. if page, err := cursor.Next(); page != 0 || err != nil {
  55. t.Error("Unexpected free logical slot page:", page, err)
  56. return
  57. }
  58. page := flsm.pager.First(view.TypeFreeLogicalSlotPage)
  59. if page != 1 {
  60. t.Error("Unexpected first free logical slot page")
  61. return
  62. }
  63. flspRec, err := sf.Get(1)
  64. if err != nil {
  65. t.Error(err)
  66. }
  67. flsp := pageview.NewFreeLogicalSlotPage(flspRec)
  68. if fsc := flsp.FreeSlotCount(); fsc != 2 {
  69. t.Error("Unexpected number of stored free slots", fsc)
  70. }
  71. // Check that both slotinfos have been written
  72. if flsp.SlotInfoLocation(0) != util.PackLocation(5, 22) {
  73. t.Error("Unexpected free slot info")
  74. return
  75. }
  76. if flsp.SlotInfoLocation(1) != util.PackLocation(6, 23) {
  77. t.Error("Unexpected free slot info")
  78. return
  79. }
  80. sf.ReleaseInUse(flspRec)
  81. // Check that we can find them
  82. loc, err := flsm.Get()
  83. if err != nil {
  84. t.Error(err)
  85. return
  86. }
  87. checkLocation(t, loc, 5, 22)
  88. if fsc := flsp.FreeSlotCount(); fsc != 1 {
  89. t.Error("Unexpected number of stored free slots", fsc)
  90. }
  91. // Test error handling in Flush
  92. flsm.Add(util.PackLocation(4, 21))
  93. rec, err := sf.Get(1)
  94. if err != nil {
  95. t.Error(err)
  96. return
  97. }
  98. err = flsm.Flush()
  99. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  100. t.Error("Unexpected Get result:", err)
  101. return
  102. }
  103. // Can get something without error from the unflushed slot list
  104. loc, err = flsm.Get()
  105. if err != nil {
  106. t.Error(err)
  107. return
  108. }
  109. checkLocation(t, loc, 4, 21)
  110. err = sf.ReleaseInUseID(rec.ID(), false)
  111. if err != nil {
  112. t.Error(err)
  113. return
  114. }
  115. loc, err = flsm.Get()
  116. if err != nil {
  117. t.Error(err)
  118. return
  119. }
  120. checkLocation(t, loc, 6, 23)
  121. // Test multiple insert
  122. flsm.Add(util.PackLocation(9, 1))
  123. if err = flsm.Flush(); err != nil {
  124. t.Error(err)
  125. return
  126. }
  127. flsm.Add(util.PackLocation(9, 2))
  128. flsm.Add(util.PackLocation(9, 3))
  129. flsm.Add(util.PackLocation(9, 4))
  130. if err = flsm.Flush(); err != nil {
  131. t.Error(err)
  132. return
  133. }
  134. // Test get error when record in use and slot list is empty
  135. rec, err = sf.Get(1)
  136. if err != nil {
  137. t.Error(err)
  138. return
  139. }
  140. _, err = flsm.Get()
  141. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  142. t.Error(err)
  143. return
  144. }
  145. err = sf.ReleaseInUseID(rec.ID(), false)
  146. if err != nil {
  147. t.Error(err)
  148. return
  149. }
  150. loc, err = flsm.Get()
  151. if err != nil {
  152. t.Error(err)
  153. return
  154. }
  155. checkLocation(t, loc, 9, 1)
  156. loc, err = flsm.Get()
  157. if err != nil {
  158. t.Error(err)
  159. return
  160. }
  161. checkLocation(t, loc, 9, 2)
  162. loc, err = flsm.Get()
  163. if err != nil {
  164. t.Error(err)
  165. return
  166. }
  167. checkLocation(t, loc, 9, 3)
  168. loc, err = flsm.Get()
  169. if err != nil {
  170. t.Error(err)
  171. return
  172. }
  173. checkLocation(t, loc, 9, 4)
  174. if fsc := flsp.FreeSlotCount(); fsc != 0 {
  175. t.Error("Unexpected number of stored free slots", fsc)
  176. }
  177. // Test special case when a page is in the pager list but has no slots allocated
  178. for i := 1; i < 1001; i++ {
  179. flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
  180. }
  181. if err = flsm.Flush(); err != nil {
  182. t.Error(err)
  183. return
  184. }
  185. // Manually free all slots on the first page
  186. flspRec, err = sf.Get(1)
  187. if err != nil {
  188. t.Error(err)
  189. }
  190. flsp = pageview.NewFreeLogicalSlotPage(flspRec)
  191. var j uint16
  192. for j = 0; j < flsp.MaxSlots(); j++ {
  193. flsp.ReleaseSlotInfo(j)
  194. }
  195. sf.ReleaseInUse(flspRec)
  196. // Check we get slotlocation 0 from second page
  197. loc, err = flsm.Get()
  198. if err != nil {
  199. t.Error(err)
  200. return
  201. }
  202. checkLocation(t, loc, 510, 510)
  203. if err := psf.Close(); err != nil {
  204. t.Error(err)
  205. return
  206. }
  207. }
  208. func testAddPanic(t *testing.T, flsm *FreeLogicalSlotManager) {
  209. defer func() {
  210. if r := recover(); r == nil {
  211. t.Error("Adding location 0 did not cause a panic.")
  212. }
  213. }()
  214. flsm.Add(0)
  215. }
  216. func TestFreeLogiclaSlotManagerScale(t *testing.T) {
  217. sf, err := file.NewDefaultStorageFile(DBDIR+"/test7", false)
  218. if err != nil {
  219. t.Error(err.Error())
  220. return
  221. }
  222. shadow, err := file.NewDefaultStorageFile(DBDIR+"/test7_", false)
  223. if err != nil {
  224. t.Error(err.Error())
  225. return
  226. }
  227. psf, err := paging.NewPagedStorageFile(sf)
  228. if err != nil {
  229. t.Error(err)
  230. return
  231. }
  232. flsm := NewFreeLogicalSlotManager(psf)
  233. // Add a lot of locations
  234. for i := 1; i < 5001; i++ {
  235. flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
  236. }
  237. // Check Flush and low level doFlush if a page can't be accessed
  238. if _, err := sf.Get(1); err != nil {
  239. t.Error(err)
  240. return
  241. }
  242. err = flsm.Flush()
  243. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  244. t.Error("Unexpected flush result:", err)
  245. return
  246. }
  247. i, err := flsm.doFlush(1, 0)
  248. if sfe, ok := err.(*file.StorageFileError); i != 0 || !ok || sfe.Type != file.ErrAlreadyInUse {
  249. t.Error("Unexpected doFlush result:", i, err)
  250. return
  251. }
  252. if err := sf.ReleaseInUseID(1, false); err != nil {
  253. t.Error(err)
  254. return
  255. }
  256. // Check the doFlush error return in Flush when allocating new pages
  257. flsm.storagefile = shadow
  258. if _, err := shadow.Get(1); err != nil {
  259. t.Error(err)
  260. return
  261. }
  262. err = flsm.Flush()
  263. if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse {
  264. t.Error("Unexpected flush result:", err)
  265. return
  266. }
  267. if err := shadow.ReleaseInUseID(1, false); err != nil {
  268. t.Error(err)
  269. return
  270. }
  271. flsm.storagefile = sf
  272. // Now do the real flush
  273. if err := flsm.Flush(); err != nil {
  274. t.Error(err)
  275. return
  276. }
  277. // Count the allocated pages
  278. c, err := paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  279. if c != 10 || err != nil {
  280. t.Error("Unexpected counting result:", c, err)
  281. return
  282. }
  283. // Remove some free slots from the list
  284. for i := 1; i < 1001; i++ {
  285. if res, err := flsm.Get(); res != util.PackLocation(uint64(i), uint16(i%1000)) || err != nil {
  286. t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
  287. return
  288. }
  289. }
  290. // Count the allocated pages (one page should be free now)
  291. c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  292. if c != 9 || err != nil {
  293. t.Error("Unexpected counting result:", c, err)
  294. return
  295. }
  296. // Now add new free slots with a different pattern
  297. for i := 1; i < 1001; i++ {
  298. flsm.Add(util.PackLocation(uint64(i+666), uint16(i%1000)))
  299. }
  300. if err := flsm.Flush(); err != nil {
  301. t.Error(err)
  302. return
  303. }
  304. c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  305. if c != 10 || err != nil {
  306. t.Error("Unexpected counting result:", c, err)
  307. return
  308. }
  309. // All slots from page 1 were removed during the first request for 1000 slots. The page was
  310. // subsequently deallocated. Page 2 was partly cleared. Adding again 1000 slots filled up page 2 again
  311. // and allocated page 1 again this time however at the end of the list. In the following loop we empty
  312. // all pages.
  313. // First we empty page 2 containing partly the new pattern and partly the old one
  314. // We then empty all pages with the old pattern
  315. // Finally we empty the remaining page with the new pattern
  316. for i := 1; i < 1001; i++ {
  317. if res, err := flsm.Get(); res != util.PackLocation(uint64(i+666), uint16(i%1000)) || err != nil {
  318. t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
  319. return
  320. }
  321. if i == 491 {
  322. for j := 1001; j < 5001; j++ {
  323. if res, err := flsm.Get(); res != util.PackLocation(uint64(j), uint16(j%1000)) || err != nil {
  324. t.Error("*Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), j, err)
  325. return
  326. }
  327. }
  328. }
  329. }
  330. // Check that all empty slots have been retrieved and nothing is left on the free pages
  331. if res, err := flsm.Get(); res != 0 || err != nil {
  332. t.Error("Unexpected final Get call result", res, err)
  333. return
  334. }
  335. if err := psf.Close(); err != nil {
  336. t.Error(err)
  337. return
  338. }
  339. if err := shadow.Close(); err != nil {
  340. t.Error(err)
  341. return
  342. }
  343. }