FAQ
What's zio.temporal.internal.StubProxies$IllegalStubProxyInvocationException?β
An exception like this one
zio.temporal.internal.StubProxies$IllegalStubProxyInvocationException: interface com.example.payments.workflows.PaymentActivity methods should not be invoked at runtime!
It's likely that you forgot to wrap Workflow/Activity calls
into ZWorkflowStub.execute/ZActivityStub.execute blocks, etc.
Method was invoked: public abstract com.example.transactions.TransactionView com.example.payments.workflows.PaymentActivity.proceed(com.example.transactions.ProceedTransactionCommand) throws com.example.payments.workflows.BankError
Actually means what it says: you forgot to wrap Workflow or Activity interaction.
Reminder: you must always wrap the workflow/activity interactions into ZWorkflowStub.execute
, ZActivityStub.execute
, ZWorkflowStub.signal
, ZWorkflowStub.query
methods, etc.
- The
ZWorkflowStub.Of[A]
,ZChildWorkflowStub.Of[A]
,ZActivityStub.Of[A]
, etc. are all compile-time stubs, so their method invocations are only valid in compile-time - Scala's method invocation would be re-written into an untyped Temporal's activity invocation
Typical problems when using zio-temporal-testkit with zio-testβ
Test clock doesn't work wellβ
If test is failing with such an error:
ERROR i.t.t.TestActivityEnvironmentInternal - Timeout trying execute activity task task_token: "test-task-token"
Try to look for ZIO warnings like this one:
Warning: A test is using time, but is not advancing the test clock, which may result in the test hanging. Use TestClock.adjust to manually advance the time.
It is likely that the Activity code is running ZIO with ZIO.sleep
.
In a combination with Temporal and zio-test, it may hang due to TestClock
.
You can easily fix it by replacing TestClock
with a real one:
test("my test") {
/*...*/
} @@ TestAspect.withLiveClock // Add the aspect
ZIO-Temporal uses Scala macros heavily. How to inspect the generated code?β
Add the following VM parameter to SBT:
-Dzio.temporal.debug.macro=true
It will print the generated code. For example:
[info] -- Info: /Users/vitaliihonta/IdeaProjects/zio-temporal/integration-tests/src/test/scala/zio/temporal/WorkflowSpec.scala:213:25
[info] 213 | thirdSnapshot <- ZWorkflowStub.query(
[info] | ^
[info] |Generated query invocation tree=_root_.zio.temporal.internal.TemporalInteraction.from[scala.collection.immutable.List[scala.Predef.String]](zio.temporal.internal.TemporalWorkflowFacade.query[scala.collection.immutable.List[scala.Predef.String]](workflowStub.toJava, "messages", scala.Nil)(ctg$proxy8))
[info] 214 | workflowStub.messages
[info] 215 | )
Typed and Untyped method namesβ
For some reason, workflow-related methods (@signalMethod
, @queryMethod
) are registered as is in
Temporal,
while activity method are registered capitalized.
For instance, having the following activity interface:
import zio.temporal._
import zio.temporal.activity._
@activityInterface
trait EchoActivity {
def echo(what: String): String
}
@workflowInterface
trait EchoWorkflow {
@workflowMethod
def echoWorkflow(what: String): String
@signalMethod
def signalSomething(): Unit
}
The activity name in untyped invocation must be Echo
(not echo
):
import zio._
import zio.temporal.workflow._
class EchoWorkflowImpl extends EchoWorkflow {
// Inside some workflow
private val activity: ZActivityStub.Untyped =
ZWorkflow.newUntypedActivityStub(
ZActivityOptions.withStartToCloseTimeout(5.seconds)
)
override def echoWorkflow(what: String): String = {
activity.execute[String]("Echo", what)
}
override def signalSomething(): Unit = {
// Do nothing, it's just for demo purposes
}
}
On the other hand, an untyped workflow signal must be invoked with the same lower-cased name:
ZIO.serviceWithZIO[ZWorkflowClient] { workflowClient =>
for {
echoWorkflow <- workflowClient.newUntypedWorkflowStub(
workflowType = "EchoWorkflow",
options = ZWorkflowOptions
.withWorkflowId("workflow-id-a1b2c3")
.withTaskQueue("task-queue")
)
_ <- echoWorkflow.start("HELLO THERE")
_ <- echoWorkflow.signal("signalSomething")
// get result
result <- echoWorkflow.result[String]
} yield ()
}