Accessing activities last attempt within activity runtime

Is there a way to access max attempt count for an activity execution ?
Only attempt is accessible from the activity info.

ActivityInfo struct {
		TaskToken         []byte
		WorkflowType      *WorkflowType
		WorkflowNamespace string
		WorkflowExecution WorkflowExecution
		ActivityID        string
		ActivityType      ActivityType
		TaskQueue         string
		HeartbeatTimeout  time.Duration // Maximum time between heartbeats. 0 means no heartbeat needed.
		ScheduledTime     time.Time     // Time of activity scheduled by a workflow
		StartedTime       time.Time     // Time of activity start
		Deadline          time.Time     // Time of activity timeout
		Attempt           int32         // Attempt starts from 1, and increased by 1 for every retry if retry policy is specified.
	}

Now, for few of our use-cases we have to perform some business logic in activities based on whether it is a LastAttempt for the activity or not. Now, since, the sdk doesn’t expose max attempt for the activity, the only option that remains available is to use DescribeWorkflow API and extract last attempt from PendingAcitvityInfo present in the response.

type PendingActivityInfo struct {
	ActivityId         string                   `protobuf:"bytes,1,opt,name=activity_id,json=activityId,proto3" json:"activity_id,omitempty"`
	ActivityType       *v1.ActivityType         `protobuf:"bytes,2,opt,name=activity_type,json=activityType,proto3" json:"activity_type,omitempty"`
	State              v11.PendingActivityState `protobuf:"varint,3,opt,name=state,proto3,enum=temporal.api.enums.v1.PendingActivityState" json:"state,omitempty"`
	HeartbeatDetails   *v1.Payloads             `protobuf:"bytes,4,opt,name=heartbeat_details,json=heartbeatDetails,proto3" json:"heartbeat_details,omitempty"`
	LastHeartbeatTime  *time.Time               `protobuf:"bytes,5,opt,name=last_heartbeat_time,json=lastHeartbeatTime,proto3,stdtime" json:"last_heartbeat_time,omitempty"`
	LastStartedTime    *time.Time               `protobuf:"bytes,6,opt,name=last_started_time,json=lastStartedTime,proto3,stdtime" json:"last_started_time,omitempty"`
	Attempt            int32                    `protobuf:"varint,7,opt,name=attempt,proto3" json:"attempt,omitempty"`
	MaximumAttempts    int32                    `protobuf:"varint,8,opt,name=maximum_attempts,json=maximumAttempts,proto3" json:"maximum_attempts,omitempty"`
	ScheduledTime      *time.Time               `protobuf:"bytes,9,opt,name=scheduled_time,json=scheduledTime,proto3,stdtime" json:"scheduled_time,omitempty"`
	ExpirationTime     *time.Time               `protobuf:"bytes,10,opt,name=expiration_time,json=expirationTime,proto3,stdtime" json:"expiration_time,omitempty"`
	LastFailure        *v13.Failure             `protobuf:"bytes,11,opt,name=last_failure,json=lastFailure,proto3" json:"last_failure,omitempty"`
	LastWorkerIdentity string                   `protobuf:"bytes,12,opt,name=last_worker_identity,json=lastWorkerIdentity,proto3" json:"last_worker_identity,omitempty"`
}

However, I am not sure if this is the best way.

Now, for few of our use-cases we have to perform some business logic in activities based on whether it is a LastAttempt for the activity or not

Imo would be maybe better to separate this logic into catching the ActivityFailure when retries are exhausted and call activities that would “compensate” this failure.

Agree. That would be better @tihomir . However, we have some legacy code built on our internal orchestrator. Hence, to maintain backward compatibility for few older use cases, we need to handle lastAttempt.

@tihomir any better recommendation here ? Also, assuming it should be ok to use DescribeWorkflow within activity execution ?

Agree. That would be better @tihomir . However, we have some legacy code built on our internal orchestrator. Hence, to maintain backward compatibility for few older use cases, we need to handle lastAttempt.

You can still execute the same activity from the workflow with a different argument like “lastAttempt=true”. If you cannot change the signature you can pass this argument using a ContextPropagator.

I guess this might work as a temporary hack… Thanks @maxim.