File size: 2,669 Bytes
05f47b6
f63c4ff
 
05f47b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f63c4ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import time
import httpx
from pydantic_settings import BaseSettings
from supabase import create_client, Client


class Settings(BaseSettings):
    supabase_url: str
    supabase_key: str
    admin_api_key: str

    class Config:
        env_file = "../.env"


settings = Settings()

# Initialize Supabase client
supabase: Client = create_client(settings.supabase_url, settings.supabase_key)


# ──────────────────────────────────────────────────────────────────────────
#  Harden the PostgREST HTTP session
#  supabase-py enables HTTP/2 on its httpx session. Supabase's edge drops idle
#  keep-alive connections by sending an HTTP/2 GOAWAY, which the client sees as
#  `ConnectionTerminated error_code:0` on the next reused connection. Forcing
#  HTTP/1.1 removes that failure mode; a small retry-on-disconnect wrapper covers
#  the residual case of a keep-alive connection closed right before a request.
# ──────────────────────────────────────────────────────────────────────────
_RETRYABLE_ERRORS = (
    httpx.RemoteProtocolError,
    httpx.ConnectError,
    httpx.ConnectTimeout,
    httpx.ReadError,
    httpx.WriteError,
    httpx.PoolTimeout,
)


class _RetryingClient(httpx.Client):
    """httpx.Client that retries idempotent transient connection failures."""

    _MAX_RETRIES = 3

    def send(self, *args, **kwargs):
        last_exc = None
        for attempt in range(self._MAX_RETRIES):
            try:
                return super().send(*args, **kwargs)
            except _RETRYABLE_ERRORS as exc:
                last_exc = exc
                if attempt < self._MAX_RETRIES - 1:
                    time.sleep(0.2 * (attempt + 1))
        raise last_exc


def _harden_postgrest_session(client: Client) -> None:
    try:
        pg = client.postgrest
        old = pg.session
        # http2=False β†’ HTTP/1.1 only; retries handle connection-establishment errors
        transport = httpx.HTTPTransport(retries=2, http2=False)
        pg.session = _RetryingClient(
            base_url=old.base_url,
            headers=old.headers,
            timeout=old.timeout,
            transport=transport,
        )
        try:
            old.close()
        except Exception:
            pass
    except Exception:
        # Never let hardening break startup; fall back to the default session
        pass


_harden_postgrest_session(supabase)