When stress testing multiple time based identity registrations, I hit 500 server errors when registering the identities concurrently:
[GIN] 2026/06/11 - 11:34:22 | 200 | 25.774833ms | 83.135.3.18 | POST "/api/time/register_identity"
[GIN] 2026/06/11 - 11:34:22 | 500 | 25.398819ms | 83.135.3.18 | POST "/api/time/register_identity"
Error #01: description: failed to register identity, metadata:
{"level":"error","error":"ReplacementNotAllowed","time":"2026-06-11T11:34:22Z","message":"failed to send transaction"}
The error seems to be triggered by getSigner() creating a TransactOpts with Nonce: nil. The go-ethereum bind package resolves this by calling PendingNonceAt. PendingNonceAt includes pending (mempool) transactions in its count, so sequential requests are safe - by the time request N+1 fetches the nonce, request N's transaction is already in the mempool and the pending nonce has advanced.
The failure only occurs when two requests are received and processed concurrently on the server and both execute PendingNonceAt before either transaction has been submitted to the mempool. There is a small window during which both calls observe the same pending nonce N, both sign and submit a transaction with nonce N, and the one submission is then rejected with ReplacementNotAllowed.
Potential fixes:
Mutex around nonce fetch + submit to serialize all tx submissions
- Disable concurrency at the registration endpoints
- In-memory nonce counter - read nonce once at startup, increment atomically, reset on nonce too low error
- Transaction queue - single goroutine owns submission
When stress testing multiple time based identity registrations, I hit
500server errors when registering the identities concurrently:The error seems to be triggered by getSigner() creating a
TransactOptswithNonce: nil. The go-ethereumbindpackage resolves this by callingPendingNonceAt.PendingNonceAtincludes pending (mempool) transactions in its count, so sequential requests are safe - by the time request N+1 fetches the nonce, request N's transaction is already in the mempool and the pending nonce has advanced.The failure only occurs when two requests are received and processed concurrently on the server and both execute
PendingNonceAtbefore either transaction has been submitted to the mempool. There is a small window during which both calls observe the same pending nonce N, both sign and submit a transaction with nonce N, and the one submission is then rejected withReplacementNotAllowed.Potential fixes:
Mutexaround nonce fetch + submit to serialize all tx submissions