[Golang] WithTimeout & WithCancel for workflow.Context

I wonder if there is a way to create a Golang context.Context from a Temporal workflow.Context in such a way that if the workflow.Context is canceled or timeout, the Golang context.Context is also canceled/timeout.

There are cases where our Temporal workflows need to perform HTTP requests/gRPC calls to third parties, which we love to cancel if the workflows are canceled or timeout.

There are cases where our Temporal workflows need to perform HTTP requests/gRPC calls to third parties,

These type of call should be done in activity code, not workflow code. Take a look at this cancellation sample to cancel from client, and this one which shows cancellation from workflow code. Your activity needs to heartbeat to receive cancel request.

Like what Tiho said, the heartbeat request acts like a messenger between the activity and server. In the request, it tells the server that the activity is still alive. In the response, it brings back the cancellation status from the server.

So given your use case, you may want to move the heartbeat into a goroutine in order to continuously check the cancellation status with the server. For example:

func (a *Activities) ActivityToBeCanceled(ctx context.Context) (string, error) {
	logger := activity.GetLogger(ctx)
	logger.Info("activity started, to cancel the Workflow Execution, use 'go run cancellation/cancel/main.go " +
		"-w <WorkflowID>' or use the CLI: 'tctl wf cancel -w <WorkflowID>'")

	go func() {
		for {
			select {
			case <-time.After(1 * time.Second):
				logger.Info("heartbeating...")
				activity.RecordHeartbeat(ctx, "")
			case <-ctx.Done():
				logger.Info("context is cancelled")
				return
			}
		}
	}()

	req, err := http.NewRequestWithContext(ctx, http.MethodPut, "https://httpbin.org/delay/6", nil)
	if err != nil {
		return "", err
	}
	c := &http.Client{}
	res, err := c.Do(req)
	if err != nil {
		return "", fmt.Errorf("request cancelled: %w", err)
	}
	defer res.Body.Close()

	logger.Info("Request completed", "StatusCode", res.StatusCode)

	return "request completed", nil
}

When you run go run cancellation/cancel/main.go you should see a “context canceled” log message looks like:

2023/04/03 13:58:54 ERROR Activity error. Namespace default TaskQueue cancel-activity WorkerID 5505@MacBook-Pro.local@ WorkflowID workflowID-to-cancel RunID de527ca5-06be-49ed-8c07-e43ec36acb79 ActivityType ActivityToBeCanceled Attempt 1 Error request cancelled: Put “https://httpbin.org/delay/6”: context canceled

@tihomir @tao thank you so much for such a comprehensive answer. I really appreciate it.