ZaStoGram/Tools/check_mtproxy_global_handshake_budget.py
loop-uh 3f8f8f27e7 Extract mtproxy module with unified retry authority
Move MTProxy policy engine from tgnet/ to self-contained jni/mtproxy/ module. Consolidate reconnect hold computation into single MtProxyRetryAuthority owner (was split across Connection, ConnectionSocket, endpoint policy, and probe coordinator, causing hold misalignment bugs). Add MtProxyTerminalDiagnostic module with host tests for pre-I/O verdict preservation (fixes 02.07/30.06 clobber livelocks). Auto-generate phase classification (isPreIoTerminalVerdict, needsReconnectBackoff, isObservationFacadePhase, isLocalSchedulerTimeout) from single Python contract into both C++ and Java to eliminate parallel maintenance. Pass native hold (coordinator terminal hold + endpoint cooldown) via JNI to Java so reconnect timer and health store use THE clock, not re-derived ones. Optimize log flushing: batch debug lines, flush errors immediately. Add module boundary guard, host build system (MSVC), and unit tests for retry logic and terminal diagnostic derivation.
2026-07-02 18:25:13 +03:00

238 lines
12 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
SOCKET = ROOT / "TMessagesProj/jni/tgnet/ConnectionSocket.cpp"
SCHEDULER = ROOT / "TMessagesProj/jni/mtproxy/MtProxyHandshakeScheduler.cpp"
SCHEDULER_HEADER = ROOT / "TMessagesProj/jni/mtproxy/MtProxyHandshakeScheduler.h"
CONNECTION = ROOT / "TMessagesProj/jni/tgnet/Connection.cpp"
DEFINES = ROOT / "TMessagesProj/jni/tgnet/Defines.h"
OPTIONS = ROOT / "TMessagesProj/jni/mtproxy/MtProxyOptions.h"
CMAKE = ROOT / "TMessagesProj/jni/CMakeLists.txt"
def read(path: Path) -> str:
return path.read_text(encoding="utf-8", errors="replace")
def require(condition: bool, message: str) -> None:
if not condition:
print(f"FAIL: {message}", file=sys.stderr)
raise SystemExit(1)
def slice_between(source: str, start: str, end: str) -> str:
start_idx = source.find(start)
require(start_idx >= 0, f"missing start marker: {start}")
end_idx = source.find(end, start_idx)
require(end_idx >= 0, f"missing end marker after {start}: {end}")
return source[start_idx:end_idx]
def slice_from(source: str, start: str) -> str:
start_idx = source.find(start)
require(start_idx >= 0, f"missing start marker: {start}")
return source[start_idx:]
def main() -> None:
socket = read(SOCKET)
scheduler = read(SCHEDULER)
scheduler_header = read(SCHEDULER_HEADER)
connection = read(CONNECTION)
defines = read(DEFINES)
options = read(OPTIONS)
cmake = read(CMAKE)
scheduler_helpers = slice_between(
scheduler,
"static pthread_mutex_t proxyHandshakeSchedulerMutex",
"MtProxyHandshakeAdmissionDecision mtProxyHandshakeSchedulerAdmit",
)
scheduler_admission = slice_between(
scheduler,
"MtProxyHandshakeAdmissionDecision mtProxyHandshakeSchedulerAdmit",
"MtProxyHandshakeReleaseDecision mtProxyHandshakeSchedulerRelease",
)
scheduler_release = slice_from(
scheduler,
"MtProxyHandshakeReleaseDecision mtProxyHandshakeSchedulerRelease",
)
socket_admission = slice_between(
socket,
"bool ConnectionSocket::scheduleProxyHandshakeAdmissionIfNeeded",
"void ConnectionSocket::scheduleProxyHandshakeAdmissionTimer",
)
socket_release = slice_between(
socket,
"void ConnectionSocket::releaseProxyHandshakeAdmission",
"bool ConnectionSocket::scheduleMtProxyEndpointCircuitBreakerIfNeeded",
)
require(
"mtproxy/MtProxyHandshakeScheduler.cpp" in cmake,
"native build must compile MtProxyHandshakeScheduler.cpp",
)
require(
"enum class MtProxyRequestClass : uint8_t" in scheduler_header
and "Generic" in scheduler_header
and "Media" in scheduler_header
and "Push" in scheduler_header
and "Download" in scheduler_header
and "Upload" in scheduler_header
and "ProxyCheck" in scheduler_header
and "mtProxyHandshakePriorityForRequestClass" in scheduler_header
and "mtProxyRequestClassForPriority" in scheduler_header
and "mtProxyRequestClassName" in scheduler_header,
"scheduler API must name MTProxy request classes instead of exposing only raw priority integers",
)
require(
"MtProxyHandshakeAdmissionRequest" in scheduler_header
and "ConnectionSocket *socket = nullptr;" in scheduler_header
and "std::string key;" in scheduler_header
and "uint32_t generation = 0;" in scheduler_header
and "MtProxyRequestClass requestClass = MtProxyRequestClass::Generic;" in scheduler_header
and "int32_t priority = 0;" in scheduler_header
and "int32_t timerMode = 0;" in scheduler_header
and "int32_t connectionPatternMode = 0;" in scheduler_header
and "bool ipv6 = false;" in scheduler_header
and "int64_t now = 0;" in scheduler_header
and "MtProxyHandshakeAdmissionDecision" in scheduler_header
and "bool queued = false;" in scheduler_header
and "bool granted = false;" in scheduler_header
and "bool publishQueue = false;" in scheduler_header
and "uint32_t delayMs = 0;" in scheduler_header
and "int32_t endpointActive = 0;" in scheduler_header
and "int32_t endpointLimit = 0;" in scheduler_header
and "int32_t globalActive = 0;" in scheduler_header
and "int32_t globalLimit = 0;" in scheduler_header
and "int64_t cooldownRemainingMs = 0;" in scheduler_header,
"scheduler facade must expose typed admission request/decision structs",
)
require(
"proxyHandshakeSchedulerMutex" not in socket
and "proxyHandshakeEndpoints" not in socket
and "proxyHandshakeGlobal" not in socket
and "struct MtProxyHandshakeQueuedRequest" not in socket
and "struct MtProxyHandshakeEndpointState" not in socket
and "mtProxyHandshakeActiveLimit" not in socket
and "mtProxyHandshakeGlobalActiveLimit" not in socket
and "mtProxyTakeNextQueuedRequestGlobalLocked" not in socket
and "mtProxyRecordHandshakeSuccess" not in socket,
"ConnectionSocket must not own global handshake scheduler state or policy helpers",
)
require(
"MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_SOFT = 2" in options
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_BROWSER = 2" in options
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_QUIET = 1" in options
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_STRICT = 1" in options
and "MT_PROXY_STARTUP_ENDPOINT_HANDSHAKES_COLD = 1" in options
and "MT_PROXY_STARTUP_ENDPOINT_HANDSHAKES_USABLE = 2" in options,
"startup handshake fanout limits must live in mtproxy/MtProxyOptions.h (module-owned), separate from established connection counts",
)
require(
"MT_PROXY_STARTUP_GLOBAL_HANDSHAKES" not in defines
and "MT_PROXY_STARTUP_ENDPOINT_HANDSHAKES" not in defines,
"Defines.h must not re-grow handshake fanout limits; they are owned by mtproxy/MtProxyOptions.h",
)
require(
"struct MtProxyHandshakeGlobalState" in scheduler_helpers
and "activeHandshakes" in scheduler_helpers
and "lastGrantTime" in scheduler_helpers
and "static MtProxyHandshakeGlobalState proxyHandshakeGlobal" in scheduler_helpers,
"scheduler must keep app-wide handshake state shared across account instances",
)
require(
"mtProxyHandshakeGlobalActiveLimit" in scheduler_helpers
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_SOFT" in scheduler_helpers
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_BROWSER" in scheduler_helpers
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_QUIET" in scheduler_helpers
and "MT_PROXY_STARTUP_GLOBAL_HANDSHAKES_STRICT" in scheduler_helpers,
"global active limit helper must be mode-aware",
)
require(
"mtProxyHandshakeEndpointHasRecentSuccess" in scheduler_helpers
and "return MT_PROXY_STARTUP_ENDPOINT_HANDSHAKES_COLD;" in scheduler_helpers
and "return MT_PROXY_STARTUP_ENDPOINT_HANDSHAKES_USABLE;" in scheduler_helpers
and "MT_PROXY_HANDSHAKE_SOFT_ACTIVE_LIMIT" not in scheduler,
"endpoint active limit must be 1 until recent usable success explicitly allows 2",
)
require(
"activeGenericPushHandshakes" in scheduler_helpers
and "mtProxyHandshakeIsGenericOrPush" in scheduler_helpers
and "mtProxyHandshakeIsHeavyRequestClass" in scheduler_helpers
and "mtProxyHandshakeHeavyBlockedBeforeUsable" in scheduler_helpers
and "activeGenericPushHandshakes > 0" in scheduler_helpers
and "mtProxyHandshakeHasGenericOrPushQueuedGlobal" in scheduler_helpers,
"heavy request classes must wait behind generic/push while the endpoint is not yet usable",
)
require(
"mtProxyHandshakeGlobalSpacingDelay" in scheduler_helpers
and "proxyHandshakeGlobal.lastGrantTime" in scheduler_helpers
and "mtProxyRecordGlobalHandshakeGrant" in scheduler_helpers,
"global budget must space grants across endpoints and accounts",
)
require(
"mtProxyHandshakeHasHigherPriorityQueuedGlobal" in scheduler_helpers
and "for (const auto &entry : proxyHandshakeEndpoints)" in scheduler_helpers
and "request.priority < priority" in scheduler_helpers,
"priority checks must see queued handshakes from every endpoint/account",
)
require(
"mtProxyTakeNextQueuedRequestGlobalLocked" in scheduler_helpers
and "proxyHandshakeGlobal.activeHandshakes >= globalLimit" in scheduler_helpers
and "entry.second.activeHandshakes >= endpointLimit" in scheduler_helpers
and "proxyHandshakeGlobal.activeHandshakes++" in scheduler_helpers,
"release must dequeue the next best request globally, not only from the same endpoint",
)
require(
"globalLimit = mtProxyHandshakeGlobalActiveLimit" in scheduler_admission
and "globalLimitReached = proxyHandshakeGlobal.activeHandshakes >= globalLimit" in scheduler_admission
and "globalLimitReached || cooldownBlocks || state.activeHandshakes >= endpointLimit" in scheduler_admission,
"scheduler admission must queue when the app-wide handshake budget is full",
)
require(
"mtProxyHandshakeHasHigherPriorityQueuedGlobal(request.priority)" in scheduler_admission
and "proxyHandshakeGlobal.activeHandshakes++" in scheduler_admission
and "mtProxyRecordGlobalHandshakeGrant(request.now, decision.delayMs)" in scheduler_admission,
"scheduler immediate grants must reserve and record an app-wide slot",
)
require(
"request.requestClass = mtProxyRequestClassForPriority(proxyHandshakeAdmissionPriority);" in socket_admission
and "request_class=%s" in socket_admission
and "MtProxyRequestClass requestClass = request.requestClass;" in scheduler_admission
and "mtProxyRequestClassForConnectionType" in connection
and "mtProxyHandshakePriorityForRequestClass(mtProxyRequestClassForConnectionType(connectionType))" in connection,
"Connection.cpp and ConnectionSocket must feed typed request class into scheduler admission while preserving priority ordering",
)
require(
"mtProxyHandshakeSchedulerAdmit(request)" in socket_admission
and "decision.queued" in socket_admission
and "decision.granted" in socket_admission
and "global_active=%d" in socket_admission
and "global_limit=%d" in socket_admission
and "mtProxyHandshakeSchedulerRelease(releaseRequest)" in socket_release
and "global_active=%d" in socket_release
and "global_limit=%d" in socket_release,
"startup diagnostics must expose global active/limit budget decisions",
)
require(
"if (request.wasActive && proxyHandshakeGlobal.activeHandshakes > 0)" in scheduler_release
and "proxyHandshakeGlobal.activeHandshakes--" in scheduler_release,
"scheduler release must return an app-wide slot exactly once",
)
require(
"mtProxyTakeNextQueuedRequestGlobalLocked(request.now, mode, decision.nextRequest)" in scheduler_release
and "MtProxyHandshakeEndpointState &nextState = proxyHandshakeEndpoints[decision.nextRequest.key]" in scheduler_release
and "mtProxyRecordGlobalHandshakeGrant(request.now, decision.nextRequest.delayMs)" in scheduler_release
and "admission_dequeue_global" in socket_release
and "grantProxyHandshakeAdmission" in socket_release,
"scheduler release must grant the next queued handshake across all accounts/endpoints",
)
print("MTProxy global handshake budget guard passed.")
if __name__ == "__main__":
main()