Skip to content

Commit 28002be

Browse files
authored
internal: Improve daemon implementation. (#2597)
* Add log date. * Phase 1. * Run in background. * Rework connection. * Add acquire. * Add version handshake. * Clean up usage. * Add monitoring. * Add background tasks. * Fix watcher. * Fix shutdown. * Fixes.
1 parent 2b29272 commit 28002be

34 files changed

Lines changed: 1861 additions & 610 deletions

CHANGELOG.md

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@
1818
cache, which can improve cache hit rates in some scenarios.
1919
- When a remote cache hit, we'll now warm the local cache with the hydrated manifest and its
2020
blobs, so the next run resolves locally instead of round-tripping to the remote.
21+
- **Daemon**
22+
- Server log files will now rotate up to 7 times. Older log files will be automatically deleted.
23+
- Webhook delivery and task output archiving are now acknowledged immediately and run in the
24+
background on the daemon, so a client exiting or hitting a deadline no longer cancels the work
25+
mid-flight.
26+
- The daemon now takes exclusive ownership of its workspace through an advisory file lock held for
27+
its entire lifetime, replacing PID-liveness checks that could be fooled by zombie processes,
28+
reused PIDs, or processes owned by another user. A crashed daemon releases the lock
29+
automatically, so a stale socket or state file can no longer block or misdirect the next start.
30+
- Whether the daemon is running is now determined by connecting to it rather than probing a PID,
31+
and its metadata is recorded in a `daemon.json` state file (replacing `moond.pid`).
32+
- Connecting to the daemon and starting it are now a single operation, so a command that needs the
33+
daemon will start one itself if the background pre-warm hasn't yet, instead of silently running
34+
without it. Concurrent starts still coordinate so only one daemon is spawned.
35+
- When connecting, the client now checks the running daemon's moon and protocol version against
36+
its own and, on a mismatch, restarts it — so a daemon left over from before a `moon upgrade` is
37+
replaced instead of serving the old binary indefinitely.
38+
- The daemon now retires itself after a long idle period (no requests), and exits immediately if
39+
its workspace is deleted, so an abandoned workspace no longer leaves a daemon running forever.
2140
- **Processes**
2241
- Improved our "stream and capture output" child process handling to operate on bytes instead of
2342
lines, which should resolve some edge cases with output not being written to the console, or
@@ -57,22 +76,39 @@
5776

5877
#### 🐞 Fixes
5978

60-
- Fixed an issue where an explicit head revision was ignored when diffing between revisions, and the
61-
current working tree was compared against instead.
62-
- Fixed an issue where diffing against the previous revision would fail in repositories with a
63-
single commit.
64-
- Fixed an issue where file names with spaces or special characters were excluded from file tree
65-
results.
66-
- Fixed an issue where Git submodules added between 2 revisions were not included when diffing.
67-
- Fixed an issue where Git hooks could not be set up from the primary working tree when other
68-
worktrees exist.
69-
- Fixed an issue where moon would take over a hooks directory managed by another tool (husky,
70-
lefthook, etc) when `core.hooksPath` was already configured, overwriting its hook files, and
71-
deleting the entire directory when hooks were disabled.
72-
- Fixed an issue where Windows hook wrappers would not forward arguments containing spaces
73-
correctly, and would arbitrarily cap forwarding at 5 arguments.
74-
- Fixed an issue where PowerShell hooks would mangle user variables that start with `$ARG`, like
75-
`$ARGS`.
79+
- **CLI**
80+
- Fixed an issue where moon would silently exit with code 141 (SIGPIPE) when a child process
81+
exited before consuming its stdin. Broken pipes are now handled explicitly instead of resetting
82+
the SIGPIPE disposition, while piping moon's output to a consumer that closes early still exits
83+
quietly with the conventional code.
84+
- **Daemon**
85+
- Fixed an issue where every daemon RPC was capped by a 1 second client-side timeout, causing slow
86+
procedures (webhook delivery, cache cleaning) to be cancelled even though the daemon was
87+
healthy. Connection establishment is now bounded separately, and each procedure has an
88+
appropriate deadline that is also enforced by the server.
89+
- Fixed an issue where connecting to the daemon while it was still starting up — or being started
90+
by another process — would fail immediately with "connection refused", and the run would
91+
continue without the daemon. Connection attempts are now retried with a bounded backoff.
92+
- Fixed an issue on Windows where the daemon briefly had no listening pipe instance between client
93+
connections, causing sporadic connection failures, and where busy pipe instances were not
94+
retried.
95+
- **VCS**
96+
- Fixed an issue where an explicit head revision was ignored when diffing between revisions, and
97+
the current working tree was compared against instead.
98+
- Fixed an issue where diffing against the previous revision would fail in repositories with a
99+
single commit.
100+
- Fixed an issue where file names with spaces or special characters were excluded from file tree
101+
results.
102+
- Fixed an issue where Git submodules added between 2 revisions were not included when diffing.
103+
- Fixed an issue where Git hooks could not be set up from the primary working tree when other
104+
worktrees exist.
105+
- Fixed an issue where moon would take over a hooks directory managed by another tool (husky,
106+
lefthook, etc) when `core.hooksPath` was already configured, overwriting its hook files, and
107+
deleting the entire directory when hooks were disabled.
108+
- Fixed an issue where Windows hook wrappers would not forward arguments containing spaces
109+
correctly, and would arbitrarily cap forwarding at 5 arguments.
110+
- Fixed an issue where PowerShell hooks would mangle user variables that start with `$ARG`, like
111+
`$ARGS`.
76112

77113
#### ⚙️ Internal
78114

Cargo.lock

Lines changed: 10 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ semver = "1.0.27"
6363
serde = { version = "1.0.228", features = ["derive"] }
6464
serde_json = "1.0.150"
6565
sha2 = "0.11.0"
66-
starbase = { version = "0.12.2", features = ["clap", "miette"] }
66+
starbase = { version = "0.12.3", features = ["clap", "miette"] }
6767
starbase_archive = { version = "0.13.2", default-features = false, features = [
6868
"miette",
6969
"tar-all",

crates/action-pipeline/src/subscribers/cleanup_subscriber.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ impl Subscriber for CleanupSubscriber {
3939
debug!("Cleaning stale cache");
4040

4141
if let Some(mut daemon) = self.daemon_client.clone() {
42-
let lifetime = self.lifetime.clone();
43-
44-
tokio::spawn(async move { daemon.clean_cache(lifetime, true).await });
42+
daemon.clean_cache(self.lifetime.clone(), true).await?;
4543
} else {
4644
self.cache_engine
4745
.clean_stale_cache(&self.lifetime, false)

crates/app/src/commands/daemon/logs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub async fn logs(session: MoonSession) -> SessionResult {
88
let connector = session.get_daemon_connector()?;
99
let log_path = connector.get_log_file();
1010

11-
if connector.is_running().is_none() || !log_path.exists() {
11+
if !connector.is_running().await || !log_path.exists() {
1212
session.console.render(element! {
1313
Container {
1414
Notice(variant: Variant::Caution) {

crates/app/src/commands/daemon/restart.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub async fn restart(session: MoonSession) -> SessionResult {
1616
}
1717

1818
let connector = session.get_daemon_connector()?;
19-
let old_pid = connector.is_running();
19+
let old_pid = connector.read_state().map(|state| state.pid);
2020

2121
connector.stop_daemon().await?;
2222

crates/app/src/commands/daemon/server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ fn install_daemon_panic_hook() {
1313
} else {
1414
"non-string panic payload".to_owned()
1515
};
16+
1617
let location = panic_info
1718
.location()
1819
.map(|location| {

crates/app/src/commands/daemon/status.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::time::Duration;
77
pub async fn status(session: MoonSession) -> SessionResult {
88
let connector = session.get_daemon_connector()?;
99

10-
if connector.is_running().is_none() {
10+
if !connector.is_running().await {
1111
session.console.render(element! {
1212
Container {
1313
Notice(variant: Variant::Caution) {
@@ -19,16 +19,21 @@ pub async fn status(session: MoonSession) -> SessionResult {
1919
return Ok(None);
2020
}
2121

22-
let Some(mut client) = connector.connect().await? else {
23-
session.console.render(element! {
24-
Container {
25-
Notice(variant: Variant::Caution) {
26-
StyledText(content: "Unable to connect to the daemon")
22+
// The PID may be stale (a crashed daemon, or the PID was reused by
23+
// another process), so a connect failure here is a report, not an error.
24+
let mut client = match connector.connect_once().await {
25+
Ok(Some(client)) => client,
26+
_ => {
27+
session.console.render(element! {
28+
Container {
29+
Notice(variant: Variant::Caution) {
30+
StyledText(content: "Unable to connect to the daemon")
31+
}
2732
}
28-
}
29-
})?;
33+
})?;
3034

31-
return Ok(None);
35+
return Ok(None);
36+
}
3237
};
3338

3439
let status = client.status().await?;
@@ -66,10 +71,10 @@ pub async fn status(session: MoonSession) -> SessionResult {
6671

6772
Section(title: "Paths") {
6873
Entry(
69-
name: "PID file",
74+
name: "State file",
7075
value: element! {
7176
StyledText(
72-
content: connector.get_pid_file().to_string_lossy(),
77+
content: connector.get_state_file().to_string_lossy(),
7378
style: Style::Path
7479
)
7580
}.into_any()

0 commit comments

Comments
 (0)