diff --git a/e2pg/e2pg.go b/e2pg/e2pg.go index 34fae833..b72e23fa 100644 --- a/e2pg/e2pg.go +++ b/e2pg/e2pg.go @@ -257,6 +257,21 @@ func (t *Task) initRows(n uint64, h []byte) error { return nil } +// lockid accepts a uint64 because, per eip155, a chain id can +// be: floor(MAX_UINT64 / 2) - 36. Since pg_advisory_xact_lock +// requires a 32 bit id, and since we are using a bit to indicate +// wheather a task is backfill or not, we simply panic for large chain +// ids. If this ever becomes a problem we can find another solution. +func lockid(x uint64, backfill bool) uint32 { + if x > ((1 << 31) - 1) { + panic("lockid input too big") + } + if backfill { + return uint32((x << 1) | 1) + } + return uint32((x << 1) &^ 1) +} + var ( ErrNothingNew = errors.New("no new blocks") ErrReorg = errors.New("reorg") @@ -286,7 +301,7 @@ func (task *Task) Converge(notx bool) error { pg = wpg.NewTxLocker(pgTx) //crc32(task) == 1384045349 const lockq = `select pg_advisory_xact_lock(1384045349, $1)` - _, err = pg.Exec(task.ctx, lockq, task.chainID) + _, err = pg.Exec(task.ctx, lockq, lockid(task.chainID, task.backfill)) if err != nil { return fmt.Errorf("task lock %d: %w", task.chainID, err) } diff --git a/e2pg/e2pg_test.go b/e2pg/e2pg_test.go index a9842722..7a4ba85f 100644 --- a/e2pg/e2pg_test.go +++ b/e2pg/e2pg_test.go @@ -756,3 +756,20 @@ func TestValidateChain(t *testing.T) { diff.Test(t, t.Errorf, tc.want, validateChain(tc.parent, tc.blks)) } } + +func TestLockID(t *testing.T) { + cases := []struct { + chid uint64 + backfill bool + want uint32 + }{ + {1, true, 0b00000000000000000000000000000011}, + {1, false, 0b00000000000000000000000000000010}, + {1024, true, 0b00000000000000000000100000000001}, + {1024, false, 0b00000000000000000000100000000000}, + } + for _, tc := range cases { + got := lockid(tc.chid, tc.backfill) + diff.Test(t, t.Errorf, tc.want, got) + } +}