Local activity executor pool exhaustion causes workflow to hang permanently — without possibility to recover without reset
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Tranquilo
- Stack tecnológico
- java
- Área
- distributed-systems
Línea de trabajo
Comienza con las entradas de la pila mencionadas en el informe, especialmente LocalActivityInvocationHandler, CompletablePromiseImpl y WorkflowThreadScheduler, y luego ejecuta la reproducción de JUnit enlazada. Compara la prueba de agotamiento del pool con el workaround scheduleToCloseTimeout; se considera terminado cuando el workflow afectado ya no permanece bloqueado permanentemente cuando todos los hilos de local activity están ocupados.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug
When the local activity executor thread pool (maxConcurrentLocalActivityExecutionSize) is fully exhausted by workflows whose local activities are blocked on a hung external call (e.g. a signalWithStart gRPC call that never returns), any subsequently scheduled workflow that attempts to execute a local activity hangs permanently and irrecoverably.
Only way to recover is to set ScheduleToClose timeout in LocalActivityOptions / reset workflow execution (best effort attempt as this can happen again in this case too)
Root cause
Local activities are dispatched to an in-process thread pool. If all threads are occupied by blocked activities, newly dispatched local activities are queued but never executed. The workflow coroutine thread parks on CompletablePromiseImpl.get waiting for a result that will never arrive.
Executions in this state cannot be recovered as workflow task never times out or fails so its never retried. Its very similar situation to awaiting condition but signal never comes in.
Only way to resume is to reset execution
Steps to reproduce
Self-contained JUnit repro test (two tests — one proves the hang, one verifies the scheduleToCloseTimeout workaround):
👉 https://gist.github.com/tsurdilo/ad0ab99dc36f63e26ea968ca25347763
The test uses a ServerSocket.accept() to simulate a hung gRPC call without any real network dependency. It runs deterministically in ~20 seconds.
Test 1 — testPoolExhaustionCausesVictimWorkflowToHang
Confirms the hang. After the pool is exhausted, the victim workflow's event history ends at 6 events with no MARKER_RECORDED, and __stack_trace shows:
workflow-method-victim-workflow-...: (BLOCKED on Feature.get)
io.temporal.internal.sync.WorkflowThreadScheduler.yieldLocked(WorkflowThreadScheduler.java:37)
io.temporal.internal.sync.WorkflowThreadContext.yield(WorkflowThreadContext.java:70)
io.temporal.internal.sync.WorkflowThreadImpl.yield(WorkflowThreadImpl.java:378)
io.temporal.internal.sync.WorkflowThread.await(WorkflowThread.java:27)
io.temporal.internal.sync.CompletablePromiseImpl.getImpl(CompletablePromiseImpl.java:65)
io.temporal.internal.sync.CompletablePromiseImpl.get(CompletablePromiseImpl.java:55)
io.temporal.internal.sync.ActivityStubBase.execute(ActivityStubBase.java:25)
io.temporal.internal.sync.LocalActivityInvocationHandler.lambda$getActivityFunc$0(LocalActivityInvocationHandler.java:59)
io.temporal.internal.sync.ActivityInvocationHandlerBase.invoke(ActivityInvocationHandlerBase.java:48)
Test 2 — testScheduleToCloseTimeoutBypassesHang
Confirms the workaround. With scheduleToCloseTimeout(5s) set, the victim fails cleanly with TIMEOUT_TYPE_SCHEDULE_TO_CLOSE exactly 5 seconds after scheduling, even though the pool is fully exhausted and the activity never gets a thread. The workflow does not hang.
Workaround
Set scheduleToCloseTimeout on LocalActivityOptions. The SDK tracks this deadline from the moment the activity is scheduled, independently of whether a pool thread is available. The workflow will fail with TIMEOUT_TYPE_SCHEDULE_TO_CLOSE rather than hang.
- Lenguaje dominante
- Java
- Estrellas
- 433
- Forks
- 249
- Merge medio
- 6 d 5 h
- PR fusionados (30 d)
- 25
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de temporalio/sdk-java
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
temporalio/sdk-java#2676 · 8 comentarios · 2 reacciones ·
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
temporalio/sdk-java#1825 ·
-
test server
Dificultad 4/5 3-5 días Aptitud para principiantes 42/100
temporalio/sdk-java#3088 · 2 comentarios ·
-
temporalio/sdk-java#3059 · 1 asignado ·
-
enhancement
temporalio/sdk-java#3058 · 1 asignado ·
Todos los issues de temporalio/sdk-java
Issues similares
-
area-deployment area-integrations triage:bot-seen
Dificultad 2/5 Medio día Aptitud para principiantes 86/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
apache/flink-agents#1156 ·
-
[source-shopify] FAILED bulk operation without partialDataUrl is silently treated as successful Abiertoarea/connectors autoteam community connectors/source/shopify needs-triage team/use type/bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100