Unable to signal current workflow

I have a use case where I need to signal my current workflow execution. I have this working locally with unit tests (using TestWorkflowEnvironment) but the same code results in a SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_FAILED_CAUSE_EXTERNAL_WORKFLOW_EXECUTION_NOT_FOUND when run against a real Temporal server.

The workflow impl follows the basic pattern of:

  1. Using a Workflow.newWorkflowQueue
  2. A signal method that will call WorkflowQueue.put against the queue
  3. The main workflow calls WorkflowQueue.take in a loop

Before blocking on the queue, the workflow kicks off an async procedure as follows:

val scope = Workflow.newCancellationScope(Runnable { pollForSomeCondition() })
Async.procedure { scope.run() }
fun pollForSomeCondition() {
    while (true) {
        Workflow.sleep(Duration.ofHours(1))
        if (getSomeConditionViaActivity()) {
            signalThisWorkflow("hello")
            break
        }
    }
}
fun signalThisWorkflow(msg: String) {
    val workflowId = Workflow.getInfo().workflowId
    val thisWorkflow = Workflow.newExternalWorkflowStub(
        SimpleWorkflow::class.java, // same as current workflow
        workflowId
    )
    thisWorkflow.signal(SignalInput(msg))
}

In the workflow history, I see a SignalExternalWorkflowExecutionInitiated followed by the following:


(sensitive data redacted in yellow but is not empty)

The workflow then hangs until it eventually times out with WorkflowExecutionTimedOut.

I do not understand why this signal is failing. The workflow clearly exists as we’re signalling ourselves, and the same code works correctly in a test environment. Could anyone please shed any light on why this is failing? And if there’s no simple fix, would a work-around be to invoke the workflow via a WorkflowClient in an activity?

Thanks!

Why do you want the workflow to signal itself?

It’s not an ideal design but the workflow normally waits on signals from an external source. There is an edge case scenario for about ~1% of workflows where they never receive a terminal signal due to a third party issue. So this async procedure is configured to poll for that particular scenario and to artificially construct a terminal signal if the condition is encountered. While hacky, this decouples the hacky behavior from the core workflow logic, and allows the core workflow logic to work without modification if the issue can be fixed in the future.

Can this code just call the signal method directly?

Perhaps. It’s in an extracted component with no reference to the top-level workflow impl class. I would need to pass in a method reference and introduce a circular dependency.

I’ve decided to go with this approach and this should unblock me for now. Thank you. But I still think the original approach should have worked as it did in unit tests, so it seems to be a bug.

You can pass an interface that defines that method to avoid direct circular dependency. Also, you can avoid passing it if you use a static method to call this and use a WorkflowLocal variable to keep a pointer to the workflow object.

I agree that it would be cleaner if self-signaling worked. Please file and issue to get this looked at.