88import statistics
99import subprocess
1010import time
11+ from dataclasses import dataclass
1112from pathlib import Path
1213from typing import Any , Callable
1314
1415# First-Party
1516from mcpgateway .config import settings
17+ from mcpgateway .middleware import request_logging_middleware
1618from mcpgateway .middleware .request_logging_middleware import mask_sensitive_data , mask_sensitive_headers
1719
1820REPO_ROOT = Path (__file__ ).resolve ().parents [2 ]
1921NATIVE_EXTENSION_MANIFEST = REPO_ROOT / "tools_rust" / "request_logging_masking_native_extension" / "Cargo.toml"
2022
2123
24+ @dataclass (frozen = True )
25+ class BenchmarkScenario :
26+ name : str
27+ python_fn : Callable [[Any ], Any ]
28+ public_native_fn : Callable [[Any ], Any ]
29+ direct_native_fn : Callable [[Any ], Any ]
30+ payload : Any
31+ iterations : int
32+
33+
34+ def _build_scenarios (native_extension : Any ) -> list [BenchmarkScenario ]:
35+ def python_data (payload : Any ) -> Any :
36+ settings .experimental_rust_request_logging_masking_enabled = False
37+ return mask_sensitive_data (payload , 12 )
38+
39+ def public_native_data (payload : Any ) -> Any :
40+ settings .experimental_rust_request_logging_masking_enabled = True
41+ return mask_sensitive_data (payload , 12 )
42+
43+ def direct_native_data (payload : Any ) -> Any :
44+ return native_extension .mask_sensitive_data (payload , 12 )
45+
46+ def python_headers (payload : Any ) -> Any :
47+ settings .experimental_rust_request_logging_masking_enabled = False
48+ return mask_sensitive_headers (payload )
49+
50+ def public_native_headers (payload : Any ) -> Any :
51+ settings .experimental_rust_request_logging_masking_enabled = True
52+ return mask_sensitive_headers (payload )
53+
54+ def direct_native_headers (payload : Any ) -> Any :
55+ return native_extension .mask_sensitive_headers (payload )
56+
57+ return [
58+ BenchmarkScenario (
59+ name = "nested_payload_masking" ,
60+ python_fn = python_data ,
61+ public_native_fn = public_native_data ,
62+ direct_native_fn = direct_native_data ,
63+ payload = {
64+ "events" : [
65+ {
66+ "actor" : {"userName" : f"user-{ index } " , "sessionToken" : f"token-{ index } " , "sessionCount" : index },
67+ "request" : {
68+ "clientSecret" : f"secret-{ index } " ,
69+ "payload" : {"safeField" : "value" * 8 , "authDevice" : f"device-{ index } " , "auth_count" : index },
70+ },
71+ }
72+ for index in range (1024 )
73+ ]
74+ },
75+ iterations = 120 ,
76+ ),
77+ BenchmarkScenario (
78+ name = "headers_masking" ,
79+ python_fn = python_headers ,
80+ public_native_fn = public_native_headers ,
81+ direct_native_fn = direct_native_headers ,
82+ payload = {
83+ ** {f"X-Custom-{ index } " : f"value-{ index } " for index in range (512 )},
84+ ** {f"X-Api-Key-{ index } " : f"secret-{ index } " for index in range (256 )},
85+ "Cookie" : "; " .join ([f"jwt_token_{ index } =abc{ index } " for index in range (128 )]),
86+ },
87+ iterations = 300 ,
88+ ),
89+ ]
90+
91+
92+ def test_build_scenarios_exposes_public_and_direct_native_paths (monkeypatch ):
93+ native_extension = type (
94+ "NativeExtension" ,
95+ (),
96+ {
97+ "mask_sensitive_data" : staticmethod (lambda payload , max_depth : {"path" : "direct" , "payload" : payload , "max_depth" : max_depth }),
98+ "mask_sensitive_headers" : staticmethod (lambda payload : {"path" : "direct" , "payload" : payload }),
99+ },
100+ )()
101+ observed_flags : list [bool ] = []
102+
103+ def fake_mask_sensitive_data (payload : Any , max_depth : int = 10 ) -> Any :
104+ observed_flags .append (settings .experimental_rust_request_logging_masking_enabled )
105+ return {"path" : "public" if settings .experimental_rust_request_logging_masking_enabled else "python" , "max_depth" : max_depth }
106+
107+ def fake_mask_sensitive_headers (payload : Any ) -> Any :
108+ observed_flags .append (settings .experimental_rust_request_logging_masking_enabled )
109+ return {"path" : "public" if settings .experimental_rust_request_logging_masking_enabled else "python" }
110+
111+ monkeypatch .setattr ("tests.performance.test_request_logging_masking_native_extension_benchmark.mask_sensitive_data" , fake_mask_sensitive_data )
112+ monkeypatch .setattr ("tests.performance.test_request_logging_masking_native_extension_benchmark.mask_sensitive_headers" , fake_mask_sensitive_headers )
113+
114+ scenarios = {scenario .name : scenario for scenario in _build_scenarios (native_extension )}
115+
116+ public_data = scenarios ["nested_payload_masking" ].public_native_fn ({"k" : "v" })
117+ direct_data = scenarios ["nested_payload_masking" ].direct_native_fn ({"k" : "v" })
118+ python_data = scenarios ["nested_payload_masking" ].python_fn ({"k" : "v" })
119+ public_headers = scenarios ["headers_masking" ].public_native_fn ({"Authorization" : "Bearer x" })
120+ direct_headers = scenarios ["headers_masking" ].direct_native_fn ({"Authorization" : "Bearer x" })
121+
122+ assert public_data ["path" ] == "public"
123+ assert direct_data ["path" ] == "direct"
124+ assert python_data ["path" ] == "python"
125+ assert public_headers ["path" ] == "public"
126+ assert direct_headers ["path" ] == "direct"
127+ assert observed_flags == [True , False , True ]
128+
129+
22130def _ensure_native_extension_installed () -> Any :
23131 subprocess .run (["uv" , "run" , "maturin" , "develop" , "--release" , "--manifest-path" , str (NATIVE_EXTENSION_MANIFEST )], check = True , cwd = REPO_ROOT )
24132 return importlib .import_module ("request_logging_masking_native_extension" )
@@ -45,74 +153,44 @@ def _assert_parity(python_fn: Callable[[Any], Any], rust_fn: Callable[[Any], Any
45153 raise AssertionError (f"Parity mismatch for payload { payload !r} : python={ python_result !r} rust={ rust_result !r} " )
46154
47155
48- def main () -> None :
49- native_extension = _ensure_native_extension_installed ()
50- settings .experimental_rust_request_logging_masking_enabled = False
156+ def _prepare_public_native_path (native_extension : Any ) -> None :
157+ settings .experimental_rust_request_logging_masking_enabled = True
158+ request_logging_middleware ._RUST_REQUEST_LOGGING_MODULE = native_extension
159+ request_logging_middleware ._RUST_REQUEST_LOGGING_IMPORT_FAILED = False
51160
52- def python_data (payload : Any ) -> Any :
53- return mask_sensitive_data (payload , 12 )
54161
55- def rust_data (payload : Any ) -> Any :
56- return native_extension .mask_sensitive_data (payload , 12 )
162+ def main () -> None :
163+ native_extension = _ensure_native_extension_installed ()
164+ _prepare_public_native_path (native_extension )
57165
58- python_headers = mask_sensitive_headers
59- rust_headers = native_extension .mask_sensitive_headers
166+ scenarios = _build_scenarios (native_extension )
60167
61168 _assert_parity (
62- python_data ,
63- rust_data ,
169+ scenarios [ 0 ]. python_fn ,
170+ scenarios [ 0 ]. direct_native_fn ,
64171 [
65172 {"password" : "secret" , "nested" : {"authToken" : "abc" , "ok" : "value" }},
66173 {"token_count" : 3 , "tokenizer" : "ok" , "privateKey" : "secret" },
67174 [{"jwt_token" : "abc" }, {"normal" : "value" }],
68175 ],
69176 )
70177 _assert_parity (
71- python_headers ,
72- rust_headers ,
178+ scenarios [ 1 ]. python_fn ,
179+ scenarios [ 1 ]. direct_native_fn ,
73180 [
74181 {"Authorization" : "Bearer abc" , "Cookie" : "jwt_token=abc; theme=dark" , "X-Trace-Id" : "123" },
75182 {"X-Auth-Count" : "5" , "X-Api-Key" : "secret" },
76183 ],
77184 )
78185
79- scenarios = [
80- (
81- "nested_payload_masking" ,
82- python_data ,
83- rust_data ,
84- {
85- "events" : [
86- {
87- "actor" : {"userName" : f"user-{ index } " , "sessionToken" : f"token-{ index } " , "sessionCount" : index },
88- "request" : {
89- "clientSecret" : f"secret-{ index } " ,
90- "payload" : {"safeField" : "value" * 8 , "authDevice" : f"device-{ index } " , "auth_count" : index },
91- },
92- }
93- for index in range (1024 )
94- ]
95- },
96- 120 ,
97- ),
98- (
99- "headers_masking" ,
100- python_headers ,
101- rust_headers ,
102- {
103- ** {f"X-Custom-{ index } " : f"value-{ index } " for index in range (512 )},
104- ** {f"X-Api-Key-{ index } " : f"secret-{ index } " for index in range (256 )},
105- "Cookie" : "; " .join ([f"jwt_token_{ index } =abc{ index } " for index in range (128 )]),
106- },
107- 300 ,
108- ),
109- ]
110-
111- for name , python_fn , rust_fn , payload , iterations in scenarios :
112- print (f"\n { name } ({ iterations } iterations)" )
113- python_median , _ = _measure ("python" , python_fn , payload , iterations )
114- rust_median , _ = _measure ("rust" , rust_fn , payload , iterations )
115- print (f"speedup={ python_median / rust_median :.2f} x" )
186+ for scenario in scenarios :
187+ print (f"\n { scenario .name } ({ scenario .iterations } iterations)" )
188+ python_median , _ = _measure ("python" , scenario .python_fn , scenario .payload , scenario .iterations )
189+ public_native_median , _ = _measure ("public_native" , scenario .public_native_fn , scenario .payload , scenario .iterations )
190+ direct_native_median , _ = _measure ("direct_native" , scenario .direct_native_fn , scenario .payload , scenario .iterations )
191+ print (f"public_speedup={ python_median / public_native_median :.2f} x" )
192+ print (f"direct_speedup={ python_median / direct_native_median :.2f} x" )
193+ print (f"public_overhead={ (public_native_median / direct_native_median - 1 ) * 100 :.1f} %" )
116194
117195
118196if __name__ == "__main__" :
0 commit comments