From 98cb0add37db3ad0f14fffd3c0bccf88056dc78d Mon Sep 17 00:00:00 2001 From: jakeross Date: Mon, 2 Mar 2026 14:02:39 -0700 Subject: [PATCH 1/2] feat: remove pg_cron dependency and related scheduling logic for improved flexibility --- ...a8b9c0_create_pygeoapi_supporting_views.py | 86 ------------------- core/initializers.py | 11 +-- db/initialization.py | 21 ++--- docker-compose.yml | 4 - docker/db/Dockerfile | 4 - 5 files changed, 9 insertions(+), 117 deletions(-) diff --git a/alembic/versions/d5e6f7a8b9c0_create_pygeoapi_supporting_views.py b/alembic/versions/d5e6f7a8b9c0_create_pygeoapi_supporting_views.py index 6aab7887..dbc9cafa 100644 --- a/alembic/versions/d5e6f7a8b9c0_create_pygeoapi_supporting_views.py +++ b/alembic/versions/d5e6f7a8b9c0_create_pygeoapi_supporting_views.py @@ -17,8 +17,6 @@ branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None REFRESH_FUNCTION_NAME = "refresh_pygeoapi_materialized_views" -REFRESH_JOB_NAME = "refresh_pygeoapi_matviews_nightly" -REFRESH_SCHEDULE = "0 3 * * *" THING_COLLECTIONS = [ ("water_wells", "water well"), @@ -231,68 +229,6 @@ def _create_refresh_function() -> str: """ -def _schedule_refresh_job() -> str: - return f""" - DO $do$ - BEGIN - BEGIN - -- Avoid direct SELECT on cron.job because managed Postgres - -- environments may deny access to the cron schema table. - PERFORM cron.unschedule('{REFRESH_JOB_NAME}'); - EXCEPTION - WHEN undefined_function THEN - NULL; - WHEN invalid_parameter_value THEN - NULL; - WHEN internal_error THEN - -- Some pg_cron builds raise internal_error when the named - -- job does not exist. Treat this as already-unscheduled. - NULL; - WHEN insufficient_privilege THEN - RAISE NOTICE - 'Skipping pg_cron unschedule for % due to insufficient privileges.', - '{REFRESH_JOB_NAME}'; - RETURN; - END; - - PERFORM cron.schedule( - '{REFRESH_JOB_NAME}', - '{REFRESH_SCHEDULE}', - $cmd$SELECT public.{REFRESH_FUNCTION_NAME}();$cmd$ - ); - EXCEPTION - WHEN insufficient_privilege THEN - RAISE NOTICE - 'Skipping pg_cron schedule for % due to insufficient privileges.', - '{REFRESH_JOB_NAME}'; - END - $do$; - """ - - -def _unschedule_refresh_job() -> str: - return f""" - DO $do$ - BEGIN - BEGIN - PERFORM cron.unschedule('{REFRESH_JOB_NAME}'); - EXCEPTION - WHEN undefined_function THEN - NULL; - WHEN invalid_parameter_value THEN - NULL; - WHEN internal_error THEN - NULL; - WHEN insufficient_privilege THEN - RAISE NOTICE - 'Skipping pg_cron unschedule for % due to insufficient privileges.', - '{REFRESH_JOB_NAME}'; - END; - END - $do$; - """ - - def upgrade() -> None: bind = op.get_bind() inspector = inspect(bind) @@ -307,16 +243,6 @@ def upgrade() -> None: f"tables are missing: {missing_tables_str}" ) - pg_cron_available = bind.execute( - text( - "SELECT EXISTS (" - "SELECT 1 FROM pg_available_extensions WHERE name = 'pg_cron'" - ")" - ) - ).scalar() - if pg_cron_available: - op.execute(text("CREATE EXTENSION IF NOT EXISTS pg_cron")) - for view_id, thing_type in THING_COLLECTIONS: safe_view_id = _safe_view_id(view_id) op.execute(text(f"DROP VIEW IF EXISTS ogc_{safe_view_id}")) @@ -360,21 +286,9 @@ def upgrade() -> None: _create_matview_indexes() op.execute(text(_create_refresh_function())) - if pg_cron_available: - op.execute(text(_schedule_refresh_job())) def downgrade() -> None: - bind = op.get_bind() - pg_cron_available = bind.execute( - text( - "SELECT EXISTS (" - "SELECT 1 FROM pg_available_extensions WHERE name = 'pg_cron'" - ")" - ) - ).scalar() - if pg_cron_available: - op.execute(text(_unschedule_refresh_job())) op.execute(text(f"DROP FUNCTION IF EXISTS public.{REFRESH_FUNCTION_NAME}()")) _drop_view_or_materialized_view("ogc_avg_tds_wells") _drop_view_or_materialized_view("ogc_latest_depth_to_water_wells") diff --git a/core/initializers.py b/core/initializers.py index c3a32d6f..ba932b9b 100644 --- a/core/initializers.py +++ b/core/initializers.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # =============================================================================== -from pathlib import Path import os +from pathlib import Path from fastapi_pagination import add_pagination from sqlalchemy import text, select @@ -66,15 +66,6 @@ def erase_and_rebuild_db(): session.execute(text("DROP SCHEMA public CASCADE")) session.execute(text("CREATE SCHEMA public")) session.execute(text("CREATE EXTENSION IF NOT EXISTS postgis")) - pg_cron_available = session.execute( - text( - "SELECT EXISTS (" - "SELECT 1 FROM pg_available_extensions WHERE name = 'pg_cron'" - ")" - ) - ).scalar() - if pg_cron_available: - session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_cron")) session.commit() Base.metadata.drop_all(session.bind) Base.metadata.create_all(session.bind) diff --git a/db/initialization.py b/db/initialization.py index d44853be..726eb716 100644 --- a/db/initialization.py +++ b/db/initialization.py @@ -10,7 +10,8 @@ from db import Base -APP_READ_GRANT_SQL = text(""" +APP_READ_GRANT_SQL = text( + """ DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_read') THEN @@ -19,7 +20,8 @@ EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app_read'; END IF; END $$; - """) + """ +) def _parse_app_read_members() -> list[str]: @@ -46,14 +48,16 @@ def grant_app_read_members(executor: Session | Connection | None) -> None: for member in members: safe_member = member.replace("'", "''") quoted = f'"{safe_member}"' - stmt = text(f""" + stmt = text( + f""" DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '{safe_member}') THEN EXECUTE 'GRANT app_read TO {quoted}'; END IF; END $$; - """) + """ + ) executor.execute(stmt) @@ -62,15 +66,6 @@ def recreate_public_schema(session: Session) -> None: session.execute(text("DROP SCHEMA public CASCADE")) session.execute(text("CREATE SCHEMA public")) session.execute(text("CREATE EXTENSION IF NOT EXISTS postgis")) - pg_cron_available = session.execute( - text( - "SELECT EXISTS (" - "SELECT 1 FROM pg_available_extensions WHERE name = 'pg_cron'" - ")" - ) - ).scalar() - if pg_cron_available: - session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_cron")) session.execute(APP_READ_GRANT_SQL) grant_app_read_members(session) session.commit() diff --git a/docker-compose.yml b/docker-compose.yml index 5b82575a..9eb88baf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,10 +6,6 @@ services: context: . dockerfile: ./docker/db/Dockerfile platform: linux/amd64 - command: > - postgres - -c shared_preload_libraries=pg_cron - -c cron.database_name=${POSTGRES_DB} environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} diff --git a/docker/db/Dockerfile b/docker/db/Dockerfile index 57f2f8ea..4a1fbd51 100644 --- a/docker/db/Dockerfile +++ b/docker/db/Dockerfile @@ -1,5 +1 @@ FROM postgis/postgis:17-3.5 - -RUN apt-get update \ - && apt-get install -y --no-install-recommends postgresql-17-cron \ - && rm -rf /var/lib/apt/lists/* From 0ba9e548e3f668847e875cf0b4cc0d93dd4faa88 Mon Sep 17 00:00:00 2001 From: jirhiker <2035568+jirhiker@users.noreply.github.com> Date: Mon, 2 Mar 2026 21:03:11 +0000 Subject: [PATCH 2/2] Formatting changes --- db/initialization.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/db/initialization.py b/db/initialization.py index 726eb716..262241fe 100644 --- a/db/initialization.py +++ b/db/initialization.py @@ -10,8 +10,7 @@ from db import Base -APP_READ_GRANT_SQL = text( - """ +APP_READ_GRANT_SQL = text(""" DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_read') THEN @@ -20,8 +19,7 @@ EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app_read'; END IF; END $$; - """ -) + """) def _parse_app_read_members() -> list[str]: @@ -48,16 +46,14 @@ def grant_app_read_members(executor: Session | Connection | None) -> None: for member in members: safe_member = member.replace("'", "''") quoted = f'"{safe_member}"' - stmt = text( - f""" + stmt = text(f""" DO $$ BEGIN IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '{safe_member}') THEN EXECUTE 'GRANT app_read TO {quoted}'; END IF; END $$; - """ - ) + """) executor.execute(stmt)