Skip to content

Commit d0658d3

Browse files
authored
Optimize reward-amounts query for postgres' (#3819)
* add index for reward-amounts query * remove where * include entry * change query * created desc for sqlite * fix tests
1 parent 7c10665 commit d0658d3

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Used by: get_all_reward_accounts() in sequencer/src/api/sql.rs
2+
3+
CREATE INDEX reward_merkle_tree_v2_idx_created
4+
ON reward_merkle_tree_v2 (idx, created) INCLUDE (entry);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Used by: get_all_reward_accounts() in sequencer/src/api/sql.rs
2+
3+
CREATE INDEX reward_merkle_tree_v2_idx_created
4+
ON reward_merkle_tree_v2 (idx, created DESC);

sequencer/src/api.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6654,7 +6654,8 @@ mod test {
66546654
.iter()
66556655
.map(|(addr, amt)| (*addr, *amt))
66566656
.collect();
6657-
expected.sort_by_key(|(acct, _)| *acct);
6657+
// Results are sorted by account address descending
6658+
expected.sort_by_key(|(acct, _)| std::cmp::Reverse(*acct));
66586659

66596660
tracing::info!("expected accounts = {expected:?}");
66606661
let limit = expected.len().min(10_000) as u64;
@@ -6808,16 +6809,16 @@ mod test {
68086809
}
68096810

68106811
// Test pagination
6811-
// results are sorted by account address
6812+
// results are sorted by account address descending
68126813
let result_limit_2 = db.get_all_reward_accounts(15, 0, 2).await?;
68136814
assert_eq!(result_limit_2.len(), 2);
6814-
assert_eq!(result_limit_2[0], (account1, RewardAmount::from(1500u64)));
6815-
assert_eq!(result_limit_2[1], (account2, RewardAmount::from(2500u64)));
6815+
assert_eq!(result_limit_2[0], (account4, RewardAmount::from(4000u64)));
6816+
assert_eq!(result_limit_2[1], (account3, RewardAmount::from(3000u64)));
68166817

68176818
let result_offset_2 = db.get_all_reward_accounts(15, 2, 2).await?;
68186819
assert_eq!(result_offset_2.len(), 2);
6819-
assert_eq!(result_offset_2[0], (account3, RewardAmount::from(3000u64)));
6820-
assert_eq!(result_offset_2[1], (account4, RewardAmount::from(4000u64)));
6820+
assert_eq!(result_offset_2[0], (account2, RewardAmount::from(2500u64)));
6821+
assert_eq!(result_offset_2[1], (account1, RewardAmount::from(1500u64)));
68216822

68226823
Ok(())
68236824
}

sequencer/src/api/sql.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -294,27 +294,40 @@ impl CatchupStorage for SqlStorage {
294294
return Ok(Vec::new());
295295
}
296296

297-
// get the latest balance for each account
298-
// We use ROW_NUMBER() to rank entries by created height per account,
299-
// then select only the most recent entry (rn = 1) for each account.
300-
let rows = query_as::<(Value, Value)>(&format!(
297+
// get the latest balance for each account.
298+
// use DISTINCT ON for Postgres
299+
// use ROW_NUMBER() as DISTINCT ON is not supported for SQLite
300+
#[cfg(not(feature = "embedded-db"))]
301+
let query = format!(
302+
"SELECT DISTINCT ON (idx) idx, entry
303+
FROM {}
304+
WHERE idx IS NOT NULL AND created <= $1
305+
ORDER BY idx DESC, created DESC
306+
LIMIT $2 OFFSET $3",
307+
RewardMerkleTreeV2::state_type()
308+
);
309+
310+
#[cfg(feature = "embedded-db")]
311+
let query = format!(
301312
"SELECT idx, entry FROM (
302313
SELECT idx, entry, ROW_NUMBER() OVER (PARTITION BY idx ORDER BY created DESC) as \
303314
rn
304315
FROM {}
305-
WHERE created <= $1 AND idx IS NOT NULL AND entry IS NOT NULL
316+
WHERE created <= $1 AND idx IS NOT NULL
306317
) sub
307318
WHERE rn = 1
308-
ORDER BY idx
319+
ORDER BY idx DESC
309320
LIMIT $2 OFFSET $3",
310321
RewardMerkleTreeV2::state_type()
311-
))
312-
.bind(height as i64)
313-
.bind(limit as i64)
314-
.bind(offset as i64)
315-
.fetch_all(tx.as_mut())
316-
.await
317-
.context("loading reward accounts from storage")?;
322+
);
323+
324+
let rows = query_as::<(Value, Value)>(&query)
325+
.bind(height as i64)
326+
.bind(limit as i64)
327+
.bind(offset as i64)
328+
.fetch_all(tx.as_mut())
329+
.await
330+
.context("loading reward accounts from storage")?;
318331

319332
let mut accounts = Vec::new();
320333
for (idx, entry) in rows {

0 commit comments

Comments
 (0)