start project

This commit is contained in:
Hemmat
2026-08-07 21:17:17 +03:30
commit ea1e1eead6
121 changed files with 8108 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
"""Concrete, checkable translation of the cPanel account ceiling (Chapter 03).
Single source of truth for the account's literal resource limits. Other
chapters should import BUDGET rather than re-hardcoding these numbers.
validate_budget_config() is called from create_app() so an out-of-budget
deployment fails loudly at startup instead of silently degrading under load
— this is the concrete mechanism behind Chapter 03's "flag, don't silently
accept" requirement.
"""
from __future__ import annotations
from dataclasses import dataclass
from flask import Flask
@dataclass(frozen=True)
class AccountBudget:
"""The hosting account's literal ceiling (Chapter 03).
iops/io_throughput are informational only here — Python config can't
enforce them directly; they constrain how Ch06/07 batch reads/writes.
"""
cpu_cores: int = 4
max_entry_processes: int = 60
memory_mb: int = 2048
iops: int = 1024
io_throughput_mb_s: int = 16
max_total_processes: int = 150
max_db_connections: int = 150
# Derived engineering targets from Chapter 03's budget table.
db_pool_size_min: int = 2
db_pool_size_max: int = 5
BUDGET = AccountBudget()
_DISALLOWED_CACHE_BACKENDS = {"redis", "rediscache", "memcached", "memcachedcache"}
def validate_budget_config(app: Flask) -> None:
"""Raise at startup if config violates a Chapter 03 rule.
Cheap checks only (string/int comparisons) since this runs on every
process boot — Passenger may recycle processes frequently (factor 9).
"""
# Validated as its own config key, not read out of
# SQLALCHEMY_ENGINE_OPTIONS — that dict is empty for SQLite (its pool
# classes reject pool_size/pool_recycle outright; see app/config.py's
# _engine_options_for), so the *intended* setting must be checked
# independently of whether the active engine actually consumes it.
pool_size = app.config.get("DB_POOL_SIZE")
if pool_size is None or not (BUDGET.db_pool_size_min <= pool_size <= BUDGET.db_pool_size_max):
raise RuntimeError(
f"DB_POOL_SIZE={pool_size} is outside the Chapter 03 budget "
f"({BUDGET.db_pool_size_min}-{BUDGET.db_pool_size_max} per process; "
f"{BUDGET.max_db_connections} total connections are shared across "
f"up to {BUDGET.max_entry_processes} entry processes)."
)
cache_type = str(app.config.get("CACHE_TYPE", "")).lower()
if any(name in cache_type for name in _DISALLOWED_CACHE_BACKENDS):
raise RuntimeError(
f"CACHE_TYPE={app.config.get('CACHE_TYPE')!r} assumes a backend "
"(Redis/Memcached) Chapter 03 says not to assume is available. "
"Use FileSystemCache or a dashboard_cache DB table (Ch06)."
)
batch_size = app.config.get("PARSE_BATCH_SIZE")
if not batch_size or batch_size <= 0:
raise RuntimeError(
"PARSE_BATCH_SIZE must be a positive integer — unbounded/whole-file "
"parsing per invocation violates the Chapter 03 memory budget."
)
max_upload_mb = app.config.get("UPLOAD_MAX_SIZE_MB")
if not max_upload_mb or max_upload_mb <= 0:
raise RuntimeError(
"UPLOAD_MAX_SIZE_MB must be a positive integer to bound disk/IOPS per upload."
)