Local activity executor pool exhaustion causes workflow to hang permanently — without possibility to recover without reset
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- java
- Ambito
- distributed-systems
Direzione di ricerca
Inizia con le voci dello stack indicate nel report, in particolare LocalActivityInvocationHandler, CompletablePromiseImpl e WorkflowThreadScheduler, quindi esegui la riproduzione JUnit collegata. Confronta il test di esaurimento del pool con il workaround scheduleToCloseTimeout; il lavoro è completato quando il workflow vittima non rimane più bloccato permanentemente quando tutti i thread di local activity sono occupati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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.
- Lingua principale
- Java
- Stelle
- 433
- Fork
- 249
- Merge medio
- 6g 5h
- PR unite (30g)
- 25
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di temporalio/sdk-java
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
temporalio/sdk-java#2676 · 8 commenti · 2 reazioni ·
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
temporalio/sdk-java#1825 ·
-
test server
Difficoltà 4/5 3-5 giorni Idoneità per principianti 42/100
temporalio/sdk-java#3088 · 2 commenti ·
-
temporalio/sdk-java#3059 · 1 assegnatario ·
-
enhancement
temporalio/sdk-java#3058 · 1 assegnatario ·
Tutte le issue di temporalio/sdk-java
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
objectionary/jeo-maven-plugin#1811 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
hiero-ledger/hiero-block-node#3693 ·
-
area/dependencies kind/cve severity/critical source/scan-dependencies status/triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
-
bug good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
-
proposal
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
beemdevelopment/Aegis#1840 ·