Skip to content

fix: prevent ghost pods.#1619

Open
AlexSCorey wants to merge 1 commit into
mainfrom
81201_ghost_activations
Open

fix: prevent ghost pods.#1619
AlexSCorey wants to merge 1 commit into
mainfrom
81201_ghost_activations

Conversation

@AlexSCorey

@AlexSCorey AlexSCorey commented Jul 16, 2026

Copy link
Copy Markdown
Member

When the monitor detects an unresponsive activation and triggers a
restart, _watch_pod_deletion falsely reports success if the watch
stream times out without receiving a DELETED event. This allows
_unresponsive_policy to restart the activation while the original
pod is still running, producing ghost pods that compete for shared
resources like Kafka consumer groups.

Two fixes:

  1. _watch_pod_deletion now tracks whether a DELETED event was actually
    received. If the watch times out without one, it retries instead of
    reporting success. After all retries, it raises ContainerCleanupError.

  2. _unresponsive_policy now calls _cleanup explicitly via a new
    _handle_unresponsive_activation helper. If cleanup fails, the
    restart is skipped to avoid creating a duplicate pod. The activation
    is still marked FAILED regardless of cleanup outcome.

Fixes: AAP-83199

Summary by CodeRabbit

  • Bug Fixes
    • Improved unresponsive activation handling by centralizing the unresponsive workflow, ensuring cleanup failures reliably prevent restart scheduling and activations are consistently marked FAILED with the right per-check message.
    • Enhanced Kubernetes job/pod cleanup to only report success after confirming the pod deletion event, with stronger watch/retry behavior (including more transient error handling).
  • Tests
    • Expanded integration coverage for Kubernetes deletion watches, including deterministic cursor handling, timeout/empty-stream scenarios, and successful deletion logging.
    • Added integration tests validating unresponsive activation behavior across restart-policy scenarios, including cleanup-failure re-raise behavior and status updates.

@AlexSCorey
AlexSCorey requested a review from a team as a code owner July 16, 2026 16:08
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Unresponsive activation handling now persists FAILED state and conditionally schedules restarts. Kubernetes pod cleanup now requires an observed DELETED event before reporting success, with resource-version tracking, retry handling, and integration-test coverage.

Changes

Activation cleanup and failure handling

Layer / File(s) Summary
Unresponsive activation failure flow
src/aap_eda/services/activation/activation_manager.py, tests/integration/services/activation/test_manager.py
Cleanup side effects persist FAILED status even when cleanup raises; restart scheduling occurs only after successful cleanup and when the restart policy allows it.
Kubernetes pod deletion confirmation
src/aap_eda/services/activation/engine/kubernetes.py, tests/integration/services/activation/engine/test_kubernetes.py
Pod deletion watches use resource versions, retry transient conditions including HTTP 410, and report cleanup success only after a DELETED event is observed.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ActivationManager
  participant KubernetesEngine
  participant KubernetesAPI
  participant ActivationState
  participant RestartScheduler

  ActivationManager->>KubernetesEngine: Run cleanup
  KubernetesEngine->>KubernetesAPI: Get resource_version and watch pod deletion
  KubernetesAPI-->>KubernetesEngine: DELETED event or watch termination
  KubernetesEngine-->>ActivationManager: Cleanup success or ContainerCleanupError
  ActivationManager->>ActivationState: Persist FAILED status
  ActivationManager->>RestartScheduler: Schedule restart when cleanup succeeds and policy allows
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly reflects the main change: preventing ghost pods during activation restarts.
Description check ✅ Passed The description covers the problem, the two code changes, and the related issue, matching the template's core requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 81201_ghost_activations

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/aap_eda/services/activation/engine/kubernetes.py (1)

592-612: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Avoid starting the deletion watch after the delete request

The pod can disappear before this watch is established, so the DELETED event is missed and cleanup falls through to ContainerCleanupError. Seed the watch from a pre-delete list_namespaced_pod resourceVersion (or use another list-then-watch handoff) so deletion cannot be skipped.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/services/activation/engine/kubernetes.py` around lines 592 - 612,
The pod deletion watch must be established from a pre-delete snapshot so a fast
deletion cannot be missed. Update the cleanup flow around watcher.stream and
list_namespaced_pod to list the target pod before issuing the delete request,
capture its resourceVersion, and start the watch from that version using the
Kubernetes list-then-watch handoff; preserve the existing pod_deleted logging
and successful return behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/aap_eda/services/activation/activation_manager.py`:
- Around line 393-397: Update _cleanup to support a propagating mode that
re-raises ContainerCleanupError, while preserving best-effort behavior for
existing callers. Have _handle_unresponsive_activation invoke this mode so
cleanup failures remain observable and prevent
_fail_instance/_unresponsive_policy from scheduling a restart. Update the
relevant tests to make container_engine.cleanup fail through the real _cleanup
implementation rather than mocking around it.

---

Outside diff comments:
In `@src/aap_eda/services/activation/engine/kubernetes.py`:
- Around line 592-612: The pod deletion watch must be established from a
pre-delete snapshot so a fast deletion cannot be missed. Update the cleanup flow
around watcher.stream and list_namespaced_pod to list the target pod before
issuing the delete request, capture its resourceVersion, and start the watch
from that version using the Kubernetes list-then-watch handoff; preserve the
existing pod_deleted logging and successful return behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 0996f425-1602-4e22-bebc-a91aa848a9d7

📥 Commits

Reviewing files that changed from the base of the PR and between 51086aa and 8dd5e6a.

📒 Files selected for processing (4)
  • src/aap_eda/services/activation/activation_manager.py
  • src/aap_eda/services/activation/engine/kubernetes.py
  • tests/integration/services/activation/engine/test_kubernetes.py
  • tests/integration/services/activation/test_manager.py

Comment thread src/aap_eda/services/activation/activation_manager.py Outdated
@AlexSCorey
AlexSCorey force-pushed the 81201_ghost_activations branch 3 times, most recently from f912295 to c99cdb4 Compare July 16, 2026 19:35
@codecov-commenter

codecov-commenter commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.89%. Comparing base (51086aa) to head (1a58669).

@@            Coverage Diff             @@
##             main    #1619      +/-   ##
==========================================
+ Coverage   92.75%   92.89%   +0.13%     
==========================================
  Files         244      244              
  Lines       11346    11368      +22     
==========================================
+ Hits        10524    10560      +36     
+ Misses        822      808      -14     
Flag Coverage Δ
unit-int-tests-3.11 92.89% <100.00%> (+0.13%) ⬆️
unit-int-tests-3.12 92.89% <100.00%> (+0.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../aap_eda/services/activation/activation_manager.py 64.38% <100.00%> (+3.52%) ⬆️
...c/aap_eda/services/activation/engine/kubernetes.py 90.71% <100.00%> (+0.37%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlexSCorey
AlexSCorey force-pushed the 81201_ghost_activations branch from c99cdb4 to 0baa2c3 Compare July 16, 2026 20:41
@AlexSCorey

Copy link
Copy Markdown
Member Author

/run-atf-tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/aap_eda/services/activation/engine/kubernetes.py (1)

641-641: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use conversion flag instead of str() in f-string.

Ruff RUF010: replace f"Error during cleanup: {str(exc)}" with f"Error during cleanup: {exc!s}".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/services/activation/engine/kubernetes.py` at line 641, Update the
cleanup error message to use the f-string conversion flag !s for exc instead of
calling str(exc) explicitly, preserving the existing “Error during cleanup”
message and behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/aap_eda/services/activation/engine/kubernetes.py`:
- Line 641: Update the cleanup error message to use the f-string conversion flag
!s for exc instead of calling str(exc) explicitly, preserving the existing
“Error during cleanup” message and behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: c323c4fe-50c1-4f1d-b719-3a5b19ec734a

📥 Commits

Reviewing files that changed from the base of the PR and between c99cdb4 and 0baa2c3.

📒 Files selected for processing (4)
  • src/aap_eda/services/activation/activation_manager.py
  • src/aap_eda/services/activation/engine/kubernetes.py
  • tests/integration/services/activation/engine/test_kubernetes.py
  • tests/integration/services/activation/test_manager.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/aap_eda/services/activation/activation_manager.py
  • tests/integration/services/activation/engine/test_kubernetes.py
  • tests/integration/services/activation/test_manager.py

@aap-pde-ci-bot

Copy link
Copy Markdown

❌ Test Results - FAILED

Summary

Metric Count
Total Tests 66
✅ Passed 49
❌ Failed 1
⚠️ Errors 0
⏭️ Skipped 16
⏱️ Duration 212.36s

Pass Rate: 74.2%

❌ Failed Tests

Test Class
test_activation_reaches_completed_status opt.test-suite.tests.api.test_activations.TestEdaActivations

…tart

  When the monitor detects an unresponsive activation and triggers a
  restart, _watch_pod_deletion falsely reports success if the watch
  stream times out without receiving a DELETED event. This allows
  _unresponsive_policy to restart the activation while the original
  pod is still running, producing ghost pods that compete for shared
  resources like Kafka consumer groups.

  Two fixes:

  1. _watch_pod_deletion now tracks whether a DELETED event was actually
     received. If the watch times out without one, it retries instead of
     reporting success. After all retries, it raises ContainerCleanupError.

  2. _unresponsive_policy now calls _cleanup explicitly via a new
     _handle_unresponsive_activation helper. If cleanup fails, the
     restart is skipped to avoid creating a duplicate pod. The activation
     is still marked FAILED regardless of cleanup outcome.
  3. adds a list_then_watch flow for deleting a pod so that we don't loose track
     of the pods we are trying to delete
  Fixes: AAP-81201
@AlexSCorey
AlexSCorey force-pushed the 81201_ghost_activations branch from 0baa2c3 to 1a58669 Compare July 17, 2026 12:52
@sonarqubecloud

Copy link
Copy Markdown

@AlexSCorey

Copy link
Copy Markdown
Member Author

/run-atf-tests

@aap-pde-ci-bot

Copy link
Copy Markdown

❌ Test Results - FAILED

Summary

Metric Count
Total Tests 66
✅ Passed 33
❌ Failed 0
⚠️ Errors 17
⏭️ Skipped 16
⏱️ Duration 64.53s

Pass Rate: 50.0%

❌ Failed Tests

Test Class
test_activation_reaches_completed_status opt.test-suite.tests.api.test_activations.TestEdaActivations
test_delete_decision_environment_with_force_flag opt.test-suite.tests.api.test_decision_environments.TestDecisionEnvironments
test_list_projects[bulk_projects0] opt.test-suite.tests.api.test_projects.TestProjects
test_create_project_duplicated opt.test-suite.tests.api.test_projects.TestProjects
test_read_project opt.test-suite.tests.api.test_projects.TestProjects
test_delete_project opt.test-suite.tests.api.test_projects.TestProjects
test_update_project opt.test-suite.tests.api.test_projects.TestProjects
test_update_project_url opt.test-suite.tests.api.test_projects.TestProjects
test_update_project_bad_foreign_keys opt.test-suite.tests.api.test_projects.TestProjects
test_update_project_null_credential opt.test-suite.tests.api.test_projects.TestProjects
test_update_project_no_changes opt.test-suite.tests.api.test_projects.TestProjects
test_update_project_duplicated_name opt.test-suite.tests.api.test_projects.TestProjects
test_create_update_project_empty_name opt.test-suite.tests.api.test_projects.TestProjects
test_filter_project_name[bulk_projects0] opt.test-suite.tests.api.test_projects.TestProjects
test_filter_project_name[bulk_projects1] opt.test-suite.tests.api.test_projects.TestProjects
test_filter_project_url[bulk_projects0] opt.test-suite.tests.api.test_projects.TestProjects
test_change_organization_of_project_to_invalid_value opt.test-suite.tests.api.test_projects.TestProjects

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.

3 participants