1717 _CGROUP_V2_STAT ,
1818 _PROC_SELF_STATUS ,
1919 MemoryMonitor ,
20+ _format_bytes ,
2021 _read_cgroup_v2_anon_bytes ,
2122 _read_process_anon_rss_bytes ,
2223)
@@ -88,6 +89,46 @@ def test_check_interval_negative_raises() -> None:
8889 MemoryMonitor (check_interval = - 1 )
8990
9091
92+ def test_init_logs_configured_thresholds (caplog : pytest .LogCaptureFixture ) -> None :
93+ """Instantiation should emit one INFO line with the configured thresholds and intervals."""
94+ with caplog .at_level (logging .INFO , logger = "airbyte" ):
95+ MemoryMonitor (check_interval = 1234 )
96+ init_logs = [r for r in caplog .records if "MemoryMonitor instantiated" in r .message ]
97+ assert len (init_logs ) == 1
98+ msg = init_logs [0 ].message
99+ assert "critical threshold: 95%" in msg
100+ assert "anon share of usage threshold: 85%" in msg
101+ assert "high-pressure threshold: 90%" in msg
102+ assert "check interval: 1234 messages" in msg
103+ assert "tightens to 100 under high pressure" in msg
104+
105+
106+ # ---------------------------------------------------------------------------
107+ # _format_bytes — unit tests
108+ # ---------------------------------------------------------------------------
109+
110+
111+ @pytest .mark .parametrize (
112+ ("num_bytes" , "expected" ),
113+ [
114+ (0 , "0 B" ),
115+ (999 , "999 B" ),
116+ (1_000 , "1.00 KB" ),
117+ (1_500 , "1.50 KB" ),
118+ (999_999 , "1000.00 KB" ),
119+ (1_000_000 , "1.00 MB" ),
120+ (960_000_000 , "960.00 MB" ),
121+ (999_999_999 , "1000.00 MB" ),
122+ (1_000_000_000 , "1.00 GB" ),
123+ (2_109_915_136 , "2.11 GB" ),
124+ (2_147_483_648 , "2.15 GB" ),
125+ ],
126+ )
127+ def test_format_bytes (num_bytes : int , expected : str ) -> None :
128+ """`_format_bytes` renders byte counts with 2 decimals using decimal units."""
129+ assert _format_bytes (num_bytes ) == expected
130+
131+
91132# ---------------------------------------------------------------------------
92133# check_memory_usage — no-op paths
93134# ---------------------------------------------------------------------------
@@ -96,6 +137,7 @@ def test_check_interval_negative_raises() -> None:
96137def test_noop_when_no_cgroup (caplog : pytest .LogCaptureFixture ) -> None :
97138 """check_memory_usage should be a no-op when cgroup is unavailable."""
98139 monitor = MemoryMonitor ()
140+ caplog .clear () # discard the one-shot instantiation log
99141 with (
100142 caplog .at_level (logging .WARNING , logger = "airbyte" ),
101143 patch .object (Path , "exists" , return_value = False ),
@@ -107,6 +149,7 @@ def test_noop_when_no_cgroup(caplog: pytest.LogCaptureFixture) -> None:
107149def test_noop_when_limit_is_max (caplog : pytest .LogCaptureFixture ) -> None :
108150 """When cgroup v2 memory.max is 'max' (unlimited), should be a no-op."""
109151 monitor = MemoryMonitor (check_interval = 1 )
152+ caplog .clear ()
110153 with (
111154 caplog .at_level (logging .WARNING , logger = "airbyte" ),
112155 patch .object (Path , "exists" , _v2_exists ),
@@ -119,6 +162,7 @@ def test_noop_when_limit_is_max(caplog: pytest.LogCaptureFixture) -> None:
119162def test_noop_when_limit_is_zero (caplog : pytest .LogCaptureFixture ) -> None :
120163 """When cgroup limit file contains '0', should be a no-op."""
121164 monitor = MemoryMonitor (check_interval = 1 )
165+ caplog .clear ()
122166 with (
123167 caplog .at_level (logging .WARNING , logger = "airbyte" ),
124168 patch .object (Path , "exists" , _v2_exists ),
@@ -136,6 +180,7 @@ def test_noop_when_limit_is_zero(caplog: pytest.LogCaptureFixture) -> None:
136180def test_no_log_below_threshold (caplog : pytest .LogCaptureFixture ) -> None :
137181 """No log should be emitted when usage is below the 90% high-pressure threshold."""
138182 monitor = MemoryMonitor (check_interval = 1 )
183+ caplog .clear ()
139184 with (
140185 caplog .at_level (logging .DEBUG , logger = "airbyte" ),
141186 patch .object (Path , "exists" , _v2_exists ),
@@ -182,6 +227,7 @@ def mock_read_text(self: Path) -> str:
182227def test_check_interval_skips_intermediate_calls (caplog : pytest .LogCaptureFixture ) -> None :
183228 """Monitor should only check cgroup files every check_interval messages."""
184229 monitor = MemoryMonitor (check_interval = 5000 )
230+ caplog .clear ()
185231 with (
186232 caplog .at_level (logging .INFO , logger = "airbyte" ),
187233 patch .object (Path , "exists" , _v2_exists ),
@@ -205,6 +251,7 @@ def test_check_interval_skips_intermediate_calls(caplog: pytest.LogCaptureFixtur
205251def test_malformed_cgroup_file_degrades_gracefully (caplog : pytest .LogCaptureFixture ) -> None :
206252 """Malformed cgroup files should not crash the sync."""
207253 monitor = MemoryMonitor (check_interval = 1 )
254+ caplog .clear ()
208255 with (
209256 caplog .at_level (logging .WARNING , logger = "airbyte" ),
210257 patch .object (Path , "exists" , _v2_exists ),
@@ -217,6 +264,7 @@ def test_malformed_cgroup_file_degrades_gracefully(caplog: pytest.LogCaptureFixt
217264def test_empty_cgroup_file_degrades_gracefully (caplog : pytest .LogCaptureFixture ) -> None :
218265 """Empty cgroup file content should not crash the sync."""
219266 monitor = MemoryMonitor (check_interval = 1 )
267+ caplog .clear ()
220268 with (
221269 caplog .at_level (logging .WARNING , logger = "airbyte" ),
222270 patch .object (Path , "exists" , _v2_exists ),
@@ -233,6 +281,7 @@ def mock_read_text(self: Path) -> str:
233281 raise OSError ("Permission denied" )
234282
235283 monitor = MemoryMonitor (check_interval = 1 )
284+ caplog .clear ()
236285 with (
237286 caplog .at_level (logging .WARNING , logger = "airbyte" ),
238287 patch .object (Path , "exists" , _v2_exists ),
@@ -342,6 +391,14 @@ def test_raises_when_cgroup_critical_and_anon_share_of_usage_above_threshold() -
342391 assert "critical threshold" in (exc_info .value .message or "" )
343392 assert "96%" in (exc_info .value .message or "" )
344393 assert "anon share of usage" in (exc_info .value .internal_message or "" )
394+ # Human-readable byte formatting: 960 MB usage, 1.00 GB limit, 840 MB anon.
395+ internal = exc_info .value .internal_message or ""
396+ assert "960.00 MB" in internal
397+ assert "1.00 GB" in internal
398+ assert "840.00 MB" in internal
399+ # Raw byte counts should no longer appear in the message.
400+ assert "960000000" not in internal
401+ assert "1000000000" not in internal
345402
346403
347404def test_no_raise_when_cgroup_critical_but_anon_share_of_usage_below_threshold (
0 commit comments