Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

improve deal accounting performance #309

Merged
merged 11 commits into from
May 6, 2020

Conversation

whyrusleeping
Copy link
Member

This should accomplish the same thing, except now payments for storage deals only happen once every hundred blocks or so (not bucketed though, otherwise that would create hot spots)

// TODO: award some small portion of slashed to caller as incentive

// Restore verified dataset allowance for verified clients.
for _, deal := range verifiedDeals {
Copy link
Collaborator

Choose a reason for hiding this comment

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

we will need to put this logic in processDealInitTimedOut

actors/builtin/market/market_actor.go 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.

Less code and better code 💯. This approach looks great, but some details are not quite right yet.

Some other notes:
We ended up with the cron actor's fixed-at-construction table not in this repo, but I guess replicated in each impl. The default parameters to the cron constructor probably belong here somewhere (and the market actor needs to be added).

Would a bitfield be strictly more efficient for storing a list of deal IDs, like in this ops queue, even though they are not expected to be as contiguous as sectors IDs? Even with large gaps, I'd expect the deltas to be less than the magnitudes.

@@ -82,7 +81,6 @@ func (a Actor) WithdrawBalance(rt Runtime, params *WithdrawBalanceParams) *adt.E
rt.State().Transaction(&st, func() interface{} {
// Before any operations that check the balance tables for funds, execute all deferred
// deal state updates.
Copy link
Member

Choose a reason for hiding this comment

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

Replace this comment with something like "The withdrawable amount might be slightly less than nominal depending on whether all relevant entries have been processed by cron".

@@ -195,7 +192,7 @@ func (a Actor) PublishStorageDeals(rt Runtime, params *PublishStorageDealsParams
rt.Abortf(exitcode.ErrIllegalState, "failed to load proposals array: %s", err)
}

dbp, err := AsSetMultimap(adt.AsStore(rt), st.DealIDsByParty)
dbp, err := AsSetMultimap(adt.AsStore(rt), st.DealOpsByEpoch)
Copy link
Member

Choose a reason for hiding this comment

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

Nit: rename dbp to dealOps or similar

builtin.RequireSuccess(rt, code, "failed to burn funds")
return nil
}
for i := st.LastCron; i <= rt.CurrEpoch(); i++ {
Copy link
Member

Choose a reason for hiding this comment

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

Using the first key from the dbe AMT would avoid the need to store this, and be more robust in case there's an error somewhere and this gets out of sync, leaving trash in the AMT.

Copy link
Member Author

Choose a reason for hiding this comment

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

But its not an AMT, so thats really doable

Copy link
Member

Choose a reason for hiding this comment

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

Ack, ok.

Not necessarily in this PR, but I'm increasingly thinking we should use an AMT for things indexed by epoch, so that we can traverse in order. The same goes for the cron callback table in the power actor. I think there's decent scope for bugs leading to missed events and junk state left behind otherwise, especially given that cron doesn't necessarily execute on the epoch scheduled (due to null rounds).

In this case, with the values as integers, it could be an AMT of bitfields; the power actor needs to store more metadata tho.

Deals []abi.DealID // TODO: RLE
}
if nextEpoch != 0 {
Assert(nextEpoch <= rt.CurrEpoch())
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 should be nextEpoch > rt.CurrEpoch()


_, code := rt.Send(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, slashedAmount)
builtin.RequireSuccess(rt, code, "failed to burn funds")
_, e := rt.Send(builtin.BurntFundsActorAddr, 0, nil, amountSlashed)
Copy link
Member

Choose a reason for hiding this comment

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

No reason I can see not to use builtin.MethodSend for clarity here

@@ -511,7 +465,7 @@ func dealGetPaymentRemaining(deal *DealProposal, epoch abi.ChainEpoch) abi.Token
}

func (st *State) MutateDealIDs(rt Runtime, f func(dbp *SetMultimap) error) {
Copy link
Member

Choose a reason for hiding this comment

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

You deleted the only caller of this from deleteDeals, but we should be careful because that means obsolete deal ids can be left in the ops queue state here.

@@ -38,9 +37,9 @@ func (mm *SetMultimap) Root() (cid.Cid, error) {
return mm.mp.Root()
}

func (mm *SetMultimap) Put(key address.Address, v abi.DealID) error {
func (mm *SetMultimap) Put(epoch abi.ChainEpoch, v abi.DealID) error {
Copy link
Member

Choose a reason for hiding this comment

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

Aside: since this is now used for only one purpose, we could rename it to reflect that, as DealOpQueue or similar.

}
return
return amountSlashed, 0
Copy link
Member

Choose a reason for hiding this comment

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

As I understand it, returning a zero epoch here means the deal will be forgotten about forever in terms of state updates. There's no longer a manual path by which a client or miner could trigger it. So returning zero should be the special, rare case though this method, only when a deal is deleted from state. All the other cases should return some future processing epoch.

Copy link
Member Author

Choose a reason for hiding this comment

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

I actually think the case youre commenting on will never be hit, it would mean that we called this method for a deal that hasnt been proven, before its starting deadline. Nothing registers a callback for deals at that point, so we really shouldnt ever hit that. I could call abort here, but i'm not 100% sure

}

if deal.StartEpoch > epoch {
return
return amountSlashed, 0
Copy link
Member

Choose a reason for hiding this comment

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

Like here, I think the deal should be next processed at the start epoch.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, i'd say theres nothing to do even at the start epoch, we probably want to schedule this to run again 100 blocks after the start epoch.

Copy link
Member Author

Choose a reason for hiding this comment

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

And I also think this case can never be hit, as we are explicitly scheduling the update to happen at StartEpoch, this shouldnt be able to happen.

actors/builtin/market/market_state.go Show resolved Hide resolved
@anorth
Copy link
Member

anorth commented Apr 27, 2020

We ended up with the cron actor's fixed-at-construction table not in this repo, but I guess replicated in each impl. The default parameters to the cron constructor probably belong here somewhere (and the market actor needs to be added).

See #326

actors/builtin/market/market_actor.go Show resolved Hide resolved
actors/builtin/market/market_actor.go 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.

Thanks for this. Just minor comments.

actors/builtin/market/market_actor.go Outdated Show resolved Hide resolved
actors/builtin/market/market_actor.go Outdated Show resolved Hide resolved
actors/builtin/market/market_actor.go Show resolved Hide resolved
actors/builtin/market/market_actor.go Outdated Show resolved Hide resolved
actors/builtin/market/market_actor.go Outdated Show resolved Hide resolved
actors/builtin/market/market_actor.go Show resolved Hide resolved
actors/builtin/market/market_state.go Show resolved Hide resolved
actors/builtin/market/market_test.go Show resolved Hide resolved
@anorth
Copy link
Member

anorth commented May 5, 2020

Thanks. While approved, I request you don't land this for a couple of days so as not to introduce any uncertainty of un-exercised code into our current interop efforts. We may still need to push through some quick fixes that we want to consume quickly and minimally.

If you object to that, or if this lasts more than a few days, we can pick up the effort of maintaining a release branch for interop (which we will do later anyway).

Copy link
Contributor

@magik6k magik6k left a comment

Choose a reason for hiding this comment

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

Last commit LGTM too

@whyrusleeping whyrusleeping merged commit d5e9b63 into master May 6, 2020
@whyrusleeping whyrusleeping deleted the feat/faster-deal-accounting branch May 6, 2020 00:02
aarshkshah1992 pushed a commit that referenced this pull request Jun 29, 2020
* improve deal accounting performance

* rerun cborgen

* clean up method tables

* address review feedback

* Correctly handle deal state not found

* a bit more cleanup

* properly process timed out verified deal reimbursements

* reuse balance table instances

* finish addressing feedback from @anorth

* add to cron tick

* fix handling of deal state initialization
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants