Accessing WorkFlow attributes after execution, using history

I have a usecase, where i want to extract the a json tree(state) built in workflow execute method. Following is the sample code.

public class TicketWorkFlowImpl implements TicketWorkFlow {

  @Override
  public String execute(Step[] steps){
    JsonNode resultNode..
    //process steps and build resultNode

  }
}

After the workflow is executed, there is another api where i want to fetch the result. I want to use replay or history to fetch the resultNode.
One solution i am thinking is to use the history of events and fetch from the events data

 GetWorkflowExecutionHistoryRequest request = getWorkflowExecutionHistoryRequest(workFlowId);
    GetWorkflowExecutionHistoryResponse resultHistory =
        service.blockingStub().getWorkflowExecutionHistory(request);

    Stream<HistoryEvent> events = resultHistory.getHistory().getEventsList().stream();

But as this is not used as activity input or output parametr i can not get the same.

The second solution i see is create a dummy Activity and pass the resultNode so that it is stored as part of events data.

public class TicketWorkFlowImpl implements TicketWorkFlow {

  @Override
  public String execute(Step[] steps){
    JsonNode resultNode..
    //process steps and build resultNode
     CallResultActivity.record(resultNode)//This is a temporal activity
  }
}

Are there any better ways for the usecase mentioned? Are there any drawbacks with the second solution

There are two ways to solve your problem.

1. Use workflow result

Return the resultNode as the workflow result. Then use the WorkflowStub.getResultAPI to extract the result.

WorkflowClient client = ...;
WorkflowStub workflow = client.newUntypedWorkflowStub("workflowId", Optional.empty(), Optional.of("TicketWorkFlow"));
JsonNode result = workflow.getResult(1, TimeUnit.SECONDS, JsonNode.class);

2. Use query

Add a query method to the workflow interface and call it to return the resultNode. Note that queries work even for closed workflows (up to the retention period).

@WorkflowInterface
public interface TicketWorkflow {
    @WorkflowMethod
    String execute(Step[] steps);

    @QueryMethod
    JsonNode getResultNode();
}

public class TicketWorkFlowImpl implements TicketWorkFlow {

    private JsonNode resultNode;

    @Override
    public JsonNode execute(Step[] steps){
        //process steps and build resultNode

    }

    JsonNode getResultNode() {
        return resultNode;
    }
}

Then use the query to retrieve result:

    WorkflowClient client = WorkflowClient.newInstance();
    TicketWorkFlow workflow = client.newWorkflowStub(TicketWorkFlow, "workflowId", Optional.empty());
    JsonNode result = workflow.getResultNode();
1 Like

Thank you @maxim, Using the second solution. It is working

@maxim

We implemented the 2nd solution mentioned.

    JsonNode getResultNode() {
        return resultNode;
    }

But we have a case where we have parentflow - invoking childflow

    WorkflowServiceStubs service = WorkflowServiceStubs.newInstance();
    WorkflowClient client = WorkflowClient.newInstance(service);
    TestWorkFlow jw = client.newWorkflowStub(TestWorkFlow.class, workFlowId, Optional.empty());
    JsonNode node = jw.getNodeResult(path);

When I use this way, the main flow is replayed but it is trying to create new childflow and I see this issue

Caused by: java.lang.IllegalStateException: COMMAND_TYPE_START_CHILD_WORKFLOW_EXECUTION doesn't match EVENT_TYPE_START_CHILD_WORKFLOW_EXECUTION_INITIATED with EventId=5
	at io.temporal.internal.statemachines.WorkflowStateMachines.assertMatch(WorkflowStateMachines.java:784)
	at io.temporal.internal.statemachines.WorkflowStateMachines.validateCommand(WorkflowStateMachines.java:750)
	at io.temporal.internal.statemachines.WorkflowStateMachines.handleCommandEvent(WorkflowStateMachines.java:272)
	at io.temporal.internal.statemachines.WorkflowStateMachines.handleEventImpl(WorkflowStateMachines.java:199)
	at io.temporal.internal.statemachines.WorkflowStateMachines.handleEvent(WorkflowStateMachines.java:178)

Please let me know if I am missing anything

It looks like your workflow code is not determninistic. Make sure that it follows all the constraints outlined in the documentation.

How do you start the child workflow?

Thanks, the below is child workflow creation code
WorkflowClientOptions clientOptions =
WorkflowClientOptions.newBuilder().setNamespace(DEFAULT_NAME_SPACE).build();
WorkflowClient client = WorkflowClient.newInstance(service, clientOptions);
ChildWorkflowOptions options = ChildWorkflowOptions.newBuilder()
.setTaskQueue(Shared.HELLO_WORLD_CHILD_TASK_QUEUE)
.setWorkflowId(workFlowId)
.build();
TestWorkflow workflow = Workflow.newChildWorkflowStub(TestWorkflow.class, options);
This is the code i am using for child workflow

I’m confused. Why are you using WorkflowClient inside the workflow code? It is not allowed as any other RPC calls.

Oh!. Thanks @maixim. I removed that and it is working now.
TestWorkflow workflow = Workflow.newChildWorkflowStub(TestWorkflow.class);

Thanks once again

1 Like

@maxim

We implemented the 2nd solution mentioned ealier

    JsonNode getResultNode() {
        return resultNode;
    }

When we call getResultNode only the mainflow is replayed at activity level and subflow activities are not replayed. As we are building some variables in the subflows(using the redisjson), the same is not getting calculated again.
I think the child workflows are replayed at workflow level, how can we make it to replay activity level.

I recommend do not think about replay when writing workflows. It doesn’t bring any value but just confuses you.

Would you explain what you are trying to achieve that is not supported by the query?