Skip to content

Commit 33e7ee7

Browse files
committed
feature(offchain): Add causality_region column to entity tables
For now this just tracks the tables that need the column and adds the column to the DDL, but still unconditionally inserts 0. Inserting the correct causality region is follow up work.
1 parent c3e1634 commit 33e7ee7

File tree

24 files changed

+236
-60
lines changed

24 files changed

+236
-60
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/src/subgraph/registrar.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,30 @@ async fn create_subgraph_version<C: Blockchain, S: SubgraphStore>(
617617
"block" => format!("{:?}", base_block.as_ref().map(|(_,ptr)| ptr.number))
618618
);
619619

620+
// Entity types that may be touched by offchain data sources need a causality region column.
621+
let needs_causality_region = manifest
622+
.data_sources
623+
.iter()
624+
.filter_map(|ds| ds.as_offchain())
625+
.map(|ds| ds.mapping.entities.iter())
626+
.chain(
627+
manifest
628+
.templates
629+
.iter()
630+
.filter_map(|ds| ds.as_offchain())
631+
.map(|ds| ds.mapping.entities.iter()),
632+
)
633+
.flatten()
634+
.cloned()
635+
.collect();
636+
620637
// Apply the subgraph versioning and deployment operations,
621638
// creating a new subgraph deployment if one doesn't exist.
622639
let deployment = DeploymentCreate::new(raw_string, &manifest, start_block)
623640
.graft(base_block)
624-
.debug(debug_fork);
641+
.debug(debug_fork)
642+
.has_causality_region(needs_causality_region);
643+
625644
deployment_store
626645
.create_subgraph_deployment(
627646
name,

graph/src/components/store/mod.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod traits;
44

55
pub use cache::{CachedEthereumCall, EntityCache, ModificationsAndCache};
66

7+
use diesel::types::{FromSql, ToSql};
78
pub use err::StoreError;
89
use itertools::Itertools;
910
pub use traits::*;
@@ -12,13 +13,14 @@ use futures::stream::poll_fn;
1213
use futures::{Async, Poll, Stream};
1314
use graphql_parser::schema as s;
1415
use serde::{Deserialize, Serialize};
16+
use std::borrow::Borrow;
1517
use std::collections::btree_map::Entry;
1618
use std::collections::{BTreeMap, BTreeSet, HashSet};
17-
use std::fmt;
1819
use std::fmt::Display;
1920
use std::sync::atomic::{AtomicUsize, Ordering};
2021
use std::sync::{Arc, RwLock};
2122
use std::time::Duration;
23+
use std::{fmt, io};
2224

2325
use crate::blockchain::{Block, Blockchain};
2426
use crate::data::store::scalar::Bytes;
@@ -71,6 +73,12 @@ impl<'a> From<&s::InterfaceType<'a, String>> for EntityType {
7173
}
7274
}
7375

76+
impl Borrow<str> for EntityType {
77+
fn borrow(&self) -> &str {
78+
&self.0
79+
}
80+
}
81+
7482
// This conversion should only be used in tests since it makes it too
7583
// easy to convert random strings into entity types
7684
#[cfg(debug_assertions)]
@@ -82,6 +90,22 @@ impl From<&str> for EntityType {
8290

8391
impl CheapClone for EntityType {}
8492

93+
impl FromSql<diesel::sql_types::Text, diesel::pg::Pg> for EntityType {
94+
fn from_sql(bytes: Option<&[u8]>) -> diesel::deserialize::Result<Self> {
95+
let s = <String as FromSql<_, diesel::pg::Pg>>::from_sql(bytes)?;
96+
Ok(EntityType::new(s))
97+
}
98+
}
99+
100+
impl ToSql<diesel::sql_types::Text, diesel::pg::Pg> for EntityType {
101+
fn to_sql<W: io::Write>(
102+
&self,
103+
out: &mut diesel::serialize::Output<W, diesel::pg::Pg>,
104+
) -> diesel::serialize::Result {
105+
<str as ToSql<diesel::sql_types::Text, diesel::pg::Pg>>::to_sql(self.0.as_str(), out)
106+
}
107+
}
108+
85109
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
86110
pub struct EntityFilterDerivative(bool);
87111

graph/src/data/graphql/object_or_interface.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl<'a> ObjectOrInterface<'a> {
117117
ObjectOrInterface::Object(object) => Some(vec![object]),
118118
ObjectOrInterface::Interface(interface) => schema
119119
.types_for_interface()
120-
.get(&interface.into())
120+
.get(interface.name.as_str())
121121
.map(|object_types| object_types.iter().collect()),
122122
}
123123
}
@@ -131,7 +131,7 @@ impl<'a> ObjectOrInterface<'a> {
131131
) -> bool {
132132
match self {
133133
ObjectOrInterface::Object(o) => o.name == typename,
134-
ObjectOrInterface::Interface(i) => types_for_interface[&i.into()]
134+
ObjectOrInterface::Interface(i) => types_for_interface[i.name.as_str()]
135135
.iter()
136136
.any(|o| o.name == typename),
137137
}

graph/src/data/subgraph/schema.rs

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use hex;
55
use lazy_static::lazy_static;
66
use rand::rngs::OsRng;
77
use rand::Rng;
8+
use std::collections::BTreeSet;
89
use std::str::FromStr;
910
use std::{fmt, fmt::Display};
1011

@@ -106,6 +107,7 @@ pub struct DeploymentCreate {
106107
pub graft_base: Option<DeploymentHash>,
107108
pub graft_block: Option<BlockPtr>,
108109
pub debug_fork: Option<DeploymentHash>,
110+
pub has_causality_region: BTreeSet<EntityType>,
109111
}
110112

111113
impl DeploymentCreate {
@@ -120,6 +122,7 @@ impl DeploymentCreate {
120122
graft_base: None,
121123
graft_block: None,
122124
debug_fork: None,
125+
has_causality_region: BTreeSet::new(),
123126
}
124127
}
125128

@@ -135,6 +138,11 @@ impl DeploymentCreate {
135138
self.debug_fork = fork;
136139
self
137140
}
141+
142+
pub fn has_causality_region(mut self, has_causality_region: BTreeSet<EntityType>) -> Self {
143+
self.has_causality_region = has_causality_region;
144+
self
145+
}
138146
}
139147

140148
/// The representation of a subgraph deployment when reading an existing
@@ -158,6 +166,7 @@ pub struct SubgraphDeploymentEntity {
158166
pub reorg_count: i32,
159167
pub current_reorg_depth: i32,
160168
pub max_reorg_depth: i32,
169+
pub has_causality_region: Vec<EntityType>,
161170
}
162171

163172
#[derive(Debug)]

graph/src/data_source/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ impl<C: Blockchain> DataSourceTemplate<C> {
241241
}
242242
}
243243

244+
pub fn as_offchain(&self) -> Option<&offchain::DataSourceTemplate> {
245+
match self {
246+
Self::Onchain(_) => None,
247+
Self::Offchain(t) => Some(&t),
248+
}
249+
}
250+
244251
pub fn into_onchain(self) -> Option<C::DataSourceTemplate> {
245252
match self {
246253
Self::Onchain(ds) => Some(ds),

graphql/src/introspection/resolver.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ fn interface_type_object(
135135
description: interface_type.description.clone(),
136136
fields:
137137
field_objects(schema, type_objects, &interface_type.fields),
138-
possibleTypes: schema.types_for_interface()[&interface_type.into()]
138+
possibleTypes: schema.types_for_interface()[interface_type.name.as_str()]
139139
.iter()
140140
.map(|object_type| r::Value::String(object_type.name.to_owned()))
141141
.collect::<Vec<_>>(),

store/postgres/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ git-testament = "0.2.0"
3333
itertools = "0.10.5"
3434
pin-utils = "0.1"
3535
hex = "0.4.3"
36+
pretty_assertions = "1.3.0"
3637

3738
[dev-dependencies]
3839
futures = "0.3"

store/postgres/examples/layout.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ extern crate clap;
22
extern crate graph_store_postgres;
33

44
use clap::{arg, Command};
5+
use std::collections::BTreeSet;
56
use std::process::exit;
67
use std::{fs, sync::Arc};
78

@@ -145,7 +146,7 @@ pub fn main() {
145146
);
146147
let site = Arc::new(make_dummy_site(subgraph, namespace, "anet".to_string()));
147148
let catalog = ensure(
148-
Catalog::for_tests(site.clone()),
149+
Catalog::for_tests(site.clone(), BTreeSet::new()),
149150
"Failed to construct catalog",
150151
);
151152
let layout = ensure(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
alter table subgraphs.subgraph_deployment drop column has_causality_region;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
alter table subgraphs.subgraph_deployment add column has_causality_region text[] not null default array[]::text[];

store/postgres/src/block_range.rs

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use crate::relational::Table;
1515
/// entities
1616
pub(crate) const BLOCK_RANGE_COLUMN: &str = "block_range";
1717

18+
/// The name of the column that stores the causality region of an entity.
19+
pub(crate) const CAUSALITY_REGION_COLUMN: &str = "causality_region";
20+
1821
/// The SQL clause we use to check that an entity version is current;
1922
/// that version has an unbounded block range, but checking for
2023
/// `upper_inf(block_range)` is slow and can't use the exclusion

store/postgres/src/catalog.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use diesel::{
66
sql_types::{Array, Double, Nullable, Text},
77
ExpressionMethods, QueryDsl,
88
};
9-
use graph::components::store::VersionStats;
10-
use std::collections::{HashMap, HashSet};
9+
use graph::components::store::{EntityType, VersionStats};
10+
use std::collections::{BTreeSet, HashMap, HashSet};
1111
use std::fmt::Write;
1212
use std::iter::FromIterator;
1313
use std::sync::Arc;
@@ -77,11 +77,15 @@ const CREATE_EXCLUSION_CONSTRAINT: bool = false;
7777
pub struct Catalog {
7878
pub site: Arc<Site>,
7979
text_columns: HashMap<String, HashSet<String>>,
80+
8081
pub use_poi: bool,
8182
/// Whether `bytea` columns are indexed with just a prefix (`true`) or
8283
/// in their entirety. This influences both DDL generation and how
8384
/// queries are generated
8485
pub use_bytea_prefix: bool,
86+
87+
/// Set of tables which have an explicit causality region column.
88+
pub(crate) has_causality_region: BTreeSet<EntityType>,
8589
}
8690

8791
impl Catalog {
@@ -90,6 +94,7 @@ impl Catalog {
9094
conn: &PgConnection,
9195
site: Arc<Site>,
9296
use_bytea_prefix: bool,
97+
has_causality_region: Vec<EntityType>,
9398
) -> Result<Self, StoreError> {
9499
let text_columns = get_text_columns(conn, &site.namespace)?;
95100
let use_poi = supports_proof_of_indexing(conn, &site.namespace)?;
@@ -98,11 +103,12 @@ impl Catalog {
98103
text_columns,
99104
use_poi,
100105
use_bytea_prefix,
106+
has_causality_region: has_causality_region.into_iter().collect(),
101107
})
102108
}
103109

104110
/// Return a new catalog suitable for creating a new subgraph
105-
pub fn for_creation(site: Arc<Site>) -> Self {
111+
pub fn for_creation(site: Arc<Site>, has_causality_region: BTreeSet<EntityType>) -> Self {
106112
Catalog {
107113
site,
108114
text_columns: HashMap::default(),
@@ -111,18 +117,23 @@ impl Catalog {
111117
// DDL generation creates indexes for prefixes of bytes columns
112118
// see: attr-bytea-prefix
113119
use_bytea_prefix: true,
120+
has_causality_region,
114121
}
115122
}
116123

117124
/// Make a catalog as if the given `schema` did not exist in the database
118125
/// yet. This function should only be used in situations where a database
119126
/// connection is definitely not available, such as in unit tests
120-
pub fn for_tests(site: Arc<Site>) -> Result<Self, StoreError> {
127+
pub fn for_tests(
128+
site: Arc<Site>,
129+
has_causality_region: BTreeSet<EntityType>,
130+
) -> Result<Self, StoreError> {
121131
Ok(Catalog {
122132
site,
123133
text_columns: HashMap::default(),
124134
use_poi: false,
125135
use_bytea_prefix: true,
136+
has_causality_region,
126137
})
127138
}
128139

store/postgres/src/deployment.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ use diesel::{
1313
sql_query,
1414
sql_types::{Nullable, Text},
1515
};
16-
use graph::prelude::{
17-
anyhow, bigdecimal::ToPrimitive, hex, web3::types::H256, BigDecimal, BlockNumber, BlockPtr,
18-
DeploymentHash, DeploymentState, Schema, StoreError,
19-
};
2016
use graph::{blockchain::block_stream::FirehoseCursor, data::subgraph::schema::SubgraphError};
17+
use graph::{
18+
components::store::EntityType,
19+
prelude::{
20+
anyhow, bigdecimal::ToPrimitive, hex, web3::types::H256, BigDecimal, BlockNumber, BlockPtr,
21+
DeploymentHash, DeploymentState, Schema, StoreError,
22+
},
23+
};
2124
use graph::{
2225
data::subgraph::{
2326
schema::{DeploymentCreate, SubgraphManifestEntity},
@@ -84,6 +87,10 @@ table! {
8487
current_reorg_depth -> Integer,
8588
max_reorg_depth -> Integer,
8689
firehose_cursor -> Nullable<Text>,
90+
91+
// Entity types that have a `causality_region` column.
92+
// Names stored as present in the schema, not in snake case.
93+
has_causality_region -> Array<Text>,
8794
}
8895
}
8996

@@ -769,6 +776,19 @@ pub(crate) fn health(conn: &PgConnection, id: DeploymentId) -> Result<SubgraphHe
769776
.map_err(|e| e.into())
770777
}
771778

779+
pub(crate) fn has_causality_region(
780+
conn: &PgConnection,
781+
id: DeploymentId,
782+
) -> Result<Vec<EntityType>, StoreError> {
783+
use subgraph_deployment as d;
784+
785+
d::table
786+
.filter(d::id.eq(id))
787+
.select(d::has_causality_region)
788+
.get_result(conn)
789+
.map_err(|e| e.into())
790+
}
791+
772792
/// Reverts the errors and updates the subgraph health if necessary.
773793
pub(crate) fn revert_subgraph_errors(
774794
conn: &PgConnection,
@@ -916,8 +936,10 @@ pub fn create_deployment(
916936
graft_base,
917937
graft_block,
918938
debug_fork,
939+
has_causality_region,
919940
} = deployment;
920941
let earliest_block_number = start_block.as_ref().map(|ptr| ptr.number).unwrap_or(0);
942+
let has_causality_region = Vec::from_iter(has_causality_region.into_iter());
921943

922944
let deployment_values = (
923945
d::id.eq(site.id),
@@ -935,6 +957,7 @@ pub fn create_deployment(
935957
d::graft_block_hash.eq(b(&graft_block)),
936958
d::graft_block_number.eq(n(&graft_block)),
937959
d::debug_fork.eq(debug_fork.as_ref().map(|s| s.as_str())),
960+
d::has_causality_region.eq(has_causality_region),
938961
);
939962

940963
let graph_node_version_id = GraphNodeVersion::create_or_get(conn)?;

store/postgres/src/deployment_store.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ impl DeploymentStore {
175175
let exists = deployment::exists(&conn, &site)?;
176176

177177
// Create (or update) the metadata. Update only happens in tests
178+
let has_causality_region = deployment.has_causality_region.clone();
178179
if replace || !exists {
179180
deployment::create_deployment(&conn, &site, deployment, exists, replace)?;
180181
};
@@ -184,7 +185,12 @@ impl DeploymentStore {
184185
let query = format!("create schema {}", &site.namespace);
185186
conn.batch_execute(&query)?;
186187

187-
let layout = Layout::create_relational_schema(&conn, site.clone(), schema)?;
188+
let layout = Layout::create_relational_schema(
189+
&conn,
190+
site.clone(),
191+
schema,
192+
has_causality_region,
193+
)?;
188194
// See if we are grafting and check that the graft is permissible
189195
if let Some(base) = graft_base {
190196
let errors = layout.can_copy_from(&base);
@@ -280,7 +286,7 @@ impl DeploymentStore {
280286
.interfaces_for_type(&key.entity_type)
281287
.into_iter()
282288
.flatten()
283-
.map(|interface| &types_for_interface[&interface.into()])
289+
.map(|interface| &types_for_interface[interface.name.as_str()])
284290
.flatten()
285291
.map(EntityType::from)
286292
.filter(|type_name| type_name != &key.entity_type),

0 commit comments

Comments
 (0)