Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit e252229

Browse files
author
Rob Mulholand
committed
Add constraint to prevent duplicate headers
- Disallow inserts of headers with the same number, hash, and node fingerprint, since it will enable duplicate log fetching for the same header
1 parent 62e1378 commit e252229

File tree

4 files changed

+39
-8
lines changed

4 files changed

+39
-8
lines changed

db/migrations/00023_create_headers_table.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ CREATE TABLE public.headers
88
block_timestamp NUMERIC,
99
check_count INTEGER NOT NULL DEFAULT 0,
1010
eth_node_id INTEGER NOT NULL REFERENCES eth_nodes (id) ON DELETE CASCADE,
11-
eth_node_fingerprint VARCHAR(128)
11+
eth_node_fingerprint VARCHAR(128),
12+
UNIQUE (block_number, hash, eth_node_fingerprint)
1213
);
1314

1415
-- Index is removed when table is

db/schema.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,14 @@ ALTER TABLE ONLY public.header_sync_transactions
921921
ADD CONSTRAINT header_sync_transactions_pkey PRIMARY KEY (id);
922922

923923

924+
--
925+
-- Name: headers headers_block_number_hash_eth_node_fingerprint_key; Type: CONSTRAINT; Schema: public; Owner: -
926+
--
927+
928+
ALTER TABLE ONLY public.headers
929+
ADD CONSTRAINT headers_block_number_hash_eth_node_fingerprint_key UNIQUE (block_number, hash, eth_node_fingerprint);
930+
931+
924932
--
925933
-- Name: headers headers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
926934
--

pkg/datastore/postgres/repositories/header_repository.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (repository HeaderRepository) CreateOrUpdateHeader(header core.Header) (int
4141
hash, err := repository.getHeaderHash(header)
4242
if err != nil {
4343
if headerDoesNotExist(err) {
44-
return repository.insertHeader(header)
44+
return repository.InternalInsertHeader(header)
4545
}
4646
log.Error("CreateOrUpdateHeader: error getting header hash: ", err)
4747
return 0, err
@@ -128,13 +128,21 @@ func (repository HeaderRepository) getHeaderHash(header core.Header) (string, er
128128
return hash, err
129129
}
130130

131-
func (repository HeaderRepository) insertHeader(header core.Header) (int64, error) {
131+
// Function is public so we can test insert being called for the same header
132+
// Can happen when concurrent processes are inserting headers
133+
// Otherwise should not occur since only called in CreateOrUpdateHeader
134+
func (repository HeaderRepository) InternalInsertHeader(header core.Header) (int64, error) {
132135
var headerId int64
133-
err := repository.database.QueryRowx(
134-
`INSERT INTO public.headers (block_number, hash, block_timestamp, raw, eth_node_id, eth_node_fingerprint) VALUES ($1, $2, $3::NUMERIC, $4, $5, $6) RETURNING id`,
135-
header.BlockNumber, header.Hash, header.Timestamp, header.Raw, repository.database.NodeID, repository.database.Node.ID).Scan(&headerId)
136+
row := repository.database.QueryRowx(
137+
`INSERT INTO public.headers (block_number, hash, block_timestamp, raw, eth_node_id, eth_node_fingerprint)
138+
VALUES ($1, $2, $3::NUMERIC, $4, $5, $6) ON CONFLICT DO NOTHING RETURNING id`,
139+
header.BlockNumber, header.Hash, header.Timestamp, header.Raw, repository.database.NodeID, repository.database.Node.ID)
140+
err := row.Scan(&headerId)
136141
if err != nil {
137-
log.Error("insertHeader: error inserting header: ", err)
142+
if err == sql.ErrNoRows {
143+
return 0, ErrValidHeaderExists
144+
}
145+
log.Error("InternalInsertHeader: error inserting header: ", err)
138146
}
139147
return headerId, err
140148
}
@@ -146,5 +154,5 @@ func (repository HeaderRepository) replaceHeader(header core.Header) (int64, err
146154
log.Error("replaceHeader: error deleting headers: ", err)
147155
return 0, err
148156
}
149-
return repository.insertHeader(header)
157+
return repository.InternalInsertHeader(header)
150158
}

pkg/datastore/postgres/repositories/header_repository_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ var _ = Describe("Block header repository", func() {
9898
Expect(len(dbHeaders)).To(Equal(1))
9999
})
100100

101+
It("does not duplicate headers in concurrent insert", func() {
102+
_, err = repo.InternalInsertHeader(header)
103+
Expect(err).NotTo(HaveOccurred())
104+
105+
_, err = repo.InternalInsertHeader(header)
106+
Expect(err).To(HaveOccurred())
107+
Expect(err).To(MatchError(repositories.ErrValidHeaderExists))
108+
109+
var dbHeaders []core.Header
110+
err = db.Select(&dbHeaders, `SELECT block_number, hash, raw FROM public.headers WHERE block_number = $1`, header.BlockNumber)
111+
Expect(err).NotTo(HaveOccurred())
112+
Expect(len(dbHeaders)).To(Equal(1))
113+
})
114+
101115
It("replaces header if hash is different", func() {
102116
_, err = repo.CreateOrUpdateHeader(header)
103117
Expect(err).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)