Skip to content

Commit d57da98

Browse files
committed
poll after head
1 parent ac00f12 commit d57da98

File tree

1 file changed

+74
-25
lines changed

1 file changed

+74
-25
lines changed

internal/ethapi/api.go

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import (
4141
"github.com/ethereum/go-ethereum/crypto"
4242
"github.com/ethereum/go-ethereum/eth/gasestimator"
4343
"github.com/ethereum/go-ethereum/eth/tracers/logger"
44-
"github.com/ethereum/go-ethereum/event"
4544
"github.com/ethereum/go-ethereum/internal/ethapi/override"
4645
"github.com/ethereum/go-ethereum/log"
4746
"github.com/ethereum/go-ethereum/p2p"
@@ -1649,61 +1648,111 @@ func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hex
16491648
return nil, err
16501649
}
16511650

1652-
// effective timeout: min(requested, max), default if none
1653-
max := api.b.RPCTxSyncMaxTimeout()
1654-
def := api.b.RPCTxSyncDefaultTimeout()
1651+
maxTimeout := api.b.RPCTxSyncMaxTimeout()
1652+
defaultTimeout := api.b.RPCTxSyncDefaultTimeout()
16551653

1656-
eff := def
1654+
timeout := defaultTimeout
16571655
if timeoutMs != nil && *timeoutMs > 0 {
16581656
req := time.Duration(*timeoutMs) * time.Millisecond
1659-
if req > max {
1660-
eff = max
1657+
if req > maxTimeout {
1658+
timeout = maxTimeout
16611659
} else {
1662-
eff = req
1660+
timeout = req
16631661
}
16641662
}
16651663

1666-
receiptCtx, cancel := context.WithTimeout(ctx, eff)
1664+
receiptCtx, cancel := context.WithTimeout(ctx, timeout)
16671665
defer cancel()
16681666

1669-
// Fast path: maybe already mined/indexed
1667+
// Fast path.
16701668
if r, err := api.GetTransactionReceipt(receiptCtx, hash); err == nil && r != nil {
16711669
return r, nil
16721670
}
16731671

1674-
// Subscribe to head changes and re-check on each new block
1672+
// Subscribe to new block events and check the receipt on each new block.
16751673
heads := make(chan core.ChainHeadEvent, 16)
1676-
var sub event.Subscription = api.b.SubscribeChainHeadEvent(heads)
1677-
if sub == nil {
1678-
return nil, errors.New("chain head subscription unavailable")
1679-
}
1674+
sub := api.b.SubscribeChainHeadEvent(heads)
16801675
defer sub.Unsubscribe()
1676+
subErrCh := sub.Err()
1677+
1678+
// calculate poll/settle intervals
1679+
const (
1680+
pollFraction = 20
1681+
pollMin = 25 * time.Millisecond
1682+
pollMax = 500 * time.Millisecond
1683+
)
1684+
settleInterval := timeout / pollFraction
1685+
if settleInterval < pollMin {
1686+
settleInterval = pollMin
1687+
} else if settleInterval > pollMax {
1688+
settleInterval = pollMax
1689+
}
1690+
1691+
// Settle: short delay to bridge receipt-indexing lag after a new head.
1692+
// resetSettle re-arms a single timer safely (stop+drain+reset).
1693+
// On head: check once immediately, then reset; on timer: re-check; repeat until deadline.
1694+
var (
1695+
settle *time.Timer
1696+
settleCh <-chan time.Time
1697+
)
1698+
resetSettle := func(d time.Duration) {
1699+
if settle == nil {
1700+
settle = time.NewTimer(d)
1701+
settleCh = settle.C
1702+
return
1703+
}
1704+
if !settle.Stop() {
1705+
select {
1706+
case <-settle.C:
1707+
default:
1708+
}
1709+
}
1710+
settle.Reset(d)
1711+
}
1712+
defer func() {
1713+
if settle != nil && !settle.Stop() {
1714+
select {
1715+
case <-settle.C:
1716+
default:
1717+
}
1718+
}
1719+
}()
1720+
1721+
// Start a settle cycle immediately to cover a missed-head race.
1722+
resetSettle(settleInterval)
16811723

16821724
for {
16831725
select {
16841726
case <-receiptCtx.Done():
1685-
// Distinguish upstream cancellation from our timeout
16861727
if err := ctx.Err(); err != nil {
16871728
return nil, err
16881729
}
16891730
return nil, &txSyncTimeoutError{
1690-
msg: fmt.Sprintf("The transaction was added to the transaction pool but wasn't processed in %v.", eff),
1731+
msg: fmt.Sprintf("The transaction was added to the transaction pool but wasn't processed in %v.", timeout),
16911732
hash: hash,
16921733
}
16931734

1694-
case err := <-sub.Err():
1735+
case err, ok := <-subErrCh:
1736+
if !ok || err == nil {
1737+
// subscription closed; disable this case and rely on settle timer
1738+
subErrCh = nil
1739+
continue
1740+
}
16951741
return nil, err
16961742

16971743
case <-heads:
1698-
receipt, getErr := api.GetTransactionReceipt(receiptCtx, hash)
1699-
// If tx-index still building, keep waiting; ignore transient errors
1700-
if getErr != nil && !errors.Is(getErr, NewTxIndexingError()) {
1701-
// ignore and wait for next head
1702-
continue
1744+
// Immediate re-check on new head, then grace to bridge indexing lag.
1745+
if r, err := api.GetTransactionReceipt(receiptCtx, hash); err == nil && r != nil {
1746+
return r, nil
17031747
}
1704-
if receipt != nil {
1705-
return receipt, nil
1748+
resetSettle(settleInterval)
1749+
1750+
case <-settleCh:
1751+
r, getErr := api.GetTransactionReceipt(receiptCtx, hash)
1752+
if r != nil && getErr == nil {
1753+
return r, nil
17061754
}
1755+
resetSettle(settleInterval)
17071756
}
17081757
}
17091758
}

0 commit comments

Comments
 (0)