Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ea6c587
Remove sentinel context from `start_reactor` (`daemonize_process`/`run`)
MadLittleMods Aug 26, 2025
bf21b4a
Simplify stop/start context pattern in `daemonize_process`
MadLittleMods Aug 27, 2025
a677d9b
Remove sentinel context from `atexit` daemonize logs
MadLittleMods Aug 26, 2025
fce59b2
DEBUG: Logging contexts getting lost
MadLittleMods Aug 27, 2025
af1944b
Revert "DEBUG: Logging contexts getting lost"
MadLittleMods Aug 27, 2025
675d94a
Fix `run_as_background_process` not following Synapse log context rules
MadLittleMods Aug 27, 2025
1bf7049
Merge branch 'develop' into madlittlemods/remove-sentinel-context
MadLittleMods Aug 28, 2025
7938e8c
DEBUG: lost `main` context after request
MadLittleMods Aug 28, 2025
0a865fd
Revert "Fix `run_as_background_process` not following Synapse log con…
MadLittleMods Sep 4, 2025
b97b2dd
Debug: Eliminate variables
MadLittleMods Sep 5, 2025
301a714
DEBUG: test
MadLittleMods Sep 6, 2025
c1996b6
WIP: Pair with @erikjohnston
MadLittleMods Sep 8, 2025
d8f68d0
Explain the `sentinel` context better
MadLittleMods Sep 8, 2025
71cd3c4
Clarify when to use the `sentinel` context
MadLittleMods Sep 8, 2025
0e990e6
Explain logging context in `run_as_background_process`
MadLittleMods Sep 8, 2025
6a98096
Add potential future simplication of `run_as_background_process`
MadLittleMods Sep 8, 2025
ea5a841
Add log context explanation to `run_in_background`
MadLittleMods Sep 8, 2025
4b2e88f
Merge branch 'madlittlemods/remove-sentinel-context' of github.com:el…
MadLittleMods Sep 8, 2025
1f4b391
Revert "Add log context explanation to `run_in_background`"
MadLittleMods Sep 9, 2025
bbe1ee7
Revert "Add potential future simplication of `run_as_background_proce…
MadLittleMods Sep 9, 2025
b5ec2da
Revert "Explain logging context in `run_as_background_process`"
MadLittleMods Sep 9, 2025
4aa0aa0
Revert "Clarify when to use the `sentinel` context"
MadLittleMods Sep 9, 2025
37a388c
Revert "Explain the `sentinel` context better"
MadLittleMods Sep 9, 2025
4b61570
Revert "DEBUG: test"
MadLittleMods Sep 9, 2025
ae8055a
Revert "Debug: Eliminate variables"
MadLittleMods Sep 9, 2025
5c05a0b
Revert "DEBUG: lost `main` context after request"
MadLittleMods Sep 9, 2025
b8c0857
Re-establish logcontext at `start`
MadLittleMods Sep 9, 2025
74ab47f
Add context for "Shutting down..." log
MadLittleMods Sep 9, 2025
e741daf
Simplify `atexit` logcontext
MadLittleMods Sep 9, 2025
fab546d
Add logcontext to database profiling loop
MadLittleMods Sep 9, 2025
e626f09
Add changelog
MadLittleMods Sep 9, 2025
dcf5fca
Merge branch 'develop' into madlittlemods/remove-sentinel-context
MadLittleMods Sep 10, 2025
6bbd0bf
Revise changelog
MadLittleMods Sep 10, 2025
9c74f69
Fix lints
MadLittleMods Sep 10, 2025
e6685cc
Remove db profiling `looping_call` change
MadLittleMods Sep 10, 2025
1f95a3b
Prefer `PreserveLoggingContext()` defaults
MadLittleMods Sep 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18870.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove `sentinel` logcontext usage where we log in `setup`, `start` and exit.
40 changes: 20 additions & 20 deletions synapse/app/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
from synapse.events.presence_router import load_legacy_presence_router
from synapse.handlers.auth import load_legacy_password_auth_providers
from synapse.http.site import SynapseSite
from synapse.logging.context import PreserveLoggingContext
from synapse.logging.context import LoggingContext, PreserveLoggingContext
from synapse.logging.opentracing import init_tracer
from synapse.metrics import install_gc_manager, register_threadpool
from synapse.metrics.background_process_metrics import run_as_background_process
Expand Down Expand Up @@ -183,25 +183,23 @@ def run() -> None:
if gc_thresholds:
gc.set_threshold(*gc_thresholds)
install_gc_manager()
run_command()

# make sure that we run the reactor with the sentinel log context,
# otherwise other PreserveLoggingContext instances will get confused
# and complain when they see the logcontext arbitrarily swapping
# between the sentinel and `run` logcontexts.
Comment on lines -188 to -191
Copy link
Contributor Author

@MadLittleMods MadLittleMods Aug 27, 2025

Choose a reason for hiding this comment

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

This comment was added in 067b00d#diff-6e21d6a61b2f6b6f2d4ce961991ba7f27e83605f927eaa4b19d2c46a975a96c1R460-R463 (matrix-org/synapse#2027)

I can't tell exactly what's it's referring to but the one spot I was seeing Expected logging context sentinel but found main around run_as_background_process(...) usage has been fixed (#18870 (comment)).

Reproduction:

  1. poetry run synapse_homeserver --config-path homeserver.yaml
  2. Ctrl + C to stop the server
  3. Notice LoggingContext: Expected logging context main was lost in the logs

#
# We also need to drop the logcontext before forking if we're daemonizing,
# otherwise the cputime metrics get confused about the per-thread resource usage
# appearing to go backwards.
Comment on lines -193 to -195
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comment was added in matrix-org/synapse#5609 and is accurately describing a real problem.

But we can be way more precise about what do to here. We only need to stop the current log context right before forking the process and start it again right after. This can be accomplished with the PreserveLoggingContext being moved strictly around the os.fork() call (search for os.fork() below).


Previously, this caused the sentinel context to be used for a whole bunch of logs (basically anything outside of a request). We now get the main context used 💪

with PreserveLoggingContext():
if daemonize:
assert pid_file is not None
# Reset the logging context when we start the reactor (whenever we yield control
# to the reactor, the `sentinel` logging context needs to be set so we don't
# leak the current logging context and erroneously apply it to the next task the
# reactor event loop picks up)
with PreserveLoggingContext():
run_command()

if daemonize:
assert pid_file is not None

if print_pidfile:
print(pid_file)

if print_pidfile:
print(pid_file)
daemonize_process(pid_file, logger)

daemonize_process(pid_file, logger)
run()
run()


def quit_with_error(error_string: str) -> NoReturn:
Expand Down Expand Up @@ -601,10 +599,12 @@ def run_sighup(*args: Any, **kwargs: Any) -> None:
hs.get_datastores().main.db_pool.start_profiling()
hs.get_pusherpool().start()

def log_shutdown() -> None:
with LoggingContext("log_shutdown"):
logger.info("Shutting down...")

# Log when we start the shut down process.
hs.get_reactor().addSystemEventTrigger(
"before", "shutdown", logger.info, "Shutting down..."
)
hs.get_reactor().addSystemEventTrigger("before", "shutdown", log_shutdown)

setup_sentry(hs)
setup_sdnotify(hs)
Expand Down
7 changes: 6 additions & 1 deletion synapse/app/generic_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,12 @@ def start(config_options: List[str]) -> None:
except Exception as e:
handle_startup_exception(e)

register_start(_base.start, hs)
async def start() -> None:
# Re-establish log context now that we're back from the reactor
with LoggingContext("start"):
await _base.start(hs)

register_start(start)

# redirect stdio to the logs, if configured.
if not hs.config.logging.no_redirect_stdio:
Expand Down
16 changes: 9 additions & 7 deletions synapse/app/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,15 +377,17 @@ def setup(config_options: List[str]) -> SynapseHomeServer:
handle_startup_exception(e)

async def start() -> None:
# Load the OIDC provider metadatas, if OIDC is enabled.
if hs.config.oidc.oidc_enabled:
oidc = hs.get_oidc_handler()
# Loading the provider metadata also ensures the provider config is valid.
await oidc.load_metadata()
# Re-establish log context now that we're back from the reactor
with LoggingContext("start"):
# Load the OIDC provider metadatas, if OIDC is enabled.
if hs.config.oidc.oidc_enabled:
oidc = hs.get_oidc_handler()
# Loading the provider metadata also ensures the provider config is valid.
await oidc.load_metadata()

await _base.start(hs)
await _base.start(hs)

hs.get_datastores().main.db_pool.updates.start_doing_background_updates()
hs.get_datastores().main.db_pool.updates.start_doing_background_updates()

register_start(start)

Expand Down
22 changes: 17 additions & 5 deletions synapse/util/daemonize.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
from types import FrameType, TracebackType
from typing import NoReturn, Optional, Type

from synapse.logging.context import (
LoggingContext,
PreserveLoggingContext,
)


def daemonize_process(pid_file: str, logger: logging.Logger, chdir: str = "/") -> None:
"""daemonize the current process
Expand Down Expand Up @@ -64,8 +69,14 @@ def daemonize_process(pid_file: str, logger: logging.Logger, chdir: str = "/") -
pid_fh.write(old_pid)
sys.exit(1)

# Fork, creating a new process for the child.
process_id = os.fork()
# Stop the existing context *before* we fork the process. Otherwise the cputime
# metrics get confused about the per-thread resource usage appearing to go backwards
# because we're comparing the resource usage from the original process to the forked
# process. `PreserveLoggingContext` already takes care of restarting the original
# context *after* the block.
with PreserveLoggingContext():
# Fork, creating a new process for the child.
process_id = os.fork()

if process_id != 0:
# parent process: exit.
Expand Down Expand Up @@ -140,9 +151,10 @@ def sigterm(signum: int, frame: Optional[FrameType]) -> NoReturn:

# Cleanup pid file at exit.
def exit() -> None:
logger.warning("Stopping daemon.")
os.remove(pid_file)
sys.exit(0)
with LoggingContext("atexit"):
logger.warning("Stopping daemon.")
os.remove(pid_file)
sys.exit(0)

atexit.register(exit)

Expand Down
Loading