Skip to content

fix(olcrtc): supervise sidecar recovery - #156

Closed
hawkff wants to merge 4 commits into
mainfrom
fix/olcrtc-sidecar-recovery
Closed

fix(olcrtc): supervise sidecar recovery#156
hawkff wants to merge 4 commits into
mainfrom
fix/olcrtc-sidecar-recovery

Conversation

@hawkff

@hawkff hawkff commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • detect terminal olcRTC runtime loss after initial readiness
  • restart live olcRTC sidecars with capped backoff and per-generation readiness checks
  • keep one-shot connection tests fail-fast
  • bound sidecar shutdown before each retry

Validation

  • CodeRabbit CLI: no findings
  • focused static lifecycle, wrapper, and integration reviews: no blockers
  • Namespace CI run 29280508001: passed

Rollout

Android end-to-end verification is still required before merge.

Greptile Summary

This PR adds supervised recovery for the olcRTC sidecar. The main changes are:

  • Restart backoff and per-generation readiness checks in the guarded process pool.
  • Ready-marker coordination between the Android parent and the Go olcRTC wrapper.
  • Runtime health polling in the wrapper after initial readiness.
  • Focused unit tests and build-script coverage for the new readiness helpers.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt Adds restart-aware process supervision with readiness callbacks, bounded termination, and capped backoff support.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt Adds olcRTC ready-marker tracking and restart wiring for live sidecar recovery.
buildScript/lib/olcrtc-src/main.go Publishes readiness markers and exits when the olcRTC runtime stops after initial readiness.
app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicy.kt Introduces validated restart backoff settings and delay progression.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt Extracts readiness marker and pending-port policy helpers.
buildScript/lib/olcrtc.sh Runs the new Go wrapper tests before cross-compiling the olcRTC sidecar.

Reviews (4): Last reviewed commit: "fix: handle empty sidecar readiness sets" | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds guarded process restart backoff and lifecycle handling, integrates olcRTC recovery with asynchronous listener readiness checks, monitors runtime shutdown health, adds Kotlin and Go tests, runs Go tests in the wrapper script, and updates the sidecar cache checksum inputs.

Changes

olcRTC lifecycle

Layer / File(s) Summary
Guarded restart management
app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt, app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicy.kt, app/src/test/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicyTest.kt
Adds validated exponential restart backoff and restructures process watching, readiness observation, exit reporting, and bounded teardown.
Sidecar recovery and readiness
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt, app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt, app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt, app/src/test/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicyTest.kt
Configures olcRTC recovery and readiness markers, adds asynchronous external-port probing, and updates readiness failure handling.
Runtime shutdown monitoring and validation
buildScript/lib/olcrtc-src/*, buildScript/lib/olcrtc.sh, .github/workflows/ci.yml
Detects unexpected runtime stoppage, tests shutdown and marker behavior, runs Go tests during wrapper preparation, and hashes the new test file for sidecar caching.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BoxInstance
  participant GuardedProcessPool
  participant olcRTC
  participant OS
  BoxInstance->>GuardedProcessPool: start olcRTC with restart policy
  GuardedProcessPool->>olcRTC: start process
  olcRTC-->>BoxInstance: publish readiness marker and listener readiness
  BoxInstance-->>GuardedProcessPool: complete restart readiness
  olcRTC-->>GuardedProcessPool: report process exit
  GuardedProcessPool->>GuardedProcessPool: calculate restart backoff
  OS-->>olcRTC: deliver SIGTERM or SIGINT
  olcRTC-->>BoxInstance: return shutdown result
Loading

Possibly related PRs

Suggested reviewers: arm64v8a

Poem

A rabbit watched the sidecar wake,
Then bounded through each restart break.
Ports grew ready, alarms grew clear,
Tests thumped softly, “all good here!”
Hop, hop—shutdown now is bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% 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
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.
Title check ✅ Passed The title concisely summarizes the main change: supervising olcRTC sidecar recovery.
Description check ✅ Passed The description matches the changeset and explains the restart, readiness, and shutdown behavior changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

🧹 Nitpick comments (1)
buildScript/lib/olcrtc-src/main.go (1)

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

Minor: extract the ticker interval into a named constant.

The 15 * time.Second literal would read better as a named constant near the top of the file, especially since it defines the unhealthy-detection granularity.

Proposed refactor
+const healthCheckInterval = 15 * time.Second
+
 func main() {
 ...
-	ticker := time.NewTicker(15 * time.Second)
+	ticker := time.NewTicker(healthCheckInterval)
🤖 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 `@buildScript/lib/olcrtc-src/main.go` around lines 95 - 106, The ticker
interval in the main runtime flow should use a named constant instead of the
inline 15-second duration. Define the constant near the top of the file and
update the time.NewTicker call in main to reference it, preserving the existing
unhealthy-detection interval.
🤖 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 `@app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt`:
- Around line 420-422: Update the sidecar readiness failure condition around the
pending-port message to base fatality on which sidecars are actually present in
pending, rather than the configuration-wide hasMasterDnsVpn or hasOlcrtc flags.
Ensure unrelated sidecar timeouts remain non-fatal when the olcRTC and
MasterDnsVPN ports are ready, while preserving strict-mode failures.
- Around line 304-315: The olcRTC restart flow around
GuardedProcessRestartPolicy and awaitExternalPortReady must prevent another
local process from impersonating the sidecar. Rotate SOCKS credentials for each
restart generation and make readiness validate a sidecar-specific authenticated
handshake rather than merely accepting any listener on 127.0.0.1:port;
alternatively use a pre-bound app-private socket while preserving restart
behavior.

---

Nitpick comments:
In `@buildScript/lib/olcrtc-src/main.go`:
- Around line 95-106: The ticker interval in the main runtime flow should use a
named constant instead of the inline 15-second duration. Define the constant
near the top of the file and update the time.NewTicker call in main to reference
it, preserving the existing unhealthy-detection interval.
🪄 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: Pro

Run ID: 71630511-190d-4f28-b057-2c914570e598

📥 Commits

Reviewing files that changed from the base of the PR and between bdda7ca and 83d9ce8.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicy.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt
  • app/src/test/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicyTest.kt
  • buildScript/lib/olcrtc-src/main.go
  • buildScript/lib/olcrtc-src/main_test.go
  • buildScript/lib/olcrtc.sh

Comment thread app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
Comment thread app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt

@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)
buildScript/lib/olcrtc-src/main.go (1)

109-112: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Remove the readiness marker before log.Fatal
log.Fatal exits via os.Exit, so the deferred removeReadyMarker is skipped on runtime-loss shutdowns. Clear the marker first to avoid leaving the app marked ready after exit.

🤖 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 `@buildScript/lib/olcrtc-src/main.go` around lines 109 - 112, Update the
runtime-loss branch after waitAfterReady to remove the readiness marker before
calling log.Fatal. Invoke the existing removeReadyMarker cleanup directly in
that branch so the marker is cleared before process termination.
🧹 Nitpick comments (1)
buildScript/lib/olcrtc-src/main_test.go (1)

60-80: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Avoid a deadlock on the test failure path.

If waitAfterReady unexpectedly returns after the healthy tick, the unbuffered signals <- syscall.SIGTERM send blocks forever instead of allowing the assertion to fail. Use buffered channels (or a timeout) for this test.

Proposed fix
-	signals := make(chan os.Signal)
-	ticks := make(chan time.Time)
+	signals := make(chan os.Signal, 1)
+	ticks := make(chan time.Time, 1)
🤖 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 `@buildScript/lib/olcrtc-src/main_test.go` around lines 60 - 80, Update
TestWaitAfterReadyHealthyTicksContinue to avoid blocking on the cleanup signal
when waitAfterReady exits unexpectedly: make the signals channel buffered, or
otherwise add a bounded send/timeout, while preserving the existing tick and
result assertions.
🤖 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
`@app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt`:
- Around line 6-10: Update shouldFailSidecarReadiness so strict mode only causes
failure when pendingPorts is non-empty, while preserving failure for pending
required ports and returning false when all ports are ready.

---

Outside diff comments:
In `@buildScript/lib/olcrtc-src/main.go`:
- Around line 109-112: Update the runtime-loss branch after waitAfterReady to
remove the readiness marker before calling log.Fatal. Invoke the existing
removeReadyMarker cleanup directly in that branch so the marker is cleared
before process termination.

---

Nitpick comments:
In `@buildScript/lib/olcrtc-src/main_test.go`:
- Around line 60-80: Update TestWaitAfterReadyHealthyTicksContinue to avoid
blocking on the cleanup signal when waitAfterReady exits unexpectedly: make the
signals channel buffered, or otherwise add a bounded send/timeout, while
preserving the existing tick and result assertions.
🪄 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: Pro

Run ID: d1051ce9-7f9a-4d75-bd26-012c9c60280c

📥 Commits

Reviewing files that changed from the base of the PR and between 83d9ce8 and 4b13ea3.

📒 Files selected for processing (6)
  • app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt
  • app/src/test/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicyTest.kt
  • buildScript/lib/olcrtc-src/main.go
  • buildScript/lib/olcrtc-src/main_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt

Comment thread app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt Outdated
@hawkff

hawkff commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #157, which includes this recovery work with validation, the tested runtime pin, and the combined follow-up fixes.

@hawkff hawkff closed this Jul 14, 2026
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.

1 participant