123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768 |
- /*
- * 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"
- "errors"
- "fmt"
- "io"
- "sync"
- "time"
- "devt.de/krotik/common/errorutil"
- "devt.de/krotik/common/fileutil"
- "devt.de/krotik/common/lockutil"
- "devt.de/krotik/eliasdb/storage/file"
- "devt.de/krotik/eliasdb/storage/paging"
- "devt.de/krotik/eliasdb/storage/slotting"
- "devt.de/krotik/eliasdb/storage/util"
- )
- /*
- VERSION constains the version of the storage API
- */
- const VERSION = 1
- /*
- FileSiffixLockfile is the file ending for lockfiles
- */
- const FileSiffixLockfile = "lck"
- /*
- FileSuffixLogicalSlots is the file ending for a logical slot storage
- */
- const FileSuffixLogicalSlots = "ix"
- /*
- FileSuffixLogicalFreeSlots is the file ending for a free logical slot storage
- */
- const FileSuffixLogicalFreeSlots = "ixf"
- /*
- FileSuffixPhysicalSlots is the file ending for a physical slot storage
- */
- const FileSuffixPhysicalSlots = "db"
- /*
- FileSuffixPhysicalFreeSlots is the file ending for a free physical slot storage
- */
- const FileSuffixPhysicalFreeSlots = "dbf"
- /*
- BlockSizePhysicalSlots is the block for a physical slot file. Physical slots will
- contain actual data they need to have fairly large block sizes.
- */
- const BlockSizePhysicalSlots = 1024 * 8
- /*
- BlockSizeLogicalSlots is the block for a logical slot file. Logical slots contain only
- pointers they only need small blocks.
- */
- const BlockSizeLogicalSlots = 1024 * 2
- /*
- BlockSizeFreeSlots is the block for a free slot files. Files containing only free slot
- pointers will always be small. They only need tiny blocks.
- */
- const BlockSizeFreeSlots = 1024
- /*
- ErrReadonly is returned when attempting a write operation on a readonly datastore.
- */
- var ErrReadonly = errors.New("Storage is readonly")
- /*
- DiskStorageManager is a storage manager which can store any gob serializable datastructure.
- */
- type DiskStorageManager struct {
- *ByteDiskStorageManager
- }
- /*
- NewDiskStorageManager creates a new disk storage manager with optional
- transaction management. If the onlyAppend flag is set then the manager will
- not attempt to reuse space once it was released after use. If the
- transDisabled flag is set then the storage manager will not support
- transactions.
- */
- func NewDiskStorageManager(filename string, readonly bool, onlyAppend bool,
- transDisabled bool, lockfileDisabled bool) *DiskStorageManager {
- return &DiskStorageManager{NewByteDiskStorageManager(filename, readonly,
- onlyAppend, transDisabled, lockfileDisabled)}
- }
- /*
- Name returns the name of the StorageManager instance.
- */
- func (dsm *DiskStorageManager) Name() string {
- return fmt.Sprint("DiskStorageFile:", dsm.ByteDiskStorageManager.filename)
- }
- /*
- Serialize serializes an object into a byte slice.
- */
- func (dsm *DiskStorageManager) Serialize(o interface{}) ([]byte, error) {
- // Request a buffer from the buffer pool
- bb := BufferPool.Get().(*bytes.Buffer)
- defer func() {
- bb.Reset()
- BufferPool.Put(bb)
- }()
- // Serialize the object into a gob bytes stream
- err := gob.NewEncoder(bb).Encode(o)
- if err != nil {
- return nil, err
- }
- return bb.Bytes(), nil
- }
- /*
- Insert inserts an object and return its storage location.
- */
- func (dsm *DiskStorageManager) Insert(o interface{}) (uint64, error) {
- b, err := dsm.Serialize(o)
- if err != nil {
- return 0, err
- }
- return dsm.ByteDiskStorageManager.Insert(b)
- }
- /*
- Update updates a storage location.
- */
- func (dsm *DiskStorageManager) Update(loc uint64, o interface{}) error {
- b, err := dsm.Serialize(o)
- if err != nil {
- return err
- }
- return dsm.ByteDiskStorageManager.Update(loc, b)
- }
- /*
- Fetch fetches an object from a given storage location and writes it to
- a given data container.
- */
- func (dsm *DiskStorageManager) Fetch(loc uint64, o interface{}) error {
- // Request a buffer from the buffer pool
- bb := BufferPool.Get().(*bytes.Buffer)
- defer func() {
- bb.Reset()
- BufferPool.Put(bb)
- }()
- if err := dsm.ByteDiskStorageManager.Fetch(loc, bb); err != nil {
- return err
- }
- // Deserialize the object from a gob bytes stream
- return gob.NewDecoder(bb).Decode(o)
- }
- /*
- ByteDiskStorageManager is a disk storage manager which can only store byte slices.
- */
- type ByteDiskStorageManager struct {
- filename string // Filename for all managed files
- readonly bool // Flag to make the storage readonly
- onlyAppend bool // Flag for append-only mode
- transDisabled bool // Flag if transactions are enabled
- mutex *sync.Mutex // Mutex to protect actual file operations
- physicalSlotsSf *file.StorageFile // StorageFile for physical slots
- physicalSlotsPager *paging.PagedStorageFile // Pager for physical slots StorageFile
- physicalFreeSlotsSf *file.StorageFile // StorageFile for free physical slots
- physicalFreeSlotsPager *paging.PagedStorageFile // Pager for free physical slots StorageFile
- physicalSlotManager *slotting.PhysicalSlotManager // Manager for physical slots
- logicalSlotsSf *file.StorageFile // StorageFile for logical slots
- logicalSlotsPager *paging.PagedStorageFile // Pager for logical slots StorageFile
- logicalFreeSlotsSf *file.StorageFile // StorageFile for free logical slots
- logicalFreeSlotsPager *paging.PagedStorageFile // Pager for free logical slots StorageFile
- logicalSlotManager *slotting.LogicalSlotManager // Manager for physical slots
- lockfile *lockutil.LockFile // Lockfile manager
- }
- /*
- NewByteDiskStorageManager creates a new disk storage manager with optional
- transaction management which can only store byte slices. If the onlyAppend
- flag is set then the manager will not attempt to reuse space once it was
- released after use. If the transDisabled flag is set then the storage
- manager will not support transactions.
- */
- func NewByteDiskStorageManager(filename string, readonly bool, onlyAppend bool,
- transDisabled bool, lockfileDisabled bool) *ByteDiskStorageManager {
- var lf *lockutil.LockFile
- // Create a lockfile which is checked every 50 milliseconds
- if !lockfileDisabled {
- lf = lockutil.NewLockFile(fmt.Sprintf("%v.%v", filename, FileSiffixLockfile),
- time.Duration(50)*time.Millisecond)
- }
- bdsm := &ByteDiskStorageManager{filename, readonly, onlyAppend, transDisabled, &sync.Mutex{}, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, lf}
- err := initByteDiskStorageManager(bdsm)
- if err != nil {
- panic(fmt.Sprintf("Could not initialize DiskStroageManager: %v", filename))
- }
- return bdsm
- }
- /*
- DataFileExist checks if the main datastore file exists.
- */
- func DataFileExist(filename string) bool {
- ret, err := fileutil.PathExists(fmt.Sprintf("%v.%v.0", filename,
- FileSuffixPhysicalSlots))
- if err != nil {
- return false
- }
- return ret
- }
- /*
- Name returns the name of the StorageManager instance.
- */
- func (bdsm *ByteDiskStorageManager) Name() string {
- return fmt.Sprint("ByteDiskStorageFile:", bdsm.filename)
- }
- /*
- Root returns a root value.
- */
- func (bdsm *ByteDiskStorageManager) Root(root int) uint64 {
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- bdsm.checkFileOpen()
- return bdsm.physicalSlotsPager.Header().Root(root)
- }
- /*
- SetRoot writes a root value.
- */
- func (bdsm *ByteDiskStorageManager) SetRoot(root int, val uint64) {
- // When readonly this operation becomes a NOP
- if bdsm.readonly {
- return
- }
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- bdsm.checkFileOpen()
- bdsm.physicalSlotsPager.Header().SetRoot(root, val)
- }
- /*
- Insert inserts an object and return its storage location.
- */
- func (bdsm *ByteDiskStorageManager) Insert(o interface{}) (uint64, error) {
- bdsm.checkFileOpen()
- // Fail operation if readonly
- if bdsm.readonly {
- return 0, ErrReadonly
- }
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Store the data in a physical slot
- b := o.([]byte)
- ploc, err := bdsm.physicalSlotManager.Insert(b, 0, uint32(len(b)))
- if err != nil {
- return 0, err
- }
- // Get a logical slot for the physical slot
- loc, err := bdsm.logicalSlotManager.Insert(ploc)
- if err != nil {
- return 0, err
- }
- return loc, nil
- }
- /*
- Update updates a storage location.
- */
- func (bdsm *ByteDiskStorageManager) Update(loc uint64, o interface{}) error {
- bdsm.checkFileOpen()
- // Fail operation if readonly
- if bdsm.readonly {
- return ErrReadonly
- }
- // Get the physical slot for the given logical slot
- bdsm.mutex.Lock()
- ploc, err := bdsm.logicalSlotManager.Fetch(loc)
- bdsm.mutex.Unlock()
- if err != nil {
- return err
- }
- if ploc == 0 {
- return NewStorageManagerError(ErrSlotNotFound, fmt.Sprint("Location:",
- util.LocationRecord(loc), util.LocationOffset(loc)), bdsm.Name())
- }
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Update the physical record
- b := o.([]byte)
- newPloc, err := bdsm.physicalSlotManager.Update(ploc, b, 0, uint32(len(b)))
- if err != nil {
- return err
- }
- // Update the logical slot if the physical slot has changed
- if newPloc != ploc {
- return bdsm.logicalSlotManager.Update(loc, newPloc)
- }
- return nil
- }
- /*
- Fetch fetches an object from a given storage location and writes it to
- a given data container.
- */
- func (bdsm *ByteDiskStorageManager) Fetch(loc uint64, o interface{}) error {
- bdsm.checkFileOpen()
- // Get the physical slot for the given logical slot
- bdsm.mutex.Lock()
- ploc, err := bdsm.logicalSlotManager.Fetch(loc)
- bdsm.mutex.Unlock()
- if err != nil {
- return err
- }
- if ploc == 0 {
- return NewStorageManagerError(ErrSlotNotFound, fmt.Sprint("Location:",
- util.LocationRecord(loc), util.LocationOffset(loc)), bdsm.Name())
- }
- // Request the stored bytes
- bdsm.mutex.Lock()
- if w, ok := o.(io.Writer); ok {
- err = bdsm.physicalSlotManager.Fetch(ploc, w)
- } else {
- var b bytes.Buffer
- err = bdsm.physicalSlotManager.Fetch(ploc, &b)
- copy(o.([]byte), b.Bytes())
- }
- bdsm.mutex.Unlock()
- return err
- }
- /*
- FetchCached is not implemented for a ByteDiskStorageManager.
- Only defined to satisfy the StorageManager interface.
- */
- func (bdsm *ByteDiskStorageManager) FetchCached(loc uint64) (interface{}, error) {
- return nil, NewStorageManagerError(ErrNotInCache, "", bdsm.Name())
- }
- /*
- Free frees a storage location.
- */
- func (bdsm *ByteDiskStorageManager) Free(loc uint64) error {
- bdsm.checkFileOpen()
- // Fail operation if readonly
- if bdsm.readonly {
- return ErrReadonly
- }
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Get the physical slot for the given logical slot
- ploc, err := bdsm.logicalSlotManager.Fetch(loc)
- if err != nil {
- return err
- }
- if ploc == 0 {
- return NewStorageManagerError(ErrSlotNotFound, fmt.Sprint("Location:",
- util.LocationRecord(loc), util.LocationOffset(loc)), bdsm.Name())
- }
- // First try to free the physical slot since here is the data
- // if this fails we don't touch the logical slot
- err = bdsm.physicalSlotManager.Free(ploc)
- if err != nil {
- return err
- }
- // This is very unlikely to fail - either way we can't do anything
- // at this point since the physical slot has already gone away
- return bdsm.logicalSlotManager.Free(loc)
- }
- /*
- Flush writes all pending changes to disk.
- */
- func (bdsm *ByteDiskStorageManager) Flush() error {
- bdsm.checkFileOpen()
- // When readonly this operation becomes a NOP
- if bdsm.readonly {
- return nil
- }
- ce := errorutil.NewCompositeError()
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Write pending changes
- if err := bdsm.physicalSlotManager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalSlotManager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.physicalSlotsPager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.physicalFreeSlotsPager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalSlotsPager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalFreeSlotsPager.Flush(); err != nil {
- ce.Add(err)
- }
- // Return errors if there were any
- if ce.HasErrors() {
- return ce
- }
- return nil
- }
- /*
- Rollback cancels all pending changes which have not yet been written to disk.
- */
- func (bdsm *ByteDiskStorageManager) Rollback() error {
- // Rollback has no effect if transactions are disabled or when readonly
- if bdsm.transDisabled || bdsm.readonly {
- return nil
- }
- bdsm.checkFileOpen()
- ce := errorutil.NewCompositeError()
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Write pending manager changes to transaction log
- if err := bdsm.physicalSlotManager.Flush(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalSlotManager.Flush(); err != nil {
- ce.Add(err)
- }
- // Rollback current transaction
- if err := bdsm.physicalSlotsPager.Rollback(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.physicalFreeSlotsPager.Rollback(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalSlotsPager.Rollback(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalFreeSlotsPager.Rollback(); err != nil {
- ce.Add(err)
- }
- // Return errors if there were any
- if ce.HasErrors() {
- return ce
- }
- return nil
- }
- /*
- Close closes the StorageManager and write all pending changes to disk.
- */
- func (bdsm *ByteDiskStorageManager) Close() error {
- bdsm.checkFileOpen()
- ce := errorutil.NewCompositeError()
- // Continue single threaded from here on
- bdsm.mutex.Lock()
- defer bdsm.mutex.Unlock()
- // Try to close all files and collect any errors which are returned
- if err := bdsm.physicalSlotsPager.Close(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.physicalFreeSlotsPager.Close(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalSlotsPager.Close(); err != nil {
- ce.Add(err)
- }
- if err := bdsm.logicalFreeSlotsPager.Close(); err != nil {
- ce.Add(err)
- }
- // Return errors if there were any
- if ce.HasErrors() {
- return ce
- }
- // Release all file related objects
- bdsm.physicalSlotsSf = nil
- bdsm.physicalSlotsPager = nil
- bdsm.physicalFreeSlotsSf = nil
- bdsm.physicalFreeSlotsPager = nil
- bdsm.physicalSlotManager = nil
- bdsm.logicalSlotsSf = nil
- bdsm.logicalSlotsPager = nil
- bdsm.logicalFreeSlotsSf = nil
- bdsm.logicalFreeSlotsPager = nil
- bdsm.logicalSlotManager = nil
- if bdsm.lockfile != nil {
- return bdsm.lockfile.Finish()
- }
- return nil
- }
- /*
- checkFileOpen checks that the files on disk are still open.
- */
- func (bdsm *ByteDiskStorageManager) checkFileOpen() {
- if bdsm.physicalSlotsSf == nil {
- panic(fmt.Sprint("Trying to access storage after it was closed: ", bdsm.filename))
- }
- if bdsm.lockfile != nil && !bdsm.lockfile.WatcherRunning() {
- err := bdsm.lockfile.Finish()
- panic(fmt.Sprint("Error while checking lockfile:", err))
- }
- }
- /*
- initByteDiskStorageManager initialises the file managers of a given ByteDiskStorageManager.
- */
- func initByteDiskStorageManager(bdsm *ByteDiskStorageManager) error {
- // Kick off the lockfile watcher
- if bdsm.lockfile != nil {
- if err := bdsm.lockfile.Start(); err != nil {
- panic(fmt.Sprintf("Could not take ownership of lockfile %v: %v",
- bdsm.filename, err))
- }
- }
- // Try to open all files and collect all errors
- ce := errorutil.NewCompositeError()
- sf, pager, err := createFileAndPager(
- fmt.Sprintf("%v.%v", bdsm.filename, FileSuffixPhysicalSlots),
- BlockSizePhysicalSlots, bdsm)
- if err != nil {
- ce.Add(err)
- }
- bdsm.physicalSlotsSf = sf
- bdsm.physicalSlotsPager = pager
- sf, pager, err = createFileAndPager(
- fmt.Sprintf("%v.%v", bdsm.filename, FileSuffixPhysicalFreeSlots),
- BlockSizeFreeSlots, bdsm)
- if err != nil {
- ce.Add(err)
- }
- bdsm.physicalFreeSlotsSf = sf
- bdsm.physicalFreeSlotsPager = pager
- if !ce.HasErrors() {
- bdsm.physicalSlotManager = slotting.NewPhysicalSlotManager(bdsm.physicalSlotsPager,
- bdsm.physicalFreeSlotsPager, bdsm.onlyAppend)
- }
- sf, pager, err = createFileAndPager(
- fmt.Sprintf("%v.%v", bdsm.filename, FileSuffixLogicalSlots),
- BlockSizeLogicalSlots, bdsm)
- if err != nil {
- ce.Add(err)
- }
- bdsm.logicalSlotsSf = sf
- bdsm.logicalSlotsPager = pager
- sf, pager, err = createFileAndPager(
- fmt.Sprintf("%v.%v", bdsm.filename, FileSuffixLogicalFreeSlots),
- BlockSizeFreeSlots, bdsm)
- if err != nil {
- ce.Add(err)
- }
- bdsm.logicalFreeSlotsSf = sf
- bdsm.logicalFreeSlotsPager = pager
- if !ce.HasErrors() {
- bdsm.logicalSlotManager = slotting.NewLogicalSlotManager(bdsm.logicalSlotsPager,
- bdsm.logicalFreeSlotsPager)
- }
- // If there were any file related errors return at this point
- if ce.HasErrors() {
- // Release the lockfile if there were errors
- if bdsm.lockfile != nil {
- bdsm.lockfile.Finish()
- }
- return ce
- }
- // Check version
- version := bdsm.Root(RootIDVersion)
- if version > VERSION {
- // Try to clean up
- bdsm.Close()
- panic(fmt.Sprint("Cannot open datastore ", bdsm.filename, " - version of disk files is "+
- "newer than supported version. Supported version:", VERSION,
- " Disk files version:", version))
- }
- if version != VERSION {
- bdsm.SetRoot(RootIDVersion, VERSION)
- }
- return nil
- }
- /*
- createFileAndPager creates a storagefile and a pager.
- */
- func createFileAndPager(filename string, recordSize uint32,
- bdsm *ByteDiskStorageManager) (*file.StorageFile, *paging.PagedStorageFile, error) {
- sf, err := file.NewStorageFile(filename, recordSize, bdsm.transDisabled)
- if err != nil {
- return nil, nil, err
- }
- pager, err := paging.NewPagedStorageFile(sf)
- return sf, pager, err
- }
|