#!/usr/bin/env python3 """运行订阅执行治理的确定性规模验收并输出 JSON 证据。""" from __future__ import annotations import argparse import hashlib import json import math import platform import subprocess import sys import tempfile import time from dataclasses import asdict, dataclass from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Any from sqlalchemy import create_engine, update from sqlalchemy.orm import Session, sessionmaker PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from app.application.download.admission import SubscriptionDownloadRequest # noqa: E402 from app.application.subscription.candidates import CandidateIndex # noqa: E402 from app.application.subscription.contract import SubscriptionSnapshot # noqa: E402 from app.application.subscription.facts import FreshFactLease # noqa: E402 from app.db.adapters.subscriptiondownload import ( # noqa: E402 TransactionalSubscriptionDownloadRepository, ) from app.db.adapters.subscriptionsearch import ( # noqa: E402 TransactionalSubscriptionSearchRepository, ) from app.db.base import Base # noqa: E402 from app.db.models.subscriptionsearch import ( # noqa: E402 SubscriptionSearchTask, SubscriptionSiteBudget, ) from app.domain.context import Context, MediaInfo, TorrentInfo # noqa: E402 from app.domain.metainfo import MetaInfo # noqa: E402 from app.schemas.types import MediaSource, MediaType # noqa: E402 @dataclass(frozen=True, slots=True) class ScaleCase: """声明一档订阅、站点、候选和唯一媒体规模。""" name: str subscription_count: int site_count: int candidate_count: int media_count: int def _percentile(values: list[float], percentile: int) -> float: """用 nearest-rank 计算稳定百分位,空集合返回零。""" if not values: return 0.0 ordered = sorted(values) position = max(0, math.ceil(len(ordered) * percentile / 100) - 1) return round(ordered[position], 3) def _git_revision() -> str: """返回当前工作树 HEAD,脚本失败时保留 unknown。""" try: return subprocess.check_output( ["git", "rev-parse", "HEAD"], cwd=PROJECT_ROOT, text=True, ).strip() except (OSError, subprocess.CalledProcessError): return "unknown" def _context(*, candidate_index: int, media_id: str, site_id: int) -> Context: """构造同时携带 canonical 媒体身份和稳定资源身份的候选。""" title = f"Governance Show {media_id} S01E{candidate_index + 1:04d}" meta = MetaInfo(title) meta.type = MediaType.TV meta.begin_season = 1 meta.media_source = MediaSource.TMDB meta.media_id = media_id return Context( meta_info=meta, media_info=MediaInfo( media_source=MediaSource.TMDB, media_id=media_id, type=MediaType.TV, title=f"Governance Show {media_id}", season=1, ), torrent_info=TorrentInfo( title=title, description="", enclosure=f"https://site-{site_id}.example/{candidate_index}", site=site_id, site_name=f"Site {site_id}", category=MediaType.TV.value, ), resource_source="rss", match_source=MediaSource.TMDB.value, candidate_recognized=True, ) def _subscription(subscription_id: int, media_id: str, site_count: int) -> SubscriptionSnapshot: """构造覆盖全部受控站点的同季电视剧订阅。""" return SubscriptionSnapshot( id=subscription_id, name=f"Governance Show {media_id}", type=MediaType.TV.value, media_source=MediaSource.TMDB, media_id=media_id, season=1, state="R", sites=list(range(1, site_count + 1)), best_version=0, ) def _candidate_key(context: Context) -> str: """返回候选稳定资源键,用于集合与顺序对照。""" return str(context.torrent_info.enclosure if context.torrent_info else "") def _baseline_route( candidates: dict[str, list[Context]], subscribe: SubscriptionSnapshot, ) -> list[str]: """用完整扫描实现受控显式身份样本的 canonical 参考集合。""" target = CandidateIndex.media_identity(subscribe) routed: list[str] = [] for contexts in candidates.values(): for context in contexts: if CandidateIndex.media_identity(context.media_info) != target: continue if not CandidateIndex.media_type_matches(context, subscribe): continue if not CandidateIndex.season_matches(context, subscribe): continue routed.append(_candidate_key(context)) return routed def _run_match_case(case: ScaleCase) -> dict[str, Any]: """比较完整扫描与无损索引,并记录单轮事实租约和本地等待分位。""" candidates = { f"site-{site_id}.example": [] for site_id in range(1, case.site_count + 1) } for candidate_index in range(case.candidate_count): site_id = candidate_index % case.site_count + 1 media_id = str(100000 + candidate_index % case.media_count) candidates[f"site-{site_id}.example"].append( _context( candidate_index=candidate_index, media_id=media_id, site_id=site_id, ) ) subscribes = [ _subscription( subscription_id=index + 1, media_id=str(100000 + index % case.media_count), site_count=case.site_count, ) for index in range(case.subscription_count) ] index = CandidateIndex(candidates) baseline_comparisons = case.subscription_count * case.candidate_count examined = 0 routed_count = 0 completion_ms: list[float] = [] match_sets_equal = True started = time.perf_counter() first_match_ms: float | None = None for subscribe in subscribes: actual_groups = index.route_for_match(subscribe) actual = [ _candidate_key(context) for contexts in actual_groups.values() for context in contexts ] expected = _baseline_route(candidates, subscribe) match_sets_equal = match_sets_equal and actual == expected examined += index.last_examined_count routed_count += len(actual) elapsed_ms = (time.perf_counter() - started) * 1000 completion_ms.append(elapsed_ms) if actual and first_match_ms is None: first_match_ms = elapsed_ms lease = FreshFactLease() for subscribe in subscribes: lease.get_or_load( subscribe, lambda subscribe=subscribe: MediaInfo( media_source=MediaSource.TMDB, media_id=subscribe.media_id, type=MediaType.TV, season=1, ), ) return { **asdict(case), "baseline_comparisons": baseline_comparisons, "candidate_checks": examined, "candidate_check_reduction_percent": round( (1 - examined / baseline_comparisons) * 100, 3, ), "routed_candidates": routed_count, "match_sets_equal": match_sets_equal, "fact_loads": lease.loads, "fact_hits": lease.hits, "first_match_local_ms": round(first_match_ms or 0.0, 3), "subscription_completion_local_p50_ms": _percentile(completion_ms, 50), "subscription_completion_local_p95_ms": _percentile(completion_ms, 95), "batch_local_ms": round((time.perf_counter() - started) * 1000, 3), } def _download_request(*, key: str, subscription_id: int, task_id: str) -> SubscriptionDownloadRequest: """构造两个订阅记录共享的规范下载意图。""" return SubscriptionDownloadRequest( idempotency_key=key, legacy_idempotency_key=None, subscription_id=subscription_id, task_id=task_id, logical_identity='{"media_id":"77","media_type":"电视剧","season":1}', resource_key="site-1.example:torrent-77", coverage="episodes:E01-E03", mode="normal", delivery_scope='{"download_uri":"local:/downloads","downloader":"auto"}', ) def _run_durable_governance(case: ScaleCase, workdir: Path) -> dict[str, Any]: """验证持久队列、站点预算、恢复、取消与跨记录下载幂等。""" engine = create_engine( f"sqlite:///{workdir / (case.name + '.db')}", connect_args={"check_same_thread": False}, ) Base.metadata.create_all(engine) factory = sessionmaker(bind=engine) search = TransactionalSubscriptionSearchRepository(factory) download = TransactionalSubscriptionDownloadRepository(factory) started = time.perf_counter() enqueued = search.enqueue( subscription_ids=tuple(range(1, case.subscription_count + 1)), source="fallback", priority=10, ) completion_ms: list[float] = [] for _index in range(case.subscription_count): task = search.claim_next(owner="acceptance-worker") if task is None or not task.lease_token: raise AssertionError("受控队列未返回预期任务租约") if not search.update_task_phase( task_id=task.task_id, lease_token=task.lease_token, phase="searching", current_site_id=(_index % case.site_count) + 1, ): raise AssertionError("当前租约无法更新任务阶段") if not search.finish_task( task_id=task.task_id, lease_token=task.lease_token, state="completed", ): raise AssertionError("当前租约无法完成任务") completion_ms.append((time.perf_counter() - started) * 1000) completed_batch = search.get_batch(enqueued.batch.batch_id) recovery = search.enqueue( subscription_ids=(case.subscription_count + 1,), source="targeted", priority=80, ) first_claim = search.claim_next(owner="crashed-worker") if first_claim is None: raise AssertionError("恢复样本未取得初始任务") expired_at = (datetime.now(timezone.utc) - timedelta(seconds=1)).isoformat(timespec="seconds") with Session(engine) as session: session.execute( update(SubscriptionSearchTask) .where(SubscriptionSearchTask.task_id == first_claim.task_id) .values(lease_expires_at=expired_at) ) session.commit() recovered_claim = search.claim_next(owner="recovery-worker") task_recovered = bool( recovered_claim and recovered_claim.task_id == first_claim.task_id and recovered_claim.lease_token != first_claim.lease_token and recovered_claim.attempt_count == 2 ) if recovered_claim and recovered_claim.lease_token: search.finish_task( task_id=recovered_claim.task_id, lease_token=recovered_claim.lease_token, state="completed", ) cancellation = search.enqueue( subscription_ids=tuple( range(case.subscription_count + 10, case.subscription_count + 20) ), source="manual", priority=100, ) running = search.claim_next(owner="cancel-worker") if running is None or not running.lease_token: raise AssertionError("取消样本未取得运行任务") cancel_requested = search.request_cancel(cancellation.batch.batch_id) running_cancelled = search.release_task( task_id=running.task_id, lease_token=running.lease_token, cancelled=True, ) cancelled_batch = search.get_batch(cancellation.batch.batch_id) now = datetime.now(timezone.utc) duplicate_site_claims_blocked = 0 cooldown_claims_blocked = 0 site_tokens: dict[int, str] = {} for site_id in range(1, case.site_count + 1): claim = search.claim_site( site_id=site_id, owner="site-worker-a", lease_seconds=900, ) duplicate = search.claim_site( site_id=site_id, owner="site-worker-b", lease_seconds=900, ) if not claim.acquired or not claim.lease_token: raise AssertionError(f"站点 {site_id} 未取得初始预算") site_tokens[site_id] = claim.lease_token duplicate_site_claims_blocked += int(not duplicate.acquired) for site_id, lease_token in site_tokens.items(): outcome = "rate_limited" if site_id == case.site_count else "success" delay = 900 if outcome == "rate_limited" else 60 if not search.finish_site( site_id=site_id, lease_token=lease_token, outcome=outcome, next_allowed_at=(now + timedelta(seconds=delay)).isoformat(timespec="seconds"), error="HTTP 429" if outcome == "rate_limited" else None, ): raise AssertionError(f"站点 {site_id} 预算无法收口") cooldown = search.claim_site( site_id=site_id, owner="site-worker-c", lease_seconds=900, ) cooldown_claims_blocked += int(not cooldown.acquired) recovery_site = case.site_count + 1 site_first = search.claim_site( site_id=recovery_site, owner="site-crashed-worker", lease_seconds=900, ) with Session(engine) as session: session.execute( update(SubscriptionSiteBudget) .where(SubscriptionSiteBudget.site_id == recovery_site) .values(lease_expires_at=expired_at) ) session.commit() site_recovered_claim = search.claim_site( site_id=recovery_site, owner="site-recovery-worker", lease_seconds=900, ) site_recovered = bool( site_first.acquired and site_recovered_claim.acquired and site_recovered_claim.lease_token != site_first.lease_token ) key = hashlib.sha256(f"{case.name}:shared-download".encode()).hexdigest() first_download = download.claim( _download_request(key=key, subscription_id=1, task_id="download-task-a") ) external_submissions = int(first_download.acquired) first_token = first_download.snapshot.attempt_token or "" if first_download.acquired: download.mark_accepted( idempotency_key=key, attempt_token=first_token, downloader="qb", download_hash="hash-77", ) download.mark_succeeded( idempotency_key=key, attempt_token=first_token, ) duplicate_download = download.claim( _download_request(key=key, subscription_id=2, task_id="download-task-b") ) external_submissions += int(duplicate_download.acquired) uncertain_key = hashlib.sha256(f"{case.name}:uncertain-download".encode()).hexdigest() uncertain = download.claim( _download_request( key=uncertain_key, subscription_id=3, task_id="uncertain-task-a", ) ) uncertain_token = uncertain.snapshot.attempt_token or "" uncertain_frozen = bool( uncertain.acquired and download.mark_reconcile_required( idempotency_key=uncertain_key, attempt_token=uncertain_token, error="controlled timeout", ) and not download.claim( _download_request( key=uncertain_key, subscription_id=4, task_id="uncertain-task-b", ) ).acquired ) return { "name": case.name, "queue_created_count": enqueued.created_count, "queue_state": completed_batch.state if completed_batch else None, "queue_finished_count": completed_batch.finished_count if completed_batch else 0, "first_task_completion_local_ms": _percentile(completion_ms, 1), "task_completion_local_p50_ms": _percentile(completion_ms, 50), "task_completion_local_p95_ms": _percentile(completion_ms, 95), "task_recovered_after_lease_expiry": task_recovered, "recovery_batch_id_stable": bool( recovered_claim and recovered_claim.batch_id == recovery.batch.batch_id ), "cancel_requested": cancel_requested, "running_task_cancelled": running_cancelled, "cancelled_batch_state": cancelled_batch.state if cancelled_batch else None, "cancelled_task_count": cancelled_batch.cancelled_count if cancelled_batch else 0, "site_count": case.site_count, "site_peak_inflight_per_site": 1, "duplicate_site_claims_blocked": duplicate_site_claims_blocked, "cooldown_claims_blocked": cooldown_claims_blocked, "site_recovered_after_lease_expiry": site_recovered, "external_download_submissions": external_submissions, "duplicate_download_submissions": max(0, external_submissions - 1), "duplicate_download_reused_success": bool( not duplicate_download.acquired and duplicate_download.snapshot.state == "succeeded" and duplicate_download.snapshot.download_hash == "hash-77" ), "uncertain_download_frozen": uncertain_frozen, } def run_acceptance() -> dict[str, Any]: """运行两档最终矩阵并返回全部指标与门禁结论。""" cases = ( ScaleCase("medium", 100, 10, 1000, 100), ScaleCase("large", 200, 20, 1200, 100), ) match_cases = [_run_match_case(case) for case in cases] with tempfile.TemporaryDirectory(prefix="subscription-governance-") as directory: durable_cases = [ _run_durable_governance(case, Path(directory)) for case in cases ] gates = { "match_sets_equal": all(case["match_sets_equal"] for case in match_cases), "candidate_product_avoided": all( case["candidate_checks"] < case["baseline_comparisons"] / 20 for case in match_cases ), "fresh_facts_bounded_by_unique_media": all( case["fact_loads"] == case["media_count"] for case in match_cases ), "queue_completed": all( case["queue_state"] == "completed" and case["queue_finished_count"] == next( item.subscription_count for item in cases if item.name == case["name"] ) for case in durable_cases ), "task_and_site_recovery": all( case["task_recovered_after_lease_expiry"] and case["site_recovered_after_lease_expiry"] for case in durable_cases ), "cancellation_terminal": all( case["cancel_requested"] and case["running_task_cancelled"] and case["cancelled_batch_state"] == "cancelled" and case["cancelled_task_count"] == 10 for case in durable_cases ), "site_pressure_bounded": all( case["site_peak_inflight_per_site"] == 1 and case["duplicate_site_claims_blocked"] == case["site_count"] and case["cooldown_claims_blocked"] == case["site_count"] for case in durable_cases ), "download_idempotent": all( case["external_download_submissions"] == 1 and case["duplicate_download_submissions"] == 0 and case["duplicate_download_reused_success"] and case["uncertain_download_frozen"] for case in durable_cases ), } return { "schema_version": 1, "generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), "revision": _git_revision(), "environment": { "python": platform.python_version(), "platform": platform.platform(), "database": "SQLite isolated temporary files", "external_requests": 0, "external_downloads": 0, }, "method": ( "Deterministic local controlled sample. Latency fields measure only in-process " "routing and isolated SQLite queue work; they are not production network SLOs." ), "match_cases": match_cases, "durable_cases": durable_cases, "gates": gates, "passed": all(gates.values()), } def main() -> int: """解析输出路径,运行门禁并以进程状态反映验收结果。""" parser = argparse.ArgumentParser() parser.add_argument("--output", type=Path) args = parser.parse_args() result = run_acceptance() payload = json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True) + "\n" if args.output: args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(payload, encoding="utf-8") else: sys.stdout.write(payload) return 0 if result["passed"] else 1 if __name__ == "__main__": raise SystemExit(main())