-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathendpoints.py
More file actions
90 lines (78 loc) · 3.03 KB
/
Copy pathendpoints.py
File metadata and controls
90 lines (78 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
from __future__ import annotations
import os
from oddish.config import Settings
# API containers are warm and long-lived (min_containers >= 1). Reuse pooled
# connections rather than opening a fresh one per request. pool_pre_ping=True,
# pool_recycle=300, and statement_cache_size=0 are already set in the engine
# for Supavisor transaction-mode compatibility.
#
# Connection budget is bounded by Supabase's two limits: the pooler's max
# *client* connections (3000 on the 4XL tier) and the transaction-mode *pool
# size* — the real Postgres backends behind it (~150-200, within the 480
# max_connections). pool_size + max_overflow is sized to API_CONCURRENCY_MAX so
# a fully-loaded container never has requests blocking on SQLAlchemy pool
# checkout (the prior 4-conn pool vs 8 inputs caused checkout waits that looked
# like DB latency under load).
#
# Client-connection budget (worst case):
# API: 64 containers × (pool_size 2 + max_overflow 1) = up to 192
# Workers: NullPool (worker/functions.py) → ~0 held during the long trial;
# only workers writing at a given instant + the
# <=MAX_WORKERS_PER_POLL claim burst consume client connections, so
# a 768-worker fleet stays far under the 3000 cap.
# Concurrent *execution* is gated by the ~150-200-backend transaction pool,
# not these client counts.
#
# API_CONCURRENCY_MAX was lowered 8->3 (with API_MAX_CONTAINERS raised 24->64)
# to bound the OOM blast radius (the memory hog itself is fixed in
# oddish.core.endpoints.list_tasks_core -- see modal_app.py). The pool is
# resized to match the new per-container concurrency so the budget above is
# unchanged (64 × 3 == 24 × 8 == 192 API client connections).
Settings.db_use_null_pool = False
Settings.db_pool_size = 2
Settings.db_pool_max_overflow = 1
import modal
from modal_app import (
API_BUFFER_CONTAINERS,
API_CONCURRENCY_MAX,
API_CONCURRENCY_TARGET,
API_CPU,
API_MAX_CONTAINERS,
API_MEMORY_MB,
API_MIN_CONTAINERS,
API_WEBHOOK_LABEL,
api_volumes,
app,
image,
runtime_secrets,
)
from api.app import create_app
from oddish.core.helpers import register_provider_teardown_delegate
async def _teardown_ec2_sandbox(external_id: str) -> bool:
function = modal.Function.from_name(
os.environ.get("MODAL_APP_NAME", "oddish"),
"teardown_ec2_sandbox",
environment_name=os.environ.get("MODAL_ENVIRONMENT") or None,
)
return bool(await function.remote.aio(external_id))
register_provider_teardown_delegate("ec2", _teardown_ec2_sandbox)
api = create_app()
@app.function(
image=image,
volumes=api_volumes,
secrets=runtime_secrets,
timeout=600,
cpu=API_CPU,
memory=API_MEMORY_MB,
min_containers=API_MIN_CONTAINERS,
buffer_containers=API_BUFFER_CONTAINERS,
max_containers=API_MAX_CONTAINERS,
)
@modal.concurrent(
target_inputs=API_CONCURRENCY_TARGET,
max_inputs=API_CONCURRENCY_MAX,
)
@modal.asgi_app(label=API_WEBHOOK_LABEL)
def api_app():
"""Single ASGI endpoint for all API routes."""
return api