Skip to content

Commit

Permalink
Problem: no abort OE in PrepareProposal (#860)
Browse files Browse the repository at this point in the history
* Abort OE in PrepareProposal

* Log the correct FinalizeBlock request height when OE aborts with mismatch (#54)

fix OE log

---------

Co-authored-by: Teddy Ding <[email protected]>
  • Loading branch information
mmsqe and teddyding authored Oct 18, 2024
1 parent 783e224 commit d78d66e
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 7 deletions.
10 changes: 9 additions & 1 deletion baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@ func (app *BaseApp) PrepareProposal(req *abci.RequestPrepareProposal) (resp *abc
return nil, errors.New("PrepareProposal handler not set")
}

// Abort any running OE so it cannot overlap with `PrepareProposal`. This could happen if optimistic
// `internalFinalizeBlock` from previous round takes a long time, but consensus has moved on to next round.
// Overlap is undesirable, since `internalFinalizeBlock` and `PrepareProoposal` could share access to
// in-memory structs depending on application implementation.
// No-op if OE is not enabled.
// Similar call to Abort() is done in `ProcessProposal`.
app.optimisticExec.Abort()

// Always reset state given that PrepareProposal can timeout and be called
// again in a subsequent round.
header := cmtproto.Header{
Expand Down Expand Up @@ -904,7 +912,7 @@ func (app *BaseApp) FinalizeBlock(req *abci.RequestFinalizeBlock) (res *abci.Res

if app.optimisticExec.Initialized() {
// check if the hash we got is the same as the one we are executing
aborted := app.optimisticExec.AbortIfNeeded(req.Hash)
aborted := app.optimisticExec.AbortIfNeeded(req)
// Wait for the OE to finish, regardless of whether it was aborted or not
res, err = app.optimisticExec.WaitResult()

Expand Down
6 changes: 3 additions & 3 deletions baseapp/oe/optimistic_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,16 @@ func (oe *OptimisticExecution) Execute(req *abci.RequestProcessProposal) {

// AbortIfNeeded aborts the OE if the request hash is not the same as the one in
// the running OE. Returns true if the OE was aborted.
func (oe *OptimisticExecution) AbortIfNeeded(reqHash []byte) bool {
func (oe *OptimisticExecution) AbortIfNeeded(req *abci.RequestFinalizeBlock) bool {
if oe == nil {
return false
}

oe.mtx.Lock()
defer oe.mtx.Unlock()

if !bytes.Equal(oe.request.Hash, reqHash) {
oe.logger.Error("OE aborted due to hash mismatch", "oe_hash", hex.EncodeToString(oe.request.Hash), "req_hash", hex.EncodeToString(reqHash), "oe_height", oe.request.Height, "req_height", oe.request.Height)
if !bytes.Equal(oe.request.Hash, req.Hash) {
oe.logger.Error("OE aborted due to hash mismatch", "oe_hash", hex.EncodeToString(oe.request.Hash), "req_hash", hex.EncodeToString(req.Hash), "oe_height", oe.request.Height, "req_height", req.Height)
oe.cancelFunc()
return true
} else if oe.abortRate > 0 && rand.Intn(100) < oe.abortRate {
Expand Down
13 changes: 10 additions & 3 deletions baseapp/oe/optimistic_execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ func TestOptimisticExecution(t *testing.T) {
assert.Nil(t, resp)
assert.EqualError(t, err, "test error")

assert.False(t, oe.AbortIfNeeded([]byte("test")))
assert.True(t, oe.AbortIfNeeded([]byte("wrong_hash")))

assert.False(t, oe.AbortIfNeeded(
&abci.RequestFinalizeBlock{
Hash: []byte("test"),
},
))
assert.True(t, oe.AbortIfNeeded(
&abci.RequestFinalizeBlock{
Hash: []byte("wrong_hash"),
},
))
oe.Reset()
}

0 comments on commit d78d66e

Please sign in to comment.