dbtsai commented on PR #56907:
URL: https://github.com/apache/spark/pull/56907#issuecomment-4848926607

   ## Review feedback
   
   Two issues in the persistent local Connect server path that are worth 
addressing before merge.
   
   ### 1. Concurrent first-time startup can fail spuriously 
(`python/pyspark/sql/connect/session.py:1379`)
   
   In non-test mode `_start_persistent_local_connect_server` requests the fixed 
default port 15002 (`session.py:1336-1338`), and the wait loop hard-fails the 
moment the spawned daemon exits:
   
   ```python
   exit_code = proc.poll()
   if exit_code is not None:
       raise 
PySparkRuntimeError(errorClass="LOCAL_CONNECT_SERVER_START_FAILED", ...)
   ```
   
   If two opted-in processes start with no discovery file yet (parallel 
workers, an IDE spawning processes, several dev scripts), both spawn a daemon 
on 15002; one wins and writes discovery, the other's JVM can't bind and exits. 
The losing client then raises `LOCAL_CONNECT_SERVER_START_FAILED` immediately 
even though a reusable server is now available. The same port-collision 
mechanism also bites after a version upgrade: 
`_local_connect_server_is_reusable` rejects the stale server on version 
mismatch (`session.py:1292`), then we try to start a new one on 15002 that the 
old process still holds.
   
   There is no lock around discovery creation and no reuse-the-winner retry on 
child exit. Suggested fix: before raising on `exit_code is not None`, do a 
final `_read_local_connect_discovery()` / `_local_connect_server_is_reusable()` 
check and reconnect if a usable server is now up; ideally also hold a file lock 
spanning discovery creation so only one process starts a server. (Note this is 
masked in tests because `SPARK_TESTING` forces an ephemeral port 0.)
   
   ### 2. The daemon drops most builder options on first startup 
(`python/pyspark/sql/connect/local_server.py:140`)
   
   The in-process path `_start_connect_server` (`session.py:1190-1234`) merges 
the full `opts` into the `SparkConf` before `SparkContext.getOrCreate(conf)`, 
so static/server-side settings (warehouse dir, app name, catalog configs, 
`spark.jars`/packages, driver settings, etc.) take effect. The persistent path 
only forwards `--master`, `--port`, `--token`, `--idle-timeout` 
(`session.py:1342-1355`), and `local_server.py:140-151` builds the JVM with 
just master / plugins / port / artifact-isolation / token. The remaining `opts` 
are dropped.
   
   After connecting, the client's `_apply_options` (`session.py:204-228`) can 
only apply runtime SQL confs per-session via `conf._set_all(...)` — it cannot 
establish static confs on an already-running JVM. So 
`.remote("local[*]").config("spark.sql.warehouse.dir", ...)` (and 
jars/packages/catalog/app-name) silently has no effect on first startup, 
diverging from the in-process behavior.
   
   Caveat: a *shared* persistent server fundamentally can't honor per-client 
static confs for the second+ client (the JVM is already warm), so this promise 
is inherently limited. But at minimum the first client's relevant startup confs 
should seed the daemon so first-run behavior matches the in-process path.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to