Skip to content

Commit 0f0e0c2

Browse files
fix(dsm): only register exit signal handler if data streams monitoring is active [backport 1.20] (#7631)
Backport fd1c9e1 from #7578 to 1.20. This pull request fixes #7273 by only registering exit signal handlers when Datastreams Monitoring is active. This works because DSM is the only code path that currently uses these handlers. The test verifies that the Flask app does not hang when it receives two `SIGINT`s, which is the closest behavior to the original issue report that I was able to replicate in the test suite. There's also a change to a test that verifies the behavior of `register_on_exit_signal`. Using `override_global_config` to activate DSM causes the assertions in the test to change. I decided that changing the assertions was more likely to cause errors than directly calling `register_on_exit_signal` in the test. Fixes #7273 Fixes #7014 ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that. Co-authored-by: Emmett Butler <[email protected]>
1 parent d72a854 commit 0f0e0c2

File tree

4 files changed

+39
-1
lines changed

4 files changed

+39
-1
lines changed

ddtrace/tracer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,11 @@ def __init__(
292292
from .internal.datastreams.processor import DataStreamsProcessor
293293

294294
self.data_streams_processor = DataStreamsProcessor(self._agent_url)
295+
register_on_exit_signal(self._atexit)
295296

296297
self._hooks = _hooks.Hooks()
297298
atexit.register(self._atexit)
298299
forksafe.register(self._child_after_fork)
299-
register_on_exit_signal(self._atexit)
300300

301301
self._shutdown_lock = RLock()
302302

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
tracing: This fix resolves an issue where ddtrace's signal handlers could cause Flask apps not to respond
5+
correctly to SIGINT.

tests/contrib/flask/test_request.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# -*- coding: utf-8 -*-
22
import json
33
import os
4+
import subprocess
5+
import sys
6+
import time
47

58
import flask
69
from flask import abort
@@ -23,6 +26,7 @@
2326

2427

2528
REMOVED_SPANS_2_2_0 = 1 if flask_version >= (2, 2, 0) else 0
29+
SIGTERM_EXIT_CODE = -15
2630

2731

2832
base_exception_name = "builtins.Exception"
@@ -1166,3 +1170,30 @@ def index():
11661170
out, err, status, pid = ddtrace_run_python_code_in_subprocess(code, env=env)
11671171
assert status == 0, (out, err)
11681172
assert err == b"", (out, err)
1173+
1174+
1175+
def test_sigint(tmpdir):
1176+
code = """
1177+
from flask import Flask
1178+
app = Flask(__name__)
1179+
1180+
@app.route('/')
1181+
def hello_world():
1182+
return 'Hello, World!'
1183+
1184+
if __name__ == '__main__':
1185+
app.run(port=8082)
1186+
"""
1187+
pyfile = tmpdir.join("test.py")
1188+
pyfile.write(code)
1189+
subp = subprocess.Popen(
1190+
["ddtrace-run", sys.executable, str(pyfile)],
1191+
stdout=subprocess.PIPE,
1192+
stderr=subprocess.PIPE,
1193+
close_fds=sys.platform != "win32",
1194+
)
1195+
time.sleep(0.5)
1196+
# send two terminate signals to forcibly kill the process
1197+
subp.terminate()
1198+
subp.terminate()
1199+
assert subp.wait() == SIGTERM_EXIT_CODE, "An instrumented Flask app should respond to SIGINT by exiting"

tests/integration/test_integration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import six
1010

1111
from ddtrace import Tracer
12+
from ddtrace.internal.atexit import register_on_exit_signal
1213
from ddtrace.internal.writer import AgentWriter
1314
from tests.integration.utils import AGENT_VERSION
1415
from tests.integration.utils import BadEncoder
@@ -39,6 +40,7 @@ def test_configure_keeps_api_hostname_and_port():
3940
def test_shutdown_on_exit_signal(mock_get_signal, mock_signal):
4041
mock_get_signal.return_value = None
4142
tracer = Tracer()
43+
register_on_exit_signal(tracer._atexit)
4244
assert mock_signal.call_count == 2
4345
assert mock_signal.call_args_list[0][0][0] == signal.SIGTERM
4446
assert mock_signal.call_args_list[1][0][0] == signal.SIGINT

0 commit comments

Comments
 (0)