From 1db2c87e17cb01e80dd6f113aec8efce874cd6a4 Mon Sep 17 00:00:00 2001 From: Jeb Bearer Date: Mon, 9 Oct 2023 11:37:08 +0200 Subject: [PATCH] Lock state.block table when performing non-atomic read/write This should prevent the duplicate key errors we have seen in production from the preconfirmations node, which runs two simultaneous tasks that both update the blocks table. --- state/pgstatestorage.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/state/pgstatestorage.go b/state/pgstatestorage.go index 66a75f8..28a8a33 100644 --- a/state/pgstatestorage.go +++ b/state/pgstatestorage.go @@ -125,6 +125,15 @@ func (p *PostgresStorage) ResetTrustedState(ctx context.Context, batchNum uint64 // AddBlock adds a new block to the State Store func (p *PostgresStorage) AddBlock(ctx context.Context, block *Block, dbTx pgx.Tx) error { e := p.getExecQuerier(dbTx) + + // This operation will both read the state.block table (to check if the block we're adding + // already exists) and modify it. These two actions do not happen atomically; without + // protection, another transaction may complete and modify the state.block table in between. + // Thus, we first need to acquire a lock that prevents concurrent updates to the table. + if _, err := e.Exec(ctx, "LOCK TABLE state.block IN EXCLUSIVE MODE"); err != nil { + return err + } + _, err := e.Exec(ctx, addBlockSQL, block.BlockNumber, block.BlockHash.String(), block.ParentHash.String(), block.ReceivedAt) return err }