feat: 完成 OpenNexus 第三阶段核心功能与生产化基础 #45

Merged
Kronecker merged 151 commits from feat/phase3-completion into main 2026-09-10 01:16:31 +08:00
2 changed files with 51 additions and 20 deletions
Showing only changes of commit 2858babfe3 - Show all commits
+2 -2
View File
@@ -225,11 +225,11 @@ def main() -> int:
"dependencies": True, "dependencies": True,
"postgres_version": stack.postgres_version, "postgres_version": stack.postgres_version,
"minio_version": stack.minio_version, "minio_version": stack.minio_version,
"worker_count": len(stack.worker_ids()),
} }
) )
assert facts["worker_count"] == 2
auth, base, vault_id = stack.create_vault("S-05 isolated") auth, base, vault_id = stack.create_vault("S-05 isolated")
facts["worker_count"] = len(stack.worker_ids())
assert facts["worker_count"] == 2
committed: dict[str, bytes] = {} committed: dict[str, bytes] = {}
cleaned: dict[str, bytes] = {} cleaned: dict[str, bytes] = {}
expected_used = 0 expected_used = 0
@@ -267,22 +267,45 @@ class SyncProductionStack:
return self return self
def create_vault(self, label: str): def create_vault(self, label: str):
code, session, _ = call( session = None
"POST", for _ in range(5):
self.origin + "/sync/v1/auth/sessions", try:
body={ code, candidate, _ = call(
"username": self.username, "POST",
"password": self.password, self.origin + "/sync/v1/auth/sessions",
"device_name": label, body={
}, "username": self.username,
) "password": self.password,
if code != 200: "device_name": label,
},
timeout=10,
)
if code == 200:
session = candidate
break
except (OSError, TimeoutError, URLError):
pass
time.sleep(0.2)
if session is None:
raise RuntimeError("ACCEPTANCE_LOGIN_FAILED") raise RuntimeError("ACCEPTANCE_LOGIN_FAILED")
auth = {"Authorization": "Bearer " + session["access_token"]} auth = {"Authorization": "Bearer " + session["access_token"]}
code, vault, _ = call( vault = None
"POST", self.origin + "/sync/v1/vaults", headers=auth, body={"name": label} for _ in range(5):
) try:
if code != 200: code, candidate, _ = call(
"POST",
self.origin + "/sync/v1/vaults",
headers=auth,
body={"name": label},
timeout=10,
)
if code == 200:
vault = candidate
break
except (OSError, TimeoutError, URLError):
pass
time.sleep(0.2)
if vault is None:
raise RuntimeError("ACCEPTANCE_VAULT_FAILED") raise RuntimeError("ACCEPTANCE_VAULT_FAILED")
return auth, self.origin + "/sync/v1/vaults/" + vault["vault_id"], vault["vault_id"] return auth, self.origin + "/sync/v1/vaults/" + vault["vault_id"], vault["vault_id"]
@@ -315,11 +338,19 @@ class SyncProductionStack:
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
def probe(_): def probe(_):
return call( try:
"GET", self.origin + "/health", headers={"Connection": "close"} return call(
)[2].get("x-opennexus-worker") "GET",
self.origin + "/health",
headers={"Connection": "close"},
timeout=10,
)[2].get("x-opennexus-worker")
except (OSError, TimeoutError, URLError):
return None
with ThreadPoolExecutor(max_workers=40) as pool: # A small pool is enough to reach both workers without exhausting the
# Windows ephemeral-port/backlog budget before the fault matrix starts.
with ThreadPoolExecutor(max_workers=8) as pool:
return {value for value in pool.map(probe, range(attempts)) if value} return {value for value in pool.map(probe, range(attempts)) if value}
def assert_running(self) -> None: def assert_running(self) -> None: