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

Yuming Wang updated SPARK-58322:
--------------------------------
    Description: 
h2. Problem

In YARN cluster mode, when a driver terminates and its RPC port is reused by a 
new driver from a {*}different application{*}, executors from the old 
application can connect to and register with the new driver. This leads to 
silent data loss.

This was observed in production with the following scenario:
 * Old app (user A, long-running ThriftServer) had its driver at host:30202
 * Old driver crashed, port 30202 became free
 * New app (user B, short ETL job) started on the same host, bound to port 
30202 (via spark.port.maxRetries)
 * Old AM (still alive) launched 278 executors with --driver-url pointing to 
host:30202. These were fresh executor processes in old app YARN containers, but 
they connected to the *new* driver
 * New driver accepted all 278 executors without any application identity 
verification
 * Tasks were scheduled to these wrong-app executors, writing data to incorrect 
staging directories
 * New driver committed the job, deleted staging directories. Data was lost.

h2. Root Causes
 # spark.authenticate defaults to false: No RPC-level identity check between 
executor and driver.
 # RegisterExecutor message does not include appId: The driver has no way to 
verify which application an executor belongs to.
 # RetrieveSparkAppConfig does not verify appId: An executor can fetch config 
from any driver that happens to be at the configured address.
 # CoarseGrainedExecutorBackend.run() overwrites spark.app.id: The line 
cfg.sparkProperties ++ Seq((spark.app.id, arguments.appId)) unconditionally 
overwrites the driver's correct spark.app.id with the executor's CLI argument 
(from --app-id). This was originally a fallback (SPARK-3377, 2014) when 
sparkProperties was eagerly computed before spark.app.id was set, but became a 
latent bug when sparkProperties was changed to a lazy val.

  was:
h2. Problem

In YARN cluster mode, when a driver terminates and its RPC port is reused by a 
new driver from a *different application*, executors from the old application 
can connect to and register with the new driver. This leads to silent data loss.

This was observed in production with the following scenario:
* Old app (user A, long-running ThriftServer) had its driver at host:30202
* Old driver crashed, port 30202 became free
* New app (user B, short ETL job) started on the same host, bound to port 30202 
(via spark.port.maxRetries)
* Old AM (still alive) launched 278 executors with --driver-url pointing to 
host:30202. These were fresh executor processes in old app YARN containers, but 
they connected to the *new* driver
* New driver accepted all 278 executors without any application identity 
verification
* Tasks were scheduled to these wrong-app executors, writing data to incorrect 
staging directories
* New driver committed the job, deleted staging directories. Data was lost.

h2. Root Causes

# spark.authenticate defaults to false: No RPC-level identity check between 
executor and driver.
# RegisterExecutor message does not include appId: The driver has no way to 
verify which application an executor belongs to.
# RetrieveSparkAppConfig does not verify appId: An executor can fetch config 
from any driver that happens to be at the configured address.
# CoarseGrainedExecutorBackend.run() overwrites spark.app.id: The line 
cfg.sparkProperties ++ Seq((spark.app.id, arguments.appId)) unconditionally 
overwrites the driver's correct spark.app.id with the executor's CLI argument 
(from --app-id). This was originally a fallback (SPARK-3377, 2014) when 
sparkProperties was eagerly computed before spark.app.id was set, but became a 
latent bug when sparkProperties was changed to a lazy val.

h2. Proposed Fix

Add appId verification in CoarseGrainedExecutorBackend.run(): after 
RetrieveSparkAppConfig returns, compare arguments.appId (from --app-id CLI arg) 
with cfg.sparkProperties[spark.app.id] (from driver's SparkConf). If they 
mismatch, throw SparkException to prevent the executor from registering to the 
wrong driver.

Also fix the ++ overwrite to only fall back to the CLI argument when the driver 
does not provide spark.app.id (race condition during SparkContext 
initialization).

Add config spark.executor.appIdVerification.enabled (default true) to allow 
disabling.

The same fix is applied to KubernetesExecutorBackend.


> Executors can register to wrong driver due to port reuse, causing silent data 
> loss
> ----------------------------------------------------------------------------------
>
>                 Key: SPARK-58322
>                 URL: https://issues.apache.org/jira/browse/SPARK-58322
>             Project: Spark
>          Issue Type: Bug
>          Components: Spark Core
>    Affects Versions: 4.2.0
>            Reporter: Yuming Wang
>            Priority: Major
>              Labels: data-loss, executor, pull-request-available, yarn
>
> h2. Problem
> In YARN cluster mode, when a driver terminates and its RPC port is reused by 
> a new driver from a {*}different application{*}, executors from the old 
> application can connect to and register with the new driver. This leads to 
> silent data loss.
> This was observed in production with the following scenario:
>  * Old app (user A, long-running ThriftServer) had its driver at host:30202
>  * Old driver crashed, port 30202 became free
>  * New app (user B, short ETL job) started on the same host, bound to port 
> 30202 (via spark.port.maxRetries)
>  * Old AM (still alive) launched 278 executors with --driver-url pointing to 
> host:30202. These were fresh executor processes in old app YARN containers, 
> but they connected to the *new* driver
>  * New driver accepted all 278 executors without any application identity 
> verification
>  * Tasks were scheduled to these wrong-app executors, writing data to 
> incorrect staging directories
>  * New driver committed the job, deleted staging directories. Data was lost.
> h2. Root Causes
>  # spark.authenticate defaults to false: No RPC-level identity check between 
> executor and driver.
>  # RegisterExecutor message does not include appId: The driver has no way to 
> verify which application an executor belongs to.
>  # RetrieveSparkAppConfig does not verify appId: An executor can fetch config 
> from any driver that happens to be at the configured address.
>  # CoarseGrainedExecutorBackend.run() overwrites spark.app.id: The line 
> cfg.sparkProperties ++ Seq((spark.app.id, arguments.appId)) unconditionally 
> overwrites the driver's correct spark.app.id with the executor's CLI argument 
> (from --app-id). This was originally a fallback (SPARK-3377, 2014) when 
> sparkProperties was eagerly computed before spark.app.id was set, but became 
> a latent bug when sparkProperties was changed to a lazy val.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

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

Reply via email to