Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yield Execution #10415

Merged
merged 80 commits into from Feb 27, 2024
Merged

Yield Execution #10415

merged 80 commits into from Feb 27, 2024

Conversation

saketh-are
Copy link
Collaborator

@saketh-are saketh-are commented Jan 12, 2024

Implements the promise_yield_create and promise_yield_resume host functions described in NEP 519.

  • promise_yield_create: Creates a promise callback with a special input data dependency to be delivered via a resume call.
  • promise_yield_resume: Delivers input data for a yielded promise, triggering execution of the callback.

A fixed-length timeout yield_timeout_length_in_blocks is configured at the protocol level. If a yielded promise is not resumed within the timeout length, the callback is triggered automatically.

Co-authored by: Simonas Kazlauskas git@kazlauskas.me

Copy link

codecov bot commented Jan 15, 2024

Codecov Report

Attention: Patch coverage is 32.04545% with 299 lines in your changes are missing coverage. Please review.

Project coverage is 72.30%. Comparing base (8383deb) to head (4e17043).

Files Patch % Lines
runtime/near-vm-runner/src/logic/logic.rs 0.00% 85 Missing ⚠️
runtime/runtime/src/lib.rs 17.85% 43 Missing and 3 partials ⚠️
core/store/src/lib.rs 9.30% 38 Missing and 1 partial ⚠️
runtime/runtime/src/ext.rs 0.00% 35 Missing ⚠️
runtime/runtime/src/actions.rs 23.33% 18 Missing and 5 partials ⚠️
runtime/runtime/src/receipt_manager.rs 8.00% 23 Missing ⚠️
...me/near-vm-runner/src/logic/mocks/mock_external.rs 0.00% 17 Missing ⚠️
runtime/near-vm-runner/src/logic/errors.rs 0.00% 7 Missing ⚠️
core/primitives/src/receipt.rs 0.00% 5 Missing and 1 partial ⚠️
core/parameters/src/view.rs 50.00% 5 Missing ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10415      +/-   ##
==========================================
- Coverage   72.42%   72.30%   -0.13%     
==========================================
  Files         732      732              
  Lines      149935   150348     +413     
  Branches   149935   150348     +413     
==========================================
+ Hits       108594   108705     +111     
- Misses      36421    36710     +289     
- Partials     4920     4933      +13     
Flag Coverage Δ
backward-compatibility 0.24% <0.00%> (-0.01%) ⬇️
db-migration 0.24% <0.00%> (-0.01%) ⬇️
genesis-check 1.42% <0.00%> (-0.01%) ⬇️
integration-tests 36.94% <15.90%> (-0.05%) ⬇️
linux 71.10% <32.04%> (-0.10%) ⬇️
linux-nightly 71.72% <29.31%> (-0.11%) ⬇️
macos 55.31% <25.87%> (-0.14%) ⬇️
pytests 1.64% <0.00%> (-0.01%) ⬇️
sanity-checks 1.43% <0.00%> (-0.01%) ⬇️
unittests 68.15% <31.59%> (-0.11%) ⬇️
upgradability 0.28% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@saketh-are saketh-are changed the title [DO NOT MERGE] draft pr for CI [DRAFT] Yield Execution Jan 19, 2024
@saketh-are saketh-are changed the title [DRAFT] Yield Execution Yield Execution Feb 23, 2024
@saketh-are saketh-are marked this pull request as ready for review February 23, 2024 15:47
@saketh-are saketh-are requested a review from a team as a code owner February 23, 2024 15:47
@saketh-are saketh-are requested review from wacban and nagisa and removed request for wacban February 23, 2024 15:47
Comment on lines +3 to +6
wasm_yield_create_base: { old: 300_000_000_000_000, new: 50_000_000_000_000 }
wasm_yield_create_byte: { old: 300_000_000_000_000, new: 10_000_000 }
wasm_yield_resume_base: { old: 300_000_000_000_000, new: 50_000_000_000_000 }
wasm_yield_resume_byte: { old: 300_000_000_000_000, new: 100_000_000 }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these fees allow for testing? I remember you mentioning the fees in nightly were too large to test right now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe they are too high. To allow local testing I have been modifying this file and using much lower costs; it is not really a blocker if they continue to be set too high until we have the proper values.

Comment on lines +309 to +312
yield_create_base -> 61 [2% host]
yield_create_byte -> 62 [2% host]
yield_resume_base -> 63 [3% host]
yield_resume_byte -> 64 [3% host]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: check if these need to be excluded from the profiles while not stable.

/// * Fees for reading the `method_name` and `arguments`;
/// * Fees for writing the Data ID to the output register;
/// * Fees for setting up the receipt.
pub fn promise_yield_create(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a note about this in the tracking issue task list.

Comment on lines 757 to 800
pub fn get_yielded_promise_indices(
trie: &dyn TrieAccess,
) -> Result<YieldedPromiseQueueIndices, StorageError> {
Ok(get(trie, &TrieKey::YieldedPromiseQueueIndices)?.unwrap_or_default())
}

pub fn set_yielded_promise_indices(
state_update: &mut TrieUpdate,
yielded_promise_indices: &YieldedPromiseQueueIndices,
) {
set(state_update, TrieKey::YieldedPromiseQueueIndices, yielded_promise_indices);
}

// Records the yielded promise and appends it to the yielded promises queue.
pub fn set_yielded_promise(
state_update: &mut TrieUpdate,
yielded_promise_indices: &mut YieldedPromiseQueueIndices,
account_id: AccountId,
data_id: CryptoHash,
expires_at: BlockHeight,
) {
set(state_update, TrieKey::YieldedPromise { data_id }, &YieldedPromise { account_id });

set(
state_update,
TrieKey::YieldedPromiseQueueEntry { index: yielded_promise_indices.next_available_index },
&YieldedPromiseQueueEntry { data_id, expires_at },
);
yielded_promise_indices.next_available_index = yielded_promise_indices
.next_available_index
.checked_add(1)
.expect("Next available index for yielded promise exceeded the integer limit");
}

pub fn get_yielded_promise(
trie: &dyn TrieAccess,
data_id: CryptoHash,
) -> Result<Option<YieldedPromise>, StorageError> {
get(trie, &TrieKey::YieldedPromise { data_id })
}

pub fn remove_yielded_promise(state_update: &mut TrieUpdate, data_id: CryptoHash) {
state_update.remove(TrieKey::YieldedPromise { data_id })
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure there's absolutely no way for these columns to get created before the feature is stabilized. One way would be to still add the usual nightly compile-time feature gates -- it is only in the contract runtime that we gate using a runtime variables; or alternatively we could assert! here that the feature is enabled through parameters.

I'm not sure if these are created lazily (at least I didn't spot any code that creates the columns explicitly), and while the contract runtime gates the relevant host functions, adding additional layers of defense wouldn't hurt.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gated compilation turned out to be quite a long thread to pull on (needing to gate any dependent code, associated imports, etc.) so I went with some asserts for now.

// Yields are only resumable by the account which created them
if yielded_promise.account_id != *self.account_id {
return Err(
HostError::YieldedPromiseNotFound { data_id: data_id.to_string() }.into()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should maintain that the error is the same when the entry does not exist in the storage and when it is malformed for the function call. The information on the distinction is not particularly useful to a well-intentioned user. I filled this as a followup point in the tracking issue.

@@ -1499,10 +1500,70 @@ impl Runtime {
prefetcher.clear();
}

// Resolve timed-out yielded promises
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: should perhaps consider splitting this out into a function. The apply is quite a bit too large as is.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I will look into sending a follow-up PR refactoring apply.

@nagisa
Copy link
Collaborator

nagisa commented Feb 26, 2024

On coverage: we're hoping to finish up the PR as a follow up, including test coverage. This PR has been becoming humongous and its less trivial than we'd like to work on our respective parts without this being in mainline.

@saketh-are saketh-are added this pull request to the merge queue Feb 27, 2024
Merged via the queue into near:master with commit 47020d4 Feb 27, 2024
25 of 30 checks passed
@saketh-are saketh-are deleted the yield_resume branch February 27, 2024 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants