Fix error sink msg duplication & false positives (#2924)
* [types] Add TransactionErrorSink to report failed txs [node] Create ErrorSink handler for unique error msgs Implemented with a LRU cache. [node] Rename ErrorSink to TransactionErrorSink * Rename RPCTransactionError to TransactionError [node] Make tx error sink not return err on Add & Remove [node] Make tx errorSink Contains check take string as tx hash param [errorsink] Move tx error sink into errorsink pkg [errorsink] Rename Errors and Count methods [errorsink] Rename NewTransactionErrorSink to NewTransactionSink [types] Move error sink to core/types * Rename NewTransactionSink to NewTransactionErrorSink * [types] Fix log msg for unfound errors * [types] Rename TransactionError to TransactionErrorReport * [core] Remove RPCTransactionError & refactor tx_pool to use TxErrorSink * [staking] Remove RPCTransactionError * [node] Refactor tx_pool init to use new TxErrorSink * [main] Construct transaction error sink before initing the node * [node] Refactor error sink reporting at RPC layer * [rpc] Refactor returned type of ErrorSink RPCs to 1 type * [core] Remove tx from TxErrorSink on Add to tx_pool * [types] Make NewTransactionErrorSink not return err * [node] Make node.New create error sink * [cmd] Revert to origin main.go * [core] Add TxErrorSink unit test & fix bad ErrExcessiveBLSKeys in tests * [testnet config] Change testnet config to allow for 5*4 external keys * [cmd] Revert main.go to original node instantiation * [rpc] Add GetPoolStats rpc to fetch pending and queued tx countspull/2934/head
parent
7ed4f22dcf
commit
5d87fdb59e
@ -0,0 +1,160 @@ |
||||
package types |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
lru "github.com/hashicorp/golang-lru" |
||||
|
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
staking "github.com/harmony-one/harmony/staking/types" |
||||
) |
||||
|
||||
const ( |
||||
plainTxSinkLimit = 1024 |
||||
stakingTxSinkLimit = 1024 |
||||
logTag = "[TransactionErrorSink]" |
||||
) |
||||
|
||||
// TransactionErrorReport ..
|
||||
type TransactionErrorReport struct { |
||||
TxHashID string `json:"tx-hash-id"` |
||||
StakingDirective string `json:"directive-kind,omitempty"` |
||||
TimestampOfRejection int64 `json:"time-at-rejection"` |
||||
ErrMessage string `json:"error-message"` |
||||
} |
||||
|
||||
// TransactionErrorReports ..
|
||||
type TransactionErrorReports []*TransactionErrorReport |
||||
|
||||
// TransactionErrorSink is where all failed transactions get reported.
|
||||
// Note that the keys of the lru caches are tx-hash strings.
|
||||
type TransactionErrorSink struct { |
||||
failedPlainTxs *lru.Cache |
||||
failedStakingTxs *lru.Cache |
||||
} |
||||
|
||||
// NewTransactionErrorSink ..
|
||||
func NewTransactionErrorSink() *TransactionErrorSink { |
||||
failedPlainTx, _ := lru.New(plainTxSinkLimit) |
||||
failedStakingTx, _ := lru.New(stakingTxSinkLimit) |
||||
return &TransactionErrorSink{ |
||||
failedPlainTxs: failedPlainTx, |
||||
failedStakingTxs: failedStakingTx, |
||||
} |
||||
} |
||||
|
||||
// Add a transaction to the error sink with the given error
|
||||
func (sink *TransactionErrorSink) Add(tx PoolTransaction, err error) { |
||||
// no-op if no error is provided
|
||||
if err == nil { |
||||
return |
||||
} |
||||
if plainTx, ok := tx.(*Transaction); ok { |
||||
hash := plainTx.Hash().String() |
||||
sink.failedPlainTxs.Add(hash, &TransactionErrorReport{ |
||||
TxHashID: hash, |
||||
TimestampOfRejection: time.Now().Unix(), |
||||
ErrMessage: err.Error(), |
||||
}) |
||||
utils.Logger().Debug(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", hash). |
||||
Msgf("Added plain transaction error message") |
||||
} else if stakingTx, ok := tx.(*staking.StakingTransaction); ok { |
||||
hash := stakingTx.Hash().String() |
||||
sink.failedStakingTxs.Add(hash, &TransactionErrorReport{ |
||||
TxHashID: hash, |
||||
StakingDirective: stakingTx.StakingType().String(), |
||||
TimestampOfRejection: time.Now().Unix(), |
||||
ErrMessage: err.Error(), |
||||
}) |
||||
utils.Logger().Debug(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", hash). |
||||
Msgf("Added staking transaction error message") |
||||
} else { |
||||
utils.Logger().Error(). |
||||
Str("tag", logTag). |
||||
Interface("tx", tx). |
||||
Msg("Attempted to add an unknown transaction type") |
||||
} |
||||
} |
||||
|
||||
// Contains checks if there is an error associated with the given hash
|
||||
// Note that the keys of the lru caches are tx-hash strings.
|
||||
func (sink *TransactionErrorSink) Contains(hash string) bool { |
||||
return sink.failedPlainTxs.Contains(hash) || sink.failedStakingTxs.Contains(hash) |
||||
} |
||||
|
||||
// Remove a transaction's error from the error sink
|
||||
func (sink *TransactionErrorSink) Remove(tx PoolTransaction) { |
||||
if plainTx, ok := tx.(*Transaction); ok { |
||||
hash := plainTx.Hash().String() |
||||
present := sink.failedPlainTxs.Remove(hash) |
||||
utils.Logger().Debug(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", hash). |
||||
Bool("was-in-error-sink", present). |
||||
Msgf("Removed plain transaction error message") |
||||
} else if stakingTx, ok := tx.(*staking.StakingTransaction); ok { |
||||
hash := stakingTx.Hash().String() |
||||
present := sink.failedStakingTxs.Remove(hash) |
||||
utils.Logger().Debug(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", hash). |
||||
Bool("was-in-error-sink", present). |
||||
Msgf("Removed staking transaction error message") |
||||
} else { |
||||
utils.Logger().Error(). |
||||
Str("tag", logTag). |
||||
Interface("tx", tx). |
||||
Msg("Attempted to remove an unknown transaction type") |
||||
} |
||||
} |
||||
|
||||
// PlainReport ..
|
||||
func (sink *TransactionErrorSink) PlainReport() TransactionErrorReports { |
||||
return reportErrorsFromLruCache(sink.failedPlainTxs) |
||||
} |
||||
|
||||
// StakingReport ..
|
||||
func (sink *TransactionErrorSink) StakingReport() TransactionErrorReports { |
||||
return reportErrorsFromLruCache(sink.failedStakingTxs) |
||||
} |
||||
|
||||
// PlainCount ..
|
||||
func (sink *TransactionErrorSink) PlainCount() int { |
||||
return sink.failedPlainTxs.Len() |
||||
} |
||||
|
||||
// StakingCount ..
|
||||
func (sink *TransactionErrorSink) StakingCount() int { |
||||
return sink.failedStakingTxs.Len() |
||||
} |
||||
|
||||
// reportErrorsFromLruCache is a helper for reporting errors
|
||||
// from the TransactionErrorSink's lru cache. Do not use this function directly,
|
||||
// use the respective public methods of TransactionErrorSink.
|
||||
func reportErrorsFromLruCache(lruCache *lru.Cache) TransactionErrorReports { |
||||
rpcErrors := TransactionErrorReports{} |
||||
for _, txHash := range lruCache.Keys() { |
||||
rpcErrorFetch, ok := lruCache.Get(txHash) |
||||
if !ok { |
||||
utils.Logger().Warn(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", txHash). |
||||
Msgf("Error not found in sink") |
||||
continue |
||||
} |
||||
rpcError, ok := rpcErrorFetch.(*TransactionErrorReport) |
||||
if !ok { |
||||
utils.Logger().Error(). |
||||
Str("tag", logTag). |
||||
Interface("tx-hash-id", txHash). |
||||
Msgf("Invalid type of value in sink") |
||||
continue |
||||
} |
||||
rpcErrors = append(rpcErrors, rpcError) |
||||
} |
||||
return rpcErrors |
||||
} |
Loading…
Reference in new issue