FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[SPARK-58021][CONNECT] Reap orphaned local pool members by ericm-db · Pull Request #58247 · apache/spark · GitHub

/ spark Public

[SPARK-58021][CONNECT] Reap orphaned local pool members - #58247

Open
ericm-db wants to merge 3 commits into
apache:masterfrom
ericm-db:local-connect-pool-recovery
Open

[SPARK-58021][CONNECT] Reap orphaned local pool members#58247
ericm-db wants to merge 3 commits into
apache:masterfrom
ericm-db:local-connect-pool-recovery

Conversation

ericm-db commented Aug 24, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

This is layer 5 of the ten-PR local Connect pool stack:

#57684 -> #57685 -> #57907 -> #57686 -> #58247 ->
#58367 -> #57687 -> #58248 -> #57102 -> #57688

This layer adds recovery after a pool member has been published or claimed:

  • a janitor for ready, claimed, and retiring members;
  • retirement of dead, unreachable, incompatible, idle, and malformed ready members;
  • retirement of claims whose client or server process generation has disappeared;
  • persisted claimant process identities so PID reuse cannot strand an orphaned claim;
  • recovery of independently valid server handles from malformed records; and
  • bounded, PID-reuse-safe completion of retirement.

It also addresses the two follow-ups from the review of #57686: successful SIGTERM delivery is
persisted so young retirement passes do not repeat the process inspection, and the daemon-pid
fallback now has coverage for removing state after that process exits.

Pending-launch, attendant, conf-file, and unreferenced member-directory recovery remain in
#58367.

Why are the changes needed?

Pool members outlive individual Python call frames. A client can be killed without releasing its
claim, a ready server can die or become unreachable, and persisted process IDs can be reused.
Without a conservative janitor these cases can strand JVMs or make unusable members count toward
the pool indefinitely.

Separating post-publication recovery from launch recovery gives each process-ownership model its
own review unit: this layer authorizes server cleanup from persisted process generations, while the
next layer handles attendant commands and launch process groups.

Does this PR introduce any user-facing change?

No. The pool is not wired into SparkSession in this layer.

How was this patch tested?

The focused pool suite's 50 tests passed:

PYTHONPATH=python:python/lib/pyspark.zip:python/lib/py4j-0.10.9.9-src.zip \
  SPARK_TESTING=1 \
  .venv/bin/python -m unittest -v \
  pyspark.sql.tests.connect.test_connect_local_server_pool

The changed files also passed Python AST parsing, Ruff checking and formatting, targeted mypy,
git diff --check, and changed-file ASCII and 100-column checks.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Fable 5) and OpenAI Codex (GPT-5)

dtenedor pushed a commit that referenced this pull request Aug 27, 2026
### What changes were proposed in this pull request?

This is layer 4 of the nine-PR local Connect pool stack:

#57684 -> #57685 -> #57907 -> #57686 -> #58247 -> #57687 -> #58248 -> #57102 -> #57688

The three lower layers are merged, so GitHub shows only this layer's two-file diff. The review unit
introduced here is commit `37dbe4b5ae7`.

This layer adds the normal server-retirement path on top of member claiming:

- atomic JSON state replacement and cleanup of interrupted-write temporary files;
- validated retired-state records with recovery of independently valid process IDs;
- crash-safe state-to-retired renames;
- graceful SIGTERM shutdown followed by bounded SIGKILL escalation; and
- idempotent claimed-member release, including forked-child and retry handling.

Janitor recovery, acquisition, forceful purge, SparkSession integration, and warmup remain in later
PRs.

### Why are the changes needed?

A claimed server must be released without losing the only process handle if the client crashes
during the state transition. Isolating retirement keeps atomic persistence, graceful shutdown, and
release semantics independently reviewable before orphan scanning and launch orchestration are
added.

### Does this PR introduce _any_ user-facing change?

No. The pool is not wired into SparkSession in this layer.

### How was this patch tested?

Added eight focused tests at this layer, bringing the suite to 33 tests, and extended the storage
test with interrupted-write and stale-temporary-file coverage. The focused command is:

```bash
python/run-tests --testnames pyspark.sql.tests.connect.test_connect_local_server_pool
```

At the stack tip, the equivalent direct `unittest` invocation passed all 56 pool tests, including
the two real-server E2E tests.

The rebuilt commit passed Python AST parsing, `git diff --check`, and changed-file ASCII and
100-column checks.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Fable 5) and OpenAI Codex (GPT-5)

Closes #57686 from ericm-db/local-connect-pool-lifecycle.

Authored-by: Eric Marnadi <eric.marnadi@databricks.com>
Signed-off-by: Daniel Tenedorio <daniel.tenedorio@databricks.com>
dtenedor pushed a commit that referenced this pull request Aug 27, 2026
### What changes were proposed in this pull request?

This is layer 4 of the nine-PR local Connect pool stack:

#57684 -> #57685 -> #57907 -> #57686 -> #58247 -> #57687 -> #58248 -> #57102 -> #57688

The three lower layers are merged, so GitHub shows only this layer's two-file diff. The review unit
introduced here is commit `37dbe4b5ae7`.

This layer adds the normal server-retirement path on top of member claiming:

- atomic JSON state replacement and cleanup of interrupted-write temporary files;
- validated retired-state records with recovery of independently valid process IDs;
- crash-safe state-to-retired renames;
- graceful SIGTERM shutdown followed by bounded SIGKILL escalation; and
- idempotent claimed-member release, including forked-child and retry handling.

Janitor recovery, acquisition, forceful purge, SparkSession integration, and warmup remain in later
PRs.

### Why are the changes needed?

A claimed server must be released without losing the only process handle if the client crashes
during the state transition. Isolating retirement keeps atomic persistence, graceful shutdown, and
release semantics independently reviewable before orphan scanning and launch orchestration are
added.

### Does this PR introduce _any_ user-facing change?

No. The pool is not wired into SparkSession in this layer.

### How was this patch tested?

Added eight focused tests at this layer, bringing the suite to 33 tests, and extended the storage
test with interrupted-write and stale-temporary-file coverage. The focused command is:

```bash
python/run-tests --testnames pyspark.sql.tests.connect.test_connect_local_server_pool
```

At the stack tip, the equivalent direct `unittest` invocation passed all 56 pool tests, including
the two real-server E2E tests.

The rebuilt commit passed Python AST parsing, `git diff --check`, and changed-file ASCII and
100-column checks.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Fable 5) and OpenAI Codex (GPT-5)

Closes #57686 from ericm-db/local-connect-pool-lifecycle.

Authored-by: Eric Marnadi <eric.marnadi@databricks.com>
Signed-off-by: Daniel Tenedorio <daniel.tenedorio@databricks.com>
(cherry picked from commit a1857f5)
Signed-off-by: Daniel Tenedorio <daniel.tenedorio@databricks.com>
ericm-db force-pushed the local-connect-pool-recovery branch from 6883333 to beee54e Compare August 27, 2026 23:41

dtenedor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

The approach generally looks reasonable. I just had a couple extra testing ideas. LGTM after all comments are resolved

self.assertNotIn("process_start_id", retired)
self.assertIsNone(unreadable_pid.poll())

def test_reap_claimed_of_dead_client(self) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Should we also have tests for:

  • _idle_timeout() returning <= 0 (disabled) or falling back on a non-integer env value
  • A healthy, non-idle server member survives a full janitor() pass in isolation (it's covered incidentally by the f2e5 case, but not directly)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

added!

Copy link
Copy Markdown
Contributor

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL