forked from getsentry/sentry-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_monitor.py
More file actions
141 lines (107 loc) · 4.15 KB
/
Copy pathtest_monitor.py
File metadata and controls
141 lines (107 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import os
import sys
from collections import Counter
from unittest import mock
import pytest
import sentry_sdk
from sentry_sdk.transport import Transport
class HealthyTestTransport(Transport):
def capture_envelope(self, _):
pass
def is_healthy(self):
return True
class UnhealthyTestTransport(HealthyTestTransport):
def is_healthy(self):
return False
def test_no_monitor_if_disabled(sentry_init):
sentry_init(
transport=HealthyTestTransport(),
enable_backpressure_handling=False,
)
assert sentry_sdk.get_client().monitor is None
def test_monitor_if_enabled(sentry_init):
sentry_init(transport=HealthyTestTransport())
monitor = sentry_sdk.get_client().monitor
assert monitor is not None
assert monitor._thread is None
assert monitor.is_healthy() is True
assert monitor.downsample_factor == 0
assert monitor._thread is not None
assert monitor._thread.name == "sentry.monitor"
def test_monitor_unhealthy(sentry_init):
sentry_init(transport=UnhealthyTestTransport())
monitor = sentry_sdk.get_client().monitor
monitor.interval = 0.1
assert monitor.is_healthy() is True
for i in range(15):
monitor.run()
assert monitor.is_healthy() is False
assert monitor.downsample_factor == (i + 1 if i < 10 else 10)
def test_transaction_uses_downsampled_rate(
sentry_init, capture_record_lost_event_calls, monkeypatch
):
sentry_init(
traces_sample_rate=1.0,
transport=UnhealthyTestTransport(),
)
record_lost_event_calls = capture_record_lost_event_calls()
monitor = sentry_sdk.get_client().monitor
monitor.interval = 0.1
assert monitor.is_healthy() is True
monitor.run()
assert monitor.is_healthy() is False
assert monitor.downsample_factor == 1
# make sure we don't sample the transaction
with mock.patch("sentry_sdk.tracing_utils.Random.randrange", return_value=750000):
with sentry_sdk.start_transaction(name="foobar") as transaction:
assert transaction.sampled is False
assert transaction.sample_rate == 0.5
assert Counter(record_lost_event_calls) == Counter(
[
("backpressure", "transaction", None, 1),
("backpressure", "span", None, 1),
]
)
def test_monitor_no_thread_on_shutdown_no_errors(sentry_init):
sentry_init(transport=HealthyTestTransport())
# make it seem like the interpreter is shutting down
with mock.patch(
"threading.Thread.start",
side_effect=RuntimeError("can't create new thread at interpreter shutdown"),
):
monitor = sentry_sdk.get_client().monitor
assert monitor is not None
assert monitor._thread is None
monitor.run()
assert monitor._thread is None
@pytest.mark.skipif(
sys.platform == "win32"
or not hasattr(os, "fork")
or not hasattr(os, "register_at_fork"),
reason="requires POSIX fork and os.register_at_fork (Python 3.7+)",
)
def test_monitor_thread_lock_reset_in_child_after_fork(sentry_init):
"""Regression test for #6148.
If os.fork() runs while Monitor._thread_lock is held, the child
inherits the lock locked. The holding thread does not exist in the
child, so the lock can never be released and _ensure_running
deadlocks forever. The after-fork hook must replace the lock with
a fresh one in the child.
"""
sentry_init(transport=HealthyTestTransport())
monitor = sentry_sdk.get_client().monitor
assert monitor is not None
original_lock = monitor._thread_lock
original_lock.acquire()
pid = os.fork()
if pid == 0:
# Child: was the lock object replaced and is the new one not
# held? Without the fix, _thread_lock is `original_lock`
# inherited locked, so `replaced` is False. blocking=False
# guarantees the child can't hang on a regression.
replaced = monitor._thread_lock is not original_lock
unheld = monitor._thread_lock.acquire(blocking=False)
os._exit(0 if replaced and unheld else 1)
original_lock.release()
_, status = os.waitpid(pid, 0)
assert os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0