Repository navigation
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
What happens to I tried it locally, and that seems to be what happens. On a synthetic DB with 3M events (80% Transfer), "Transfers from wallet X" over the whole chain went from 0.14 ms to 710 ms. That cost grows with the total Transfer count, so would it time out on mainnet? This test pins the expected index for each criteria shape. On the current branch, // Pins the index chosen for each criteria shape so new indexes can't silently steal plans.
func TestEventQueryPlanPerCriteriaShape(t *testing.T) {
db, err := NewMem()
require.NoError(t, err)
defer db.Close()
addr := thor.BytesToAddress([]byte("a"))
t0, t1, t2, t3 := randBytes32(), randBytes32(), randBytes32(), randBytes32()
shapes := []struct {
name string
criteria EventCriteria
wantIndex string
}{
{"topic0", EventCriteria{Topics: [5]*thor.Bytes32{&t0}}, "event_i6"},
{"address", EventCriteria{Address: &addr}, "event_i0"},
{"address+topic0", EventCriteria{Address: &addr, Topics: [5]*thor.Bytes32{&t0}}, "event_i1"},
{"topic0+topic1", EventCriteria{Topics: [5]*thor.Bytes32{&t0, &t1}}, "event_i2"},
{"topic0+topic2", EventCriteria{Topics: [5]*thor.Bytes32{&t0, nil, &t2}}, "event_i3"},
{"topic0+topic3", EventCriteria{Topics: [5]*thor.Bytes32{&t0, nil, nil, &t3}}, "event_i4"},
{"address+topic0+topic1", EventCriteria{Address: &addr, Topics: [5]*thor.Bytes32{&t0, &t1}}, "event_i2"},
}
ranges := map[string]*Range{
"narrow": {From: 100, To: 100},
"wide": {From: 0, To: 25_000_000},
"nil": nil,
}
for _, s := range shapes {
for rname, r := range ranges {
t.Run(s.name+"/"+rname, func(t *testing.T) {
c := s.criteria
query, args, err := buildEventFilterSubQuery(&EventFilter{
Range: r,
CriteriaSet: []*EventCriteria{&c},
Options: &Options{Limit: 1000},
})
require.NoError(t, err)
rows, err := db.readDB.QueryContext(context.Background(), "EXPLAIN QUERY PLAN "+query, args...)
require.NoError(t, err)
defer rows.Close()
var plan []string
for rows.Next() {
var id, parent, notUsed int
var detail string
require.NoError(t, rows.Scan(&id, &parent, ¬Used, &detail))
if strings.Contains(detail, "SEARCH event") {
plan = append(plan, detail)
}
}
require.NoError(t, rows.Err())
assert.Contains(t, strings.Join(plan, "\n"), "INDEX "+s.wantIndex+" ")
})
}
}
}Would one of these work?
Whichever approach you take, could this test go in with it so the plans stay pinned? |
|
Close this in favor of #1693 |
Description
Add additional event indexes for improved query performance.
Symptom
POST /logs/eventwith a narrow block range and one or moretopic0-only criteria (noaddress) takes seconds and hits the 10s API timeout on a fresh node. Example (single block, 6 topic0 criteria,limit: 1000, 10 rows returned):{ "range": {"unit": "block", "from": 25769599, "to": 25769599}, "criteriaSet": [ {"topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"}, {"topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"}, {"topic0": "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"}, {"topic0": "0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65"}, {"topic0": "0xda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b"}, {"topic0": "0xc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec"} ], "order": "asc", "options": {"offset": 0, "limit": 1000} }Root cause
The generated subquery is
EXPLAIN QUERY PLAN:event_i1is(topic0, address)— effectively(topic0, address, seq)sinceseqis the rowid.seqis only ordered inside each(topic0, address)group, so withaddressunconstrained the planner cannot boundseqin the index. It positions ontopic0, reads every entry for that topic0 chain-wide, filters each byseq, then re-sorts. Cost is O(total rows with that topic0), independent of the block range. The rows actually in the requested range are typically tens.SQLite cannot skip-scan the middle
addresscolumn (skip-scan only skips leading columns), and thor never runsANALYZE, so the planner always prefers the index over the rowid range.sqlite_stat1would not fix it either — range selectivity is not estimated without STAT4.Proposed fix
Add a single-column index:
Type of change
Checklist: