Skip to content
Merged
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions sender/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"sync"
"time"

"github.com/sei-protocol/sei-load/generator"
"github.com/sei-protocol/sei-load/stats"
Expand Down Expand Up @@ -95,6 +96,10 @@ func (d *Dispatcher) Run(ctx context.Context) error {
return nil
}

// Stamp before hand-off: the dispatcher is sole owner here (tx just
// returned by the generator, not yet enqueued), so this write is race-free.
tx.IntendedSendTime = time.Now()

// Send the transaction
if err := d.sender.Send(ctx, tx); err != nil {
return err
Expand All @@ -117,6 +122,9 @@ func (d *Dispatcher) RunBatch(ctx context.Context, count int) error {
if !ok {
return fmt.Errorf("dispatcher: generator returned nil transaction (batch %d/%d)", i+1, count)
}
// Stamp before hand-off (see Run).
tx.IntendedSendTime = time.Now()

// Send the transaction
if err := d.sender.Send(ctx, tx); err != nil {
log.Printf("Dispatcher: Failed to send transaction %d/%d: %v", i+1, count, err)
Expand Down
3 changes: 3 additions & 0 deletions sender/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ func (w *Worker) processTransactions(ctx context.Context, client *http.Client) e
}

startTime := time.Now()
// This goroutine solely owns tx between dequeue and the sentTxs hand-off,
// so stamping the actual send-attempt time here is race-free (see LoadTx).
tx.AttemptedSendTime = startTime
err = w.sendTransaction(ctx, client, tx)
// Record statistics if collector is available
if w.collector != nil {
Expand Down
21 changes: 21 additions & 0 deletions types/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,38 @@ import (
"encoding/json"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)

// LoadTx is a wrapper that has pre-encoded json rpc payload and eth transaction.
//
// Lifecycle timestamp concurrency contract: a *LoadTx is passed by pointer
// through buffered channels (txChan, sentTxs). Each lifecycle timestamp is
// written at most once, by whichever goroutine owns the tx at that stage, and
// is immutable thereafter; ownership transfers with the pointer across the
// channels, so the writes need no locking. A zero timestamp means "not
// recorded" (e.g. prewarm txs, or a stage not yet reached) — consumers must
// treat it as untracked, never as the zero epoch.
type LoadTx struct {
EthTx *ethtypes.Transaction
JSONRPCPayload []byte
Payload []byte
Scenario *TxScenario

// IntendedSendTime is when the tx was scheduled to be sent, written by the
// dispatcher before the tx is enqueued. It currently holds the enqueue time,
// which is back-pressured under load; until an open-loop scheduler sets it to
// the intended schedule instant, it must not be used to derive latency.
IntendedSendTime time.Time
// AttemptedSendTime is when the send was actually attempted, written by the
// worker goroutine that owns the tx between dequeue and the sentTxs hand-off.
AttemptedSendTime time.Time
// InclusionTime is when the tx was observed included on-chain, written only
// by the inclusion tracker.
InclusionTime time.Time
}

// JSONRPCRequest represents json rpc request.
Expand Down
Loading