@@ -2,6 +2,7 @@ use anyhow::{anyhow, bail};
22use std:: borrow:: Borrow ;
33use std:: collections:: HashMap ;
44use std:: fmt:: { self , Debug } ;
5+ use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
56use std:: sync:: Arc ;
67
78use crate :: cheap_clone:: CheapClone ;
@@ -21,6 +22,52 @@ pub type EntityLfuCache = LfuCache<EntityKey, Option<Arc<Entity>>>;
2122// Currently none is used, but lets reserve a few more.
2223const RESERVED_VIDS : u32 = 100 ;
2324
25+ /// Shared generator for VID and entity ID sequences within a block.
26+ ///
27+ /// Created once per block and shared (via `Arc`) across all `EntityCache`
28+ /// instances that operate on the same block. This prevents VID collisions
29+ /// when multiple isolated caches (e.g. ipfs.map callbacks, offchain
30+ /// triggers) write entities in the same block.
31+ #[ derive( Clone , Debug ) ]
32+ pub struct SeqGenerator {
33+ block : BlockNumber ,
34+ vid_seq : Arc < AtomicU32 > ,
35+ id_seq : Arc < AtomicU32 > ,
36+ }
37+
38+ impl CheapClone for SeqGenerator {
39+ fn cheap_clone ( & self ) -> Self {
40+ self . clone ( )
41+ }
42+ }
43+
44+ impl SeqGenerator {
45+ pub fn new ( block : BlockNumber ) -> Self {
46+ SeqGenerator {
47+ block,
48+ vid_seq : Arc :: new ( AtomicU32 :: new ( RESERVED_VIDS ) ) ,
49+ id_seq : Arc :: new ( AtomicU32 :: new ( 0 ) ) ,
50+ }
51+ }
52+
53+ /// Return the next VID. The VID encodes the block number in the upper
54+ /// 32 bits and a monotonically increasing sequence in the lower 32
55+ /// bits.
56+ pub fn vid ( & self ) -> i64 {
57+ let seq = self . vid_seq . fetch_add ( 1 , Ordering :: Relaxed ) ;
58+ ( ( self . block as i64 ) << 32 ) + seq as i64
59+ }
60+
61+ /// Generate the next entity ID for the given ID type. The ID encodes
62+ /// the block number in the upper 32 bits and a monotonically
63+ /// increasing sequence in the lower 32 bits.
64+ pub fn id ( & self , id_type : IdType ) -> anyhow:: Result < Id > {
65+ let seq = self . id_seq . fetch_add ( 1 , Ordering :: Relaxed ) ;
66+
67+ id_type. generate_id ( self . block , seq)
68+ }
69+ }
70+
2471/// The scope in which the `EntityCache` should perform a `get` operation
2572pub enum GetScope {
2673 /// Get from all previously stored entities in the store
@@ -103,16 +150,8 @@ pub struct EntityCache {
103150
104151 pub schema : InputSchema ,
105152
106- /// A sequence number for generating entity IDs. We use one number for
107- /// all id's as the id's are scoped by block and a u32 has plenty of
108- /// room for all changes in one block. To ensure reproducability of
109- /// generated IDs, the `EntityCache` needs to be newly instantiated for
110- /// each block
111- 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 ,
153+ /// Shared sequence generator for VIDs and entity IDs within a block.
154+ pub seq_gen : SeqGenerator ,
116155}
117156
118157impl Debug for EntityCache {
@@ -131,16 +170,15 @@ pub struct ModificationsAndCache {
131170}
132171
133172impl EntityCache {
134- pub fn new ( store : Arc < dyn s:: ReadStore > ) -> Self {
173+ pub fn new ( store : Arc < dyn s:: ReadStore > , seq_gen : SeqGenerator ) -> Self {
135174 Self {
136175 current : LfuCache :: new ( ) ,
137176 updates : HashMap :: new ( ) ,
138177 handler_updates : HashMap :: new ( ) ,
139178 in_handler : false ,
140179 schema : store. input_schema ( ) ,
141180 store,
142- seq : 0 ,
143- vid_seq : RESERVED_VIDS ,
181+ seq_gen,
144182 }
145183 }
146184
@@ -152,19 +190,26 @@ impl EntityCache {
152190 self . schema . make_entity ( iter)
153191 }
154192
155- pub fn with_current ( store : Arc < dyn s:: ReadStore > , current : EntityLfuCache ) -> EntityCache {
193+ pub fn with_current (
194+ store : Arc < dyn s:: ReadStore > ,
195+ current : EntityLfuCache ,
196+ seq_gen : SeqGenerator ,
197+ ) -> EntityCache {
156198 EntityCache {
157199 current,
158200 updates : HashMap :: new ( ) ,
159201 handler_updates : HashMap :: new ( ) ,
160202 in_handler : false ,
161203 schema : store. input_schema ( ) ,
162204 store,
163- seq : 0 ,
164- vid_seq : RESERVED_VIDS ,
205+ seq_gen,
165206 }
166207 }
167208
209+ pub fn seq_gen ( & self ) -> SeqGenerator {
210+ self . seq_gen . cheap_clone ( )
211+ }
212+
168213 pub ( crate ) fn enter_handler ( & mut self ) {
169214 assert ! ( !self . in_handler) ;
170215 self . in_handler = true ;
@@ -368,7 +413,6 @@ impl EntityCache {
368413 & mut self ,
369414 key : EntityKey ,
370415 entity : Entity ,
371- block : BlockNumber ,
372416 write_capacity_remaining : Option < & mut usize > ,
373417 ) -> Result < ( ) , anyhow:: Error > {
374418 // check the validate for derived fields
@@ -386,9 +430,7 @@ impl EntityCache {
386430 * write_capacity_remaining -= weight;
387431 }
388432
389- // The next VID is based on a block number and a sequence within the block
390- let vid = ( ( block as i64 ) << 32 ) + self . vid_seq as i64 ;
391- self . vid_seq += 1 ;
433+ let vid = self . seq_gen . vid ( ) ;
392434 let mut entity = entity;
393435 let old_vid = entity. set_vid ( vid) . expect ( "the vid should be set" ) ;
394436 // Make sure that there was no VID previously set for this entity.
@@ -457,16 +499,6 @@ impl EntityCache {
457499 for ( key, op) in other. updates {
458500 self . entity_op ( key, op) ;
459501 }
460- // Carry forward vid_seq to prevent VID collisions when the caller
461- // continues writing entities after merging.
462- self . vid_seq = self . vid_seq . max ( other. vid_seq ) ;
463- }
464-
465- /// Generate an id.
466- pub fn generate_id ( & mut self , id_type : IdType , block : BlockNumber ) -> anyhow:: Result < Id > {
467- let id = id_type. generate_id ( block, self . seq ) ?;
468- self . seq += 1 ;
469- Ok ( id)
470502 }
471503
472504 /// Return the changes that have been made via `set` and `remove` as
0 commit comments