Skip to content

feat(serve): add --foreground flag for container/supervisor use (#126)#129

Merged
pdf-amzn merged 3 commits into
ExtendDB:mainfrom
marvelefe:feat/foreground-serve
May 29, 2026
Merged

feat(serve): add --foreground flag for container/supervisor use (#126)#129
pdf-amzn merged 3 commits into
ExtendDB:mainfrom
marvelefe:feat/foreground-serve

Conversation

@marvelefe
Copy link
Copy Markdown
Contributor

What

Adds a --foreground flag (alias --no-daemon) to extenddb serve so the
process can run attached to its controlling terminal instead of daemonizing.

When the flag is set:

  • The Daemonize::execute() block and the syslog panic hook are skipped.
  • Tracing logs go to stderr instead of syslog, so a process supervisor
    captures them naturally.
  • The PID file is still written (now from serve_inner rather than by
    daemonize), so extenddb status and extenddb stop keep working.
  • The startup banner reports log_output=stderr instead of log_output=syslog.

When the flag is not set, behavior is byte-identical to the previous
release: same daemonize path, same syslog logging, same panic hook, same
PID-file lifecycle.

Five unit tests in crates/bin/src/cmd_serve.rs cover the argv contract:
default-is-daemon, the flag, the --no-daemon alias, combination with
--config/--port, and rejection of unknown flag spellings.

README updates:

  • Features list mentions --foreground for container and supervisor use.
  • New "Running in Containers" section with a usage example and a list of
    supervisors it targets (Docker, Kubernetes, systemd Type=simple, runit, s6).
  • CLI Reference table includes a --foreground example line.

Why

Closes #126.

extenddb serve always double-forks, which is awkward inside a container or
under a process supervisor that expects PID 1 to stay attached. Operators
running ExtendDB under Docker, Kubernetes, systemd Type=simple, runit, or
s6 currently have to wrap the binary just to undo the daemonization. A first-
class flag is the standard fix and keeps the daemon-mode default intact for
existing users.

Testing done

Static checks

cargo fmt --all -- --check          # clean
cargo clippy -p extenddb --all-targets -- -D warnings  # clean
cargo test -p extenddb              # 8 passed (3 pre-existing + 5 new)

New tests:

test cmd_serve::tests::defaults_run_in_daemon_mode ... ok
test cmd_serve::tests::foreground_flag_is_recognized ... ok
test cmd_serve::tests::no_daemon_alias_is_recognized ... ok
test cmd_serve::tests::foreground_combines_with_other_flags ... ok
test cmd_serve::tests::unknown_flag_is_rejected ... ok

End-to-end (PostgreSQL 16 via Homebrew, macOS)

Init:

./extenddb init --config extenddb.toml \
  --pg-user extenddb --pg-pass extenddb-local-dev \
  --extenddb-user extenddb --extenddb-pass extenddb-local-dev \
  --pg-host 127.0.0.1 --pg-port 5432 --bind-addr 127.0.0.1
# Catalog + data DBs created, TLS cert generated, admin user provisioned.

Daemon mode (regression check, default behavior):

./extenddb serve --config extenddb.toml
# extenddb 0.1.0 (catalog 0.0.2) starting on 127.0.0.1:8000
# extenddb server started (pid 94884, 127.0.0.1:8000)
./extenddb status --config extenddb.toml
# extenddb is running on port 8000 (pid 94884)
curl --cacert ~/.extenddb/tls/cert.pem https://127.0.0.1:8000/health
# {"status":"healthy"}
./extenddb stop --config extenddb.toml
# extenddb stopped (pid 94884)

Foreground mode (new behavior):

./extenddb serve --config extenddb.toml --foreground
# Process stays attached. Logs stream to stderr:
# 2026-05-26T04:37:44Z  INFO extenddb::cmd_serve: extenddb 0.1.0 ... log_output=stderr ...
# 2026-05-26T04:37:44Z  INFO extenddb_server: extenddb listening on 127.0.0.1:8000 (HTTPS)

While running in foreground, in another shell:

./extenddb status --config extenddb.toml
# extenddb is running on port 8000 (pid 95732)
curl --cacert ~/.extenddb/tls/cert.pem https://127.0.0.1:8000/health
# {"status":"healthy"}

# Real DynamoDB workload via AWS CLI with SigV4-signed credentials.
aws dynamodb list-tables --endpoint-url https://127.0.0.1:8000          # []
aws dynamodb create-table --endpoint-url https://127.0.0.1:8000 \
  --table-name fg-test \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST                                        # CREATING
aws dynamodb wait table-exists --table-name fg-test --endpoint-url https://127.0.0.1:8000
aws dynamodb put-item --endpoint-url https://127.0.0.1:8000 \
  --table-name fg-test \
  --item '{"id":{"S":"hello"},"msg":{"S":"foreground works"}}'
aws dynamodb get-item --endpoint-url https://127.0.0.1:8000 \
  --table-name fg-test --key '{"id":{"S":"hello"}}'
# {"Item":{"id":{"S":"hello"},"msg":{"S":"foreground works"}}}

Graceful shutdown via SIGTERM (the supervisor scenario):

kill -TERM "$(cat ~/.extenddb/run/extenddb-8000.pid)"
# Foreground process logs:
# INFO extenddb_server: Shutdown signal received, draining connections...
# Process exits cleanly. PID file removed automatically.
./extenddb status --config extenddb.toml
# extenddb is not running (port 8000 is not in use).

Cleanup with extenddb destroy succeeded.

Checklist

  • I have read CONTRIBUTING.md
  • All tests pass (cargo test --workspace)
  • Code is formatted (cargo fmt --check)
  • Clippy is clean (cargo clippy -- -W clippy::pedantic)
  • I have added or updated tests for new functionality
  • I have updated documentation if behavior changed
  • Breaking changes are noted below (if any)

Breaking changes

None. The flag defaults to false, so extenddb serve without the flag is
behaviorally identical to the previous release: same daemonize path, same
syslog logging, same panic hook, same PID-file location and lifecycle.


By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.

pdf-amzn
pdf-amzn previously approved these changes May 27, 2026
Copy link
Copy Markdown
Collaborator

@pdf-amzn pdf-amzn left a comment

Choose a reason for hiding this comment

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

Looks good. One of us will need to manually try this out before approving but the code is nice and straightforward. I have a few comments, I don't think any are blocking. If you want to update this PR in the meantime, you can. Otherwise, please do a followup PR or cut a followup issue for at least 1, and please cut a followup issue for 3 but I don't think our current test framework is ready for a test like that yet, so I do not recommend you try to solve 3 at this time.

Review notes (AI generated; 1, 4, and 5 are the ones I personally think are noteworthy):

  1. Logging initialization duplication (medium)

The tracing subscriber setup now has 4 branches (foreground×json, foreground×text, daemon×json, daemon×text) with near-identical code:

rust

if foreground {
if json { /* stderr + json / } else { / stderr + text / }
} else {
if json { /
syslog + json / } else { / syslog + text */ }
}
This could be simplified by extracting the writer choice first, then having a single json/text branch. Not a correctness issue, but increases maintenance surface.

  1. PID file race window in foreground mode (low risk)

In daemon mode, Daemonize::new().pid_file(&pid_file) writes the PID atomically during the fork. In foreground mode, the PID file is written later in serve_inner — after catalog version lookup. If the process crashes between startup and PID write, extenddb stop won't find it. The window is small (milliseconds) and the process would be dead anyway, so this is acceptable.

  1. No SIGTERM handler test

The PR description shows manual SIGTERM testing, but there's no automated test for graceful shutdown in foreground mode. The existing signal handling code is unchanged, so this is low risk, but a test would be nice for a feature specifically targeting supervisor environments.

  1. --foreground doesn't suppress the startup banner to stdout

Looking at run(), the banner "extenddb server started (pid X, addr)" is printed to stdout before entering serve_inner. In foreground mode, this mixes stdout (banner) and stderr (tracing logs). Supervisors typically expect all output on one stream. Minor UX issue.

  1. No --foreground in extenddb.toml config

The flag is CLI-only. For container deployments, an env var (EXTENDDB_FOREGROUND=1) or config file option would be more ergonomic than modifying the command line. However, this is a feature request, not a bug — CLI-only is fine for v1.

@marvelefe
Copy link
Copy Markdown
Contributor Author

Thanks for the review @pdf-amzn

  1. Logging dedup - fixed. Writer (stderr/Syslog) is picked first via BoxMakeWriter, then a single match chooses json/text and strips timestamps on the syslog path. One try_init, one Syslog::new.
  2. PID file race - left as-is per your call.
  3. SIGTERM test - followup issue filed (Add an automated graceful-shutdown test for extenddb serve --foreground #137).
  4. Banner stream - fixed. Banner is eprintln! in --foreground, so stdout stays empty and supervisors get banner + tracing on stderr. Daemon mode unchanged.
  5. Env var / config option - followup issue filed (Configure foreground mode via env var or extenddb.toml, not just CLI #138).

pdf-amzn
pdf-amzn previously approved these changes May 29, 2026
Copy link
Copy Markdown
Collaborator

@pdf-amzn pdf-amzn left a comment

Choose a reason for hiding this comment

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

Thanks for the updates! I tried this out and it worked well; approving. Please rebase. Never mind, I rebased for you.

@robinnsc robinnsc self-requested a review May 29, 2026 18:25
@pdf-amzn pdf-amzn merged commit 22dab77 into ExtendDB:main May 29, 2026
3 checks passed
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.

[Feature] Non-daemonized serve command

3 participants