Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recover from panic in cosmos store #1257

Merged
merged 2 commits into from
Aug 24, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions database/store/cosmos.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package store

import (
"errors"
"fmt"

"github.com/cosmos/cosmos-sdk/types"
)
Expand All @@ -24,32 +25,67 @@ func (s *CosmosStore) NewIterator() Iterator {
}

// Has returns true if the key is set in the store.
func (s *CosmosStore) Has(key []byte) (bool, error) {
return s.store.Has(key), nil
func (s *CosmosStore) Has(key []byte) (has bool, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("store: %s", r)
}
}
}()
has = s.store.Has(key)
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we return explicitly the error here and avoid to have named outputs to be consistent with the rest of the codebase

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, that's not possible with the error recover system. The output have to be named in order to be "replaced" by the defer function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

declaring an error variable at the beginning of the function wouldn't work?

func (s *CosmosStore) Has(key []byte) (bool, error) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			if err, ok = r.(error); !ok {
				err = fmt.Errorf("store: %s", r)
			}
		}
	}()
	return s.store.Has(key), err
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, because defer is invoked
"after" retrun statment, not before :/

you can read about this in details here https://medium.com/@blanchon.vincent/go-how-does-defer-statement-work-1a9492689b6e

}

// Get retrives service from store. It returns an error if the store does not contains the key.
func (s *CosmosStore) Get(key []byte) ([]byte, error) {
func (s *CosmosStore) Get(key []byte) (out []byte, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("store: %s", r)
}
}
}()

has, err := s.Has(key)
if err != nil {
return nil, err
}
if !has {
return nil, errors.New("not found")
}
return s.store.Get(key), nil
out = s.store.Get(key)
return
}

// Delete deletes the value for the given key. Delete will not returns error if key doesn't exist.
func (s *CosmosStore) Delete(key []byte) error {
func (s *CosmosStore) Delete(key []byte) (err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("store: %s", r)
}
}
}()
s.store.Delete(key)
return nil
return
}

// Put sets the value for the given key. It overwrites any previous value.
func (s *CosmosStore) Put(key []byte, value []byte) error {
func (s *CosmosStore) Put(key []byte, value []byte) (err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
if err, ok = r.(error); !ok {
err = fmt.Errorf("store: %s", r)
}
}
}()
s.store.Set(key, value)
return nil
return
}

// Close closes the store.
Expand All @@ -60,6 +96,7 @@ func (s *CosmosStore) Close() error {
// CosmosIterator is a Cosmos KVStore's iterator implementation of Iterator.
type CosmosIterator struct {
iter types.Iterator
err error
}

// NewCosmosIterator returns a new Cosmos KVStore Iterator wrapper.
Expand All @@ -71,29 +108,43 @@ func NewCosmosIterator(iter types.Iterator) *CosmosIterator {

// Next moves the iterator to the next sequential key in the store.
func (i *CosmosIterator) Next() bool {
if i.iter.Valid() {
defer i.handleError()
valid := i.iter.Valid()
if valid {
i.iter.Next()
return true
}
return false
return valid
}

// Key returns the key of the cursor.
func (i *CosmosIterator) Key() []byte {
defer i.handleError()
return i.iter.Key()
}

// Value returns the value of the cursor.
func (i *CosmosIterator) Value() []byte {
defer i.handleError()
return i.iter.Value()
}

// Release releases the Iterator.
func (i *CosmosIterator) Release() {
defer i.handleError()
i.iter.Close()
}

// Error returns any accumulated error.
func (i *CosmosIterator) Error() error {
return nil
return i.err
}

// returns any accumulated error.
func (i *CosmosIterator) handleError() {
if r := recover(); r != nil {
var ok bool
if i.err, ok = r.(error); !ok {
i.err = fmt.Errorf("store iterator: %s", r)
}
}
}