diff --git a/Procfile b/Procfile index 6effc3d..9af4d6b 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app:app --worker-class gevent --workers=4 \ No newline at end of file +web: gunicorn app:app --workers=4 \ No newline at end of file diff --git a/db.py b/db.py index 3c4ebe8..708bb01 100644 --- a/db.py +++ b/db.py @@ -1,7 +1,7 @@ import os -import psycopg2 -from psycopg2 import pool -from psycopg2.extras import RealDictCursor +import psycopg +from psycopg_pool import ConnectionPool +from psycopg.rows import dict_row from datetime import datetime from dateutil.relativedelta import relativedelta from urllib.parse import urlparse @@ -32,14 +32,11 @@ class DataBase(): raise Exception("No DATABASE_URL environment variable set") if DataBase._pool is None: - db_url = urlparse(os.environ['DATABASE_URL']) - DataBase._pool = pool.ThreadedConnectionPool( - 1, 20, # minconn, maxconn - database=db_url.path[1:], - user=db_url.username, - password=db_url.password, - host=db_url.hostname, - port=db_url.port + # Note: psycopg3 ConnectionPool takes a conninfo string directly, not parsed kwargs + DataBase._pool = ConnectionPool( + conninfo=os.environ['DATABASE_URL'], + min_size=1, + max_size=20 ) def getDB(self): @@ -54,7 +51,8 @@ class DataBase(): def execute(self, query, args=(), one=False, commit=False): conn = self.getDB() - cur = conn.cursor(cursor_factory=RealDictCursor) + cur = conn.cursor(row_factory=dict_row) + # Convert any custom placeholders from %s to standard %s format used by psycopg3 cur.execute(query, args) rv = None if cur.description is not None: diff --git a/requirements.txt b/requirements.txt index ec23831..7328354 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ Flask>=3.0.0 gunicorn>=21.2.0 -gevent>=23.9.1 Jinja2>=3.1.0 jinja-partials==0.1.1 -psycopg2-binary>=2.9.9 +psycopg[binary]>=3.0.0 +psycopg_pool>=3.2.0 flask-htmx>=0.4.0 python-dateutil==2.8.2 minify-html==0.10.3