Skip to content

Commit

Permalink
Improve error handling in txfile package (#17)
Browse files Browse the repository at this point in the history
* Improve error handling in txfile package

- Add checks when beginning a transaction if the transaction type
  requested is compatible to the file open mode.
- Update pq to return an error if txfile transaction creation fails
- introduce Error type that is compatible to txerr
- improve checking and recovering from root cause by introducing error
  kinds and re-export error kinds
- add more context like file-name, transaction id, page id to errors and
  panics
- introduce transaction ID (atomic counter)
- create trace of errors annotated with operations that failed
  • Loading branch information
Steffen Siering authored and ph committed Aug 2, 2018
1 parent 0284002 commit 372d179
Show file tree
Hide file tree
Showing 27 changed files with 819 additions and 268 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Add `Flags` to txfile.Options. PR #5
- Add support to increase a file's maxSize on open. PR #5
- Add support to pre-allocate the meta area. PR #7
- Begin returns an error if transaction is not compatible to file open mode. PR #17
- Introduce Error type to txfile package. PR #17

### Changed
- Refine platform dependent file syncing. PR #10
Expand Down
46 changes: 33 additions & 13 deletions alloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package txfile

import (
"fmt"
"math"

"github.com/elastic/go-txfile/internal/invariant"
Expand Down Expand Up @@ -156,15 +157,21 @@ func (a *allocator) makeTxAllocState(withOverflow bool, growPercentage int) txAl
}
}

func (a *allocator) fileCommitPrepare(st *allocCommitState, tx *txAllocState, forceUpdate bool) {
func (a *allocator) fileCommitPrepare(
st *allocCommitState,
tx *txAllocState,
forceUpdate bool,
) {
st.tx = tx
st.updated = forceUpdate || tx.Updated()
if st.updated {
a.MetaAllocator().FreeRegions(tx, a.freelistPages)
}
}

func (a *allocator) fileCommitAlloc(st *allocCommitState) error {
func (a *allocator) fileCommitAlloc(st *allocCommitState) reason {
const op = "txfile/commit-alloc-meta"

if !st.updated {
return nil
}
Expand Down Expand Up @@ -197,7 +204,8 @@ func (a *allocator) fileCommitAlloc(st *allocCommitState) error {
if n := prediction.count; n > 0 {
allocRegions = a.MetaAllocator().AllocRegions(st.tx, n)
if allocRegions == nil {
return errOutOfMemory
return a.err(op).of(OutOfMemory).
report("not enough space to allocate freelist meta pages")
}
}

Expand Down Expand Up @@ -278,12 +286,19 @@ func releaseOverflowPages(

func (a *allocator) fileCommitSerialize(
st *allocCommitState,
onPage func(id PageID, buf []byte) error,
) error {
onPage func(id PageID, buf []byte) reason,
) reason {
const op = "txfile/commit-serialize-alloc"

if !st.updated || len(st.allocRegions) == 0 {
return nil
}
return writeFreeLists(st.allocRegions, a.pageSize, st.metaList, st.dataList, onPage)

err := writeFreeLists(st.allocRegions, a.pageSize, st.metaList, st.dataList, onPage)
if err != nil {
return a.errWrap(op, err).report("failed to serialize allocator state")
}
return nil
}

func (a *allocator) fileCommitMeta(meta *metaPage, st *allocCommitState) {
Expand Down Expand Up @@ -331,6 +346,14 @@ func (a *allocator) Rollback(st *txAllocState) {
a.data.rollback(&st.data)
}

func (a *allocator) err(op string) *Error {
return &Error{op: op}
}

func (a *allocator) errWrap(op string, err error) *Error {
return a.err(op).causedBy(err)
}

func (a *allocArea) commit(endMarker PageID, regions regionList) {
a.endMarker = endMarker
a.freelist.regions = regions
Expand Down Expand Up @@ -400,12 +423,9 @@ func (mm *metaManager) Ensure(st *txAllocState, n uint) bool {
// Can not grow until 'requiredMax' -> try to grow up to requiredMin,
// potentially allocating pages from the overflow area
requiredMin := szMinMeta - total
if mm.tryGrow(st, requiredMin, st.options.overflowAreaEnabled) {
return true
}

// out of memory
return false
// returns false if we are out of memory
return mm.tryGrow(st, requiredMin, st.options.overflowAreaEnabled)
}

func (mm *metaManager) tryGrow(
Expand Down Expand Up @@ -576,7 +596,7 @@ func (a *dataAllocator) Free(st *txAllocState, id PageID) {
traceln("free page:", id)

if id < 2 || id >= a.data.endMarker {
panic(errOutOfBounds)
panic(fmt.Sprintf("freed page ID %v out of bounds", id))
}

if !st.data.new.Has(id) {
Expand Down Expand Up @@ -713,7 +733,7 @@ func (s *txAllocArea) Updated() bool {
// allocator state (de-)serialization
// ----------------------------------

func readAllocatorState(a *allocator, f *File, meta *metaPage, opts Options) error {
func readAllocatorState(a *allocator, f *File, meta *metaPage, opts Options) reason {
if a.maxSize > 0 {
a.maxPages = a.maxSize / a.pageSize
}
Expand Down
16 changes: 16 additions & 0 deletions errkind_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 372d179

Please sign in to comment.