mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
164 lines
7.5 KiB
Go
164 lines
7.5 KiB
Go
package workerdeployment
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
commonpb "go.temporal.io/api/common/v1"
|
|
enumspb "go.temporal.io/api/enums/v1"
|
|
"go.temporal.io/api/serviceerror"
|
|
"go.temporal.io/sdk/temporal"
|
|
"go.temporal.io/sdk/workflow"
|
|
deploymentspb "go.temporal.io/server/api/deployment/v1"
|
|
"go.temporal.io/server/common/sdk"
|
|
"go.temporal.io/server/common/searchattribute"
|
|
"go.temporal.io/server/common/worker_versioning"
|
|
)
|
|
|
|
const (
|
|
// Workflow types
|
|
WorkerDeploymentVersionWorkflowType = "temporal-sys-worker-deployment-version-workflow"
|
|
WorkerDeploymentWorkflowType = "temporal-sys-worker-deployment-workflow"
|
|
|
|
// Namespace division
|
|
WorkerDeploymentNamespaceDivision = "TemporalWorkerDeployment"
|
|
|
|
// Updates
|
|
RegisterWorkerInDeploymentVersion = "register-task-queue-worker" // for Worker Deployment Version wf
|
|
SyncVersionState = "sync-version-state" // for Worker Deployment Version wfs
|
|
UpdateVersionMetadata = "update-version-metadata" // for Worker Deployment Version wfs
|
|
RegisterWorkerInWorkerDeployment = "register-worker-in-deployment" // for Worker Deployment wfs
|
|
SetCurrentVersion = "set-current-version" // for Worker Deployment wfs
|
|
SetRampingVersion = "set-ramping-version" // for Worker Deployment wfs
|
|
AddVersionToWorkerDeployment = "add-version-to-worker-deployment" // for Worker Deployment wfs
|
|
DeleteVersion = "delete-version" // for WorkerDeployment wfs
|
|
DeleteDeployment = "delete-deployment" // for WorkerDeployment wfs
|
|
SetManagerIdentity = "set-manager-identity" // for WorkerDeployment wfs
|
|
|
|
// Signals
|
|
ForceCANSignalName = "force-continue-as-new" // for Worker Deployment Version _and_ Worker Deployment wfs
|
|
SyncDrainageSignalName = "sync-drainage-status"
|
|
TerminateDrainageSignal = "terminate-drainage"
|
|
SyncVersionSummarySignal = "sync-version-summary"
|
|
|
|
// Queries
|
|
QueryDescribeVersion = "describe-version" // for Worker Deployment Version wf
|
|
QueryDescribeDeployment = "describe-deployment" // for Worker Deployment wf
|
|
|
|
// Memos
|
|
WorkerDeploymentMemoField = "WorkerDeploymentMemo" // for Worker Deployment wf
|
|
|
|
// Prefixes, Delimeters and Keys
|
|
WorkerDeploymentVersionWorkflowIDPrefix = "temporal-sys-worker-deployment-version"
|
|
WorkerDeploymentVersionWorkflowIDDelimeter = ":"
|
|
WorkerDeploymentVersionWorkflowIDInitialSize = len(WorkerDeploymentVersionWorkflowIDDelimeter) + len(WorkerDeploymentVersionWorkflowIDPrefix)
|
|
WorkerDeploymentNameFieldName = "WorkerDeploymentName"
|
|
WorkerDeploymentBuildIDFieldName = "BuildID"
|
|
|
|
// Application error names for rejected updates
|
|
errNoChangeType = "errNoChange"
|
|
errTooManyVersions = "errTooManyVersions"
|
|
errTooManyDeployments = "errTooManyDeployments"
|
|
errVersionAlreadyExistsType = "errVersionAlreadyExists"
|
|
errMaxTaskQueuesInVersionType = "errMaxTaskQueuesInVersion"
|
|
errVersionNotFound = "Version not found in deployment"
|
|
|
|
errConflictTokenMismatchType = "errConflictTokenMismatch"
|
|
errFailedPrecondition = "FailedPrecondition"
|
|
|
|
ErrVersionIsDraining = "Version cannot be deleted since it is draining."
|
|
ErrVersionHasPollers = "Version cannot be deleted since it has active pollers."
|
|
ErrVersionIsCurrentOrRamping = "Version cannot be deleted since it is current or ramping."
|
|
|
|
ErrRampingVersionDoesNotHaveAllTaskQueues = "proposed ramping version is missing active task queues from the current version; these would become unversioned if it is set as the ramping version"
|
|
ErrCurrentVersionDoesNotHaveAllTaskQueues = "proposed current version is missing active task queues from the current version; these would become unversioned if it is set as the current version"
|
|
ErrManagerIdentityMismatch = "ManagerIdentity '%s' is set and does not match user identity '%s'; to proceed, set your own identity as the ManagerIdentity, remove the ManagerIdentity, or wait for the other client to do so"
|
|
ErrWorkerDeploymentNotFound = "no Worker Deployment found with name %s; does your Worker Deployment have pollers?"
|
|
)
|
|
|
|
var (
|
|
WorkerDeploymentVisibilityBaseListQuery = fmt.Sprintf(
|
|
"%s = '%s' AND %s = '%s' AND %s = '%s'",
|
|
searchattribute.WorkflowType,
|
|
WorkerDeploymentWorkflowType,
|
|
searchattribute.TemporalNamespaceDivision,
|
|
WorkerDeploymentNamespaceDivision,
|
|
searchattribute.ExecutionStatus,
|
|
enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING.String(),
|
|
)
|
|
)
|
|
|
|
var (
|
|
defaultActivityOptions = workflow.ActivityOptions{
|
|
StartToCloseTimeout: 1 * time.Minute,
|
|
RetryPolicy: &temporal.RetryPolicy{
|
|
InitialInterval: 100 * time.Millisecond,
|
|
MaximumAttempts: 5,
|
|
},
|
|
}
|
|
)
|
|
|
|
// validateVersionWfParams is a helper that verifies if the fields used for generating
|
|
// Worker Deployment Version related workflowID's are valid
|
|
func validateVersionWfParams(fieldName string, field string, maxIDLengthLimit int) error {
|
|
// Length checks
|
|
if field == "" {
|
|
return serviceerror.NewInvalidArgumentf("%v cannot be empty", fieldName)
|
|
}
|
|
|
|
// Length of each field should be: (MaxIDLengthLimit - (prefix + delimeter length)) / 2
|
|
if len(field) > (maxIDLengthLimit-WorkerDeploymentVersionWorkflowIDInitialSize)/2 {
|
|
return serviceerror.NewInvalidArgumentf("size of %v larger than the maximum allowed", fieldName)
|
|
}
|
|
|
|
// deploymentName cannot have "."
|
|
// TODO: remove this restriction once the old version strings are completely cleaned from external and internal API
|
|
if fieldName == WorkerDeploymentNameFieldName && strings.Contains(field, worker_versioning.WorkerDeploymentVersionIdDelimiterV31) {
|
|
return serviceerror.NewInvalidArgumentf("worker deployment name cannot contain '%s'", worker_versioning.WorkerDeploymentVersionIdDelimiterV31)
|
|
}
|
|
// deploymentName cannot have ":"
|
|
if fieldName == WorkerDeploymentNameFieldName && strings.Contains(field, worker_versioning.WorkerDeploymentVersionIdDelimiter) {
|
|
return serviceerror.NewInvalidArgumentf("worker deployment name cannot contain '%s'", worker_versioning.WorkerDeploymentVersionIdDelimiter)
|
|
}
|
|
|
|
// buildID or deployment name cannot start with "__"
|
|
if strings.HasPrefix(field, "__") {
|
|
return serviceerror.NewInvalidArgumentf("%v cannot start with '__'", fieldName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func DecodeWorkerDeploymentMemo(memo *commonpb.Memo) *deploymentspb.WorkerDeploymentWorkflowMemo {
|
|
var workerDeploymentWorkflowMemo deploymentspb.WorkerDeploymentWorkflowMemo
|
|
err := sdk.LenientFromPayloadProtoConverter(memo.Fields[WorkerDeploymentMemoField], &workerDeploymentWorkflowMemo)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return &workerDeploymentWorkflowMemo
|
|
}
|
|
|
|
func getSafeDurationConfig(ctx workflow.Context, id string, unsafeGetter func() any, defaultValue time.Duration) (time.Duration, error) {
|
|
get := func(_ workflow.Context) interface{} {
|
|
return unsafeGetter()
|
|
}
|
|
var value time.Duration
|
|
if err := workflow.MutableSideEffect(ctx, id, get, durationEq).Get(&value); err != nil {
|
|
return defaultValue, err
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
func durationEq(a, b any) bool {
|
|
return a == b
|
|
}
|
|
|
|
// isFailedPrecondition checks if the error is a FailedPrecondition error. It also checks if the FailedPrecondition error is wrapped in an ApplicationError.
|
|
func isFailedPrecondition(err error) bool {
|
|
var failedPreconditionError *serviceerror.FailedPrecondition
|
|
var applicationError *temporal.ApplicationError
|
|
return errors.As(err, &failedPreconditionError) || (errors.As(err, &applicationError) && applicationError.Type() == errFailedPrecondition)
|
|
}
|