freelogicalslotmanager_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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. if err = flsm.Flush(); err != file.ErrAlreadyInUse {
  99. t.Error("Unexpected Get result:", err)
  100. return
  101. }
  102. // Can get something without error from the unflushed slot list
  103. loc, err = flsm.Get()
  104. if err != nil {
  105. t.Error(err)
  106. return
  107. }
  108. checkLocation(t, loc, 4, 21)
  109. err = sf.ReleaseInUseID(rec.ID(), false)
  110. if err != nil {
  111. t.Error(err)
  112. return
  113. }
  114. loc, err = flsm.Get()
  115. if err != nil {
  116. t.Error(err)
  117. return
  118. }
  119. checkLocation(t, loc, 6, 23)
  120. // Test multiple insert
  121. flsm.Add(util.PackLocation(9, 1))
  122. if err = flsm.Flush(); err != nil {
  123. t.Error(err)
  124. return
  125. }
  126. flsm.Add(util.PackLocation(9, 2))
  127. flsm.Add(util.PackLocation(9, 3))
  128. flsm.Add(util.PackLocation(9, 4))
  129. if err = flsm.Flush(); err != nil {
  130. t.Error(err)
  131. return
  132. }
  133. // Test get error when record in use and slot list is empty
  134. rec, err = sf.Get(1)
  135. if err != nil {
  136. t.Error(err)
  137. return
  138. }
  139. loc, err = flsm.Get()
  140. if err != file.ErrAlreadyInUse {
  141. t.Error(err)
  142. return
  143. }
  144. err = sf.ReleaseInUseID(rec.ID(), false)
  145. if err != nil {
  146. t.Error(err)
  147. return
  148. }
  149. loc, err = flsm.Get()
  150. if err != nil {
  151. t.Error(err)
  152. return
  153. }
  154. checkLocation(t, loc, 9, 1)
  155. loc, err = flsm.Get()
  156. if err != nil {
  157. t.Error(err)
  158. return
  159. }
  160. checkLocation(t, loc, 9, 2)
  161. loc, err = flsm.Get()
  162. if err != nil {
  163. t.Error(err)
  164. return
  165. }
  166. checkLocation(t, loc, 9, 3)
  167. loc, err = flsm.Get()
  168. if err != nil {
  169. t.Error(err)
  170. return
  171. }
  172. checkLocation(t, loc, 9, 4)
  173. if fsc := flsp.FreeSlotCount(); fsc != 0 {
  174. t.Error("Unexpected number of stored free slots", fsc)
  175. }
  176. // Test special case when a page is in the pager list but has no slots allocated
  177. for i := 1; i < 1001; i++ {
  178. flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
  179. }
  180. if err = flsm.Flush(); err != nil {
  181. t.Error(err)
  182. return
  183. }
  184. // Manually free all slots on the first page
  185. flspRec, err = sf.Get(1)
  186. if err != nil {
  187. t.Error(err)
  188. }
  189. flsp = pageview.NewFreeLogicalSlotPage(flspRec)
  190. var j uint16
  191. for j = 0; j < flsp.MaxSlots(); j++ {
  192. flsp.ReleaseSlotInfo(j)
  193. }
  194. sf.ReleaseInUse(flspRec)
  195. // Check we get slotlocation 0 from second page
  196. loc, err = flsm.Get()
  197. if err != nil {
  198. t.Error(err)
  199. return
  200. }
  201. checkLocation(t, loc, 510, 510)
  202. if err := psf.Close(); err != nil {
  203. t.Error(err)
  204. return
  205. }
  206. }
  207. func testAddPanic(t *testing.T, flsm *FreeLogicalSlotManager) {
  208. defer func() {
  209. if r := recover(); r == nil {
  210. t.Error("Adding location 0 did not cause a panic.")
  211. }
  212. }()
  213. flsm.Add(0)
  214. }
  215. func TestFreeLogiclaSlotManagerScale(t *testing.T) {
  216. sf, err := file.NewDefaultStorageFile(DBDIR+"/test7", false)
  217. if err != nil {
  218. t.Error(err.Error())
  219. return
  220. }
  221. shadow, err := file.NewDefaultStorageFile(DBDIR+"/test7_", false)
  222. if err != nil {
  223. t.Error(err.Error())
  224. return
  225. }
  226. psf, err := paging.NewPagedStorageFile(sf)
  227. if err != nil {
  228. t.Error(err)
  229. return
  230. }
  231. flsm := NewFreeLogicalSlotManager(psf)
  232. // Add a lot of locations
  233. for i := 1; i < 5001; i++ {
  234. flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
  235. }
  236. // Check Flush and low level doFlush if a page can't be accessed
  237. if _, err := sf.Get(1); err != nil {
  238. t.Error(err)
  239. return
  240. }
  241. if err := flsm.Flush(); err != file.ErrAlreadyInUse {
  242. t.Error("Unexpected flush result:", err)
  243. return
  244. }
  245. if i, err := flsm.doFlush(1, 0); i != 0 || err != file.ErrAlreadyInUse {
  246. t.Error("Unexpected doFlush result:", i, err)
  247. return
  248. }
  249. if err := sf.ReleaseInUseID(1, false); err != nil {
  250. t.Error(err)
  251. return
  252. }
  253. // Check the doFlush error return in Flush when allocating new pages
  254. flsm.storagefile = shadow
  255. if _, err := shadow.Get(1); err != nil {
  256. t.Error(err)
  257. return
  258. }
  259. if err := flsm.Flush(); err != file.ErrAlreadyInUse {
  260. t.Error("Unexpected flush result:", err)
  261. return
  262. }
  263. if err := shadow.ReleaseInUseID(1, false); err != nil {
  264. t.Error(err)
  265. return
  266. }
  267. flsm.storagefile = sf
  268. // Now do the real flush
  269. if err := flsm.Flush(); err != nil {
  270. t.Error(err)
  271. return
  272. }
  273. // Count the allocated pages
  274. c, err := paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  275. if c != 10 || err != nil {
  276. t.Error("Unexpected counting result:", c, err)
  277. return
  278. }
  279. // Remove some free slots from the list
  280. for i := 1; i < 1001; i++ {
  281. if res, err := flsm.Get(); res != util.PackLocation(uint64(i), uint16(i%1000)) || err != nil {
  282. t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
  283. return
  284. }
  285. }
  286. // Count the allocated pages (one page should be free now)
  287. c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  288. if c != 9 || err != nil {
  289. t.Error("Unexpected counting result:", c, err)
  290. return
  291. }
  292. // Now add new free slots with a different pattern
  293. for i := 1; i < 1001; i++ {
  294. flsm.Add(util.PackLocation(uint64(i+666), uint16(i%1000)))
  295. }
  296. if err := flsm.Flush(); err != nil {
  297. t.Error(err)
  298. return
  299. }
  300. c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
  301. if c != 10 || err != nil {
  302. t.Error("Unexpected counting result:", c, err)
  303. return
  304. }
  305. // All slots from page 1 were removed during the first request for 1000 slots. The page was
  306. // subsequently deallocated. Page 2 was partly cleared. Adding again 1000 slots filled up page 2 again
  307. // and allocated page 1 again this time however at the end of the list. In the following loop we empty
  308. // all pages.
  309. // First we empty page 2 containing partly the new pattern and partly the old one
  310. // We then empty all pages with the old pattern
  311. // Finally we empty the remaining page with the new pattern
  312. for i := 1; i < 1001; i++ {
  313. if res, err := flsm.Get(); res != util.PackLocation(uint64(i+666), uint16(i%1000)) || err != nil {
  314. t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
  315. return
  316. }
  317. if i == 491 {
  318. for j := 1001; j < 5001; j++ {
  319. if res, err := flsm.Get(); res != util.PackLocation(uint64(j), uint16(j%1000)) || err != nil {
  320. t.Error("*Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), j, err)
  321. return
  322. }
  323. }
  324. }
  325. }
  326. // Check that all empty slots have been retrieved and nothing is left on the free pages
  327. if res, err := flsm.Get(); res != 0 || err != nil {
  328. t.Error("Unexpected final Get call result", res, err)
  329. return
  330. }
  331. if err := psf.Close(); err != nil {
  332. t.Error(err)
  333. return
  334. }
  335. if err := shadow.Close(); err != nil {
  336. t.Error(err)
  337. return
  338. }
  339. }