Skip to content

Commit 12427fd

Browse files
committed
graph, store: Keep all pending changes in one large list
When we start combining batches, we will want to mutate some changes in place to avoid the data copies that we would need if we kept them separated by the kind of change that is needed.
1 parent 9f3c5aa commit 12427fd

8 files changed

Lines changed: 456 additions & 304 deletions

File tree

‎graph/src/components/store/write.rs‎

Lines changed: 319 additions & 166 deletions
Large diffs are not rendered by default.

‎store/postgres/src/deployment.rs‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,18 +816,21 @@ pub fn update_deployment_status(
816816
.map_err(StoreError::from)
817817
}
818818

819-
/// Insert the errors and check if the subgraph needs to be set as unhealthy.
819+
/// Insert the errors and check if the subgraph needs to be set as
820+
/// unhealthy. The `latest_block` is only used to check whether the subgraph
821+
/// is healthy as of that block; errors are inserted according to the
822+
/// `block_ptr` they contain
820823
pub(crate) fn insert_subgraph_errors(
821824
conn: &PgConnection,
822825
id: &DeploymentHash,
823826
deterministic_errors: &[SubgraphError],
824-
block: BlockNumber,
827+
latest_block: BlockNumber,
825828
) -> Result<(), StoreError> {
826829
for error in deterministic_errors {
827830
insert_subgraph_error(conn, error)?;
828831
}
829832

830-
check_health(conn, id, block)
833+
check_health(conn, id, latest_block)
831834
}
832835

833836
#[cfg(debug_assertions)]

‎store/postgres/src/deployment_store.rs‎

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ use diesel::prelude::*;
55
use diesel::r2d2::{ConnectionManager, PooledConnection};
66
use graph::anyhow::Context;
77
use graph::blockchain::block_stream::FirehoseCursor;
8-
use graph::components::store::write::{EntityRef, EntityWrite, RowGroup, RowGroups};
8+
use graph::components::store::write::RowGroup;
99
use graph::components::store::{
1010
Batch, DerivedEntityQuery, EntityKey, EntityType, PrunePhase, PruneReporter, PruneRequest,
1111
PruningStrategy, StoredDynamicDataSource, VersionStats,
1212
};
1313
use graph::components::versions::VERSIONS;
1414
use graph::data::query::Trace;
1515
use graph::data::subgraph::{status, SPEC_VERSION_0_0_6};
16+
use graph::data::value::Word;
1617
use graph::data_source::CausalityRegion;
1718
use graph::prelude::futures03::FutureExt;
1819
use graph::prelude::{
@@ -272,7 +273,8 @@ impl DeploymentStore {
272273
&self,
273274
conn: &PgConnection,
274275
layout: &Layout,
275-
key: &EntityKey,
276+
entity_type: &EntityType,
277+
entity_id: &Word,
276278
) -> Result<(), StoreError> {
277279
// Collect all types that share an interface implementation with this
278280
// entity type, and make sure there are no conflicting IDs.
@@ -291,100 +293,71 @@ impl DeploymentStore {
291293
.expect("API schema should be present")
292294
.clone();
293295
let types_for_interface = schema.types_for_interface();
294-
let entity_type = key.entity_type.to_string();
296+
let entity_type_str = entity_type.to_string();
295297
let types_with_shared_interface = Vec::from_iter(
296298
schema
297-
.interfaces_for_type(&key.entity_type)
299+
.interfaces_for_type(entity_type)
298300
.into_iter()
299301
.flatten()
300302
.flat_map(|interface| &types_for_interface[&EntityType::from(interface)])
301303
.map(EntityType::from)
302-
.filter(|type_name| type_name != &key.entity_type),
304+
.filter(|type_name| type_name != entity_type),
303305
);
304306

305307
if !types_with_shared_interface.is_empty() {
306308
if let Some(conflicting_entity) =
307-
layout.conflicting_entity(conn, &key.entity_id, types_with_shared_interface)?
309+
layout.conflicting_entity(conn, entity_id, types_with_shared_interface)?
308310
{
309311
return Err(StoreError::ConflictingId(
310-
entity_type,
311-
key.entity_id.to_string(),
312+
entity_type_str,
313+
entity_id.to_string(),
312314
conflicting_entity,
313315
));
314316
}
315317
}
316318
Ok(())
317319
}
318320

319-
fn apply_entity_modifications(
321+
fn apply_entity_modifications<'a>(
320322
&self,
321323
conn: &PgConnection,
322324
layout: &Layout,
323-
inserts: &RowGroups<EntityWrite>,
324-
overwrites: &RowGroups<EntityWrite>,
325-
removes: &RowGroups<EntityRef>,
325+
groups: impl Iterator<Item = &'a RowGroup>,
326326
stopwatch: &StopwatchMetrics,
327327
) -> Result<i32, StoreError> {
328328
let mut count = 0;
329329

330-
// Apply modification groups.
331-
// Inserts:
332-
for group in &inserts.groups {
330+
for group in groups {
331+
if group.has_clamps() {
332+
count -= self.remove_entities(group, conn, layout, stopwatch)? as i32;
333+
}
333334
count += self.insert_entities(group, conn, layout, stopwatch)? as i32
334335
}
335336

336-
// Overwrites:
337-
for group in &overwrites.groups {
338-
// we do not update the count since the number of entities remains the same
339-
self.overwrite_entities(group, conn, layout, stopwatch)?;
340-
}
341-
342-
// Removals
343-
for group in &removes.groups {
344-
count -= self.remove_entities(group, conn, layout, stopwatch)? as i32;
345-
}
346337
Ok(count)
347338
}
348339

349340
fn insert_entities<'a>(
350341
&'a self,
351-
group: &'a RowGroup<EntityWrite>,
342+
group: &'a RowGroup,
352343
conn: &PgConnection,
353344
layout: &'a Layout,
354345
stopwatch: &StopwatchMetrics,
355346
) -> Result<usize, StoreError> {
356347
let section = stopwatch.start_section("check_interface_entity_uniqueness");
357-
for row in group.rows.iter() {
348+
for row in group.writes() {
358349
// WARNING: This will potentially execute 2 queries for each entity key.
359-
self.check_interface_entity_uniqueness(conn, layout, &row.key)?;
350+
self.check_interface_entity_uniqueness(conn, layout, &group.entity_type, &row.id())?;
360351
}
361352
section.end();
362353

363354
let _section = stopwatch.start_section("apply_entity_modifications_insert");
364355
layout.insert(conn, group, stopwatch)
365356
}
366357

367-
fn overwrite_entities<'a>(
368-
&'a self,
369-
group: &'a RowGroup<EntityWrite>,
370-
conn: &PgConnection,
371-
layout: &'a Layout,
372-
stopwatch: &StopwatchMetrics,
373-
) -> Result<usize, StoreError> {
374-
let section = stopwatch.start_section("check_interface_entity_uniqueness");
375-
for row in group.rows.iter() {
376-
// WARNING: This will potentially execute 2 queries for each entity key.
377-
self.check_interface_entity_uniqueness(conn, layout, &row.key)?;
378-
}
379-
section.end();
380-
381-
let _section = stopwatch.start_section("apply_entity_modifications_update");
382-
layout.update(conn, group, stopwatch)
383-
}
384-
385358
fn remove_entities(
386359
&self,
387-
group: &RowGroup<EntityRef>,
360+
group: &RowGroup,
388361
conn: &PgConnection,
389362
layout: &Layout,
390363
stopwatch: &StopwatchMetrics,
@@ -1137,9 +1110,7 @@ impl DeploymentStore {
11371110
let count = self.apply_entity_modifications(
11381111
&conn,
11391112
layout.as_ref(),
1140-
&batch.inserts,
1141-
&batch.overwrites,
1142-
&batch.removes,
1113+
batch.groups(),
11431114
stopwatch,
11441115
)?;
11451116
section.end();

‎store/postgres/src/relational.rs‎

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use diesel::types::{FromSql, ToSql};
2424
use diesel::{connection::SimpleConnection, Connection};
2525
use diesel::{debug_query, OptionalExtension, PgConnection, RunQueryDsl};
2626
use graph::cheap_clone::CheapClone;
27-
use graph::components::store::write::{EntityRef, EntityWrite, RowGroup};
27+
use graph::components::store::write::RowGroup;
2828
use graph::constraint_violation;
2929
use graph::data::graphql::TypeExt as _;
3030
use graph::data::query::Trace;
@@ -654,7 +654,7 @@ impl Layout {
654654
pub fn insert<'a>(
655655
&'a self,
656656
conn: &PgConnection,
657-
group: &'a RowGroup<EntityWrite>,
657+
group: &'a RowGroup,
658658
stopwatch: &StopwatchMetrics,
659659
) -> Result<usize, StoreError> {
660660
let table = self.table_for_entity(&group.entity_type)?;
@@ -664,10 +664,13 @@ impl Layout {
664664
// We insert the entities in chunks to make sure each operation does
665665
// not exceed the maximum number of bindings allowed in queries
666666
let chunk_size = InsertQuery::chunk_size(table);
667-
for chunk in group.rows.chunks(chunk_size) {
668-
count += InsertQuery::new(table, chunk)?
669-
.get_results(conn)
670-
.map(|ids| ids.len())?
667+
for chunk in group.write_chunks(chunk_size) {
668+
// Empty chunks would lead to invalid SQL
669+
if !chunk.is_empty() {
670+
count += InsertQuery::new(table, &chunk)?
671+
.get_results(conn)
672+
.map(|ids| ids.len())?
673+
}
671674
}
672675
Ok(count)
673676
}
@@ -800,15 +803,15 @@ impl Layout {
800803
pub fn update<'a>(
801804
&'a self,
802805
conn: &PgConnection,
803-
group: &'a RowGroup<EntityWrite>,
806+
group: &'a RowGroup,
804807
stopwatch: &StopwatchMetrics,
805808
) -> Result<usize, StoreError> {
806809
let table = self.table_for_entity(&group.entity_type)?;
807-
if table.immutable {
810+
if table.immutable && group.has_clamps() {
808811
let ids = group
809812
.rows
810813
.iter()
811-
.map(|row| row.key.entity_id.as_str())
814+
.map(|row| row.id().as_str())
812815
.collect::<Vec<_>>()
813816
.join(", ");
814817
return Err(constraint_violation!(
@@ -819,9 +822,8 @@ impl Layout {
819822
}
820823

821824
let section = stopwatch.start_section("update_modification_clamp_range_query");
822-
for (block, rows) in group.runs() {
823-
let entity_keys: Vec<&str> =
824-
rows.iter().map(|row| row.key.entity_id.as_str()).collect();
825+
for (block, rows) in group.clamps_by_block() {
826+
let entity_keys: Vec<&str> = rows.iter().map(|row| row.id().as_str()).collect();
825827

826828
ClampRangeQuery::new(table, &entity_keys, block)?.execute(conn)?;
827829
}
@@ -833,8 +835,8 @@ impl Layout {
833835
// We insert the entities in chunks to make sure each operation does
834836
// not exceed the maximum number of bindings allowed in queries
835837
let chunk_size = InsertQuery::chunk_size(table);
836-
for chunk in group.rows.chunks(chunk_size) {
837-
count += InsertQuery::new(table, chunk)?.execute(conn)?;
838+
for chunk in group.write_chunks(chunk_size) {
839+
count += InsertQuery::new(table, &chunk)?.execute(conn)?;
838840
}
839841

840842
Ok(count)
@@ -843,24 +845,21 @@ impl Layout {
843845
pub fn delete(
844846
&self,
845847
conn: &PgConnection,
846-
group: &RowGroup<EntityRef>,
848+
group: &RowGroup,
847849
stopwatch: &StopwatchMetrics,
848850
) -> Result<usize, StoreError> {
849851
let table = self.table_for_entity(&group.entity_type)?;
850852
if table.immutable {
851853
return Err(constraint_violation!(
852854
"entities of type `{}` can not be deleted since they are immutable. Entity ids are [{}]",
853-
table.object, group.rows.iter().map(|eref| &eref.key.entity_id).join(", ")
855+
table.object, group.rows.iter().map(|eref| eref.id()).join(", ")
854856
));
855857
}
856858

857859
let _section = stopwatch.start_section("delete_modification_clamp_range_query");
858860
let mut count = 0;
859-
for (block, rows) in group.runs() {
860-
let ids: Vec<_> = rows
861-
.iter()
862-
.map(|eref| eref.key.entity_id.as_str())
863-
.collect();
861+
for (block, rows) in group.clamps_by_block() {
862+
let ids: Vec<_> = rows.iter().map(|eref| eref.id().as_str()).collect();
864863
for chunk in ids.chunks(DELETE_OPERATION_CHUNK_SIZE) {
865864
count += ClampRangeQuery::new(table, chunk, block)?.execute(conn)?
866865
}

0 commit comments

Comments
 (0)