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