Skip to content

Conversation

@rustatian
Copy link
Member

@rustatian rustatian commented Oct 21, 2025

Reason for This PR

closes: #2243
closes: #929

Description of Changes

  1. Add additional signal handling: SIGQUIT and SIGUSR2 for reload app [not supported on Windows].
  2. Docs update required

License Acceptance

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

PR Checklist

[Author TODO: Meet these criteria.]
[Reviewer TODO: Verify that these criteria are met. Request changes if not]

  • All commits in this PR are signed (git commit -s).
  • The reason for this PR is clearly provided (issue no. or explanation).
  • The description of changes is clear and encompassing.
  • Any required documentation changes (code and docs) are included in this PR.
  • Any user-facing changes are mentioned in CHANGELOG.md.
  • All added/changed functionality is tested.

Summary by CodeRabbit

  • New Features

    • Silent mode for the serve command to suppress output.
    • Process restart via SIGUSR2 and improved lifecycle/signal handling.
    • Windows-specific serve command for Windows workflows.
  • Chores

    • Updated many dependencies to newer patch/minor versions.
    • Added and committed editor launch configurations; .vscode is no longer ignored.

Signed-off-by: Valery Piashchynski <[email protected]>
@rustatian rustatian self-assigned this Oct 21, 2025
@rustatian rustatian added the C-enhancement Category: enhancement. Meaning improvements of current module, transport, etc.. label Oct 21, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between d3adcd1 and 32a0f20.

📒 Files selected for processing (1)
  • internal/cli/serve/command.go (5 hunks)
 _______________________________________________
< Dyson of code review: I suck up all the bugs. >
 -----------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Walkthrough

Adds SIGQUIT and SIGUSR2 handling and an exec-based restart flow to the serve command (platform-specific implementations for non-Windows and Windows added/updated), removes .vscode from .gitignore and adds a VSCode launch configuration, and upgrades/removes toolchain entry in go.mod with multiple dependency bumps.

Changes

Cohort / File(s) Change Summary
Serve command (non-Windows)
internal/cli/serve/command.go
Added log(msg string, silent bool) helper; switched prints to conditional logging; added SIGQUIT and SIGUSR2 handling; introduced restartCh and exec-based restart flow; refined signal handling, sdnotify and watchdog logging, and error paths.
Serve command (Windows)
internal/cli/serve/command_windows.go
New Windows-specific NewCommand(...) *cobra.Command implementation: Windows-targeted serve flow with validation, container setup, signal handling, sdnotify/watchdog support, and graceful shutdown logic.
Dev tooling / editor config
.gitignore, .vscode/launch.json
Removed .vscode from .gitignore; added .vscode/launch.json with three Go launch configurations (AMQP, HTTP, workers) targeting cmd/rr/main.go.
Module deps / metadata
go.mod
Removed explicit toolchain declaration; bumped many indirect/transitive dependency versions (golang.org/x/, google.golang.org/, gRPC, OpenTelemetry, Temporal, etc.) without changing exported APIs.

Sequence Diagram(s)

sequenceDiagram
    participant OS as Operating System
    participant RR as RoadRunner (serve)
    participant Container as Application Container

    rect rgb(235,248,255)
    note right of OS: Restart (SIGUSR2)
    OS->>RR: SIGUSR2
    activate RR
    RR->>Container: stop container (graceful)
    RR->>RR: resolve executable path
    RR->>RR: exec replacement (os.Exec) -> new process
    deactivate RR
    end

    rect rgb(245,255,235)
    note right of OS: Graceful stop (SIGQUIT/SIGTERM/SIGINT)
    OS->>RR: SIGQUIT / SIGTERM / SIGINT
    activate RR
    RR->>Container: stop container with grace timeout
    RR->>RR: notify sdnotify (READY/STOPPING), teardown watchdog
    RR->>OS: exit gracefully
    deactivate RR
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • wolfy-j

Poem

🐰 A thump, a nudge, a gentle restart song,
SIGUSR2 whispers, "time to move along."
SIGQUIT tips its hat, no panic nor fright,
VSCode lines the runes, ready for flight.
The rabbit hops off, munching "fresh start" delight.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR introduces several changes that appear unrelated to the stated objectives of adding signal handling. The removal of .vscode from .gitignore and the addition of .vscode/launch.json (VSCode debug configurations) are development convenience changes not mentioned in the PR objectives. Additionally, go.mod contains broad dependency updates across numerous packages (cloud.google.com/go, grpc, prometheus, temporal, golang.org packages, etc.) that extend beyond what would be necessary for implementing SIGQUIT/SIGUSR2 signal handling. These changes appear to be maintenance or preparatory work bundled with the feature implementation.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feature: additional signal handling" is concise, clear, and directly summarizes the primary change introduced in this PR. It accurately reflects the main objective of adding SIGQUIT and SIGUSR2 signal handling as described in the PR description and linked issues. The title is specific enough for a developer reviewing the repository history to understand the core purpose of the changeset.
Linked Issues Check ✅ Passed The code changes appear to address both linked issue requirements. Issue #2243's objective to handle SIGQUIT as a graceful stop and add SIGUSR2 for reloads is reflected in the command.go changes, which expand signal handling with SIGQUIT notification and introduce a restart flow for SIGUSR2. Issue #929's requirement for runtime symlink re-resolution is addressed in the restart flow logic that includes "resolve executable" followed by process replacement via Exec. The Windows-specific command file appropriately reflects that signal handling differs on Windows platforms, aligning with the PR's stated limitation that SIGQUIT/SIGUSR2 are not supported there.
Description Check ✅ Passed The PR description follows the repository's template structure and includes all required sections: a clear reason for the PR with linked issue numbers (#2243 and #929), a description of changes explaining the addition of SIGQUIT/SIGUSR2 signal handling and the note that it's not supported on Windows, explicit license acceptance confirmation, and a completed PR checklist with all items marked as done. The description adequately communicates the main objectives and scope of the changes.

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 and usage tips.

Signed-off-by: Valery Piashchynski <[email protected]>
@codecov
Copy link

codecov bot commented Oct 21, 2025

Codecov Report

❌ Patch coverage is 0% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.61%. Comparing base (502765a) to head (32a0f20).
⚠️ Report is 6 commits behind head on master.

Files with missing lines Patch % Lines
internal/cli/serve/command.go 0.00% 44 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2244      +/-   ##
==========================================
- Coverage   36.26%   35.61%   -0.66%     
==========================================
  Files          18       18              
  Lines        1092     1112      +20     
==========================================
  Hits          396      396              
- Misses        657      677      +20     
  Partials       39       39              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 502765a and beab93d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • .gitignore (0 hunks)
  • .vscode/launch.json (1 hunks)
  • go.mod (7 hunks)
  • internal/cli/serve/command.go (4 hunks)
💤 Files with no reviewable changes (1)
  • .gitignore
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-02T16:44:02.549Z
Learnt from: CR
PR: roadrunner-server/roadrunner#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T16:44:02.549Z
Learning: Applies to /go.mod : Set the module path to github.com/roadrunner-server/roadrunner/v2025 in go.mod

Applied to files:

  • go.mod
🧬 Code graph analysis (1)
internal/cli/serve/command.go (2)
internal/meta/meta.go (2)
  • Version (12-20)
  • BuildTime (23-23)
internal/sdnotify/sdnotify.go (3)
  • SdNotify (61-87)
  • Ready (37-37)
  • StartWatchdog (89-114)
🪛 Biome (2.1.2)
.vscode/launch.json

[error] 18-18: Expected a property but instead found '}'.

Expected a property here.

(parse)


[error] 30-30: Expected a property but instead found '}'.

Expected a property here.

(parse)


[error] 42-42: Expected a property but instead found '}'.

Expected a property here.

(parse)


[error] 43-43: Expected an array, an object, or a literal but instead found ']'.

Expected an array, an object, or a literal here.

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Golang-CI (lint)
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
internal/cli/serve/command.go (4)

19-24: LGTM!

The log helper function is a clean refactoring that ensures all output respects the silent flag.


85-89: LGTM! Signal handling expanded as intended.

The changes correctly add SIGQUIT for graceful shutdown and introduce a dedicated channel for SIGUSR2 restart signals, aligning with the PR objectives.


106-126: LGTM!

The startup and sdnotify integration correctly uses the new log helper for consistent output handling.


132-139: LGTM!

The stop signal handling correctly logs the shutdown and stops the container gracefully.

go.mod (2)

3-3: Go 1.25.0 is a valid, released version. No action required.

Go 1.25 was released and as of October 21, 2025, the latest stable version is Go 1.25.3 (released October 13, 2025). The version specified in go.mod is correct.

Likely an incorrect or invalid review comment.


65-65: Remove the broken replace directive or ensure the sibling directory exists.

The replace directive points to ../plugins/roadrunner-temporal, which doesn't exist outside the repository. This path will break go mod commands for all developers and CI/CD workflows:

  • Local: go mod tidy, go build, etc. will fail
  • CI: No workflows include steps to clone or fetch the sibling directory

Either remove the replace directive if it's not needed, or ensure the plugins directory is properly set up and documented (e.g., as a git submodule or separate checkout step).

⛔ Skipped due to learnings
Learnt from: CR
PR: roadrunner-server/roadrunner#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-02T16:44:02.549Z
Learning: Applies to /go.mod : Set the module path to github.com/roadrunner-server/roadrunner/v2025 in go.mod

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/cli/serve/command.go (1)

143-165: Fix restart flow: preserve symlink semantics, notify systemd, and remove unreachable return.

  • Use argv[0] + exec.LookPath to preserve symlink re-resolution for deployments switching a “current” symlink; os.Executable() resolves the link and prevents #929 behavior.
  • Send sdnotify.Stopping before stopping the container on restart.
  • Remove the dead return nil after syscall.Exec (Exec never returns on success).
  • Preflight the target path before stopping to avoid downtime if Exec fails.

Apply within the shown range:

-				case <-restartCh:
-					log("restart signal [SIGUSR2] received", *silent)
-					executable, err := os.Executable()
-					if err != nil {
-						log(fmt.Sprintf("restart failed: %s", err), *silent)
-						return errors.E("failed to restart")
-					}
-					args := os.Args
-					env := os.Environ()
-
-					if err := cont.Stop(); err != nil {
-						log(fmt.Sprintf("restart failed: %s", err), *silent)
-						return errors.E("failed to restart")
-					}
-
-					err = syscall.Exec(executable, args, env)
-					if err != nil {
-						log(fmt.Sprintf("restart failed: %s", err), *silent)
-						return errors.E("failed to restart")
-					}
-
-					return nil
+				case <-restartCh:
+					log("restart signal [SIGUSR2] received", *silent)
+					args := os.Args
+					env := os.Environ()
+
+					// Preserve symlink semantics (e.g., capistrano-style `current`):
+					// resolve what would run given argv[0] instead of resolving the real executable path.
+					resolvedPath, err := exec.LookPath(args[0])
+					if err != nil {
+						log(fmt.Sprintf("restart preflight failed (resolve target): %s", err), *silent)
+						return errors.E(op, err)
+					}
+
+					// Notify systemd that we are stopping due to restart.
+					_, _ = sdnotify.SdNotify(sdnotify.Stopping)
+
+					if err := cont.Stop(); err != nil {
+						log(fmt.Sprintf("restart failed (stop container): %s", err), *silent)
+						return errors.E(op, err)
+					}
+
+					// Replace the current process. On success, this never returns.
+					if err := syscall.Exec(resolvedPath, args, env); err != nil {
+						log(fmt.Sprintf("restart failed (exec): %s", err), *silent)
+						return errors.E(op, err)
+					}

Add imports outside this range:

 import (
 	"fmt"
 	"os"
 	"os/signal"
 	"syscall"
+	"os/exec"
 )

Verification scripts:

#!/bin/bash
# 1) Confirm no dead code remains after Exec
rg -n 'syscall\.Exec' -n internal/cli/serve/command.go -A3

# 2) Ensure we reference exec.LookPath and import os/exec
rg -n 'exec\.LookPath|os/exec' internal/cli/serve/command.go

# 3) Manual symlink check suggestion (run locally):
# ln -s /path/to/rr-new /usr/local/bin/rr ; send SIGUSR2 and ensure new binary runs.
🧹 Nitpick comments (1)
internal/cli/serve/command.go (1)

87-92: Minor: consider signal.NotifyContext and dedup signals.

You can simplify by using signal.NotifyContext and dropping duplicate SIGINT (os.Interrupt already covers it). Not required.

Example (for reference only):

ctx, stopFn := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGABRT, syscall.SIGQUIT)
defer stopFn()

Also applies to: 130-141

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdf781d and d3adcd1.

📒 Files selected for processing (2)
  • internal/cli/serve/command.go (5 hunks)
  • internal/cli/serve/command_windows.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
internal/cli/serve/command_windows.go (4)
internal/cli/serve/command.go (1)
  • NewCommand (28-169)
container/config.go (2)
  • NewConfig (27-52)
  • ParseLogLevel (54-67)
internal/meta/meta.go (2)
  • Version (12-20)
  • BuildTime (23-23)
internal/sdnotify/sdnotify.go (3)
  • SdNotify (61-87)
  • Stopping (41-41)
  • StartWatchdog (89-114)
internal/cli/serve/command.go (2)
internal/meta/meta.go (2)
  • Version (12-20)
  • BuildTime (23-23)
internal/sdnotify/sdnotify.go (3)
  • SdNotify (61-87)
  • Ready (37-37)
  • StartWatchdog (89-114)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Unit tests
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
internal/cli/serve/command.go (1)

87-92: SIGQUIT now mapped to graceful stop — LGTM.

SIGQUIT is included in Notify and will follow the same graceful path as SIGTERM, satisfying #2243 expectations.

Optional: consider documenting in the help/CHANGELOG that SIGQUIT acts as graceful stop (honors grace timeout).

internal/cli/serve/command_windows.go (1)

105-126: Windows flow parity — LGTM.

Startup, sdnotify ready/watchdog, and graceful stop mirror non‑Windows behavior appropriately; absence of restart handling aligns with platform notes.

Also applies to: 127-139

Signed-off-by: Valery Piashchynski <[email protected]>
@rustatian rustatian merged commit 2bfe7e1 into master Oct 22, 2025
13 of 14 checks passed
@rustatian rustatian deleted the feature/handle-additional-signals branch October 22, 2025 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-enhancement Category: enhancement. Meaning improvements of current module, transport, etc..

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[💡 FEATURE REQUEST]: Add graceful handling for SIGQUIT and SIGUSR2 [💡FEATURE REQUEST]: A command to refresh working dir (re-resolve symlink)

2 participants