fix(mail): replace os.Exit with graceful shutdown in mail watch#273
fix(mail): replace os.Exit with graceful shutdown in mail watch#273cookier wants to merge 2 commits intolarksuite:mainfrom
Conversation
Replace os.Exit(0) in the signal handler of `mail +watch` with context-based graceful shutdown using context.WithCancel and a shutdown channel. The os.Exit(0) call had several problems: - Bypasses deferred cleanup functions in the call stack - Makes the code untestable (process terminates immediately) - Not idiomatic for Go CLI tools Changes: - Use context.WithCancel to create a cancellable watch context - Signal handler now cancels the context and closes shutdownCh - cli.Start uses watchCtx instead of the parent context - Distinguish between signal-triggered shutdown and real errors via select on shutdownCh Closes larksuite#269 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
shaoqi seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
📝 WalkthroughWalkthroughIntroduces a cancellable Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Main
participant SignalHandler
participant CLI as WebSocketClient
participant Mailbox
User->>Main: start `mail watch` command (ctx)
Main->>Main: create watchCtx, shutdownCh
Main->>SignalHandler: register signal handler (sigCh)
Main->>CLI: Start(watchCtx)
CLI->>Mailbox: open websocket subscribe
SignalHandler->>SignalHandler: receives interrupt (Ctrl+C)
SignalHandler->>SignalHandler: signal.Stop(sigCh)
SignalHandler->>Main: cancelWatch() (cancel watchCtx)
SignalHandler->>Main: close shutdownCh
WatchCtx-->>CLI: canceled
CLI->>Main: Start returns error (context canceled)
Main->>Mailbox: unsubscribe() or skip if canceled
Main->>Main: graceful shutdown completes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@shortcuts/mail/mail_watch.go`:
- Around line 448-449: The shutdown channel is closed after cancelling the
watch, creating a race where cli.Start(watchCtx) may return before shutdownCh is
closed and the subsequent select treats it as a network error; to fix, close
shutdownCh before calling cancelWatch() so the signal is visible to any
goroutine that returns from cli.Start(watchCtx), i.e., swap the order of
close(shutdownCh) and cancelWatch() around the watch shutdown logic while
keeping the same ownership semantics for shutdownCh and cancelWatch().
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 462e0b75-29eb-4ca6-8b15-55e774b8450d
📒 Files selected for processing (1)
shortcuts/mail/mail_watch.go
Greptile SummaryReplaces Confidence Score: 5/5Safe to merge — the previous P1 race condition is resolved and no new issues were found. The race between cancelWatch() and a shutdownCh is eliminated by using watchCtx.Err() as recommended by the prior review. Both the graceful-shutdown and real-error paths have unit test coverage. No P0/P1 issues remain. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Main
participant SignalHandler
participant WebSocket as cli.Start
Main->>WebSocket: cli.Start(watchCtx)
Note over Main: Blocks — waiting for mail events
SignalHandler-->>SignalHandler: Receives SIGINT/SIGTERM
SignalHandler->>SignalHandler: signal.Stop(sigCh)
SignalHandler->>SignalHandler: unsubscribe()
SignalHandler->>Main: cancelWatch()
WebSocket-->>Main: returns error (context canceled)
Main->>Main: handleMailWatchStartError(err, watchCtx, ...)
Note over Main: watchCtx.Err() != nil → return nil
Greploops — Automatically fix all review issues by running Reviews (2): Last reviewed commit: "fix(mail): avoid shutdown race in mail w..." | Re-trigger Greptile |
|
Fixed. I removed the That removes the race between |
haidaodashushu
left a comment
There was a problem hiding this comment.
Found one cleanup edge case in the new graceful-shutdown path.
| } | ||
|
|
||
| func handleMailWatchStartError(err error, watchCtx context.Context, unsubscribe func() error) error { | ||
| if watchCtx.Err() != nil { |
There was a problem hiding this comment.
watchCtx.Err() != nil is broader than "graceful shutdown via SIGINT/SIGTERM" here. watchCtx is derived from the parent ctx, so external cancellation from the caller/runtime will also hit this branch and return nil immediately, even though only the signal path actually performs unsubscribe() before canceling the watch. That means non-signal context cancellation can now skip mailbox cleanup entirely. Please distinguish signal-triggered shutdown from generic context cancellation instead of using watchCtx.Err() != nil as the only condition.
Replace os.Exit(0) in the signal handler of
mail +watchwith context-based graceful shutdown using context.WithCancel and a shutdown channel.The os.Exit(0) call had several problems:
Changes:
Closes #268
Summary
Changes
Test Plan
lark xxxcommand works as expectedRelated Issues
Summary by CodeRabbit
Bug Fixes
Refactor