123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- /*
- * 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 storage
- import (
- "bytes"
- "encoding/gob"
- "os"
- "sync"
- "testing"
- "time"
- "devt.de/krotik/common/lockutil"
- "devt.de/krotik/common/testutil"
- "devt.de/krotik/eliasdb/storage/file"
- "devt.de/krotik/eliasdb/storage/slotting/pageview"
- "devt.de/krotik/eliasdb/storage/util"
- )
- func TestDiskStorageManagerLockFilePanic(t *testing.T) {
- dsm := NewDiskStorageManager(DBDIR+"/test0", false, false, true, false)
- defer func() {
- if r := recover(); r == nil {
- t.Error("Doing an operation with a storagefile with stopped lock watcher goroutine did not cause a panic.")
- }
- // Start the watcher goroutine again and close the file
- dsm.lockfile.Start()
- dsm.Close()
- }()
- file, err := os.OpenFile(DBDIR+"/test0.lck", os.O_CREATE|os.O_TRUNC, 0660)
- if err != nil {
- t.Error(err)
- return
- }
- file.WriteString("t")
- file.Close()
- // Give the watcher goroutine time to die
- time.Sleep(time.Duration(100) * time.Millisecond)
- if dsm.lockfile.WatcherRunning() {
- t.Error("Watcher goroutine did not die")
- }
- // This should cause a panic
- dsm.Free(util.PackLocation(2, 20))
- }
- func TestDiskStorageManager1(t *testing.T) {
- var res string
- dsm := NewDiskStorageManager(DBDIR+"/test1", false, false, true, false)
- if dsm.Name() != "DiskStorageFile:"+DBDIR+"/test1" {
- t.Error("Unexpected name for DiskStorageManager:", dsm.Name())
- return
- }
- // Make sure that another process which attempts to open the same
- // storage would panic
- time.Sleep(100 * time.Millisecond) // Need some time here otherwise Windows fails sometimes
- testLockfileStartPanic(t)
- // Test simple insert
- loc, err := dsm.Insert("This is a test")
- if err != nil {
- t.Error(err)
- }
- checkLocation(t, loc, 1, pageview.OffsetTransData)
- dsm.Fetch(loc, &res)
- if res != "This is a test" {
- t.Error("Unexpected fetch result:", res)
- }
- // Test case where we give a byte slice
- var res2 string
- bs := make([]byte, 18)
- dsm.ByteDiskStorageManager.Fetch(loc, bs)
- err = gob.NewDecoder(bytes.NewReader(bs)).Decode(&res2)
- if err != nil || string(res2) != "This is a test" {
- t.Error("Unexpected fetch result:", err, res2)
- }
- // Get physical slot for stored data
- ploc, err := dsm.logicalSlotManager.Fetch(loc)
- if err != nil {
- t.Error(err)
- return
- }
- // Test uodate
- // The next update should allocate a new physical record
- err = dsm.Update(loc, "This is another test")
- if err != nil {
- t.Error(err)
- }
- // Get new physical slot for stored data
- newPloc, err := dsm.logicalSlotManager.Fetch(loc)
- if err != nil {
- t.Error(err)
- return
- }
- if ploc == newPloc {
- t.Error("Physical address should have changed")
- return
- }
- dsm.Fetch(loc, &res)
- if res != "This is another test" {
- t.Error("Unexpected fetch result:", res)
- }
- // Test insert error
- _, err = dsm.Insert(&testutil.GobTestObject{Name: "test", EncErr: true, DecErr: false})
- if err == nil {
- t.Error(err)
- return
- }
- psp := dsm.physicalSlotsPager
- fpsp := dsm.physicalFreeSlotsPager
- lsp := dsm.logicalSlotsPager
- flsp := dsm.logicalFreeSlotsPager
- record, err := psp.StorageFile().Get(1)
- if err != nil {
- t.Error(err)
- return
- }
- _, err = dsm.Insert(&testutil.GobTestObject{Name: "test", EncErr: false, DecErr: false})
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- psp.StorageFile().ReleaseInUse(record)
- record, err = lsp.StorageFile().Get(1)
- if err != nil {
- t.Error(err)
- return
- }
- _, err = dsm.Insert(&testutil.GobTestObject{Name: "test", EncErr: false, DecErr: false})
- if err != file.ErrAlreadyInUse {
- t.Error(err, loc)
- }
- lsp.StorageFile().ReleaseInUse(record)
- rpsp, _ := psp.StorageFile().Get(2)
- rfpsp, _ := fpsp.StorageFile().Get(1)
- rlsp, _ := lsp.StorageFile().Get(1)
- rflsp, _ := flsp.StorageFile().Get(1)
- err = dsm.Flush()
- if err.Error() != "Record is already in-use (storagemanagertest/test1.dbf - Record 1); "+
- "Record is already in-use (storagemanagertest/test1.ixf - Record 1); "+
- "Records are still in-use (storagemanagertest/test1.db - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.dbf - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.ix - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.ixf - Records 1)" {
- t.Error(err)
- }
- err = dsm.Close()
- if err.Error() != "Records are still in-use (storagemanagertest/test1.db - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.dbf - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.ix - Records 1); "+
- "Records are still in-use (storagemanagertest/test1.ixf - Records 1)" {
- t.Error(err)
- }
- psp.StorageFile().ReleaseInUse(rpsp)
- fpsp.StorageFile().ReleaseInUse(rfpsp)
- lsp.StorageFile().ReleaseInUse(rlsp)
- flsp.StorageFile().ReleaseInUse(rflsp)
- _, err = dsm.FetchCached(0)
- if err != ErrNotInCache {
- t.Error("Unexpected FetchCached result:", err)
- return
- }
- if err = dsm.Close(); err != nil {
- t.Error(err)
- return
- }
- if !DataFileExist(DBDIR + "/test1") {
- t.Error("Main disk storage file was not detected.")
- return
- }
- if DataFileExist(DBDIR + "/" + InvalidFileName) {
- t.Error("Main disk storage file with invalid name should not exist.")
- return
- }
- }
- func testLockfileStartPanic(t *testing.T) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Attempting to open the same DiskStorageManager twice did not cause a panic.")
- }
- }()
- dsm := NewDiskStorageManager(DBDIR+"/test1", false, false, false, false)
- dsm.Close()
- }
- func TestDiskStorageManager2(t *testing.T) {
- var res string
- dsm := NewDiskStorageManager(DBDIR+"/test2", false, false, true, true)
- loc, err := dsm.Insert("This is a test")
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc, 1, 18)
- // Insert some filler data
- for i := 1; i < 253; i++ {
- loc2, err := dsm.Insert("1234")
- if err != nil {
- t.Error(err)
- return
- }
- checkLocation(t, loc2, 1, uint16(18+i*8))
- }
- record, _ := dsm.logicalSlotsSf.Get(2)
- _, err = dsm.Insert("This is a test")
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- err = dsm.Fetch(util.PackLocation(2, 18), &res)
- if err != ErrSlotNotFound {
- t.Error(err)
- return
- }
- dsm.logicalSlotsSf.ReleaseInUse(record)
- err = dsm.Fetch(util.PackLocation(3, 18), &res)
- if err != ErrSlotNotFound {
- t.Error(err)
- return
- }
- record, _ = dsm.logicalSlotsSf.Get(1)
- err = dsm.Update(loc, "test")
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- err = dsm.Fetch(loc, &res)
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- dsm.logicalSlotsSf.ReleaseInUse(record)
- err = dsm.Update(util.PackLocation(2, 18), "test")
- if err != ErrSlotNotFound {
- t.Error(err)
- return
- }
- err = dsm.Update(loc, &testutil.GobTestObject{Name: "test", EncErr: true, DecErr: false})
- if err == nil {
- t.Error(err)
- return
- }
- err = dsm.Update(loc, &testutil.GobTestObject{Name: "test", EncErr: false, DecErr: false})
- if err != nil {
- t.Error(err)
- return
- }
- testres := &testutil.GobTestObject{Name: "test", EncErr: false, DecErr: true}
- err = dsm.Fetch(loc, &testres)
- if err == nil {
- t.Error("Unexpected decode result")
- return
- }
- record, _ = dsm.physicalSlotsSf.Get(1)
- var testres2 testutil.GobTestObject
- err = dsm.Fetch(loc, &testres2)
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- // Test a normal update
- err = dsm.Update(loc, "tree")
- if err != file.ErrAlreadyInUse {
- t.Error(err)
- return
- }
- dsm.physicalSlotsSf.ReleaseInUse(record)
- err = dsm.Update(loc, "tree")
- if err != nil {
- t.Error(err)
- return
- }
- err = dsm.Fetch(loc, &res)
- if err != nil {
- t.Error(err)
- return
- }
- if res != "tree" {
- t.Error("Unexpected fetch result:", res)
- return
- }
- pl, _ := dsm.logicalSlotManager.Fetch(loc)
- if util.LocationRecord(pl) != 1 {
- t.Error("Unexpected initial location:", util.LocationRecord(pl))
- }
- _, err = dsm.Insert("test" + string(make([]byte, 10000)) + "test")
- if err != nil {
- t.Error(err)
- return
- }
- // Test reallocation
- err = dsm.Update(loc, "test"+string(make([]byte, 1000))+"test")
- if err != nil {
- t.Error(err)
- return
- }
- err = dsm.Update(loc, "test"+string(make([]byte, 1000))+"test")
- if err != nil {
- t.Error(err)
- return
- }
- pl, _ = dsm.logicalSlotManager.Fetch(loc)
- if util.LocationRecord(pl) != 2 {
- t.Error("Unexpected relocated location:", util.LocationRecord(pl))
- }
- if err = dsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- if err = dsm.Close(); err != nil {
- t.Error(err)
- }
- // Reopen datastore readonly
- dsm = NewDiskStorageManager(DBDIR+"/test2", true, false, true, true)
- // Try write operations
- if l, err := dsm.Insert("Test"); l != 0 || err != ErrReadonly {
- t.Error("Unexpected result:", l, err)
- }
- if err := dsm.Update(loc, "Test"); err != ErrReadonly {
- t.Error("Unexpected result:", err)
- }
- if err := dsm.Free(loc); err != ErrReadonly {
- t.Error("Unexpected result:", err)
- }
- // NOP operations
- dsm.Rollback()
- dsm.SetRoot(1, 5)
- // Try reading
- if dsm.Root(1) != 1 {
- t.Error("Unexpected root:", dsm.Root(1))
- return
- }
- err = dsm.Fetch(util.PackLocation(1, 90), &res)
- if err != nil {
- t.Error(err)
- return
- }
- if res != "1234" {
- t.Error("Unexpected fetch result:", res)
- return
- }
- if dsm.Flush() != nil {
- t.Error("Flushing failed:", err)
- return
- }
- if err = dsm.Close(); err != nil {
- t.Error(err)
- }
- }
- func TestDiskStorageManager3(t *testing.T) {
- var res string
- dsm := NewDiskStorageManager(DBDIR+"/test3", false, false, true, true)
- if dsm.Free(util.PackLocation(2, 18)) != ErrSlotNotFound {
- t.Error("Unexpected free result")
- return
- }
- loc, err := dsm.Insert("This is a test")
- if err != nil {
- t.Error(err)
- return
- }
- dsm.Fetch(loc, &res)
- if res != "This is a test" {
- t.Error("Unexpected fetch result:", res)
- }
- record, _ := dsm.physicalSlotsSf.Get(1)
- if dsm.Free(loc) != file.ErrAlreadyInUse {
- t.Error("Unexpected free result")
- return
- }
- dsm.physicalSlotsSf.ReleaseInUse(record)
- record, _ = dsm.logicalSlotsSf.Get(1)
- if dsm.Free(loc) != file.ErrAlreadyInUse {
- t.Error("Unexpected free result")
- return
- }
- dsm.logicalSlotsSf.ReleaseInUse(record)
- if err := dsm.Free(loc); err != nil {
- t.Error(err)
- return
- }
- // Rollback call should have no effect
- if err := dsm.Rollback(); err != nil {
- t.Error(err)
- return
- }
- if err = dsm.Close(); err != nil {
- t.Error(err)
- return
- }
- }
- func TestDiskStorageManagerRollback(t *testing.T) {
- dsm := NewDiskStorageManager(DBDIR+"/test4", false, false, false, true)
- var res string
- // Test expected behaviour
- loc, err := dsm.Insert("This is a test")
- if err != nil {
- t.Error(err)
- }
- checkLocation(t, loc, 1, pageview.OffsetTransData)
- if err := dsm.Rollback(); err != nil {
- t.Error(err)
- return
- }
- dsm.Fetch(loc, &res)
- if res != "" {
- t.Error("Unexpected fetch result:", res)
- }
- loc, err = dsm.Insert("This is a test")
- if err != nil {
- t.Error(err)
- }
- checkLocation(t, loc, 1, pageview.OffsetTransData)
- if err := dsm.Flush(); err != nil {
- t.Error(err)
- return
- }
- if err := dsm.Rollback(); err != nil {
- t.Error(err)
- return
- }
- dsm.Fetch(loc, &res)
- if res != "This is a test" {
- t.Error("Unexpected fetch result:", res)
- }
- // Test error cases
- if err := dsm.Free(loc); err != nil {
- t.Error(err)
- return
- }
- psp := dsm.physicalSlotsPager
- fpsp := dsm.physicalFreeSlotsPager
- lsp := dsm.logicalSlotsPager
- flsp := dsm.logicalFreeSlotsPager
- rpsp, _ := psp.StorageFile().Get(1)
- rfpsp, _ := fpsp.StorageFile().Get(1)
- rlsp, _ := lsp.StorageFile().Get(1)
- rflsp, _ := flsp.StorageFile().Get(1)
- err = dsm.Rollback()
- if err.Error() != "Record is already in-use (storagemanagertest/test4.dbf - Record 1); "+
- "Record is already in-use (storagemanagertest/test4.ixf - Record 1); "+
- "Records are still in-use (storagemanagertest/test4.db - Records 1); "+
- "Records are still in-use (storagemanagertest/test4.dbf - Records 1); "+
- "Records are still in-use (storagemanagertest/test4.ix - Records 1); "+
- "Records are still in-use (storagemanagertest/test4.ixf - Records 1)" {
- t.Error(err)
- }
- psp.StorageFile().ReleaseInUse(rpsp)
- fpsp.StorageFile().ReleaseInUse(rfpsp)
- lsp.StorageFile().ReleaseInUse(rlsp)
- flsp.StorageFile().ReleaseInUse(rflsp)
- if err := dsm.Close(); err != nil {
- t.Error(err)
- return
- }
- }
- const InvalidFileName = "**" + string(0x0)
- func TestDiskStorageManagerInit(t *testing.T) {
- lockfile := lockutil.NewLockFile(DBDIR+"/"+"lock0.lck", time.Duration(50)*time.Millisecond)
- dsm := &DiskStorageManager{&ByteDiskStorageManager{DBDIR + "/" + InvalidFileName, false, true, true, &sync.Mutex{},
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, lockfile}}
- err := initByteDiskStorageManager(dsm.ByteDiskStorageManager)
- if err == nil {
- t.Error("Initialising a DiskStorageManager with an invalid filename should cause an error")
- return
- }
- testCannotInitPanic(t)
- dsm = &DiskStorageManager{&ByteDiskStorageManager{DBDIR + "/test999", false, true, true, &sync.Mutex{},
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}}
- err = initByteDiskStorageManager(dsm.ByteDiskStorageManager)
- if err != nil {
- t.Error(err)
- return
- }
- dsm.SetRoot(RootIDVersion, VERSION+1)
- err = dsm.Close()
- if err != nil {
- t.Error(err)
- }
- testClosedPanic(t, dsm)
- testVersionCheckPanic(t)
- }
- func testCannotInitPanic(t *testing.T) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Creating DiskStorageManager with invalid filename did not cause a panic.")
- }
- }()
- NewDiskStorageManager(DBDIR+"/"+InvalidFileName, false, false, true, true)
- }
- func testClosedPanic(t *testing.T, dsm *DiskStorageManager) {
- defer func() {
- if r := recover(); r == nil {
- t.Error("Closing a closed DiskStorageManager did not cause a panic.")
- }
- }()
- dsm.Close()
- }
- func testVersionCheckPanic(t *testing.T) {
- dsm := &DiskStorageManager{&ByteDiskStorageManager{DBDIR + "/test999", false, true, true, &sync.Mutex{},
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}}
- defer func() {
- if r := recover(); r == nil {
- t.Error("Wrong version did not cause a panic.")
- }
- }()
- initByteDiskStorageManager(dsm.ByteDiskStorageManager)
- }
- 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)
- }
- }
|