/* * 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 ( "flag" "fmt" "os" "testing" "devt.de/krotik/common/fileutil" "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" ) const DBDIR = "buckettest" // Main function for all tests in this package func TestMain(m *testing.M) { flag.Parse() // Setup if res, _ := fileutil.PathExists(DBDIR); res { os.RemoveAll(DBDIR) } err := os.Mkdir(DBDIR, 0770) if err != nil { fmt.Print("Could not create test directory:", err.Error()) os.Exit(1) } // Run the tests res := m.Run() // Teardown err = os.RemoveAll(DBDIR) if err != nil { fmt.Print("Could not remove test directory:", err.Error()) } os.Exit(res) } func TestFreePhysicalSlotManager(t *testing.T) { sf, err := file.NewDefaultStorageFile(DBDIR+"/test1", false) if err != nil { t.Error(err.Error()) return } psf, err := paging.NewPagedStorageFile(sf) if err != nil { t.Error(err) return } fpsm := NewFreePhysicalSlotManager(psf, false) // Add some locations fpsm.Add(util.PackLocation(5, 22), 30) fpsm.Add(util.PackLocation(6, 23), 35) out := fpsm.String() if out != "FreePhysicalSlotManager: buckettest/test1 (onlyAppend:false lastMaxSlotSize:0)\n"+ "Ids :[327702 393239]\n"+ "Sizes:[30 35]" { t.Error("Unexpected output of FreePhysicalSlotManager:", out) } if err = fpsm.Flush(); err != nil { t.Error(nil) return } if len(fpsm.slots) != 0 || len(fpsm.sizes) != 0 { t.Error("Nothing should be left in the slot cache after a flush") return } // Check pages are allocated cursor := paging.NewPageCursor(fpsm.pager, view.TypeFreePhysicalSlotPage, 0) if page, err := cursor.Next(); page != 1 || err != nil { t.Error("Unexpected free physical slot page:", page, err) return } if page, err := cursor.Next(); page != 0 || err != nil { t.Error("Unexpected free physical slot page:", page, err) return } page := fpsm.pager.First(view.TypeFreePhysicalSlotPage) if page != 1 { t.Error("Unexpected first free physical slot page") return } fpspRec, err := sf.Get(1) if err != nil { t.Error(err) } fpsp := pageview.NewFreePhysicalSlotPage(fpspRec) if fsc := fpsp.FreeSlotCount(); fsc != 2 { t.Error("Unexpected number of stored free slots", fsc) } // Check that both slotinfos have been written if fpsp.SlotInfoLocation(0) != util.PackLocation(5, 22) { t.Error("Unexpected free slot info") return } if fpsp.SlotInfoLocation(1) != util.PackLocation(6, 23) { t.Error("Unexpected free slot info") return } sf.ReleaseInUse(fpspRec) // Check that we can find them loc, err := fpsm.Get(31) if err != nil { t.Error(err) return } if util.LocationRecord(loc) != 6 || util.LocationOffset(loc) != 23 { t.Error("Unexpected location was found", util.LocationRecord(loc), util.LocationOffset(loc)) return } if fsc := fpsp.FreeSlotCount(); fsc != 1 { t.Error("Unexpected number of stored free slots", fsc) } // Test only append flag fpsm.onlyAppend = true loc, err = fpsm.Get(29) if err != nil || loc != 0 { t.Error("Unexpected onlyAppend result:", loc, err) } fpsm.onlyAppend = false loc, err = fpsm.Get(29) if err != nil { t.Error(err) return } if util.LocationRecord(loc) != 5 || util.LocationOffset(loc) != 22 { t.Error("Unexpected location was found", util.LocationRecord(loc), util.LocationOffset(loc)) return } if fsc := fpsp.FreeSlotCount(); fsc != 0 { t.Error("Unexpected number of stored free slots", fsc) } if err := psf.Close(); err != nil { t.Error(err) return } } func TestFreePhysicalSlotManagerScale(t *testing.T) { sf, err := file.NewDefaultStorageFile(DBDIR+"/test2", false) if err != nil { t.Error(err.Error()) return } shadow, err := file.NewDefaultStorageFile(DBDIR+"/test2_", false) if err != nil { t.Error(err.Error()) return } psf, err := paging.NewPagedStorageFile(sf) if err != nil { t.Error(err) return } fpsm := NewFreePhysicalSlotManager(psf, false) // Add a lot of locations for i := 0; i < 5000; i++ { fpsm.Add(util.PackLocation(uint64(i), uint16(i%1000)), uint32(i%500)) } // Check Flush and low level doFlush if a page can't be accessed if _, err := sf.Get(1); err != nil { t.Error(err) return } err = fpsm.Flush() if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse { t.Error("Unexpected flush result:", err) return } i, err := fpsm.doFlush(1, 0) if sfe, ok := err.(*file.StorageFileError); i != 0 || !ok || sfe.Type != 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 fpsm.storagefile = shadow if _, err := shadow.Get(1); err != nil { t.Error(err) return } err = fpsm.Flush() if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse { t.Error("Unexpected flush result:", err) return } if err := shadow.ReleaseInUseID(1, false); err != nil { t.Error(err) return } fpsm.storagefile = sf // Now do the real flush if err := fpsm.Flush(); err != nil { t.Error(err) return } // Count the allocated pages c, err := paging.CountPages(fpsm.pager, view.TypeFreePhysicalSlotPage) if c != 15 || err != nil { t.Error("Unexpected counting result:", c, err) return } // Check lastMaxSlotSize works if loc, err := fpsm.Get(600); loc != 0 || err != nil { t.Error("Unexpected Get result:", loc, err) return } if fpsm.lastMaxSlotSize != 499 { t.Error("Unexpected lastMaxSlotSize:", fpsm.lastMaxSlotSize) return } // Any subsequent call should fail more quickly if loc, err := fpsm.Get(600); loc != 0 || err != nil { t.Error("Unexpected Get result:", loc, err) return } // Free slot for size 499 should be on page 2 if _, err := sf.Get(2); err != nil { t.Error(err) return } loc, err := fpsm.Get(499) if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse { t.Error("Unexpected Get result:", loc, err) return } if err := sf.ReleaseInUseID(2, false); err != nil { t.Error(err) return } loc, err = fpsm.Get(499) if err != nil || loc != 32702963 { t.Error("Unexpected Get result:", err, loc) return } if fpsm.lastMaxSlotSize != 0 { t.Error("Unexpected lastMaxSlotSize:", fpsm.lastMaxSlotSize) return } // Next free slot for size 499 should be on page 3 if _, err := sf.Get(3); err != nil { t.Error(err) return } loc, err = fpsm.Get(499) if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse { t.Error("Unexpected Get result:", loc, err) return } if err := sf.ReleaseInUseID(3, false); err != nil { t.Error(err) return } // There was an error check that the lastMaxSlotSize was reset if fpsm.lastMaxSlotSize != 0 { t.Error("Unexpected lastMaxSlotSize:", fpsm.lastMaxSlotSize) return } loc, err = fpsm.Get(499) if err != nil || loc != 65471463 { t.Error("Unexpected Get result:", err, loc) return } if fpsm.lastMaxSlotSize != 0 { t.Error("Unexpected lastMaxSlotSize:", fpsm.lastMaxSlotSize) return } fpsm.Add(65471666, 699) if _, err := sf.Get(2); err != nil { t.Error(err) return } err = fpsm.Flush() if sfe, ok := err.(*file.StorageFileError); !ok || sfe.Type != file.ErrAlreadyInUse { t.Error("Unexpected Flush result:", err) return } if err := sf.ReleaseInUseID(2, false); err != nil { t.Error(err) return } if err := fpsm.Flush(); err != nil { t.Error(err) return } loc, err = fpsm.Get(698) if err != nil || loc != 65471666 { t.Error("Unexpected Get result:", err, loc) return } if err := psf.Close(); err != nil { t.Error(err) return } if err := shadow.Close(); err != nil { t.Error(err) return } }