How to return the state of a completed workflow

HI all,

I have a workflow with WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY
I want to modify my code to return the state of the workflow if it has already been completed without triggering it again. Is there a way to do this?

Use

client.GetWorkflow(ctx, <workflowId>, "").Get(ctx, &result)

Example

When the workflow was first triggered, it returned value 5.
When the same workflow is triggered again, instead of running the workflow, I want to return 5 (the previous state)

How do I know whether the workflow has been completed or not?

The WorkflowResult.Get blocks until the workflow is completed.

func ExecuteSpecialWorkflow(ctx context.Context, c TemporalClient, cfg TemporalClientConfig, uuid string, workflowParams params) (client.WorkflowRun, error) {
	timeout := time.Duration(cfg.Timeout) * time.Second
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	workflowId := uuid
	workflowOptions := client.StartWorkflowOptions{
		TaskQueue:             cfg.TaskQueue,
		ID:                    workflowId,
		WorkflowIDReusePolicy: enumspb.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY, // Allowing the workflow to be triggered from the beginning if it has errored out
		WorkflowTaskTimeout:   timeout,
	}

	return c.SignalWithStartWorkflow(ctx, workflowId, signal.ResumeSignalChannel, common.RequestHeaderFromContext(ctx), workflowOptions, SpecialAction, workflowParams)
}

So my workflow will be waiting for a Signal if it encountered an issue in the previous run. So when triggered, if the previous run is not completed, I want to send a resume signal.

Adding a blocking call just above the SignalWithStartWorkflow would break the resume functionality, right?

I think SignalWithStart is enough. It is going to send a signal if a workflow is still running and start a new one if it is already completed.

But I don’t want to start a new one, instead just return the state of the previous successful run.

Ideally what I’m looking for is something like this

func ExecuteSpecialWorkflow(ctx context.Context, c TemporalClient, cfg TemporalClientConfig, uuid string, workflowParams params) (client.WorkflowRun, error) {
	timeout := time.Duration(cfg.Timeout) * time.Second
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	workflowId := uuid
	workflowOptions := client.StartWorkflowOptions{
		TaskQueue:             cfg.TaskQueue,
		ID:                    workflowId,
		WorkflowIDReusePolicy: enumspb.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY, // Allowing the workflow to be triggered from the beginning if it has errored out
		WorkflowTaskTimeout:   timeout,
	}

       if workflowCompleted(workflowId) {
            return previousWorkflowRun, nil
       }

	return c.SignalWithStartWorkflow(ctx, workflowId, signal.ResumeSignalChannel, common.RequestHeaderFromContext(ctx), workflowOptions, SpecialAction, workflowParams)
}

What is the use case? It is hard to provide a recommendation without understanding what you are trying to achieve from the business point of view.

My app works as a REST API. It exposes an endpoint /v1/customer/photo
When a customer submits his photo to this endpoint, my workflow will save it in the backend and update some systems. Customer is identified by the customer number and the same is used as the workflow ID.
Workflow has several activities: save photo, update customer details, verify details. If one of these activities fail due to a downstream system, the workflow will hang and wait for a resume signal. If all has passed, the workflow will return a unique key which will be the response to the REST API call. Let’s say the customer has uploaded a photo, but has forgotten about it and tries to upload it again. Instead of running the workflow the app should get the unique key from the previous run and send it to the customer without doing any of the activities since it’s the same customer and the same workflow ID.

I believe just invoking workflow synchronously with the same id and WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE policy is going to return the result of an already closed workflow.

Ok, there’s another usecase that I missed.
If it’s the first activity that failed in the workflow, the workflow fails without hanging waiting for a resume signal. If the Workflow has failed, and the reuse policy is WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE, we won’t be able to retry the workflow for the same customer right? It would always return an error.

Trying to understand a bit here (sorry if I misunderstand). So the workflow has an initial activity that you want to fail the workflow, but all the other activity failures you set the workflow to wait for a signal before retrying? I think a SignalWithStart using WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY should work for completely re-running a failure, but otherwise just signalling an existing one waiting. You’ll want to make sure you take a signal at the start too so that the first start drains that signal.

If at all possible for debugging and clarity, if you can make those activities you otherwise hang on failure waiting for signal instead be idempotent/reentrant in whatever way is safe with your systems, then you could fail the workflow for any reason and do this without the signal (using that same reuse policy mentioned above) and have each workflow represent an attempt.