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

Batch deal activations #1310

Merged
merged 23 commits into from
Jun 26, 2023
Merged

Batch deal activations #1310

merged 23 commits into from
Jun 26, 2023

Conversation

alexytsu
Copy link
Contributor

@alexytsu alexytsu commented Jun 8, 2023

Addressing #1278

A small tidying refactor and reduction of messaging overhead costs is seen here. Gas savings are not as significant as #1304 since the costliness of the state updates themselves have not been reduced per se.

Design caveats:

  • ActivateDeals is modified in place to become BatchActivateDeals (which implicitly assumes batches by sector)
  • In order to more easily preserve a parallel mapping between params and results, callers (miner actor) of BatchActivateDeals no longer check for sectors with zero deals. Instead, these are handled gracefully (though through a shortcut path) in the market actor.

TODOs

  • Improve parameter and result documentation
  • Optimise handling of sectors with zero deals
  • Use map to enforce parallel return structure

@alexytsu alexytsu force-pushed the alexytsu/1278-batch-deal-activation branch from 9cae3bb to 3b6e862 Compare June 8, 2023 14:41
@alexytsu alexytsu force-pushed the alexytsu/1278-batch-deal-activation branch from 3b6e862 to 12a4ae8 Compare June 8, 2023 15:09
@alexytsu alexytsu force-pushed the alexytsu/1278-batch-deal-activation branch from e4e5bab to 97f07d0 Compare June 8, 2023 16:12
@codecov-commenter
Copy link

codecov-commenter commented Jun 12, 2023

Codecov Report

Merging #1310 (2ff38ab) into master (3825cc1) will decrease coverage by 0.01%.
The diff coverage is 95.52%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1310      +/-   ##
==========================================
- Coverage   90.75%   90.75%   -0.01%     
==========================================
  Files         137      137              
  Lines       27296    27348      +52     
==========================================
+ Hits        24772    24819      +47     
- Misses       2524     2529       +5     
Impacted Files Coverage Δ
actors/verifreg/src/types.rs 100.00% <ø> (ø)
actors/verifreg/src/lib.rs 92.11% <93.33%> (-0.10%) ⬇️
actors/market/src/lib.rs 92.49% <93.47%> (+0.23%) ⬆️
actors/miner/src/lib.rs 82.99% <97.61%> (+0.08%) ⬆️
actors/market/src/types.rs 96.87% <100.00%> (+0.10%) ⬆️
actors/miner/src/ext.rs 62.50% <100.00%> (+1.20%) ⬆️
runtime/src/util/batch_return.rs 98.57% <100.00%> (+18.57%) ⬆️
test_vm/src/expects.rs 95.73% <100.00%> (+0.03%) ⬆️

... and 5 files with indirect coverage changes

@alexytsu alexytsu requested a review from anorth June 12, 2023 23:01
@alexytsu alexytsu marked this pull request as ready for review June 12, 2023 23:01
Copy link
Member

@anorth anorth left a comment

Choose a reason for hiding this comment

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

Thanks, this looks good.

I would like to explore reducing the verbosity of the intra-batch error handling and re-use of existing batch result patterns. Inside batch_activate_deals, the match blocks and return None are very noisy compared with propagating ?. How about producing a Vec<Result<ActivateDealsResult, ActorError>> instead of a Vec<Option<...>>, and then you can use .with_context(...)? inside the map closure? Then convert this vector into a BatchReturn and array of only the successful ActivateDealsResult. This will return more info to the caller about what went wrong (tho initially will probably be ignored). You mentioned that the None results were to ease parallel iteration, but I think adding a method to BatchReturn to iterate the exit codes alongside a vector of values corresponding only to the OKs will not be hard, and will be re-usable elsewhere. The caller can use this to get the same parallel results.

FYI @ZenGround0 for opinions.

actors/market/src/lib.rs Outdated Show resolved Hide resolved
actors/market/src/lib.rs Outdated Show resolved Hide resolved
actors/market/src/lib.rs Outdated Show resolved Hide resolved
actors/market/src/types.rs Outdated Show resolved Hide resolved
actors/market/src/types.rs Outdated Show resolved Hide resolved
actors/miner/src/lib.rs Outdated Show resolved Hide resolved
actors/market/src/lib.rs Outdated Show resolved Hide resolved
.sectors
.iter()
.map(|p| {
if p.deal_ids.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be filtered out on the caller side

Copy link
Member

Choose a reason for hiding this comment

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

I think this method would actually work fine without this check - it's just a minor optimisation. This method could either:

  • check and fail if a provided sector has no deals
  • just work and activate no deals

I think the latter is more robust here, and this early return not worth the lines of code. Everything below should handle empty collections too (and most likely already does).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just work and activate no deals

The shortcut path in the miner actor previously simulated this behaviour. The market actor is robust against sectors empty of deals so passing them through the usual path is ok. Though the batch_return pattern works well elsewhere in the PR for handling real errors, this is a valid use case and handling it in the miner actor will add a bit of code complexity to filter then re-interleave the sectors that either succeeded via shortcut or succeeded via market activation.

}
};

let deal_spaces = match validate_and_return_deal_space(
Copy link
Contributor

Choose a reason for hiding this comment

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

There's potentially a major security issue here.
validate_and_return_deal_spaces needs access to all deals at once to make sure we're not double activating deals and over claiming QA power. But as I understand this change this is being called once per sector. A concrete attack on this is a batch of n deal activations all over the same deal ids giving the caller an amplification of n times their qa power.

It might be that verified registry now saves us here, will have to look more.

We should have a test of this case as part of this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok it looks like the duplicates will be fed to CLAIM_ALLOCATIONS which doesn't have the same bug and will just fail the whole thing ✅
That said we should still avoid double activations on market actor or convince ourselves that activation is idempotent and then remove all duplication checks.

Copy link
Member

Choose a reason for hiding this comment

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

It's get_proposals that used to check for duplicates, but does't achieve it any more. It's the verified registry's problem to ensure QA power isn't amplified – this actor shouldn't be trusted for that any more and I'm glad to hear it's not.

But we should still avoid double-activating deals. I think it would be pretty confusing for this method to return deal space info for deals that were not activated (because they were already active). So I propose we check and fail the whole thing here if a duplicate deal is detected between sectors. Accumulate deal IDs similar to get_proposals (there's some duplication of work but I don't think worth fixing).

I note that verify_deals_for_activation (called at pre-commit and, redundantly, replica update) won't catch this either, and might be worth fixing in a follow-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made changes so that if a duplicate deal is detected between sectors, the subsequent activations are skipped and marked as failed. This preserves the behaviour checked in replica_update_test::dealincluded_in_multiple_sectors_failure

@ZenGround0
Copy link
Contributor

How about producing a Vec<Result<ActivateDealsResult, ActorError>> instead of a Vec<Option<...>>, and then you can use .with_context(...)? inside the map closure?

Yes I think this will be a lot better

Then convert this vector into a BatchReturn and array of only the successful ActivateDealsResult

It would be ideal to use this as it was an attempt to standardize batch return data types for ease of use

You mentioned that the None results were to ease parallel iteration, but I think adding a method to BatchReturn to iterate the exit codes alongside a vector of values corresponding only to the OKs will not be hard, and will be re-usable elsewhere.

If using BatchReturn is being unnecessarily clunky then we can perhaps improve it here so that it is more likely to stick as a standard solution.

pub sector_expiry: ChainEpoch,
#[serde(transparent)]
pub struct BatchActivateDealsParams {
pub sectors: Vec<SectorDeals>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sector deals introduces sector_type: RegisteredSealProof which is unecessary in this PR but will be used in #1312 to optimise ProveReplicaUpdates

.sectors
.iter()
.map(|p| {
if p.deal_ids.is_empty() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

just work and activate no deals

The shortcut path in the miner actor previously simulated this behaviour. The market actor is robust against sectors empty of deals so passing them through the usual path is ok. Though the batch_return pattern works well elsewhere in the PR for handling real errors, this is a valid use case and handling it in the miner actor will add a bit of code complexity to filter then re-interleave the sectors that either succeeded via shortcut or succeeded via market activation.

actors/market/src/lib.rs Show resolved Hide resolved
Copy link
Member

@anorth anorth left a comment

Choose a reason for hiding this comment

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

The change itself looks good, only minor comments.

This new batch functionality needs testing. Please add a few market actor unit tests for batch sizes >1, including the basics like:

  • multiple sectors succeeding
  • one deal causing one sector to fail
  • multiple sectors failing
  • condition that causes whole call to fail
  • a sector with no deals passing through alongside another with deals

actors/market/tests/activate_deal_failures.rs Outdated Show resolved Hide resolved
actors/market/tests/market_actor_test.rs Outdated Show resolved Hide resolved
actors/miner/src/lib.rs Show resolved Hide resolved
runtime/src/util/batch_return.rs Outdated Show resolved Hide resolved
actors/market/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@ZenGround0 ZenGround0 left a comment

Choose a reason for hiding this comment

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

The case of same deal across different sectors now LGTM. Can look in more detail tomorrow at the rest of the change.

@alexytsu alexytsu force-pushed the alexytsu/1278-batch-deal-activation branch from 1541e83 to 2ff38ab Compare June 21, 2023 08:15
actors/miner/src/lib.rs Outdated Show resolved Hide resolved
@alexytsu alexytsu force-pushed the alexytsu/1278-batch-deal-activation branch from 2ff38ab to ddc6934 Compare June 22, 2023 03:44
@alexytsu
Copy link
Contributor Author

Running a final benchmark on this gets gas down to: 213,060,403 from 239,773,152 or a ~11% reduction in gas cost.

Full traces

@anorth anorth requested a review from ZenGround0 June 22, 2023 19:52
@alexytsu alexytsu added this pull request to the merge queue Jun 26, 2023
Merged via the queue into master with commit f414380 Jun 26, 2023
@alexytsu alexytsu deleted the alexytsu/1278-batch-deal-activation branch June 26, 2023 05:23
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.

4 participants