dtenedor commented on code in PR #57684:
URL: https://github.com/apache/spark/pull/57684#discussion_r3692554785


##########
python/pyspark/sql/connect/local_server.py:
##########
@@ -269,20 +321,20 @@ def _token(self) -> str:
         )
 
     def _pick_port(self) -> int:
-        """Under SPARK_TESTING always use an OS-assigned free port so suites 
can run in
-        parallel; otherwise honor the configured/default port, falling back to 
a free one if
-        another process holds it. (A live stale server of ours also holds the 
port, but that
-        start fails later at spark-daemon.sh's pid-file check regardless of 
port.) The sbin
-        script cannot report an ephemeral port back, so the free port is 
picked and released
-        here, with a small race until the server binds it.
+        """Use an OS-assigned free port when requested or under SPARK_TESTING 
so suites can
+        run in parallel. Otherwise honor the configured/default port, falling 
back to a free
+        one if another process holds it. (A live stale server of ours also 
holds the port, but
+        that start fails later at spark-daemon.sh's pid-file check regardless 
of port.) The
+        sbin script cannot report an ephemeral port back, so the free port is 
picked and
+        released here, with a small race until the server binds it.
         """
 
         def free_port() -> int:
             with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                 sock.bind(("localhost", 0))
                 return sock.getsockname()[1]
 
-        if "SPARK_TESTING" in os.environ:
+        if self._use_ephemeral_port or "SPARK_TESTING" in os.environ:

Review Comment:
   **[Medium — test coverage]** This is the new production path that matters 
for pool attendants (they will not have `SPARK_TESTING` set). 
`test_start_delegates_launch_options` only asserts that `ServerLauncher(...)` 
received `use_ephemeral_port=True`; it mocks away the launcher, so `_pick_port` 
never runs. Under a normal test env where `SPARK_TESTING` is set, the flag is 
also a no-op.
   
   Suggestion: add a unit test that pops `SPARK_TESTING`, constructs a launcher 
with `use_ephemeral_port=True`, and asserts `_pick_port()` takes the free-port 
path rather than the configured/default port. Optionally also cover 
`use_ephemeral_port=False` still honoring the configured port when testing is 
unset.



##########
python/pyspark/sql/connect/local_server.py:
##########
@@ -303,20 +355,9 @@ def _seed_conf(self) -> Dict[str, Any]:
         opt-in keys. Only the run that starts the server can seed static 
confs; later runs
         find the JVM already warm.
         """
-        conf: Dict[str, Any] = {}
-        for i in range(int(os.environ.get("PYSPARK_REMOTE_INIT_CONF_LEN", 
"0"))):
-            conf = 
json.loads(os.environ["PYSPARK_REMOTE_INIT_CONF_{}".format(i)])
-        conf.update(self._opts)
-        for k in list(conf):
-            if k in (
-                "spark.remote",
-                "spark.api.mode",
-                "spark.master",
-                "spark.connect.authenticate.token",
-                "spark.connect.grpc.binding.port",
-            ) or k.startswith("spark.local.connect."):
-                conf.pop(k)
-        return conf
+        if self._seed_override is not None:
+            return dict(self._seed_override)

Review Comment:
   **[Medium — correctness]** The override path returns `seed_conf` verbatim 
and skips the key stripping that `startup_seed_conf` applies (`spark.master`, 
`spark.connect.grpc.binding.port`, `spark.local.connect.*`, token, etc.).
   
   #57102 happens to call `startup_seed_conf(opts)` before passing the result, 
so it is safe today, but this API does not enforce that. A caller that passes 
raw builder opts as `seed_conf` can put conflicting keys into 
`--properties-file`.
   
   Suggestion: always run the same strip on the override (idempotent if already 
sanitized), so the invariant lives in one place — or document + assert that 
`seed_conf` must already be post-`startup_seed_conf`.



##########
python/pyspark/sql/tests/connect/test_connect_local_server.py:
##########
@@ -114,6 +114,65 @@ def test_discovery_location(self) -> None:
             self.assertIn("spark-connect-{}".format(getpass.getuser()), 
default.directory)
             self.assertEqual(os.stat(default.directory).st_mode & 0o777, 0o700)
 
+    def test_startup_seed_conf(self) -> None:
+        from unittest import mock
+
+        initial = {
+            "spark.sql.shuffle.partitions": "8",
+            "spark.master": "local[1]",
+        }
+        opts = {
+            "spark.sql.warehouse.dir": os.path.join(self._tmpdir, "warehouse"),
+            "spark.local.connect.reuse": "true",
+            "spark.connect.grpc.binding.port": "0",
+        }
+        env = {
+            "PYSPARK_REMOTE_INIT_CONF_LEN": "1",
+            "PYSPARK_REMOTE_INIT_CONF_0": json.dumps(initial),
+        }
+        with mock.patch.dict(os.environ, env):
+            self.assertEqual(
+                local_server.startup_seed_conf(opts),
+                {
+                    "spark.sql.shuffle.partitions": "8",
+                    "spark.sql.warehouse.dir": opts["spark.sql.warehouse.dir"],
+                },
+            )
+
+    def test_start_delegates_launch_options(self) -> None:

Review Comment:
   **[Low — test coverage]** Together with `test_startup_seed_conf`, this 
covers the helper and the `LocalConnectServer.start` wiring, but the 
`ServerLauncher` seed-override path itself is never exercised: `_seed_conf()` 
returning the override, and especially `{}` vs `None` (load-bearing for the 
pool attendant, which passes `opts={}` plus a precomputed `seed_conf` — empty 
dict must *not* fall through to env `PYSPARK_REMOTE_INIT_CONF_*`).
   
   Suggestion: small `ServerLauncher` unit tests for `_seed_conf` / 
`_seed_properties_file` with override, `None`, and `{}`.



##########
python/pyspark/sql/connect/local_server.py:
##########
@@ -303,20 +355,9 @@ def _seed_conf(self) -> Dict[str, Any]:
         opt-in keys. Only the run that starts the server can seed static 
confs; later runs
         find the JVM already warm.
         """

Review Comment:
   **[Low — docs]** This docstring still describes only the merge+strip path 
and does not mention the `seed_conf` override short-circuit (or that overrides 
are currently taken as-is). Easy to misread when implementing callers — worth 
updating alongside any sanitization change.



-- 
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