Skip to content

Commit 1cb3401

Browse files
committed
Subgraph composition: Rework generation of the VID
1 parent 9658d8c commit 1cb3401

File tree

24 files changed

+536
-277
lines changed

24 files changed

+536
-277
lines changed

chain/substreams/src/trigger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ where
237237
logger,
238238
);
239239

240-
state.entity_cache.set(key, entity)?;
240+
state.entity_cache.set(key, entity, block.number)?;
241241
}
242242
ParsedChanges::Delete(entity_key) => {
243243
let entity_type = entity_key.entity_type.cheap_clone();

core/src/subgraph/runner.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,7 @@ async fn update_proof_of_indexing(
16031603
key: EntityKey,
16041604
digest: Bytes,
16051605
block_time: BlockTime,
1606+
block: BlockNumber,
16061607
) -> Result<(), Error> {
16071608
let digest_name = entity_cache.schema.poi_digest();
16081609
let mut data = vec![
@@ -1617,11 +1618,12 @@ async fn update_proof_of_indexing(
16171618
data.push((entity_cache.schema.poi_block_time(), block_time));
16181619
}
16191620
let poi = entity_cache.make_entity(data)?;
1620-
entity_cache.set(key, poi)
1621+
entity_cache.set(key, poi, block)
16211622
}
16221623

16231624
let _section_guard = stopwatch.start_section("update_proof_of_indexing");
16241625

1626+
let block_number = proof_of_indexing.get_block();
16251627
let mut proof_of_indexing = proof_of_indexing.take();
16261628

16271629
for (causality_region, stream) in proof_of_indexing.drain() {
@@ -1657,6 +1659,7 @@ async fn update_proof_of_indexing(
16571659
entity_key,
16581660
updated_proof_of_indexing,
16591661
block_time,
1662+
block_number,
16601663
)?;
16611664
}
16621665

graph/src/components/store/entity_cache.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::anyhow;
1+
use anyhow::{anyhow, bail};
22
use std::borrow::Borrow;
33
use std::collections::HashMap;
44
use std::fmt::{self, Debug};
@@ -17,6 +17,10 @@ use super::{BlockNumber, DerivedEntityQuery, LoadRelatedRequest, StoreError};
1717

1818
pub type EntityLfuCache = LfuCache<EntityKey, Option<Arc<Entity>>>;
1919

20+
// Number of VIDs that are reserved outside of the generated ones here.
21+
// Currently none is used, but lets reserve a few more.
22+
const RESERVED_VIDS: u32 = 100;
23+
2024
/// The scope in which the `EntityCache` should perform a `get` operation
2125
pub enum GetScope {
2226
/// Get from all previously stored entities in the store
@@ -105,6 +109,10 @@ pub struct EntityCache {
105109
/// generated IDs, the `EntityCache` needs to be newly instantiated for
106110
/// each block
107111
seq: u32,
112+
113+
// Sequence number of the next VID value for this block. The value written
114+
// in the database consist of a block number and this SEQ number.
115+
pub vid_seq: u32,
108116
}
109117

110118
impl Debug for EntityCache {
@@ -132,6 +140,7 @@ impl EntityCache {
132140
schema: store.input_schema(),
133141
store,
134142
seq: 0,
143+
vid_seq: RESERVED_VIDS,
135144
}
136145
}
137146

@@ -152,6 +161,7 @@ impl EntityCache {
152161
schema: store.input_schema(),
153162
store,
154163
seq: 0,
164+
vid_seq: RESERVED_VIDS,
155165
}
156166
}
157167

@@ -278,7 +288,7 @@ impl EntityCache {
278288
) -> Result<Option<Entity>, anyhow::Error> {
279289
match op {
280290
EntityOp::Update(entity) | EntityOp::Overwrite(entity)
281-
if query.matches(key, entity) =>
291+
if query.matches(key, &entity) =>
282292
{
283293
Ok(Some(entity.clone()))
284294
}
@@ -349,10 +359,30 @@ impl EntityCache {
349359
/// with existing data. The entity will be validated against the
350360
/// subgraph schema, and any errors will result in an `Err` being
351361
/// returned.
352-
pub fn set(&mut self, key: EntityKey, entity: Entity) -> Result<(), anyhow::Error> {
362+
pub fn set(
363+
&mut self,
364+
key: EntityKey,
365+
entity: Entity,
366+
block: BlockNumber,
367+
) -> Result<(), anyhow::Error> {
353368
// check the validate for derived fields
354369
let is_valid = entity.validate(&key).is_ok();
355370

371+
// The next VID is based on a block number and a sequence within the block
372+
let vid = ((block as i64) << 32) + self.vid_seq as i64;
373+
self.vid_seq += 1;
374+
let mut entity = entity;
375+
let old_vid = entity.set_vid(vid).expect("the vid should be set");
376+
// Make sure that there was no VID previously set for this entity.
377+
if let Some(ovid) = old_vid {
378+
bail!(
379+
"VID: {} of entity: {} with ID: {} was already present when set in EntityCache",
380+
ovid,
381+
key.entity_type,
382+
entity.id()
383+
);
384+
}
385+
356386
self.entity_op(key.clone(), EntityOp::Update(entity));
357387

358388
// The updates we were given are not valid by themselves; force a
@@ -489,7 +519,7 @@ impl EntityCache {
489519
// Entity was removed and then updated, so it will be overwritten
490520
(Some(current), EntityOp::Overwrite(data)) => {
491521
let data = Arc::new(data);
492-
self.current.insert(key.clone(), Some(data.clone()));
522+
self.current.insert(key.clone(), Some(data.cheap_clone()));
493523
if current != data {
494524
Some(Overwrite {
495525
key,

graph/src/components/subgraph/proof_of_indexing/online.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ impl ProofOfIndexing {
242242
pub fn take(self) -> HashMap<Id, BlockEventStream> {
243243
self.per_causality_region
244244
}
245+
246+
pub fn get_block(&self) -> BlockNumber {
247+
self.block_number
248+
}
245249
}
246250

247251
pub struct ProofOfIndexingFinisher {

graph/src/data/store/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
derive::CacheWeight,
44
prelude::{lazy_static, q, r, s, CacheWeight, QueryExecutionError},
55
runtime::gas::{Gas, GasSizeOf},
6-
schema::{EntityKey, EntityType},
6+
schema::{input::VID_FIELD, EntityKey, EntityType},
77
util::intern::{self, AtomPool},
88
util::intern::{Error as InternError, NullValue, Object},
99
};
@@ -910,6 +910,29 @@ impl Entity {
910910
Id::try_from(self.get("id").unwrap().clone()).expect("the id is set to a valid value")
911911
}
912912

913+
/// Return the VID of this entity and if its missing or of a type different than
914+
/// i64 it panics.
915+
pub fn vid(&self) -> i64 {
916+
self.get(VID_FIELD)
917+
.expect("the vid is set")
918+
.as_int8()
919+
.expect("the vid is set to a valid value")
920+
}
921+
922+
/// Sets the VID of the entity. The previous one is returned.
923+
pub fn set_vid(&mut self, value: i64) -> Result<Option<Value>, InternError> {
924+
self.0.insert(VID_FIELD, value.into())
925+
}
926+
927+
/// Sets the VID if it's not already set. Should be used only for tests.
928+
#[cfg(debug_assertions)]
929+
pub fn set_vid_if_empty(&mut self) {
930+
let vid = self.get(VID_FIELD);
931+
if vid.is_none() {
932+
let _ = self.set_vid(100).expect("the vid should be set");
933+
}
934+
}
935+
913936
/// Merges an entity update `update` into this entity.
914937
///
915938
/// If a key exists in both entities, the value from `update` is chosen.

graph/src/data/subgraph/api_version.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ pub const SPEC_VERSION_1_1_0: Version = Version::new(1, 1, 0);
5454
// Enables eth call declarations and indexed arguments(topics) filtering in manifest
5555
pub const SPEC_VERSION_1_2_0: Version = Version::new(1, 2, 0);
5656

57-
// Enables subgraphs as datasource
57+
// Enables subgraphs as datasource.
58+
// Changes the way the VID field is generated. It used to be autoincrement. Now its
59+
// based on block number and the order of the entities in a block. The latter
60+
// represents the write order across all entity types in the subgraph.
5861
pub const SPEC_VERSION_1_3_0: Version = Version::new(1, 3, 0);
5962

6063
// The latest spec version available
61-
pub const LATEST_VERSION: &Version = &SPEC_VERSION_1_2_0;
64+
pub const LATEST_VERSION: &Version = &SPEC_VERSION_1_3_0;
6265

6366
pub const MIN_SPEC_VERSION: Version = Version::new(0, 0, 2);
6467

graph/src/schema/input/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub(crate) const POI_OBJECT: &str = "Poi$";
3535
const POI_DIGEST: &str = "digest";
3636
/// The name of the PoI attribute for storing the block time
3737
const POI_BLOCK_TIME: &str = "blockTime";
38+
pub(crate) const VID_FIELD: &str = "vid";
3839

3940
pub mod kw {
4041
pub const ENTITY: &str = "entity";
@@ -1597,6 +1598,8 @@ fn atom_pool(document: &s::Document) -> AtomPool {
15971598
pool.intern(POI_DIGEST);
15981599
pool.intern(POI_BLOCK_TIME);
15991600

1601+
pool.intern(VID_FIELD);
1602+
16001603
for definition in &document.definitions {
16011604
match definition {
16021605
s::Definition::TypeDefinition(typedef) => match typedef {

graph/src/schema/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub mod ast;
2121
mod entity_key;
2222
mod entity_type;
2323
mod fulltext;
24-
mod input;
24+
pub(crate) mod input;
2525

2626
pub use api::{is_introspection_field, APISchemaError, INTROSPECTION_QUERY_TYPE};
2727

runtime/test/src/test.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,13 +476,13 @@ async fn test_ipfs_block() {
476476
// The user_data value we use with calls to ipfs_map
477477
const USER_DATA: &str = "user_data";
478478

479-
fn make_thing(id: &str, value: &str) -> (String, EntityModification) {
479+
fn make_thing(id: &str, value: &str, vid: i64) -> (String, EntityModification) {
480480
const DOCUMENT: &str = " type Thing @entity { id: String!, value: String!, extra: String }";
481481
lazy_static! {
482482
static ref SCHEMA: InputSchema = InputSchema::raw(DOCUMENT, "doesntmatter");
483483
static ref THING_TYPE: EntityType = SCHEMA.entity_type("Thing").unwrap();
484484
}
485-
let data = entity! { SCHEMA => id: id, value: value, extra: USER_DATA };
485+
let data = entity! { SCHEMA => id: id, value: value, extra: USER_DATA, vid: vid };
486486
let key = THING_TYPE.parse_key(id).unwrap();
487487
(
488488
format!("{{ \"id\": \"{}\", \"value\": \"{}\"}}", id, value),
@@ -552,8 +552,8 @@ async fn test_ipfs_map(api_version: Version, json_error_msg: &str) {
552552
let subgraph_id = "ipfsMap";
553553

554554
// Try it with two valid objects
555-
let (str1, thing1) = make_thing("one", "eins");
556-
let (str2, thing2) = make_thing("two", "zwei");
555+
let (str1, thing1) = make_thing("one", "eins", 100);
556+
let (str2, thing2) = make_thing("two", "zwei", 100);
557557
let ops = run_ipfs_map(
558558
ipfs.clone(),
559559
subgraph_id,
@@ -1022,8 +1022,8 @@ async fn test_entity_store(api_version: Version) {
10221022

10231023
let schema = store.input_schema(&deployment.hash).unwrap();
10241024

1025-
let alex = entity! { schema => id: "alex", name: "Alex" };
1026-
let steve = entity! { schema => id: "steve", name: "Steve" };
1025+
let alex = entity! { schema => id: "alex", name: "Alex", vid: 0i64 };
1026+
let steve = entity! { schema => id: "steve", name: "Steve", vid: 1i64 };
10271027
let user_type = schema.entity_type("User").unwrap();
10281028
test_store::insert_entities(
10291029
&deployment,

runtime/wasm/src/host_exports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ impl HostExports {
350350

351351
state.metrics.track_entity_write(&entity_type, &entity);
352352

353-
state.entity_cache.set(key, entity)?;
353+
state.entity_cache.set(key, entity, block)?;
354354

355355
Ok(())
356356
}

0 commit comments

Comments
 (0)