Skip to content

Do not kill the process outright on download timeout - #298

Open
bjk7119 wants to merge 2 commits into
mainfrom
wrapper
Open

Do not kill the process outright on download timeout#298
bjk7119 wants to merge 2 commits into
mainfrom
wrapper

Conversation

@bjk7119

@bjk7119 bjk7119 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Bug Fixes

  • Improved download timeout handling on Windows by allowing active download operations to stop cleanly before termination.
  • Git and HTTP downloads now consistently report timeout errors after cleanup.
  • Partial downloads are removed when a timeout occurs, reducing the risk of incomplete or unusable files.
  • Timeout behavior is now more consistent across supported download methods and operating systems.

@bjk7119
bjk7119 requested a review from dd-jy July 31, 2026 16:21
@bjk7119 bjk7119 self-assigned this Jul 31, 2026
@bjk7119 bjk7119 added the chore [PR/Issue] Refactoring, maintenance the code label Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The Windows download watchdog now tracks child processes, records timeout state, and avoids immediate process termination. Git and HTTP/wget paths clean partial downloads and raise TimeOutException after watchdog termination.

Changes

Download timeout handling

Layer / File(s) Summary
Watchdog process control
src/fosslight_util/download.py
Adds child-process registration, timeout-state tracking, child-process termination, and raise_if_timed_out cleanup handling.
Git and HTTP/wget integration
src/fosslight_util/download.py
Registers Git and wget subprocesses. Timeout termination now removes partial output and raises TimeOutException.

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

Suggested reviewers: dd-jy, soimkim

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preserving the parent process while handling download timeouts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wrapper

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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/fosslight_util/download.py`:
- Around line 99-106: Update the timeout parsing helper around the
FOSSLIGHT_DOWNLOAD_TIMEOUT handling to validate parsed non-zero values against
the platform limits supported by signal.alarm() and threading.Event.wait(). When
the value exceeds the supported limit, log it as invalid and return
SIGNAL_TIMEOUT; preserve the existing fallback for missing, blank, or
non-integer values and allow valid zero values.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5791e6fb-ceea-43c5-a2ca-a4291a92366c

📥 Commits

Reviewing files that changed from the base of the PR and between 6d2ec31 and 9cf7dfa.

📒 Files selected for processing (1)
  • src/fosslight_util/download.py

Comment thread src/fosslight_util/download.py Outdated
Comment on lines +99 to +106
raw = os.environ.get("FOSSLIGHT_DOWNLOAD_TIMEOUT")
if raw is None or raw.strip() == "":
return SIGNAL_TIMEOUT
try:
return int(raw)
except ValueError:
logger.warning("Invalid FOSSLIGHT_DOWNLOAD_TIMEOUT=%s; using %d sec.", raw, SIGNAL_TIMEOUT)
return SIGNAL_TIMEOUT

@coderabbitai coderabbitai Bot Jul 31, 2026

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import signal
import threading

print("threading.TIMEOUT_MAX =", getattr(threading, "TIMEOUT_MAX", None))

if hasattr(signal, "alarm"):
    for value in (2**31, 2**32, 10**20):
        try:
            signal.alarm(value)
            print(f"signal.alarm({value}) accepted")
        except (OverflowError, OSError, ValueError) as error:
            print(f"signal.alarm({value}) rejected: {error}")
        finally:
            signal.alarm(0)
PY

Repository: fosslight/fosslight_util

Length of output: 438


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching download.py:\n'
fd -a 'download\.py$' . | sed 's#^\./##'

printf '\nRelevant section:\n'
sed -n '85,125p' src/fosslight_util/download.py

printf '\nUsages of get_download_timeout:\n'
rg -n "get_download_timeout|FOSSLIGHT_DOWNLOAD_TIMEOUT|signal\.alarm|Thread\(" src/fosslight_util/download.py

Repository: fosslight/fosslight_util

Length of output: 2265


Reject out-of-range download watchdog timeout values.

FOSSLIGHT_DOWNLOAD_TIMEOUT currently accepts values that can make the watchdog unusable. On Python, signal.alarm() rejects values too large to fit in a C int, while threading.Event.wait() still accepts values above threading.TIMEOUT_MAX. If timeout is non-zero but exceeds the supported platform limit, clamp it to SIGNAL_TIMEOUT instead of starting with an invalid value.

🤖 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/fosslight_util/download.py` around lines 99 - 106, Update the timeout
parsing helper around the FOSSLIGHT_DOWNLOAD_TIMEOUT handling to validate parsed
non-zero values against the platform limits supported by signal.alarm() and
threading.Event.wait(). When the value exceeds the supported limit, log it as
invalid and return SIGNAL_TIMEOUT; preserve the existing fallback for missing,
blank, or non-integer values and allow valid zero values.

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.

@coderabbitai review

@bjk7119 bjk7119 changed the title Make the download watchdog timeout configurable Do not kill the process outright on download timeout Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@soimkim soimkim 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.

머지시 커밋 메세지 본문 부분 필수 수정 필요 건.

그리고 이 수정은 timeout에 대한 이벤트 잡는게 아님.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/fosslight_util/download.py (1)

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

Log the swallowed exception during process cleanup.

_kill_download_processes() catches Exception and discards it silently when proc.poll()/proc.kill() fails. Log the exception at debug level so a failed cleanup attempt is visible during troubleshooting.

♻️ Proposed fix
     for proc in procs:
         try:
             if proc.poll() is None:
                 proc.kill()
-        except Exception:
-            pass
+        except Exception as error:
+            logger.debug(f"Failed to kill tracked download process: {error}")
🤖 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/fosslight_util/download.py` around lines 118 - 119, Update the exception
handler in _kill_download_processes() to log the caught cleanup exception at
debug level instead of silently passing, while preserving the existing cleanup
flow and exception suppression.

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.

Inline comments:
In `@src/fosslight_util/download.py`:
- Around line 1188-1192: Remove the early _cancel_download_watchdog() call from
download_git_repository so the alarm started by download_git_clone remains
active through run_git_clone_with_size_guard. Preserve the existing
raise_if_timed_out(alarm, target_dir) handling and let the watchdog enforce the
overall clone timeout on both platforms.

---

Nitpick comments:
In `@src/fosslight_util/download.py`:
- Around line 118-119: Update the exception handler in
_kill_download_processes() to log the caught cleanup exception at debug level
instead of silently passing, while preserving the existing cleanup flow and
exception suppression.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: faae5b4c-f8e8-47bd-9bcd-94c36dd2d570

📥 Commits

Reviewing files that changed from the base of the PR and between e385632 and 07daf9b.

📒 Files selected for processing (1)
  • src/fosslight_util/download.py

Comment on lines +1188 to +1192
# The watchdog kills the clone on timeout, so the call above returns with a
# partial checkout. Turn that into TimeOutException instead of reporting a
# confusing git error, and clean the half-written target directory.
raise_if_timed_out(alarm, target_dir)

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.

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Git-path timeout propagation never fires; the watchdog is already cancelled before the clone starts.

download_git_clone starts the watchdog at Line 1164 (alarm = _start_download_watchdog()), then calls download_git_repository, whose first action (Line 1091-1094, unchanged) is:

try:
    _cancel_download_watchdog()
except Exception:
    pass

This call cancels the same alarm object just started, before run_git_clone_with_size_guard spawns the git subprocess and registers it with register_download_process (Line 995-1002). On Windows, Alarm._cancelled.set() fires before the timeout thread can ever observe a timeout, so alarm.timed_out stays False. On POSIX, signal.alarm(0) disarms SIGALRM before the clone even begins. As a result, raise_if_timed_out(alarm, target_dir) at Line 1191 can never raise TimeOutException for git downloads; it is unreachable dead code in practice.

This also means run_git_clone_with_size_guard has no external time bound. Its own loop only aborts a clone that exceeds size_limit_gb; when the clone stays under the limit (or no limit is set), proc.communicate(timeout=size_check_interval_sec) loops indefinitely. A stalled clone (network hang, credential wait) never times out on either platform.

The size-guard's own default, size_check_after_sec: int = SIGNAL_TIMEOUT (Line 981), suggests the intended design already expects the watchdog and the size-guard to share the same time budget, not for one to disable the other. Given the watchdog's new behavior only kills the tracked child process and raises a graceful TimeOutException (it no longer force-exits the whole interpreter), the original justification for the early cancellation ("Avoid hard process exit from parent watchdog while size-guarded clone may run longer") no longer applies.

Remove the early cancellation so the watchdog stays armed for the whole git clone, matching the wget/HTTP path, which has no equivalent cancellation and where the new timeout handling does work as intended.

🐛 Suggested direction for the fix
     logger.info(f"Download git url :{git_url}, version:{refs_to_checkout}")
 
-    # Avoid hard process exit from parent watchdog while size-guarded clone may run longer
-    try:
-        _cancel_download_watchdog()
-    except Exception:
-        pass
-
     env = os.environ.copy()

Do you want me to work out the full fix, including confirming SIGNAL_TIMEOUT is an acceptable overall bound for large clones guarded by size_limit_gb?

🤖 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/fosslight_util/download.py` around lines 1188 - 1192, Remove the early
_cancel_download_watchdog() call from download_git_repository so the alarm
started by download_git_clone remains active through
run_git_clone_with_size_guard. Preserve the existing raise_if_timed_out(alarm,
target_dir) handling and let the watchdog enforce the overall clone timeout on
both platforms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore [PR/Issue] Refactoring, maintenance the code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants