-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Allow transaction for offchain indexing #7290
Conversation
preparing use of change set internally.
|
||
Ok(StorageChanges { | ||
main_storage_changes: main_storage_changes.collect(), | ||
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(), | ||
#[cfg(feature = "std")] | ||
offchain_storage_changes: Default::default(), | ||
offchain_storage_changes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering how the changes were communicated from state-machine to client-db, with this value set to default?
Edit: probably the change delta was not extracted but use with the change struct as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @bkchr , I am not hundred percent sure things were not passing through the &mut parameter (something I did remove in this PR (see macro change)), but yes client db probably don't read from the right data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which macro changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why wasn't it reading from the correct data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the part you also change in #7940 (just terrible way of saying it :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLDR, everything is okay here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect :D Then merge it :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. I wonder what are the consequences now though. Would the offchain changes be reverted in case the block is found non-canon? If yes, then we should rather put the indexing storage changes to LOCAL
database not PERSISTENT
one, cause the latter have different promises.
/// Activate offchain indexing. | ||
/// Note that this function must only be call before any transactional | ||
/// changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Activate offchain indexing. | |
/// Note that this function must only be call before any transactional | |
/// changes. | |
/// Activate offchain indexing. | |
/// | |
/// Note that this function must only be called before any transactional changes. |
TBH I'd rather prefer OverlayedChanges::with_offchain_indexing()
to make sure it can't be abused (i.e. altered in the middle of execution).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeap agree with this @tomaka
primitives/core/src/offchain/mod.rs
Outdated
@@ -746,6 +746,14 @@ impl TransactionPoolExt { | |||
} | |||
} | |||
|
|||
/// Change to be applied to the offchain worker db in regards to a key. | |||
#[derive(Debug,Clone,Hash,Eq,PartialEq)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[derive(Debug,Clone,Hash,Eq,PartialEq)] | |
#[derive(Debug, Clone, Hash, Eq, PartialEq)] |
} | ||
|
||
/// Drain all elements of offchain changeset. | ||
pub fn drain_offchain(&mut self) -> OffchainOverlayedChangesIntoIter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only see this method being used in tests, where do we actually apply these changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
offchain_drain_commited
is in use for the changes (goes through runtime api macro generated fn at the end.
I tried to do a stack trace:
https://github.com/cheme/substrate/blob/a9e1356208c2eb8e50fa9f1ec977ee6672a6cd6a/primitives/state-machine/src/overlayed_changes/mod.rs#L550
https://github.com/cheme/substrate/blob/a9e1356208c2eb8e50fa9f1ec977ee6672a6cd6a/primitives/state-machine/src/overlayed_changes/mod.rs#L514
https://github.com/cheme/substrate/blob/a9e1356208c2eb8e50fa9f1ec977ee6672a6cd6a/primitives/state-machine/src/overlayed_changes/mod.rs#L500
https://github.com/cheme/substrate/blob/a9e1356208c2eb8e50fa9f1ec977ee6672a6cd6a/client/service/src/client/client.rs#L876
But it is true that old function iterator are not all in use. This one is clearly redundant with offchain_drain_committed
, I will switch that.
/// Stores the changes that this overlay constitutes. | ||
changes: BTreeMap<StorageKey, OverlayedValue>, | ||
pub(crate) changes: BTreeMap<K, OverlayedEntry<V>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the pub(crate)
stuff for tests? I think it would be better to get an accessor method that is only compiled when #[cfg(test)]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is to access the field in primitives/state-machine/src/overlayed_changes/offchain.rs.
I can move the struct declaration in primitives/state-machine/src/overlayed_changes/mod.rs or use 'pub(super)'. I think I prefer the second option.
No, offchain changes are only reverted when transaction are used during block processing (which happens probably only for contract at this point), so we still got every non-canonical changes commited. So, what is the more suitable?
|
That's okay then. The Indexing API was designed for
I'm not sure I fully understand this option here though. My idea of |
I think #7291 is a bit overcomplicated for the use case. (if we only need to be able to access a single state).
IIUC this implies option 1 is enough (put changes in journals and only write them to db on canonicalization, so when you query content it is only canonical content), which is probably the better choice (simpler and OW probably only care with finalized content most of the time). |
I got the option 1 (journal change and only write them on block finalization) implemented in cheme/substrate@offchain_indexing_tx...cheme:offchain_indexing_tx_2 (did not update most doc and lacks testing). |
Wait, that completely changes semantics and is going to be really cumbersome (close to impossible) to use. OCWs don't have any idea about finality and the purpose of Indexing API is to pass data directly to OCWs. If the changes are only applied on finality it means OCWs would need to access the state of the finalized block somehow and it's not currently possible (they only run right after the latest new block is imported and can only access state of that block). This solution could be acceptable (although imho quite limiting) if we re-triggered OCWs for finalized blocks passing this information somehow, but currently it's not how it works at all. |
I guess I misinterpreted you previous reply, it sounds like what is targetted by #7291 or option 2 (query does report non canonical change for a given fork). |
Maybe this discussion (local ocw) should move to #7291, since it is a bit out of the scope of this PR (just have offchain indexing conform to state-machine transactional layer). About idea to use some changeset layer from previous comment, I think it can only lead to concurrency issue, and approach of #7291 (explicitely allow change for a given block hash) is correct. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm! Couple of typedefs could be introduced to make the iterator types more readable.
btree_map::Iter<'i, (Vec<u8>,Vec<u8>), OverlayedEntry<OffchainOverlayedChange>>, | ||
fn((&'i (Vec<u8>, Vec<u8>), &'i OverlayedEntry<OffchainOverlayedChange>)) | ||
-> (&'i (Vec<u8>, Vec<u8>), &'i OffchainOverlayedChange), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add some typedefs here? This is really hard to read :)
|
||
/// Iterate by reference over the prepared offchain worker storage changes. | ||
pub struct OffchainOverlayedChangesIter<'i> { | ||
inner: Option<sp_std::iter::Map< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you use something like impl Iterator<...>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish I could. In fact if I remove all OffchainIterator struct and just have a function return impl Iterator<...>
it will work nice and easy.
I wanted to keep those iterator types to avoid breaking too much the api, but I guess I will try to remove them, I am not sure they are usefull here.
btree_map::IntoIter<(Vec<u8>,Vec<u8>), OverlayedEntry<OffchainOverlayedChange>>, | ||
fn(((Vec<u8>, Vec<u8>), OverlayedEntry<OffchainOverlayedChange>)) | ||
-> ((Vec<u8>, Vec<u8>), OffchainOverlayedChange), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, deserves a bunch of typedefs. Especially the (Vec<u8>, Vec<u8>)
stuff across the entire file.
@@ -101,23 +95,21 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> | |||
let changes_trie_config = storage.top.get(CHANGES_TRIE_CONFIG) | |||
.and_then(|v| Decode::decode(&mut &v[..]).ok()); | |||
overlay.set_collect_extrinsics(changes_trie_config.is_some()); | |||
overlay.enable_offchain_indexing(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's not possible to move that to the constructor function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC that is totally doable (I did consider it), will try.
💯 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@cheme I think tests are missing? |
yes will complete that at state-machine level (about the overlay and transaction), do you expect other tests? |
I think statemachine level should be enough, as this shouldn't propagate to upper layers ;) |
I did merge master post #7940 . I choose as @bkchr to remove the optimization that avoid writing data in state machine (mainly because with test executor it is a bit awkward to use). Thinking twice I could also have change the default to active and unactivate it in client/service/src/client/call_executor.rs . Please tell me what you think @todr , @ateissen , @bkchr Will call 'bœt mërge' tomorrow evening if no disagreement until then. |
No objections. I like the simplification resulting in expressing a |
@@ -388,6 +390,7 @@ impl OverlayedChanges { | |||
.expect("Top and children changesets are started in lockstep; qed"); | |||
!changeset.is_empty() | |||
}); | |||
self.offchain.overlay_mut().rollback_transaction()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably use an expect like for children above?
@@ -401,6 +404,7 @@ impl OverlayedChanges { | |||
changeset.commit_transaction() | |||
.expect("Top and children changesets are started in lockstep; qed"); | |||
} | |||
self.offchain.overlay_mut().commit_transaction()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
@@ -414,6 +418,7 @@ impl OverlayedChanges { | |||
changeset.enter_runtime() | |||
.expect("Top and children changesets are entering runtime in lockstep; qed") | |||
} | |||
self.offchain.overlay_mut().enter_runtime()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
@@ -427,6 +432,7 @@ impl OverlayedChanges { | |||
changeset.exit_runtime() | |||
.expect("Top and children changesets are entering runtime in lockstep; qed"); | |||
} | |||
self.offchain.overlay_mut().exit_runtime()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
|
||
Ok(StorageChanges { | ||
main_storage_changes: main_storage_changes.collect(), | ||
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(), | ||
#[cfg(feature = "std")] | ||
offchain_storage_changes: Default::default(), | ||
offchain_storage_changes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which macro changes?
|
||
Ok(StorageChanges { | ||
main_storage_changes: main_storage_changes.collect(), | ||
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(), | ||
#[cfg(feature = "std")] | ||
offchain_storage_changes: Default::default(), | ||
offchain_storage_changes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why wasn't it reading from the correct data?
bot merge |
Trying merge. |
This PR enables state-machine transactional layer for the offchain indexing.
from some crates api and is a breaking change.
Users no longer instantiate a offchain layer change with method 'enabled' but use 'enable_storage_change' method on existing OverlayStorageChange if needed.
OverlayedChangeSet
with offchain indexing key and value types.I wonder if some function could be removed, but I tried to keep changes from touching code logic.