go.temporal.io/server/components/nexusoperations/tasks.go
365 LOC · 63 covered · 302 uncovered · 28 ranges · 292 concepts · 11 introducers · 130 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
package nexusoperations
import (
"errors"
"fmt"
"time"
enumspb "go.temporal.io/api/enums/v1"
"go.temporal.io/api/serviceerror"
enumsspb "go.temporal.io/server/api/enums/v1"
persistencespb "go.temporal.io/server/api/persistence/v1"
"go.temporal.io/server/common/persistence/serialization"
"go.temporal.io/server/service/history/consts"
"go.temporal.io/server/service/history/hsm"
"google.golang.org/protobuf/proto"
)
const (
TaskTypeInvocation = "nexusoperations.Invocation"
TaskTypeBackoff = "nexusoperations.Backoff"
TaskTypeCancelation = "nexusoperations.Cancelation"
TaskTypeCancelationBackoff = "nexusoperations.CancelationBackoff"
// NOTE: the name `Timeout` is used for backward compatibility with existing persisted tasks and predates the addition of more flexible timeout types.
TaskTypeScheduleToCloseTimeout = "nexusoperations.Timeout"
TaskTypeScheduleToStartTimeout = "nexusoperations.ScheduleToStartTimeout"
TaskTypeStartToCloseTimeout = "nexusoperations.StartToCloseTimeout"
)
var errSerializationCast = errors.New("cannot serialize HSM task. unable to cast to expected type")
type ScheduleToCloseTimeoutTask struct {
deadline time.Time
}
var _ hsm.Task = ScheduleToCloseTimeoutTask{}
return TaskTypeScheduleToCloseTimeout
}
return t.deadline
}
return ""
}
// Validate checks if the timeout task is still valid to execute for the given node state.
func (t ScheduleToCloseTimeoutTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
tasks.go ×12
if err := node.CheckRunning(); err != nil {
return err
}
if err != nil {
return err
}
return fmt.Errorf(
"%w: %w: cannot timeout machine in state %v",
consts.ErrStaleReference,
hsm.ErrInvalidTransition,
op.State(),
)
}
}
type TimeoutTaskSerializer struct{}
func (TimeoutTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
return ScheduleToCloseTimeoutTask{deadline: attrs.Deadline}, nil
}
return nil, nil
}
type InvocationTask struct {
EndpointName string
Attempt int32
}
var _ hsm.Task = InvocationTask{}
return TaskTypeInvocation
}
return hsm.Immediate
}
return t.EndpointName
}
func (InvocationTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
tasks.go ×12
if err := node.CheckRunning(); err != nil {
return err
}
return hsm.ValidateState[enumsspb.NexusOperationState, Operation](node, enumsspb.NEXUS_OPERATION_STATE_SCHEDULED)
tasks.go ×12
}
type InvocationTaskSerializer struct{}
func (InvocationTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
var info persistencespb.NexusInvocationTaskInfo
err := proto.Unmarshal(data, &info)
if err != nil {
return nil, serialization.NewDeserializationError(enumspb.ENCODING_TYPE_PROTO3, err)
}
return InvocationTask{EndpointName: attrs.Destination, Attempt: info.Attempt}, nil
}
switch task := task.(type) {
case InvocationTask:
return proto.Marshal(&persistencespb.NexusInvocationTaskInfo{Attempt: task.Attempt})
default:
return nil, serviceerror.NewInternalf("unknown HSM task type while serializing: %v", task)
}
}
type BackoffTask struct {
deadline time.Time
}
var _ hsm.Task = BackoffTask{}
return TaskTypeBackoff
}
return t.deadline
}
func (t BackoffTask) Destination() string {
return ""
}
func (t BackoffTask) Validate(_ *persistencespb.StateMachineRef, node *hsm.Node) error {
if err := node.CheckRunning(); err != nil {
return err
}
return hsm.ValidateState[enumsspb.NexusOperationState, Operation](node, enumsspb.NEXUS_OPERATION_STATE_BACKING_OFF)
}
type BackoffTaskSerializer struct{}
func (BackoffTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
return BackoffTask{deadline: attrs.Deadline}, nil
}
func (BackoffTaskSerializer) Serialize(hsm.Task) ([]byte, error) {
return nil, nil
}
type CancelationTask struct {
EndpointName string
Attempt int32
}
var _ hsm.Task = CancelationTask{}
return TaskTypeCancelation
}
func (CancelationTask) Deadline() time.Time {
return hsm.Immediate
}
func (t CancelationTask) Destination() string {
return t.EndpointName
}
func (CancelationTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
if err := node.CheckRunning(); err != nil {
return err
}
return hsm.ValidateState[enumspb.NexusOperationCancellationState, Cancelation](node, enumspb.NEXUS_OPERATION_CANCELLATION_STATE_SCHEDULED)
}
type CancelationTaskSerializer struct{}
func (CancelationTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
var info persistencespb.NexusCancelationTaskInfo
err := proto.Unmarshal(data, &info)
if err != nil {
return nil, serialization.NewDeserializationError(enumspb.ENCODING_TYPE_PROTO3, err)
}
return CancelationTask{EndpointName: attrs.Destination, Attempt: info.Attempt}, nil
}
func (CancelationTaskSerializer) Serialize(task hsm.Task) ([]byte, error) {
switch task := task.(type) {
case CancelationTask:
return proto.Marshal(&persistencespb.NexusCancelationTaskInfo{Attempt: task.Attempt})
default:
return nil, serviceerror.NewInternalf("unknown HSM task type while serializing: %v", task)
}
}
type CancelationBackoffTask struct {
deadline time.Time
}
var _ hsm.Task = CancelationBackoffTask{}
return TaskTypeCancelationBackoff
}
return t.deadline
}
func (CancelationBackoffTask) Destination() string {
return ""
}
func (CancelationBackoffTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
if err := node.CheckRunning(); err != nil {
return err
}
return hsm.ValidateState[enumspb.NexusOperationCancellationState, Cancelation](node, enumspb.NEXUS_OPERATION_CANCELLATION_STATE_BACKING_OFF)
}
type CancelationBackoffTaskSerializer struct{}
func (CancelationBackoffTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
return CancelationBackoffTask{deadline: attrs.Deadline}, nil
}
func (CancelationBackoffTaskSerializer) Serialize(hsm.Task) ([]byte, error) {
return nil, nil
}
type ScheduleToStartTimeoutTask struct {
deadline time.Time
}
var _ hsm.Task = ScheduleToStartTimeoutTask{}
return TaskTypeScheduleToStartTimeout
}
func (t ScheduleToStartTimeoutTask) Deadline() time.Time {
return t.deadline
}
func (ScheduleToStartTimeoutTask) Destination() string {
return ""
}
// Validate checks if the schedule-to-start timeout task is still valid.
// Only valid if operation is still in SCHEDULED or BACKING_OFF state.
func (t ScheduleToStartTimeoutTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
if err := node.CheckRunning(); err != nil {
return err
}
op, err := hsm.MachineData[Operation](node)
if err != nil {
return err
}
// Only timeout if we haven't started yet
switch op.State() {
case enumsspb.NEXUS_OPERATION_STATE_SCHEDULED,
enumsspb.NEXUS_OPERATION_STATE_BACKING_OFF:
return nil
default:
// Already started or completed, timeout not applicable
return fmt.Errorf(
"%w: %w: cannot apply schedule-to-start timeout to machine in state %v",
consts.ErrStaleReference,
hsm.ErrInvalidTransition,
op.State(),
)
}
}
type ScheduleToStartTimeoutTaskSerializer struct{}
func (ScheduleToStartTimeoutTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
return ScheduleToStartTimeoutTask{deadline: attrs.Deadline}, nil
}
func (ScheduleToStartTimeoutTaskSerializer) Serialize(hsm.Task) ([]byte, error) {
return nil, nil
}
type StartToCloseTimeoutTask struct {
deadline time.Time
}
var _ hsm.Task = StartToCloseTimeoutTask{}
return TaskTypeStartToCloseTimeout
}
func (t StartToCloseTimeoutTask) Deadline() time.Time {
return t.deadline
}
func (StartToCloseTimeoutTask) Destination() string {
return ""
}
// Validate checks if the start-to-close timeout task is still valid.
// Only valid if operation is in STARTED state.
func (t StartToCloseTimeoutTask) Validate(ref *persistencespb.StateMachineRef, node *hsm.Node) error {
if err := node.CheckRunning(); err != nil {
return err
}
op, err := hsm.MachineData[Operation](node)
if err != nil {
return err
}
// Only timeout if we're in started state
if op.State() != enumsspb.NEXUS_OPERATION_STATE_STARTED {
return fmt.Errorf(
"%w: %w: cannot apply start-to-close timeout to machine in state %v",
consts.ErrStaleReference,
hsm.ErrInvalidTransition,
op.State(),
)
}
return nil
}
type StartToCloseTimeoutTaskSerializer struct{}
func (StartToCloseTimeoutTaskSerializer) Deserialize(data []byte, attrs hsm.TaskAttributes) (hsm.Task, error) {
return StartToCloseTimeoutTask{deadline: attrs.Deadline}, nil
}
func (StartToCloseTimeoutTaskSerializer) Serialize(hsm.Task) ([]byte, error) {
return nil, nil
}
if err := reg.RegisterTaskSerializer(TaskTypeScheduleToCloseTimeout, TimeoutTaskSerializer{}); err != nil {
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeInvocation, InvocationTaskSerializer{}); err != nil {
tasks.go ×7
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeBackoff, BackoffTaskSerializer{}); err != nil {
tasks.go ×7
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeCancelation, CancelationTaskSerializer{}); err != nil {
tasks.go ×7
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeCancelationBackoff, CancelationBackoffTaskSerializer{}); err != nil { // nolint:revive
tasks.go ×7
return err
}
if err := reg.RegisterTaskSerializer(TaskTypeScheduleToStartTimeout, ScheduleToStartTimeoutTaskSerializer{}); err != nil {
tasks.go ×7
return err
}
return reg.RegisterTaskSerializer(TaskTypeStartToCloseTimeout, StartToCloseTimeoutTaskSerializer{})
tasks.go ×7
}