| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 | /* * 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 slottingimport (	"bufio"	"bytes"	"fmt"	"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 TestPhysicalSlotManager(t *testing.T) {	sf, err := file.NewDefaultStorageFile(DBDIR+"/test5_data", false)	if err != nil {		t.Error(err.Error())		return	}	psf, err := paging.NewPagedStorageFile(sf)	if err != nil {		t.Error(err)		return	}	fsf, err := file.NewDefaultStorageFile(DBDIR+"/test5_free", false)	if err != nil {		t.Error(err.Error())		return	}	fpsf, err := paging.NewPagedStorageFile(fsf)	if err != nil {		t.Error(err)		return	}	psm := NewPhysicalSlotManager(psf, fpsf, false)	// Build up a data array	arr := make([]byte, 9000)	for i := 0; i < 9000; i++ {		arr[i] = byte(i%5) + 1	}	arr2 := make([]byte, 9000)	for i := 0; i < 9000; i++ {		arr2[i] = byte(i%5) + 5	}	loc, err := psm.Insert(arr, 1, 8999)	if err != nil {		t.Error(err)		return	}	// Location should be beginning of the first record	//checkLocation(t, loc, 1, 20)	// Read back the written data	var b bytes.Buffer	buf := bufio.NewWriter(&b)	if err := psm.Fetch(loc, buf); err != nil {		t.Error("Unexpected read result:", err)		return	}	buf.Flush()	str1 := fmt.Sprint(b.Bytes())	str2 := fmt.Sprint(arr[1:])	if str1 != str2 {		t.Error("Unexpected result reading back what was written")		return	}	loc, err = psm.Update(loc, arr2, 0, 9000)	if err != nil {		t.Error(err)		return	}	// Location should have changed now	checkLocation(t, loc, 3, 871)	// Make sure the new free slots are known	psm.Flush()	// Insert new data - the manager should reuse the previous location	loc, err = psm.Insert(arr2, 1, 8999)	if err != nil {		t.Error(err)		return	}	checkLocation(t, loc, 1, 20)	if err := psm.Free(loc); err != nil {		t.Error(err)		return	}	if err := psm.Flush(); err != nil {		t.Error(err)		return	}	// Test error cases	testInsertPanic(t, psm)	record, err := fsf.Get(1)	if err != nil {		t.Error(err)		return	}	_, err = psm.Insert(make([]byte, 1), 0, 1)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected insert result:", err)		return	}	fsf.ReleaseInUse(record)	if err := psm.Free(util.PackLocation(0, 20)); err != file.ErrAlreadyInUse {		t.Error("Unexpected free result:", err)		return	}	record, err = sf.Get(5)	if err != nil {		t.Error(err)		return	}	_, err = psm.allocate(10)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected allocate result:", err)		return	}	sf.ReleaseInUse(record)	record, err = sf.Get(1)	if err != nil {		t.Error(err)		return	}	// This slot shot be free on page 1	_, err = psm.Insert(arr2, 1, 8999)	// The insert should have failed. The allocated space	// for it should have been send back to the free manager	if err != file.ErrAlreadyInUse {		t.Error("Unexpected insert result:", err)		return	}	sf.ReleaseInUse(record)	// This should write the recovered free location	// back to the free manager	psm.Flush()	record, err = sf.Get(2)	if err != nil {		t.Error(err)		return	}	_, err = psm.Insert(arr2, 1, 8999)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected update result:", err)		return	}	sf.ReleaseInUse(record)	checkLocation(t, loc, 1, 20)	// Write the free data which has been declared during the	// last failed call to disk	psm.Flush()	loc, err = psm.Insert(arr2, 1, 8999)	if err != nil {		t.Error("Unexpected insert result:", err)		return	}	checkLocation(t, loc, 1, 20)	record, err = sf.Get(1)	if err != nil {		t.Error(err)		return	}	_, err = psm.Update(loc, arr2, 1, 8999)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected update result:", err)		return	}	sf.ReleaseInUse(record)	record, err = sf.Get(2)	if err != nil {		t.Error(err)		return	}	_, err = psm.Update(loc, arr2, 1, 8999)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected update result:", err)		return	}	sf.ReleaseInUse(record)	record, err = sf.Get(5)	if err != nil {		t.Error(err)		return	}	_, err = psm.Update(loc, arr2, 0, 9000)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected update result:", err)		return	}	sf.ReleaseInUse(record)	record, err = sf.Get(5)	if err != nil {		t.Error(err)		return	}	_, err = psm.Update(loc, arr2, 0, 9000)	if err != file.ErrAlreadyInUse {		t.Error("Unexpected update result:", err)		return	}	sf.ReleaseInUse(record)	if err := psf.Close(); err != nil {		t.Error(err)		return	}	if err := fpsf.Close(); err != nil {		t.Error(err)		return	}}func testInsertPanic(t *testing.T, psm *PhysicalSlotManager) {	defer func() {		if r := recover(); r == nil {			t.Error("Inserting 0 bytes did not cause a panic.")		}	}()	psm.Insert(make([]byte, 0), 0, 0)}func TestPhysicalSlotManagerReadWrite(t *testing.T) {	sf, err := file.NewDefaultStorageFile(DBDIR+"/test4_data", false)	if err != nil {		t.Error(err.Error())		return	}	psf, err := paging.NewPagedStorageFile(sf)	if err != nil {		t.Error(err)		return	}	fsf, err := file.NewDefaultStorageFile(DBDIR+"/test4_free", false)	if err != nil {		t.Error(err.Error())		return	}	fpsf, err := paging.NewPagedStorageFile(fsf)	if err != nil {		t.Error(err)		return	}	psm := NewPhysicalSlotManager(psf, fpsf, false)	// Allocate some space	loc1, err := psm.allocateNew(10000, 0)	if err != nil {		t.Error(err)		return	}	// Expected offset is the beginning of page 1	checkLocation(t, loc1, 1, 20)	// Allocate some more space	loc2, err := psm.allocateNew(10, 3)	if err != nil {		t.Error(err)		return	}	// Expected offset is on page 3	checkLocation(t, loc2, 3, 1872)	// Build up a data array	arr := make([]byte, 9000)	for i := 0; i < 9000; i++ {		arr[i] = byte(i%5) + 1	}	// Now write the data array in the allocated space	if err := psm.write(loc1, arr, 1, 8999); err != nil {		t.Error("Unexpected write result:", err)		return	}	// Now check the actual written data	record, err := sf.Get(1)	if err != nil {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Slot size header should be 10000 available and 8999 current	if asize := util.AvailableSize(record, 20); asize != 10000 {		t.Error("Unexpected available size:", asize)		return	}	if csize := util.CurrentSize(record, 20); csize != 8999 {		t.Error("Unexpected current size:", csize)		return	}	// Check the beginning of the written data	if wdata := record.ReadUInt16(24); wdata != 0x0203 {		t.Error("Unexpected beginning of written data:", wdata)		return	}	record, err = sf.Get(2)	if err != nil {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Check that the second page is a full data page	pv := pageview.NewDataPage(record)	if of := pv.OffsetFirst(); of != 0 {		t.Error("Unexpected first offset:", of)		return	}	if record.ReadSingleByte(20) != 0x04 || record.ReadSingleByte(4095) != 0x04 {		t.Error("Unexpected record data:", record)		return	}	record, err = sf.Get(3)	if err != nil {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Check that the last page is partially written	// Offset should be the location of the second allocated data block	pv = pageview.NewDataPage(record)	if of := pv.OffsetFirst(); of != 1872 {		t.Error("Unexpected first offset:", of)		return	}	// Data should end with 5 on the following location	// 8999 data written + 4 byte header = 9003 bytes written	// 9003 - 4076 page1 - 4076 page2 = 851 bytes for the last page	// 20 bytes header + 851 written bytes = 871 bytes (offset 870)	if lastByte := record.ReadSingleByte(870); lastByte != 5 {		t.Error("Unexpected last byte:", lastByte)		return	}	if lastByteAfter := record.ReadSingleByte(871); lastByteAfter != 0 {		t.Error("Unexpected byte after last byte:", lastByteAfter)		return	}	// Read back the written data	var b bytes.Buffer	buf := bufio.NewWriter(&b)	if err := psm.Fetch(loc1, buf); err != nil {		t.Error("Unexpected read result:", err)		return	}	buf.Flush()	str1 := fmt.Sprint(b.Bytes())	str2 := fmt.Sprint(arr[1:])	if str1 != str2 {		t.Error("Unexpected result reading back what was written")		return	}	// Test some special cases	record, err = sf.Get(util.LocationRecord(loc2))	if err != nil {		t.Error(err)		return	}	if err := psm.write(loc2, make([]byte, 0), 0, 0); err != file.ErrAlreadyInUse {		t.Error("Unexpected write result:", err)	}	if err := psm.Fetch(loc2, buf); err != file.ErrAlreadyInUse {		t.Error("Unexpected read result:", err)		return	}	sf.ReleaseInUse(record)	if err := psm.write(loc2, make([]byte, 0), 0, 0); err != nil {		t.Error("Unexpected write result:", err)	}	var b2 bytes.Buffer	buf = bufio.NewWriter(&b2)	if err := psm.Fetch(loc2, buf); err != nil {		t.Error("Unexpected read result:", err)		return	}	buf.Flush()	if len(b2.Bytes()) != 0 {		t.Error("Nothing should have been read back")		return	}	if asize := util.AvailableSize(record, int(util.LocationOffset(loc2))); asize != 10 {		t.Error("Unexpected available size:", asize)		return	}	if csize := util.CurrentSize(record, int(util.LocationOffset(loc2))); csize != 0 {		t.Error("Unexpected current size:", csize)		return	}	record, err = sf.Get(3)	if err != nil {		t.Error(err)		return	}	if err := psm.write(loc2, make([]byte, 10000), 0, 9999); err != file.ErrAlreadyInUse {		t.Error("Unexpected write result:", err)	}	if err := psm.Fetch(loc2, buf); err != file.ErrAlreadyInUse {		t.Error("Unexpected read result:", err)		return	}	sf.ReleaseInUse(record)	loc3, err := psm.allocateNew(10000, 3)	if err != nil {		t.Error(err)		return	}	record, err = sf.Get(5)	if err != nil {		t.Error(err)		return	}	if err := psm.write(loc3, make([]byte, 10000), 0, 9999); err != file.ErrAlreadyInUse {		t.Error("Unexpected write result:", err)	}	if err := psm.Fetch(loc3, buf); err != file.ErrAlreadyInUse {		t.Error("Unexpected read result:", err)		return	}	sf.ReleaseInUse(record)	if err := psf.Close(); err != nil {		t.Error(err)		return	}	if err := fpsf.Close(); err != nil {		t.Error(err)		return	}}func TestPhysicalSlotManagerAllocateNew(t *testing.T) {	sf, err := file.NewDefaultStorageFile(DBDIR+"/test3_data", false)	if err != nil {		t.Error(err.Error())		return	}	psf, err := paging.NewPagedStorageFile(sf)	if err != nil {		t.Error(err)		return	}	fsf, err := file.NewDefaultStorageFile(DBDIR+"/test3_free", false)	if err != nil {		t.Error(err.Error())		return	}	fpsf, err := paging.NewPagedStorageFile(fsf)	if err != nil {		t.Error(err)		return	}	psm := NewPhysicalSlotManager(psf, fpsf, false)	// Check the simple case	size := util.NormalizeSlotSize(500)	// Error case new allocated page is already in use	record, err := sf.Get(1)	if err != nil {		t.Error(err)		return	}	_, err = psm.allocateNew(size, 0)	if err != file.ErrAlreadyInUse {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Test first allocation	loc, err := psm.allocateNew(size, 0)	if err != nil {		t.Error(err)		return	}	checkLocation(t, loc, 1, pageview.OffsetData)	// Error case existing page is already in use	record, err = sf.Get(1)	if err != nil {		t.Error(err)		return	}	_, err = psm.allocateNew(10, 1)	if err != file.ErrAlreadyInUse {		t.Error(err)		return	}	sf.ReleaseInUse(record)	loc, err = psm.allocateNew(10, 1)	if err != nil {		t.Error(err)		return	}	// Expected offset is 524	// Page header (20) + prev. allocated data (500) + SizeInfoSize header (4)	exploc := pageview.OffsetData + 500 + util.SizeInfoSize	if exploc != 524 {		t.Error("Expected location should be 532 but is:", exploc)		return	}	checkLocation(t, loc, 1, uint16(exploc))	loc, err = psm.allocateNew(7000, 1)	if err != nil {		t.Error(err)		return	}	// Expected offset is 538	// Last offset (524) + prev. allocated data (10) + SizeInfoSize header (4)	checkLocation(t, loc, 1, 538)	// Last page is now page 2	loc, err = psm.allocateNew(10, 2)	if err != nil {		t.Error(err)		return	}	// Expected offset is 3466 (+ 1 page)	// Last offset (538) + prev. allocated data (7000) + SizeInfoSize header (4)	// Default size for one record is 4096 - 20 bytes header = 4076	// 7542 - 4076 = 3466	checkLocation(t, loc, 2, 3466)	loc, err = psm.allocateNew(10000, 2)	if err != nil {		t.Error(err)		return	}	// Expected offset is 3480	// Last offset (3466) + prev. allocated data (10) + SizeInfoSize header (4)	checkLocation(t, loc, 2, 3480)	// Last page is now page 5 - This allocation should fill up page 5 exacly	// - allocation should be rounded up by 6	loc, err = psm.allocateNew(2830, 5)	if err != nil {		t.Error(err)		return	}	// Expected offset is 1256 (+ 3 pages)	// Last offset (3480) + prev. allocated data (10000) + SizeInfoSize header (4)	// Default size for one record is 4096 - 20 bytes header = 4076	// 13484 - 4076 - 4076 - 4076 = 1256	checkLocation(t, loc, 5, 1256)	// Since page 5 was filled up we should be now allocated to page 6 at	// the beginning - the next allocation should take up page 6 and 7	loc, err = psm.allocateNew(8147, 5)	if err != nil {		t.Error(err)		return	}	// Expected offset is the beginning of page 6	checkLocation(t, loc, 6, 20)	// With the allocated space we should fill page 7	// Rounded up by 1	if lap := psm.pager.Last(view.TypeDataPage); lap != 7 {		t.Error("Unexpected last allocated page", lap)		return	}	// Since page 7 was filled up completely and its first offset is 0	// the algorithm should allocate a new page.	loc, err = psm.allocateNew(10, 7)	if err != nil {		t.Error(err)	}	// Expected offset is the beginning of page 8	checkLocation(t, loc, 8, 20)	// Construct a page where not enough space is free for an allocation	page, err := psm.pager.AllocatePage(view.TypeDataPage)	if err != nil {		t.Error(err)		return	}	record, err = psm.storagefile.Get(page)	if err != nil {		t.Error(err)		return	}	pv := pageview.NewDataPage(record)	pv.SetOffsetFirst(uint16(4093))	psm.storagefile.ReleaseInUseID(page, true)	loc, err = psm.allocateNew(10, 9)	if err != nil {		t.Error(err)	}	// Expected offset is the beginning of page 10	checkLocation(t, loc, 10, 20)	// Now a two error tests which will cause the page pointers get out of sync	record, err = sf.Get(12)	if err != nil {		t.Error(err)		return	}	_, err = psm.allocateNew(8147, 5)	if err != file.ErrAlreadyInUse {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Page 11 was now allocated but not written to	loc, err = psm.allocateNew(10, 9)	if err != nil {		t.Error(err)	}	// Expected offset is the beginning of page 12	checkLocation(t, loc, 12, 20)	record, err = sf.Get(14)	if err != nil {		t.Error(err)		return	}	_, err = psm.allocateNew(8147, 12)	if err != file.ErrAlreadyInUse {		t.Error(err)		return	}	sf.ReleaseInUse(record)	// Page 13 was now allocated but not written to	loc, err = psm.allocateNew(10, 9)	if err != nil {		t.Error(err)	}	// Expected offset is the beginning of page 14	checkLocation(t, loc, 14, 20)	if err := psf.Close(); err != nil {		t.Error(err)		return	}	if err := fpsf.Close(); err != nil {		t.Error(err)		return	}}func checkLocation(t *testing.T, loc uint64, record uint64, offset uint16) {	lrecord := util.LocationRecord(loc)	loffset := util.LocationOffset(loc)	if lrecord != record || loffset != offset {		t.Error("Unexpected location. Expected:", record, offset, "Got:", lrecord, loffset)	}}
 |