Skip to content

Commit 967887e

Browse files
committed
Improve locking on batch transaction submission to better handler concurrent requests
1 parent c5f0aca commit 967887e

2 files changed

Lines changed: 33 additions & 15 deletions

File tree

services/requester/batch_tx_pool.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,28 @@ func (t *BatchTxPool) Add(
125125
}
126126

127127
t.txMux.Lock()
128-
defer t.txMux.Unlock()
129128

130129
eoaActivity, found := t.eoaActivityCache.Get(from)
131130
nonce := tx.Nonce()
132131

133132
// Skip transactions that have been already submitted,
134133
// as they are *likely* to fail.
135134
if found && slices.Contains(eoaActivity.txNonces, nonce) {
135+
t.txMux.Unlock()
136136
t.logger.Info().
137137
Str("evm_tx", tx.Hash().Hex()).
138138
Str("from", from.Hex()).
139139
Uint64("nonce", nonce).
140140
Msg("tx with same nonce has been already submitted")
141+
141142
return nil
142143
}
143144

145+
t.updateEOAActivityMetadata(from, nonce)
146+
147+
// Determine action while holding lock
148+
var shouldSubmitSingle bool
149+
144150
// Scenarios
145151
// 1. EOA activity not found:
146152
// => We send the transaction individually, without adding it
@@ -160,38 +166,47 @@ func (t *BatchTxPool) Add(
160166
// [X] is equal to the configured `TxBatchInterval` duration.
161167
if !found {
162168
// Case 1. EOA activity not found:
163-
err = t.submitSingleTransaction(ctx, hexEncodedTx)
169+
shouldSubmitSingle = true
164170
} else if time.Since(eoaActivity.lastSubmission) > t.config.TxBatchInterval {
171+
// Case 2. EOA activity found AND it was more than [X] seconds ago:
172+
165173
// If the EOA has pooled transactions, which are not yet processed,
166174
// due to congestion or anything, make sure to include the current
167175
// tx on that batch.
168-
hasBatch := len(t.pooledTxs[from]) > 0
169-
if hasBatch {
170-
userTx := pooledEvmTx{txPayload: hexEncodedTx, nonce: nonce}
171-
t.pooledTxs[from] = append(t.pooledTxs[from], userTx)
172-
}
173-
174-
// If it wasn't batched, submit individually
175-
if !hasBatch {
176-
// Case 2. EOA activity found AND it was more than [X] seconds ago:
177-
err = t.submitSingleTransaction(ctx, hexEncodedTx)
178-
}
176+
shouldSubmitSingle = (len(t.pooledTxs[from]) == 0)
179177
} else {
180178
// Case 3. EOA activity found AND it was less than [X] seconds ago:
179+
shouldSubmitSingle = false
180+
}
181+
182+
// Pool transaction in the batch
183+
if !shouldSubmitSingle {
181184
userTx := pooledEvmTx{txPayload: hexEncodedTx, nonce: nonce}
182185
t.pooledTxs[from] = append(t.pooledTxs[from], userTx)
183186
}
184187

188+
// Release lock before network I/O operation
189+
t.txMux.Unlock()
190+
191+
// Submit single transaction without holding lock
192+
if shouldSubmitSingle {
193+
err = t.submitSingleTransaction(ctx, hexEncodedTx)
194+
}
195+
185196
if err != nil {
197+
t.txMux.Lock()
198+
// If there was an error during tx submission, remove the entry
199+
// from the cache, to not block future requests with same nonce.
200+
t.eoaActivityCache.Remove(from)
201+
t.txMux.Unlock()
202+
186203
t.logger.Error().Err(err).Msgf(
187204
"failed to submit single Flow transaction for EOA: %s",
188205
from.Hex(),
189206
)
190207
return err
191208
}
192209

193-
t.updateEOAActivityMetadata(from, nonce)
194-
195210
return nil
196211
}
197212

tests/tx_batching_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,9 @@ func Test_TransactionSubmissionWithPreviouslySubmittedTransactions(t *testing.T)
552552
})
553553
}
554554

555+
err = g.Wait()
556+
require.NoError(t, err)
557+
555558
expectedBalance := big.NewInt(6 * transferAmount)
556559

557560
assert.Eventually(t, func() bool {

0 commit comments

Comments
 (0)