123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package slotting
- import (
- "testing"
- "devt.de/krotik/eliasdb/storage/file"
- "devt.de/krotik/eliasdb/storage/paging"
- "devt.de/krotik/eliasdb/storage/paging/view"
- "devt.de/krotik/eliasdb/storage/slotting/pageview"
- "devt.de/krotik/eliasdb/storage/util"
- )
- func TestFreeLogicalSlotManager(t *testing.T) {
- sf, err := file.NewDefaultStorageFile(DBDIR+"/test6", false)
- if err != nil {
- t.Error(err.Error())
- return
- }
- psf, err := paging.NewPagedStorageFile(sf)
- if err != nil {
- t.Error(err)
- return
- }
- flsm := NewFreeLogicalSlotManager(psf)
- testAddPanic(t, flsm)
- // Add some locations
- flsm.Add(util.PackLocation(5, 22))
- flsm.Add(util.PackLocation(6, 23))
- out := flsm.String()
- if out != "FreeLogicalSlotManager: buckettest/test6\n"+
- "Ids :[327702 393239]\n" {
- t.Error("Unexpected output of FreeLogicalSlotManager:", out)
- }
- if err = flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- if len(flsm.slots) != 0 {
- t.Error("Nothing should be left in the slot cache after a flush")
- return
- }
- // Check pages are allocated
- cursor := paging.NewPageCursor(flsm.pager, view.TypeFreeLogicalSlotPage, 0)
- if page, err := cursor.Next(); page != 1 || err != nil {
- t.Error("Unexpected free logical slot page:", page, err)
- return
- }
- if page, err := cursor.Next(); page != 0 || err != nil {
- t.Error("Unexpected free logical slot page:", page, err)
- return
- }
- page := flsm.pager.First(view.TypeFreeLogicalSlotPage)
- if page != 1 {
- t.Error("Unexpected first free logical slot page")
- return
- }
- flspRec, err := sf.Get(1)
- if err != nil {
- t.Error(err)
- }
- flsp := pageview.NewFreeLogicalSlotPage(flspRec)
- if fsc := flsp.FreeSlotCount(); fsc != 2 {
- t.Error("Unexpected number of stored free slots", fsc)
- }
- // Check that both slotinfos have been written
- if flsp.SlotInfoLocation(0) != util.PackLocation(5, 22) {
- t.Error("Unexpected free slot info")
- return
- }
- if flsp.SlotInfoLocation(1) != util.PackLocation(6, 23) {
- t.Error("Unexpected free slot info")
- return
- }
- sf.ReleaseInUse(flspRec)
- // Check that we can find them
- loc, err := flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 5, 22)
- if fsc := flsp.FreeSlotCount(); fsc != 1 {
- t.Error("Unexpected number of stored free slots", fsc)
- }
- // Test error handling in Flush
- flsm.Add(util.PackLocation(4, 21))
- rec, err := sf.Get(1)
- if err != nil {
- t.Error(err)
- return
- }
- if err = flsm.Flush(); err != file.ErrAlreadyInUse {
- t.Error("Unexpected Get result:", err)
- return
- }
- // Can get something without error from the unflushed slot list
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 4, 21)
- err = sf.ReleaseInUseID(rec.ID(), false)
- if err != nil {
- t.Error(err)
- return
- }
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 6, 23)
- // Test multiple insert
- flsm.Add(util.PackLocation(9, 1))
- if err = flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- flsm.Add(util.PackLocation(9, 2))
- flsm.Add(util.PackLocation(9, 3))
- flsm.Add(util.PackLocation(9, 4))
- if err = flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- // Test get error when record in use and slot list is empty
- rec, err = sf.Get(1)
- if err != nil {
- t.Error(err)
- return
- }
- loc, err = flsm.Get()
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- err = sf.ReleaseInUseID(rec.ID(), false)
- if err != nil {
- t.Error(err)
- return
- }
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 9, 1)
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 9, 2)
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 9, 3)
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 9, 4)
- if fsc := flsp.FreeSlotCount(); fsc != 0 {
- t.Error("Unexpected number of stored free slots", fsc)
- }
- // Test special case when a page is in the pager list but has no slots allocated
- for i := 1; i < 1001; i++ {
- flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
- }
- if err = flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- // Manually free all slots on the first page
- flspRec, err = sf.Get(1)
- if err != nil {
- t.Error(err)
- }
- flsp = pageview.NewFreeLogicalSlotPage(flspRec)
- var j uint16
- for j = 0; j < flsp.MaxSlots(); j++ {
- flsp.ReleaseSlotInfo(j)
- }
- sf.ReleaseInUse(flspRec)
- // Check we get slotlocation 0 from second page
- loc, err = flsm.Get()
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 510, 510)
- if err := psf.Close(); err != nil {
- t.Error(err)
- return
- }
- }
- func testAddPanic(t *testing.T, flsm *FreeLogicalSlotManager) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Adding location 0 did not cause a panic.")
- }
- }()
- flsm.Add(0)
- }
- func TestFreeLogiclaSlotManagerScale(t *testing.T) {
- sf, err := file.NewDefaultStorageFile(DBDIR+"/test7", false)
- if err != nil {
- t.Error(err.Error())
- return
- }
- shadow, err := file.NewDefaultStorageFile(DBDIR+"/test7_", false)
- if err != nil {
- t.Error(err.Error())
- return
- }
- psf, err := paging.NewPagedStorageFile(sf)
- if err != nil {
- t.Error(err)
- return
- }
- flsm := NewFreeLogicalSlotManager(psf)
- // Add a lot of locations
- for i := 1; i < 5001; i++ {
- flsm.Add(util.PackLocation(uint64(i), uint16(i%1000)))
- }
- // Check Flush and low level doFlush if a page can't be accessed
- if _, err := sf.Get(1); err != nil {
- t.Error(err)
- return
- }
- if err := flsm.Flush(); err != file.ErrAlreadyInUse {
- t.Error("Unexpected flush result:", err)
- return
- }
- if i, err := flsm.doFlush(1, 0); i != 0 || err != file.ErrAlreadyInUse {
- t.Error("Unexpected doFlush result:", i, err)
- return
- }
- if err := sf.ReleaseInUseID(1, false); err != nil {
- t.Error(err)
- return
- }
- // Check the doFlush error return in Flush when allocating new pages
- flsm.storagefile = shadow
- if _, err := shadow.Get(1); err != nil {
- t.Error(err)
- return
- }
- if err := flsm.Flush(); err != file.ErrAlreadyInUse {
- t.Error("Unexpected flush result:", err)
- return
- }
- if err := shadow.ReleaseInUseID(1, false); err != nil {
- t.Error(err)
- return
- }
- flsm.storagefile = sf
- // Now do the real flush
- if err := flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- // Count the allocated pages
- c, err := paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
- if c != 10 || err != nil {
- t.Error("Unexpected counting result:", c, err)
- return
- }
- // Remove some free slots from the list
- for i := 1; i < 1001; i++ {
- if res, err := flsm.Get(); res != util.PackLocation(uint64(i), uint16(i%1000)) || err != nil {
- t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
- return
- }
- }
- // Count the allocated pages (one page should be free now)
- c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
- if c != 9 || err != nil {
- t.Error("Unexpected counting result:", c, err)
- return
- }
- // Now add new free slots with a different pattern
- for i := 1; i < 1001; i++ {
- flsm.Add(util.PackLocation(uint64(i+666), uint16(i%1000)))
- }
- if err := flsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- c, err = paging.CountPages(flsm.pager, view.TypeFreeLogicalSlotPage)
- if c != 10 || err != nil {
- t.Error("Unexpected counting result:", c, err)
- return
- }
- // All slots from page 1 were removed during the first request for 1000 slots. The page was
- // subsequently deallocated. Page 2 was partly cleared. Adding again 1000 slots filled up page 2 again
- // and allocated page 1 again this time however at the end of the list. In the following loop we empty
- // all pages.
- // First we empty page 2 containing partly the new pattern and partly the old one
- // We then empty all pages with the old pattern
- // Finally we empty the remaining page with the new pattern
- for i := 1; i < 1001; i++ {
- if res, err := flsm.Get(); res != util.PackLocation(uint64(i+666), uint16(i%1000)) || err != nil {
- t.Error("Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), i, err)
- return
- }
- if i == 491 {
- for j := 1001; j < 5001; j++ {
- if res, err := flsm.Get(); res != util.PackLocation(uint64(j), uint16(j%1000)) || err != nil {
- t.Error("*Unexpected Get result", util.LocationRecord(res), util.LocationOffset(res), j, err)
- return
- }
- }
- }
- }
- // Check that all empty slots have been retrieved and nothing is left on the free pages
- if res, err := flsm.Get(); res != 0 || err != nil {
- t.Error("Unexpected final Get call result", res, err)
- return
- }
- if err := psf.Close(); err != nil {
- t.Error(err)
- return
- }
- if err := shadow.Close(); err != nil {
- t.Error(err)
- return
- }
- }
|