fix: keep provider TCP channels stable

This commit is contained in:
pikastech
2026-07-20 04:18:15 +02:00
parent 18d8fc1010
commit d4e9d1d325
3 changed files with 5 additions and 61 deletions
@@ -303,43 +303,6 @@ class ProtocolHelpersTest(unittest.TestCase):
stderr_thread.join.assert_called_once_with()
manager.finish_session.assert_called_once_with("session", 0)
def test_recycles_only_the_oldest_expired_idle_channel(self):
manager = ProviderManager.__new__(ProviderManager)
manager.state_lock = provider.threading.RLock()
manager.logger = MagicMock()
expired = DataChannel(manager, "expired")
expired.status = "ready"
expired.idle_since = 100.0
newer = DataChannel(manager, "newer")
newer.status = "ready"
newer.idle_since = 200.0
claimed = DataChannel(manager, "claimed")
claimed.status = "claimed"
claimed.session_id = "session"
claimed.idle_since = 0.0
manager.channels = {value.id: value for value in (newer, claimed, expired)}
for channel in manager.channels.values():
channel.close = MagicMock()
self.assertTrue(manager.recycle_idle_channel(now=1000.0))
expired.close.assert_called_once_with("空闲通道主动轮换")
newer.close.assert_not_called()
claimed.close.assert_not_called()
def test_keeps_recent_idle_channels(self):
manager = ProviderManager.__new__(ProviderManager)
manager.state_lock = provider.threading.RLock()
manager.logger = MagicMock()
channel = DataChannel(manager, "recent")
channel.status = "ready"
channel.idle_since = 500.0
channel.close = MagicMock()
manager.channels = {channel.id: channel}
self.assertFalse(manager.recycle_idle_channel(now=1000.0))
channel.close.assert_not_called()
class UpdateManagerTest(unittest.TestCase):
@staticmethod
def _source(version: str = "9.9.9") -> bytes:
@@ -56,7 +56,7 @@ else:
APP_NAME = "UniDesk Windows Provider"
DISPLAY_NAME = "UniDesk Windows Provider" if sys.platform == "win32" else "UniDesk Linux Provider"
APP_VERSION = "0.4.0"
APP_VERSION = "0.4.1"
CONFIG_DIR = Path.home() / ".unidesk"
CONFIG_PATH = CONFIG_DIR / "provider.yaml"
LOG_DIR = CONFIG_DIR / "logs"
@@ -69,7 +69,6 @@ POOL_SIZE = 32
MAX_HEADER_BYTES = 64 * 1024
MAX_PAYLOAD_BYTES = 16 * 1024 * 1024
HEARTBEAT_SECONDS = 15
DATA_CHANNEL_MAX_IDLE_SECONDS = 10 * 60
TCP_KEEPALIVE_IDLE_SECONDS = 30
TCP_KEEPALIVE_INTERVAL_SECONDS = 10
TCP_KEEPALIVE_PROBES = 3
@@ -1262,7 +1261,6 @@ class ProviderManager:
while not self.stop_event.is_set():
if time.time() - last_heartbeat >= HEARTBEAT_SECONDS:
self._send_heartbeat()
self.recycle_idle_channel()
last_heartbeat = time.time()
try:
opcode, payload = control.receive()
@@ -1444,23 +1442,6 @@ class ProviderManager:
channel.idle_since = time.monotonic()
self.pool_changed()
def recycle_idle_channel(self, now: float | None = None) -> bool:
current = time.monotonic() if now is None else now
with self.state_lock:
candidates = [
channel
for channel in self.channels.values()
if channel.status == "ready"
and channel.session_id is None
and current - channel.idle_since >= DATA_CHANNEL_MAX_IDLE_SECONDS
]
channel = min(candidates, key=lambda value: value.idle_since) if candidates else None
if channel is None:
return False
self.logger.info("主动轮换空闲 TCP 通道: %s", channel.id)
channel.close("空闲通道主动轮换")
return True
def handle_data_frame(self, channel: DataChannel, header: dict, payload: bytes) -> None:
session_id = str(header.get("sessionId") or "")
with self.state_lock:
+4 -4
View File
@@ -1,9 +1,9 @@
{
"schemaVersion": 1,
"channel": "stable",
"latestVersion": "0.4.0",
"latestVersion": "0.4.1",
"downloadUrl": "unideskcprovider.py",
"bytes": 91440,
"sha256": "75fade32b5beeab31b8614a130a2123094d3e59a37d0b0b33b19954b60697bb7",
"releaseNotes": "同一 Python Provider 新增 Linux headless、Bash、systemd 一键安装与跨平台升级支持。"
"bytes": 90608,
"sha256": "136c146036324092b88ab118850e86428d214fd0310dec4f6c9eb608a2809e81",
"releaseNotes": "Linux 原生 Provider 禁用无协调的空闲 TCP 通道主动轮换,避免高并发分配竞态。"
}