Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions crates/iota-graphql-rpc/src/backward_view/consistent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ pub(crate) fn query(
/// Returns active objects from `checkpointed_objects` that were consistent
/// also at the given checkpoint.
///
/// Uses a LEFT JOIN against `objects_backward_history` to exclude objects
/// that have any entry with `superseded_at_checkpoint > checkpoint_viewed_at`.
/// Uses a NOT EXISTS subquery against `objects_backward_history` to exclude
/// objects that have any entry with
/// `superseded_at_checkpoint > checkpoint_viewed_at`.
///
/// # Implementation notes
///
/// NOT EXISTS lets Postgres answer "did this object change?" row by row,
/// with one index lookup each. A LEFT JOIN on a `SELECT DISTINCT` subquery
/// takes that option away: the full list of changed objects must always be
/// built first, and in the worst plans it is then also scanned for every
/// row.
fn consistent_checkpointed_objects(
checkpoint_viewed_at: i64,
page: &Page<Cursor>,
Expand All @@ -47,17 +56,19 @@ fn consistent_checkpointed_objects(
format!("object_status = {ACTIVE}")
);

let changed_subquery = query!(format!(
"SELECT DISTINCT object_id FROM objects_backward_history \
WHERE superseded_at_checkpoint > {checkpoint_viewed_at}"
));
let mut source = query!(
r#"SELECT candidates.* FROM ({}) candidates
LEFT JOIN ({}) changed ON candidates.object_id = changed.object_id"#,
checkpointed_filtered,
changed_subquery
"SELECT candidates.* FROM ({}) candidates",
checkpointed_filtered
);
source = filter!(
source,
format!(
"NOT EXISTS (\
SELECT 1 FROM objects_backward_history changed \
WHERE changed.object_id = candidates.object_id \
AND changed.superseded_at_checkpoint > {checkpoint_viewed_at})"
)
);
source = filter!(source, "changed.object_id IS NULL");
page.apply::<StoredBackwardObject>(source)
}

Expand Down
21 changes: 11 additions & 10 deletions crates/iota-graphql-rpc/src/types/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,20 @@ fn balance_query(
address,
coin_type.clone(),
);
let changed = query!(format!(
"SELECT DISTINCT object_id FROM objects_backward_history \
WHERE superseded_at_checkpoint > {checkpoint_viewed_at}"
));
// NOT EXISTS instead of a JOIN; see `consistent_checkpointed_objects`
// for explanation.
let source_a = filter!(
query!(
r#"SELECT candidates.object_id, candidates.coin_balance, candidates.coin_type
FROM ({}) candidates
LEFT JOIN ({}) changed ON candidates.object_id = changed.object_id"#,
checkpointed,
changed
"SELECT candidates.object_id, candidates.coin_balance, candidates.coin_type \
FROM ({}) candidates",
checkpointed
),
"changed.object_id IS NULL"
format!(
"NOT EXISTS (\
SELECT 1 FROM objects_backward_history changed \
WHERE changed.object_id = candidates.object_id \
AND changed.superseded_at_checkpoint > {checkpoint_viewed_at})"
)
);

let mut history = filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Restore the default extended-statistics target.
ALTER STATISTICS checkpointed_objects_type_stats SET STATISTICS -1;
ALTER STATISTICS objects_backward_history_type_stats SET STATISTICS -1;

ANALYZE checkpointed_objects (object_type, object_type_package, object_type_module, object_type_name);
ANALYZE objects_backward_history (object_type, object_type_package, object_type_module, object_type_name);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Raise the extended-statistics target on the backward-diff tables so all
-- distinct object types fit in the multi-column MCV list; types outside the
-- list get a rows=1 estimate, leading to plans that time out.
ALTER STATISTICS checkpointed_objects_type_stats SET STATISTICS 2500;
ALTER STATISTICS objects_backward_history_type_stats SET STATISTICS 2500;

-- Rebuild the statistics objects modified above with their new target.
-- ANALYZE rebuilds them only if all their columns are listed; listing just
-- those columns keeps the ANALYZE cheap.
ANALYZE checkpointed_objects (object_type, object_type_package, object_type_module, object_type_name);
ANALYZE objects_backward_history (object_type, object_type_package, object_type_module, object_type_name);
Loading