Skip to content

Commit

Permalink
Fix data race when creating sessions
Browse files Browse the repository at this point in the history
Fixes the following exception:

    Traceback (most recent call last):
    File "/venv/lib64/python3.12/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
        self.dialect.do_execute(
    File "/venv/lib64/python3.12/site-packages/sqlalchemy/engine/default.py", line 941, in do_execute
        cursor.execute(statement, parameters)
    psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "pg_class_relname_nsp_index"
    DETAIL:  Key (relname, relnamespace)=(sessions_id_seq, 2200) already exists.
  • Loading branch information
hluk committed Jan 6, 2025
1 parent 11708fd commit 3e2e645
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 16 deletions.
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ WavierDB offers a HTTP REST API.
REST API
========

.. autoflask:: waiverdb.app:create_app(config_obj='waiverdb.config.DevelopmentConfig',create_session=False)
.. autoflask:: waiverdb.app:create_app(config_obj='waiverdb.config.DevelopmentConfig')
:undoc-static:
:order: path
4 changes: 2 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class EnabledMessagedConfig(config.Config):

@patch("waiverdb.app.event.listen")
def test_disabled_messaging_should_not_register_events(mock_listen):
app.create_app(DisabledMessagingConfig, create_session=False)
app.create_app(DisabledMessagingConfig)
calls = [
c for c in mock_listen.mock_calls if c == call(ANY, ANY, app.publish_new_waiver)
]
Expand All @@ -33,7 +33,7 @@ def test_disabled_messaging_should_not_register_events(mock_listen):

@patch("waiverdb.app.event.listen")
def test_enabled_messaging_should_register_events(mock_listen):
app.create_app(EnabledMessagedConfig, create_session=False)
app.create_app(EnabledMessagedConfig)
calls = [
c for c in mock_listen.mock_calls if c == call(ANY, ANY, app.publish_new_waiver)
]
Expand Down
23 changes: 16 additions & 7 deletions waiverdb/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
except ImportError:
from urlparse import urlparse, urlunsplit

import sqlalchemy
from flask import Flask, current_app, send_from_directory
from flask_cors import CORS
from flask_migrate import Migrate
from flask_pydantic.exceptions import ValidationError
from flask_session import Session
from flask_wtf.csrf import CSRFProtect
from sqlalchemy import event, text
from sqlalchemy.exc import ProgrammingError
import requests

Expand Down Expand Up @@ -90,7 +90,7 @@ def populate_db_config(app):


# applicaiton factory http://flask.pocoo.org/docs/0.12/patterns/appfactories/
def create_app(config_obj=None, create_session=True):
def create_app(config_obj=None):
app = Flask(__name__)
csrf.init_app(app)

Expand Down Expand Up @@ -120,9 +120,7 @@ def create_app(config_obj=None, create_session=True):
# initialize db
db.init_app(app)
# initialize session
app.config["SESSION_SQLALCHEMY"] = db
if create_session:
Session(app)
init_session(app)
# initialize tracing
with app.app_context():
init_tracing(app, db.engine)
Expand All @@ -146,6 +144,17 @@ def create_app(config_obj=None, create_session=True):
return app


def init_session(app):
app.config["SESSION_SQLALCHEMY"] = db
app.server_session = Session(app)
if app.config["SESSION_TYPE"] == "sqlalchemy":
with app.app_context():
inspect = sqlalchemy.inspect(db.engine)
table = app.config["SESSION_SQLALCHEMY_TABLE"]
if not inspect.has_table(table):
db.create_all()


def healthcheck():
"""
Request handler for performing an application-level health check. This is
Expand All @@ -155,7 +164,7 @@ def healthcheck():
Returns a 200 response if the application is alive and able to serve requests.
"""
try:
db.session.execute(text("SELECT 1 FROM waiver LIMIT 0")).fetchall()
db.session.execute(sqlalchemy.text("SELECT 1 FROM waiver LIMIT 0")).fetchall()
except ProgrammingError:
current_app.logger.exception('Healthcheck failed on DB query.')
raise RuntimeError('Unable to communicate with database.')
Expand All @@ -180,7 +189,7 @@ def register_event_handlers(app):
attached as the ``session`` attribute.
"""
if app.config['MESSAGE_BUS_PUBLISH']:
event.listen(db.session, 'after_commit', publish_new_waiver)
sqlalchemy.event.listen(db.session, 'after_commit', publish_new_waiver)


def favicon():
Expand Down
8 changes: 2 additions & 6 deletions waiverdb/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
import click
from flask.cli import FlaskGroup
from sqlalchemy.exc import OperationalError
from waiverdb.app import create_app
from waiverdb.models import db


def create_waiver_app():
from waiverdb.app import create_app # noqa: F401
return create_app(create_session=False)


@click.group(cls=FlaskGroup, create_app=create_waiver_app)
@click.group(cls=FlaskGroup, create_app=create_app)
def cli():
pass

Expand Down

0 comments on commit 3e2e645

Please sign in to comment.