File size: 1,455 Bytes
17a78b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import time
import logging
import psycopg2
from contextlib import contextmanager
from src.config import settings

logger = logging.getLogger("cashy.db")

NEON_RETRY_DELAY = 3  # seconds to wait for Neon cold start


def _connect():
    """Create a psycopg2 connection, retrying once on cold-start failures."""
    conn_kwargs = dict(
        host=settings.db_host,
        port=settings.db_port,
        dbname=settings.resolved_db_name,
        user=settings.db_user,
        password=settings.db_password,
    )
    if settings.db_sslmode:
        conn_kwargs["sslmode"] = settings.db_sslmode

    try:
        return psycopg2.connect(**conn_kwargs)
    except psycopg2.OperationalError:
        logger.info("DB connection failed -- retrying in %ds (Neon cold start?)", NEON_RETRY_DELAY)
        time.sleep(NEON_RETRY_DELAY)
        return psycopg2.connect(**conn_kwargs)


@contextmanager
def get_connection():
    """Context manager for database connections.

    Usage:
        with get_connection() as conn:
            with conn.cursor() as cur:
                cur.execute("SELECT ...")
                rows = cur.fetchall()
    """
    logger.debug("Opening DB connection")
    conn = _connect()
    try:
        yield conn
    except Exception:
        logger.warning("DB error -- rolling back")
        conn.rollback()
        raise
    else:
        conn.commit()
    finally:
        conn.close()
        logger.debug("DB connection closed")