mirror of
https://github.com/hanzoai/tasks.git
synced 2026-11-03 03:56:04 +00:00
## What changed? <!-- Describe what has changed in this PR --> I added time-based caching to our mockgen invocations in go:generate directives. The tool gets bypassed in the CI. ## Why? <!-- Tell your future self why have you made these changes --> I run `make ci-build-misc` a lot during development, and I noticed that `make go-generate` is a huge bottleneck there, taking about 45s-1min for me. I tested our other generate directives like the rpcwrapper, but that one seems fast, it looks like `mockgen` is the real culprit. I tried upgrading our dep to the latest version in the Uber fork to see if there was a quick win there, but it seems even slower now somehow. I considered batching our commands as well, but there isn't a way to batch mockgen commands in source mode (multiple `-source` and `-destination` flags). So, I figured I would borrow what `Make` does and just only run `mockgen` if the generated code is stale when compared to the source code. ## How did you test it? <!-- How have you verified this change? Tested locally? Added a unit test? Checked in staging env? --> Running `make go-generate` locally now takes about `7.7s`, down from `44.0s`, so it's about 6x as fast: ```shell temporal snowden/cached-mockgen [$] ➜ time make go-generate Process go:generate directives... make go-generate 6.74s user 5.61s system 159% cpu 7.743 total temporal snowden/cached-mockgen [$] took 7.7s ➜ git checkout main Switched to branch 'main' Your branch is up to date with 'origin/main'. temporal main [$?] ➜ time make go-generate Process go:generate directives... make go-generate 68.13s user 123.43s system 435% cpu 43.955 total temporal main [$?] took 44.0s ➜ ``` I also added unit tests which check cases like the destination file not being present, and it tested that manually as well. Finally, I also deleted all `_mock.go` files and reran the command to verify that it works ok even when everything is out-of-date. In that case, there is a slight regression in time, up to 1 minute. I don't know why that's the case, but it should still be faster when there's no mocks. ## Potential risks <!-- Assuming the worst case, what can be broken when deploying this change to production? --> ## Documentation <!-- Have you made sure this change doesn't falsify anything currently stated in `docs/`? If significant new behavior is added, have you described that in `docs/`? --> ## Is hotfix candidate? <!-- Is this PR a hotfix candidate or does it require a notification to be sent to the broader community? (Yes/No) -->
244 lines
6.3 KiB
Go
244 lines
6.3 KiB
Go
// The MIT License
|
|
//
|
|
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
|
|
//
|
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package ndc
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/pborman/uuid"
|
|
"go.temporal.io/api/serviceerror"
|
|
|
|
"go.temporal.io/server/common"
|
|
"go.temporal.io/server/common/definition"
|
|
"go.temporal.io/server/common/log"
|
|
"go.temporal.io/server/common/namespace"
|
|
"go.temporal.io/server/common/persistence"
|
|
"go.temporal.io/server/common/persistence/versionhistory"
|
|
serviceerrors "go.temporal.io/server/common/serviceerror"
|
|
"go.temporal.io/server/common/util"
|
|
"go.temporal.io/server/service/history/shard"
|
|
"go.temporal.io/server/service/history/workflow"
|
|
)
|
|
|
|
const (
|
|
resendOnResetWorkflowMessage = "Resend events due to reset workflow"
|
|
)
|
|
|
|
type (
|
|
resetter interface {
|
|
resetWorkflow(
|
|
ctx context.Context,
|
|
now time.Time,
|
|
baseLastEventID int64,
|
|
baseLastEventVersion int64,
|
|
incomingFirstEventID int64,
|
|
incomingFirstEventVersion int64,
|
|
) (workflow.MutableState, error)
|
|
}
|
|
|
|
resetterImpl struct {
|
|
shard shard.Context
|
|
transactionMgr TransactionManager
|
|
executionMgr persistence.ExecutionManager
|
|
stateRebuilder StateRebuilder
|
|
|
|
namespaceID namespace.ID
|
|
workflowID string
|
|
baseRunID string
|
|
newContext workflow.Context
|
|
newRunID string
|
|
|
|
logger log.Logger
|
|
}
|
|
)
|
|
|
|
var _ resetter = (*resetterImpl)(nil)
|
|
|
|
func NewResetter(
|
|
shard shard.Context,
|
|
transactionMgr TransactionManager,
|
|
namespaceID namespace.ID,
|
|
workflowID string,
|
|
baseRunID string,
|
|
newContext workflow.Context,
|
|
newRunID string,
|
|
logger log.Logger,
|
|
) *resetterImpl {
|
|
|
|
return &resetterImpl{
|
|
shard: shard,
|
|
transactionMgr: transactionMgr,
|
|
executionMgr: shard.GetExecutionManager(),
|
|
stateRebuilder: NewStateRebuilder(shard, logger),
|
|
|
|
namespaceID: namespaceID,
|
|
workflowID: workflowID,
|
|
baseRunID: baseRunID,
|
|
newContext: newContext,
|
|
newRunID: newRunID,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (r *resetterImpl) resetWorkflow(
|
|
ctx context.Context,
|
|
now time.Time,
|
|
baseLastEventID int64,
|
|
baseLastEventVersion int64,
|
|
incomingFirstEventID int64,
|
|
incomingFirstEventVersion int64,
|
|
) (workflow.MutableState, error) {
|
|
|
|
baseBranchToken, err := r.getBaseBranchToken(
|
|
ctx,
|
|
baseLastEventID,
|
|
baseLastEventVersion,
|
|
incomingFirstEventID,
|
|
incomingFirstEventVersion,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resetBranchToken, err := r.getResetBranchToken(ctx, baseBranchToken, baseLastEventID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
requestID := uuid.New()
|
|
rebuildMutableState, rebuiltHistorySize, err := r.stateRebuilder.Rebuild(
|
|
ctx,
|
|
now,
|
|
definition.NewWorkflowKey(
|
|
r.namespaceID.String(),
|
|
r.workflowID,
|
|
r.baseRunID,
|
|
),
|
|
baseBranchToken,
|
|
baseLastEventID,
|
|
util.Ptr(baseLastEventVersion),
|
|
definition.NewWorkflowKey(
|
|
r.namespaceID.String(),
|
|
r.workflowID,
|
|
r.newRunID,
|
|
),
|
|
resetBranchToken,
|
|
requestID,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rebuildMutableState.AddHistorySize(rebuiltHistorySize)
|
|
|
|
r.newContext.Clear()
|
|
return rebuildMutableState, nil
|
|
}
|
|
|
|
func (r *resetterImpl) getBaseBranchToken(
|
|
ctx context.Context,
|
|
baseLastEventID int64,
|
|
baseLastEventVersion int64,
|
|
incomingFirstEventID int64,
|
|
incomingFirstEventVersion int64,
|
|
) (baseBranchToken []byte, retError error) {
|
|
|
|
baseWorkflow, err := r.transactionMgr.LoadWorkflow(
|
|
ctx,
|
|
r.namespaceID,
|
|
r.workflowID,
|
|
r.baseRunID,
|
|
)
|
|
switch err.(type) {
|
|
case nil:
|
|
defer func() {
|
|
baseWorkflow.GetReleaseFn()(retError)
|
|
}()
|
|
|
|
baseVersionHistories := baseWorkflow.GetMutableState().GetExecutionInfo().GetVersionHistories()
|
|
index, err := versionhistory.FindFirstVersionHistoryIndexByVersionHistoryItem(
|
|
baseVersionHistories,
|
|
versionhistory.NewVersionHistoryItem(baseLastEventID, baseLastEventVersion),
|
|
)
|
|
if err != nil {
|
|
// the base event and incoming event are from different branch
|
|
// only re-replicate the gap on the incoming branch
|
|
// the base branch event will eventually arrived
|
|
return nil, serviceerrors.NewRetryReplication(
|
|
resendOnResetWorkflowMessage,
|
|
r.namespaceID.String(),
|
|
r.workflowID,
|
|
r.newRunID,
|
|
common.EmptyEventID,
|
|
common.EmptyVersion,
|
|
incomingFirstEventID,
|
|
incomingFirstEventVersion,
|
|
)
|
|
}
|
|
|
|
baseVersionHistory, err := versionhistory.GetVersionHistory(baseVersionHistories, index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return baseVersionHistory.GetBranchToken(), nil
|
|
case *serviceerror.NotFound:
|
|
return nil, serviceerrors.NewRetryReplication(
|
|
resendOnResetWorkflowMessage,
|
|
r.namespaceID.String(),
|
|
r.workflowID,
|
|
r.newRunID,
|
|
common.EmptyEventID,
|
|
common.EmptyVersion,
|
|
incomingFirstEventID,
|
|
incomingFirstEventVersion,
|
|
)
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
func (r *resetterImpl) getResetBranchToken(
|
|
ctx context.Context,
|
|
baseBranchToken []byte,
|
|
baseLastEventID int64,
|
|
) ([]byte, error) {
|
|
|
|
// fork a new history branch
|
|
shardID := r.shard.GetShardID()
|
|
resp, err := r.executionMgr.ForkHistoryBranch(ctx, &persistence.ForkHistoryBranchRequest{
|
|
ForkBranchToken: baseBranchToken,
|
|
ForkNodeID: baseLastEventID + 1,
|
|
Info: persistence.BuildHistoryGarbageCleanupInfo(r.namespaceID.String(), r.workflowID, r.newRunID),
|
|
ShardID: shardID,
|
|
NamespaceID: r.namespaceID.String(),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp.NewBranchToken, nil
|
|
}
|