docs: 将仓库代码注释统一为中文
CI / docs-check (push) Canceled after 0s
CI / backend-test (push) Canceled after 0s
CI / service-test (push) Canceled after 0s
CI / frontend-test (push) Canceled after 0s
CI / rust-core (push) Canceled after 0s
CI / docs-check (pull_request) Canceled after 0s
CI / backend-test (pull_request) Canceled after 0s
CI / service-test (pull_request) Canceled after 0s
CI / frontend-test (pull_request) Canceled after 0s
CI / rust-core (pull_request) Canceled after 0s

This commit is contained in:
2026-09-10 00:40:56 +08:00
parent 51c592841d
commit d703ab64e3
249 changed files with 707 additions and 900 deletions
+6 -11
View File
@@ -29,7 +29,7 @@ class SyncError(Exception):
class StagingDamaged(Exception):
"""Internal signal used to commit cleanup before returning UPLOAD_DAMAGED."""
"""用于在返回 UPLOAD_DAMAGED 之前提交清理的内部信号。"""
def __init__(self, upload):
self.upload_id = upload["id"]
@@ -131,8 +131,7 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim
@app.get("/health")
def health(response: Response):
# An ephemeral identifier lets deployment probes prove that both
# configured workers receive traffic without exposing host identity.
# 临时标识符可以让部署探测证明两个配置的工作线程都接收流量,而不会暴露主机身份。
response.headers["X-OpenNexus-Worker"] = worker_id
return {"status": "ok"}
@@ -281,8 +280,7 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim
return path
def discard_damaged_upload(damaged):
# Re-open after the failed operation released its transaction. Deleting
# inside the failed transaction would be rolled back with the response.
# 失败的操作释放其事务后重新打开。失败事务中的删除操作将随响应一起回滚。
with db.transaction() as conn:
suffix = " FOR UPDATE" if not db.sqlite else ""
row(conn, "SELECT id FROM vaults WHERE id=:v" + suffix, v=damaged.vault_id)
@@ -294,8 +292,7 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim
device=damaged.device_id,
)
if upload:
# File first: interruption leaves a row whose quota reservation
# can still be released by expiry maintenance.
# 文件优先:中断留下一行,其配额保留仍可通过到期维护释放。
(staging / upload["id"]).unlink(missing_ok=True)
run(conn, "DELETE FROM uploads WHERE id=:id", id=upload["id"])
@@ -321,8 +318,7 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim
if len(chunk) > 1048576 - len(data):
raise SyncError(413, "CHUNK_TOO_LARGE")
data.extend(chunk)
# Keep the transaction and durable write on one worker thread. A slow
# database lock or fsync must not block this worker's ASGI event loop.
# 将事务和持久写入保留在一个工作线程上。缓慢的数据库锁定或 fsync 不得阻止此工作线程的 ASGI 事件循环。
return await run_in_threadpool(persist_upload_chunk, vault_id, upload_id,
authorization, offset, data)
@@ -447,8 +443,7 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim
if not obj:
raise SyncError(404, "OBJECT_NOT_FOUND")
expected_size = obj["size"]
# Verify before returning any bytes, without holding a database transaction
# or buffering an entire attachment in RAM.
# 在返回任何字节之前进行验证,而不保留数据库事务或在 RAM 中缓冲整个附件。
temporary = tempfile.NamedTemporaryFile(prefix="download-", dir=staging, delete=False)
path = Path(temporary.name)
try:
+1 -1
View File
@@ -36,7 +36,7 @@ class Database:
def migrate(self):
with self.transaction() as conn:
if not self.sqlite:
# Serialize factory startup migrations across the supported workers.
# 在受支持的工作人员之间序列化工厂启动迁移。
conn.execute(text("SELECT pg_advisory_xact_lock(1330534488)"))
conn.execute(text(SCHEMA[0]))
version = conn.execute(text("SELECT version FROM schema_version")).scalar()
+3 -3
View File
@@ -1,4 +1,4 @@
"""Delete expired upload staging only; referenced historical objects are never GC'd."""
"""仅删除过期的上传暂存;引用的历史对象永远不会是 GC'd。"""
import re
import time
from pathlib import Path
@@ -11,7 +11,7 @@ def cleanup_expired_uploads(db, staging: Path, *, now=None, limit=500):
expired = rows(conn, "SELECT id,vault_id FROM uploads WHERE expires<=:now ORDER BY expires LIMIT :limit", now=now, limit=limit)
removed = 0
for candidate in expired:
# Same lock order as PUT/complete. Recheck expiry after acquiring the lock.
# 与 PUT 相同的锁定顺序/完成。获取锁后重新检查过期时间。
with db.transaction() as conn:
suffix = " FOR UPDATE" if not db.sqlite else ""
row(conn, "SELECT id FROM vaults WHERE id=:v" + suffix, v=candidate["vault_id"])
@@ -19,7 +19,7 @@ def cleanup_expired_uploads(db, staging: Path, *, now=None, limit=500):
if upload:
if not re.fullmatch(r"[0-9a-f]{32}", upload["id"]):
raise RuntimeError("UPLOAD_ID_INVALID")
# File first: interruption leaves an expired row that can be retried.
# 文件优先:中断留下可以重试的过期行。
(staging / upload["id"]).unlink(missing_ok=True)
run(conn, "DELETE FROM uploads WHERE id=:id", id=upload["id"])
removed += 1
+2 -2
View File
@@ -1,4 +1,4 @@
"""Consistent PostgreSQL/S3 backup and empty-instance restore operations."""
"""一致的 PostgreSQL/S3 备份和空实例恢复操作。"""
from __future__ import annotations
@@ -51,7 +51,7 @@ TABLES: dict[str, tuple[str, ...]] = {
class OperationsError(RuntimeError):
"""Stable operator-facing failure without credentials or response bodies."""
"""稳定的面向操作员的故障,无需凭证或响应主体。"""
def sha256_file(path: Path) -> str:
+3 -5
View File
@@ -1,4 +1,4 @@
"""Bound readiness work even when a synchronous dependency ignores its timeout."""
"""即使同步依赖项忽略其超时,绑定准备工作也会起作用。"""
import asyncio
import time
@@ -15,8 +15,7 @@ class Readiness:
@staticmethod
def consume(task):
# A request can time out or disconnect before the synchronous probe ends.
# Retrieve late exceptions without logging dependency messages/secrets.
# 在同步探测结束之前,请求可能会超时或断开连接。检索晚期异常而不记录依赖项消息/秘密。
if not task.cancelled():
task.exception()
@@ -28,8 +27,7 @@ class Readiness:
self.running = asyncio.create_task(asyncio.to_thread(self.probe))
self.running.add_done_callback(self.consume)
try:
# Cancelling a to_thread await does not stop its OS thread. Keep
# the task alive so subsequent requests reuse the same probe.
# 取消 to_thread 等待不会停止其 OS 线程。保持任务处于活动状态,以便后续请求重用相同的探测器。
await asyncio.wait_for(asyncio.shield(self.running), self.timeout)
self.ok = True
except Exception:
+2 -5
View File
@@ -67,7 +67,7 @@ class S3Objects:
self.bucket = bucket
def ensure_bucket(self) -> bool:
"""Create the configured bucket when absent; never alter an existing bucket."""
"""不在时创建配置的桶;切勿更改现有存储桶。"""
from botocore.exceptions import ClientError
try:
@@ -108,10 +108,7 @@ class S3Objects:
def put_file(self, key: str, path: Path, content_hash: str):
with path.open("rb") as stream:
# Objects are capped at 100 MiB, well below S3's 5 GiB single-PUT
# limit. A direct streaming request has one explicit connection
# lifetime; constructing a transfer manager per completion can
# retain pooled MinIO connections under repeated multi-worker use.
# 对象的上限为 100 MiB,远低于 S3 5 GiB 单 PUT 限制。直接流请求有一个显式的连接生命周期;每次完成构建一个传输管理器可以在重复的多工作线程使用下保留池化的 MinIO 连接。
self.client.put_object(
Bucket=self.bucket,
Key=key,