Skip to content

Commit

Permalink
Merge pull request #4918 from multiversx/feat/transaction-ordering-on…
Browse files Browse the repository at this point in the history
…-execution

Feat/transaction ordering on execution
  • Loading branch information
miiu96 authored Sep 1, 2023
2 parents d87941e + d5a1b97 commit 6d4dc24
Show file tree
Hide file tree
Showing 69 changed files with 2,242 additions and 1,468 deletions.
11 changes: 9 additions & 2 deletions api/shared/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ const thresholdMinAPICallDurationToLog = 200 * time.Millisecond
// LogAPIActionDurationIfNeeded will log the duration of an action triggered by an API call if it's above a threshold
func LogAPIActionDurationIfNeeded(startTime time.Time, action string) {
duration := time.Since(startTime)
if duration > thresholdMinAPICallDurationToLog {
log.Debug(fmt.Sprintf("%s took %s", action, duration))
if duration < thresholdMinAPICallDurationToLog {
return
}

if duration.Nanoseconds() == 1<<63-1 {
// fix for this issue https://github.com/tcnksm/go-httpstat/issues/13
// in some situations, a duration of 2562047h47m16.854775807s was printed
return
}
log.Debug(fmt.Sprintf("%s took %s", action, duration))
}
57 changes: 57 additions & 0 deletions common/disabled/orderedCollection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package disabled

import (
"errors"
)

type orderedCollection struct {
}

var errDisabledComponent = errors.New("disabled component")

// NewOrderedCollection creates a new ordered collection
func NewOrderedCollection() *orderedCollection {
return &orderedCollection{}
}

// Add does nothing
func (oc *orderedCollection) Add(_ []byte) {}

// GetItemAtIndex does nothing
func (oc *orderedCollection) GetItemAtIndex(_ uint32) ([]byte, error) {
return nil, errDisabledComponent
}

// GetOrder does nothing
func (oc *orderedCollection) GetOrder(_ []byte) (int, error) {
return 0, errDisabledComponent
}

// Remove does nothing
func (oc *orderedCollection) Remove(_ []byte) {}

// RemoveMultiple does nothing
func (oc *orderedCollection) RemoveMultiple(_ [][]byte) {}

// GetItems does nothing
func (oc *orderedCollection) GetItems() [][]byte {
return make([][]byte, 0)
}

// Contains does nothing
func (oc *orderedCollection) Contains(_ []byte) bool {
return false
}

// Clear does nothing
func (oc *orderedCollection) Clear() {}

// Len does nothing
func (oc *orderedCollection) Len() int {
return 0
}

// IsInterfaceNil returns true if there is no value under the interface
func (oc *orderedCollection) IsInterfaceNil() bool {
return oc == nil
}
29 changes: 29 additions & 0 deletions common/disabled/orderedCollection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package disabled

import (
"testing"

"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestOrderedCollection_MethodsShouldNotPanic(t *testing.T) {
t.Parallel()

oc := NewOrderedCollection()
assert.False(t, check.IfNil(oc))

require.NotPanics(t, func() {
oc.Add([]byte("key"))
_, _ = oc.GetItemAtIndex(0)
_, _ = oc.GetOrder([]byte("key"))
oc.Remove([]byte("key"))
oc.RemoveMultiple([][]byte{[]byte("key1"), []byte("key2")})
_ = oc.GetItems()
_ = oc.Contains([]byte("key"))
oc.Clear()
_ = oc.Len()
_ = oc.IsInterfaceNil()
})
}
24 changes: 24 additions & 0 deletions common/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,27 @@ type ManagedPeersMonitor interface {
GetWaitingManagedKeys() ([][]byte, error)
IsInterfaceNil() bool
}

// TxExecutionOrderHandler is used to collect and provide the order of transactions execution
type TxExecutionOrderHandler interface {
Add(txHash []byte)
GetItemAtIndex(index uint32) ([]byte, error)
GetOrder(txHash []byte) (int, error)
Remove(txHash []byte)
RemoveMultiple(txHashes [][]byte)
GetItems() [][]byte
Contains(txHash []byte) bool
Clear()
Len() int
IsInterfaceNil() bool
}

// ExecutionOrderGetter defines the functionality of a component that can return the execution order of a block transactions
type ExecutionOrderGetter interface {
GetItemAtIndex(index uint32) ([]byte, error)
GetOrder(txHash []byte) (int, error)
GetItems() [][]byte
Contains(txHash []byte) bool
Len() int
IsInterfaceNil() bool
}
9 changes: 9 additions & 0 deletions common/ordering/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ordering

import "errors"

// ErrItemNotFound is returned when an item is not found in the ordered collection
var ErrItemNotFound = errors.New("item not found")

// ErrIndexOutOfBounds is returned when an index is out of bounds
var ErrIndexOutOfBounds = errors.New("index out of bounds")
5 changes: 5 additions & 0 deletions common/ordering/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ordering

func GetNilOrderedCollection() *orderedCollection {
return nil
}
124 changes: 124 additions & 0 deletions common/ordering/orderedCollection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package ordering

import "sync"

type orderedCollection struct {
itemsMap map[string]int
itemsArray [][]byte
mut sync.RWMutex
}

// NewOrderedCollection creates a new ordered collection
func NewOrderedCollection() *orderedCollection {
return &orderedCollection{
itemsMap: make(map[string]int),
itemsArray: make([][]byte, 0, 100),
mut: sync.RWMutex{},
}
}

// Add adds a new item to the order collector
func (oc *orderedCollection) Add(item []byte) {
oc.mut.Lock()
defer oc.mut.Unlock()
_, ok := oc.itemsMap[string(item)]
if ok {
return
}
oc.itemsMap[string(item)] = len(oc.itemsArray)
oc.itemsArray = append(oc.itemsArray, item)
}

// GetItemAtIndex returns the item at the given index
func (oc *orderedCollection) GetItemAtIndex(index uint32) ([]byte, error) {
oc.mut.RLock()
defer oc.mut.RUnlock()

if index >= uint32(len(oc.itemsArray)) {
return nil, ErrIndexOutOfBounds
}

return oc.itemsArray[index], nil
}

// GetOrder returns the order of the item in the ordered collection
func (oc *orderedCollection) GetOrder(item []byte) (int, error) {
oc.mut.RLock()
defer oc.mut.RUnlock()
order, ok := oc.itemsMap[string(item)]
if !ok {
return 0, ErrItemNotFound
}

return order, nil
}

// Remove removes an item from the order collector if it exists, adapting the order of the remaining items
func (oc *orderedCollection) Remove(item []byte) {
oc.mut.Lock()
defer oc.mut.Unlock()

oc.removeOneUnprotected(item)
}

func (oc *orderedCollection) removeOneUnprotected(item []byte) {
index, ok := oc.itemsMap[string(item)]
if !ok {
return
}

delete(oc.itemsMap, string(item))

oc.itemsArray = append(oc.itemsArray[:index], oc.itemsArray[index+1:]...)
for i := index; i < len(oc.itemsArray); i++ {
oc.itemsMap[string(oc.itemsArray[i])]--
}
}

// RemoveMultiple removes multiple items from the order collector if they exist, adapting the order of the remaining items
func (oc *orderedCollection) RemoveMultiple(items [][]byte) {
oc.mut.Lock()
defer oc.mut.Unlock()

for _, item := range items {
oc.removeOneUnprotected(item)
}
}

// GetItems returns the items in the order they were added
func (oc *orderedCollection) GetItems() [][]byte {
oc.mut.RLock()
defer oc.mut.RUnlock()

cpItems := make([][]byte, len(oc.itemsArray))
copy(cpItems, oc.itemsArray)
return cpItems
}

// Contains returns true if the item is in the ordered collection
func (oc *orderedCollection) Contains(item []byte) bool {
oc.mut.RLock()
defer oc.mut.RUnlock()
_, ok := oc.itemsMap[string(item)]
return ok
}

// Clear clears the ordered collection
func (oc *orderedCollection) Clear() {
oc.mut.Lock()
defer oc.mut.Unlock()
oc.itemsArray = make([][]byte, 0, 100)
oc.itemsMap = make(map[string]int)
}

// Len returns the number of items in the ordered collection
func (oc *orderedCollection) Len() int {
oc.mut.RLock()
defer oc.mut.RUnlock()
return len(oc.itemsArray)
}

// IsInterfaceNil returns true if there is no value under the interface
func (oc *orderedCollection) IsInterfaceNil() bool {
return oc == nil
}
Loading

0 comments on commit 6d4dc24

Please sign in to comment.