[ 
https://issues.apache.org/jira/browse/SPARK-60222?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

L. C. Hsieh updated SPARK-60222:
--------------------------------
    Description: 
TaskContextImpl runs task listeners on one thread at a time, tracked by 
listenerInvocationThread. In the following cases, listeners that should run on 
the thread that completes the task, before the task is torn down, run elsewhere 
or not at all:

1. When another thread is invoking listeners as the task completes, 
markTaskCompleted returns without running any completion listener. For example, 
Task.kill calls markInterrupted on the kill thread, and if an interrupt 
listener throws, markTaskFailed runs the failure listeners on that thread. The 
completion listeners are then left until some other thread adds a listener, or 
skipped entirely. The task can then reach cleanUpAllAllocatedMemory while 
resources that the completion listeners would release are still in use. The 
ErrorNotifier doc and a StreamingShuffleSuite test describe this race.
2. When a listener throws, invokeListeners clears listenerInvocationThread 
while markTaskFailed runs the failure listeners, and reclaims it afterwards. 
Another thread that adds a listener in between can take over running the 
pending listeners, possibly concurrently with this thread.
3. When another thread calls markTaskFailed while the completing thread runs 
the completion listeners, it returns without running the failure listeners, and 
the completing thread does not run them either. Failure cleanup, such as 
aborting a state store update before the completion listener releases the 
store, is skipped.

The proposed fix keeps these listeners on the completing thread: 
markTaskCompleted claims the invocation in the same step that sets completed, 
and if another thread holds it, waits until that thread hands it over directly; 
when a listener throws, the failure listeners run inside the current 
invocation; and failure listeners that another thread queues while 
markTaskCompleted runs the completion listeners run before its next completion 
listener. This was first done in apache/spark#59332 and split out of it during 
review.

Out of scope: listeners added after markTaskCompleted has finished, while 
another thread holds the invocation, are still dropped, and so are 
post-status-update listeners in the same situation. Running them would mean 
running them on another thread, without the task's TaskContext, after the 
executor has released the task's block locks and memory.


  was:
TaskContextImpl runs task listeners on one thread at a time, tracked by 
listenerInvocationThread. In two cases, the completion listeners do not run on 
the thread that completes the task:

1. When another thread is invoking listeners as the task completes, 
markTaskCompleted returns without running any completion listener. For example, 
Task.kill calls markInterrupted on the kill thread, and if an interrupt 
listener throws, markTaskFailed runs the failure listeners on that thread. The 
completion listeners are then left until some other thread adds a listener, or 
skipped entirely. The task can then reach cleanUpAllAllocatedMemory while 
resources that the completion listeners would release are still in use. The 
ErrorNotifier doc and a StreamingShuffleSuite test describe this race.
2. When a listener throws, invokeListeners clears listenerInvocationThread 
while markTaskFailed runs the failure listeners, and reclaims it afterwards. 
Another thread that adds a listener in between can take over running the 
pending listeners, possibly concurrently with this thread.

Failure listeners that another thread queues while a thread runs the completion 
listeners, and completion listeners queued while a thread runs failure 
listeners after completion, are also dropped.

This matters for helper threads that the completion listeners stop and wait 
for, such as the pipelined Python UDF writer (SPARK-60113): if the helper 
thread adds a listener lazily at such a moment, it ends up running the listener 
that waits for itself.

The proposed fix keeps the completion listeners on the completing thread: 
markTaskCompleted waits for another thread invoking listeners and takes the 
invocation over directly, failure listeners run inside the current invocation 
when a listener throws, and a thread runs the listeners left to it before it 
releases the invocation. This was first done in apache/spark#59332 and split 
out of it during review.



> Keep task completion listeners on the completing thread when another thread 
> invokes listeners
> ---------------------------------------------------------------------------------------------
>
>                 Key: SPARK-60222
>                 URL: https://issues.apache.org/jira/browse/SPARK-60222
>             Project: Spark
>          Issue Type: Bug
>          Components: Spark Core
>    Affects Versions: 4.3.0, 4.4.0
>            Reporter: L. C. Hsieh
>            Priority: Major
>              Labels: pull-request-available
>
> TaskContextImpl runs task listeners on one thread at a time, tracked by 
> listenerInvocationThread. In the following cases, listeners that should run 
> on the thread that completes the task, before the task is torn down, run 
> elsewhere or not at all:
> 1. When another thread is invoking listeners as the task completes, 
> markTaskCompleted returns without running any completion listener. For 
> example, Task.kill calls markInterrupted on the kill thread, and if an 
> interrupt listener throws, markTaskFailed runs the failure listeners on that 
> thread. The completion listeners are then left until some other thread adds a 
> listener, or skipped entirely. The task can then reach 
> cleanUpAllAllocatedMemory while resources that the completion listeners would 
> release are still in use. The ErrorNotifier doc and a StreamingShuffleSuite 
> test describe this race.
> 2. When a listener throws, invokeListeners clears listenerInvocationThread 
> while markTaskFailed runs the failure listeners, and reclaims it afterwards. 
> Another thread that adds a listener in between can take over running the 
> pending listeners, possibly concurrently with this thread.
> 3. When another thread calls markTaskFailed while the completing thread runs 
> the completion listeners, it returns without running the failure listeners, 
> and the completing thread does not run them either. Failure cleanup, such as 
> aborting a state store update before the completion listener releases the 
> store, is skipped.
> The proposed fix keeps these listeners on the completing thread: 
> markTaskCompleted claims the invocation in the same step that sets completed, 
> and if another thread holds it, waits until that thread hands it over 
> directly; when a listener throws, the failure listeners run inside the 
> current invocation; and failure listeners that another thread queues while 
> markTaskCompleted runs the completion listeners run before its next 
> completion listener. This was first done in apache/spark#59332 and split out 
> of it during review.
> Out of scope: listeners added after markTaskCompleted has finished, while 
> another thread holds the invocation, are still dropped, and so are 
> post-status-update listeners in the same situation. Running them would mean 
> running them on another thread, without the task's TaskContext, after the 
> executor has released the task's block locks and memory.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to