Commit 0a675430 authored by Lars Kruse's avatar Lars Kruse
Browse files

Revert "refactor: use short-lived sessions when accessing the database"

The introduction of short-lived sessions caused issues with functions,
which return database objects.  Such objects are not accessible (due to
lazy loading), after the session is closed.

Instead we should either move the session scope the respective callers
or use a request-based session handling (e.g. the one provided by
flask-sqlalchemy).

This reverts commit 0b594e50 and 70ffd1a0.

See: #93
parent cfad2cf1
...@@ -138,8 +138,7 @@ class FallbackManager: ...@@ -138,8 +138,7 @@ class FallbackManager:
if fallback_type != self.state.get("previous_fallback_type"): if fallback_type != self.state.get("previous_fallback_type"):
timeslot = self.state["timeslot"] timeslot = self.state["timeslot"]
if timeslot: if timeslot:
with DB.Session() as session: DB.session.merge(timeslot)
session.merge(timeslot)
self.engine.event_dispatcher.on_fallback_active(timeslot, fallback_type) self.engine.event_dispatcher.on_fallback_active(timeslot, fallback_type)
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import contextlib
import sys import sys
import time import time
import logging import logging
...@@ -25,7 +24,6 @@ import datetime ...@@ -25,7 +24,6 @@ import datetime
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy
from sqlalchemy import BigInteger, Boolean, Column, DateTime, Integer, String, ForeignKey, ColumnDefault from sqlalchemy import BigInteger, Boolean, Column, DateTime, Integer, String, ForeignKey, ColumnDefault
from sqlalchemy.orm import scoped_session from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
...@@ -42,40 +40,14 @@ config = AuraConfig() ...@@ -42,40 +40,14 @@ config = AuraConfig()
engine = sa.create_engine(config.get_database_uri()) engine = sa.create_engine(config.get_database_uri())
Base = declarative_base() Base = declarative_base()
Base.metadata.bind = engine Base.metadata.bind = engine
__sqlalchemy_version = tuple(int(item) for item in sqlalchemy.__version__.split(".")[:2])
class DB(): class DB():
session_factory = sessionmaker(bind=engine) session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory) Session = scoped_session(session_factory)
session = Session()
Model = Base Model = Base
# Monkey-patch the above DB.Session generator for SQLAlchemy before v1.4.
# Such older versions of SQLAlchemy do not support contexts.
if __sqlalchemy_version < (1, 4):
@contextlib.contextmanager
def get_session_context():
""" provide a context for a session
This context is the same as the one provided by a "scoped_session" in SQLAlchemy v1.4 or
later.
see https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it
"""
session = scoped_session(DB.session_factory)
# Commented out because of https://gitlab.servus.at/aura/engine/-/issues/90
# try:
# yield session
# finally:
# session.close()
yield session
DB.Session = get_session_context
class AuraDatabaseModel(): class AuraDatabaseModel():
""" """
...@@ -97,32 +69,29 @@ class AuraDatabaseModel(): ...@@ -97,32 +69,29 @@ class AuraDatabaseModel():
""" """
Store to the database Store to the database
""" """
with DB.Session() as session: if add:
if add: DB.session.add(self)
session.add(self) else:
else: DB.session.merge(self)
session.merge(self) if commit:
if commit: DB.session.commit()
session.commit()
def delete(self, commit=False): def delete(self, commit=False):
""" """
Delete from the database Delete from the database
""" """
with DB.Session() as session: DB.session.delete(self)
session.delete(self) if commit:
if commit: DB.session.commit()
session.commit()
def refresh(self): def refresh(self):
""" """
Refreshes the correct record Refreshes the correct record
""" """
with DB.Session() as session: DB.session.expire(self)
session.expire(self) DB.session.refresh(self)
session.refresh(self)
def _asdict(self): def _asdict(self):
...@@ -163,8 +132,7 @@ class AuraDatabaseModel(): ...@@ -163,8 +132,7 @@ class AuraDatabaseModel():
""" """
Base.metadata.drop_all() Base.metadata.drop_all()
Base.metadata.create_all() Base.metadata.create_all()
with DB.Session() as session: DB.session.commit()
session.commit()
if systemexit: if systemexit:
sys.exit(0) sys.exit(0)
...@@ -197,27 +165,27 @@ class Timeslot(DB.Model, AuraDatabaseModel): ...@@ -197,27 +165,27 @@ class Timeslot(DB.Model, AuraDatabaseModel):
playlist = relationship("Playlist", playlist = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
default_schedule_playlist = relationship("Playlist", default_schedule_playlist = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.default_schedule_playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.default_schedule_playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
default_show_playlist = relationship("Playlist", default_show_playlist = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.default_show_playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.default_show_playlist_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
schedule_fallback = relationship("Playlist", schedule_fallback = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.schedule_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.schedule_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
show_fallback = relationship("Playlist", show_fallback = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.show_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.show_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
station_fallback = relationship("Playlist", station_fallback = relationship("Playlist",
primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \ primaryjoin="and_(Timeslot.timeslot_start==Playlist.timeslot_start, \
Timeslot.station_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)", Timeslot.station_fallback_id==Playlist.playlist_id, Timeslot.show_name==Playlist.show_name)",
uselist=False, back_populates="timeslot", lazy='subquery') uselist=False, back_populates="timeslot")
playlist_id = Column(Integer) playlist_id = Column(Integer)
default_schedule_playlist_id = Column(Integer) default_schedule_playlist_id = Column(Integer)
...@@ -255,12 +223,7 @@ class Timeslot(DB.Model, AuraDatabaseModel): ...@@ -255,12 +223,7 @@ class Timeslot(DB.Model, AuraDatabaseModel):
Args: Args:
date_time (datetime): date and time when the timeslot starts date_time (datetime): date and time when the timeslot starts
""" """
with DB.Session() as session: return DB.session.query(Timeslot).filter(Timeslot.timeslot_start == date_time).first()
return (
session.query(Timeslot)
.filter(Timeslot.timeslot_start == date_time)
.first()
)
@staticmethod @staticmethod
...@@ -275,13 +238,10 @@ class Timeslot(DB.Model, AuraDatabaseModel): ...@@ -275,13 +238,10 @@ class Timeslot(DB.Model, AuraDatabaseModel):
Returns: Returns:
([Timeslot]): List of timeslots ([Timeslot]): List of timeslots
""" """
with DB.Session() as session: timeslots = DB.session.query(Timeslot).\
return ( filter(Timeslot.timeslot_start >= date_from).\
session.query(Timeslot) order_by(Timeslot.timeslot_start).all()
.filter(Timeslot.timeslot_start >= date_from) return timeslots
.order_by(Timeslot.timeslot_start)
.all()
)
def set_active_entry(self, entry): def set_active_entry(self, entry):
...@@ -381,8 +341,8 @@ class Playlist(DB.Model, AuraDatabaseModel): ...@@ -381,8 +341,8 @@ class Playlist(DB.Model, AuraDatabaseModel):
timeslot_start = Column(DateTime, ForeignKey("timeslot.timeslot_start")) timeslot_start = Column(DateTime, ForeignKey("timeslot.timeslot_start"))
# Relationships # Relationships
timeslot = relationship("Timeslot", uselist=False, back_populates="playlist", lazy='subquery') timeslot = relationship("Timeslot", uselist=False, back_populates="playlist")
entries = relationship("PlaylistEntry", back_populates="playlist", lazy='subquery') entries = relationship("PlaylistEntry", back_populates="playlist")
# Data # Data
playlist_id = Column(Integer, autoincrement=False) playlist_id = Column(Integer, autoincrement=False)
...@@ -395,8 +355,7 @@ class Playlist(DB.Model, AuraDatabaseModel): ...@@ -395,8 +355,7 @@ class Playlist(DB.Model, AuraDatabaseModel):
# """ # """
# Fetches all entries # Fetches all entries
# """ # """
# with DB.Session() as session: # all_entries = DB.session.query(Playlist).filter(Playlist.fallback_type == 0).all()
# all_entries = session.query(Playlist).filter(Playlist.fallback_type == 0).all()
# cnt = 0 # cnt = 0
# for entry in all_entries: # for entry in all_entries:
...@@ -422,12 +381,7 @@ class Playlist(DB.Model, AuraDatabaseModel): ...@@ -422,12 +381,7 @@ class Playlist(DB.Model, AuraDatabaseModel):
Exception: In case there a inconsistent database state, such es multiple playlists for given date/time. Exception: In case there a inconsistent database state, such es multiple playlists for given date/time.
""" """
playlist = None playlist = None
with DB.Session() as session: playlists = DB.session.query(Playlist).filter(Playlist.timeslot_start == start_date).all()
playlists = (
session.query(Playlist)
.filter(Playlist.timeslot_start == start_date)
.all()
)
for p in playlists: for p in playlists:
if p.playlist_id == playlist_id: if p.playlist_id == playlist_id:
...@@ -447,13 +401,7 @@ class Playlist(DB.Model, AuraDatabaseModel): ...@@ -447,13 +401,7 @@ class Playlist(DB.Model, AuraDatabaseModel):
Returns: Returns:
(Array<Playlist>): An array holding the playlists (Array<Playlist>): An array holding the playlists
""" """
with DB.Session() as session: return DB.session.query(Playlist).filter(Playlist.playlist_id == playlist_id).order_by(Playlist.timeslot_start).all()
return (
session.query(Playlist)
.filter(Playlist.playlist_id == playlist_id)
.order_by(Playlist.timeslot_start)
.all()
)
@staticmethod @staticmethod
...@@ -461,11 +409,10 @@ class Playlist(DB.Model, AuraDatabaseModel): ...@@ -461,11 +409,10 @@ class Playlist(DB.Model, AuraDatabaseModel):
""" """
Checks if the given is empty Checks if the given is empty
""" """
with DB.Session() as session: try:
try: return not DB.session.query(Playlist).one_or_none()
return not session.query(Playlist).one_or_none() except sa.orm.exc.MultipleResultsFound:
except sa.orm.exc.MultipleResultsFound: return False
return False
@hybrid_property @hybrid_property
...@@ -540,8 +487,8 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel): ...@@ -540,8 +487,8 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel):
artificial_playlist_id = Column(Integer, ForeignKey("playlist.artificial_id")) artificial_playlist_id = Column(Integer, ForeignKey("playlist.artificial_id"))
# Relationships # Relationships
playlist = relationship("Playlist", uselist=False, back_populates="entries", lazy='subquery') playlist = relationship("Playlist", uselist=False, back_populates="entries")
meta_data = relationship("PlaylistEntryMetaData", uselist=False, back_populates="entry", lazy='subquery') meta_data = relationship("PlaylistEntryMetaData", uselist=False, back_populates="entry")
# Data # Data
entry_num = Column(Integer) entry_num = Column(Integer)
...@@ -562,37 +509,26 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel): ...@@ -562,37 +509,26 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel):
""" """
Selects one entry identified by `playlist_id` and `entry_num`. Selects one entry identified by `playlist_id` and `entry_num`.
""" """
with DB.Session() as session: return DB.session.query(PlaylistEntry).filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id, PlaylistEntry.entry_num == entry_num).first()
return (
session.query(PlaylistEntry)
.filter(PlaylistEntry.entry_num == entry_num)
.filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id)
.first()
)
@staticmethod @staticmethod
def delete_entry(artificial_playlist_id, entry_num): def delete_entry(artificial_playlist_id, entry_num):
""" """
Deletes the playlist entry and associated metadata. Deletes the playlist entry and associated metadata.
""" """
with DB.Session() as session: entry = PlaylistEntry.select_playlistentry_for_playlist(artificial_playlist_id, entry_num)
entry = PlaylistEntry.select_playlistentry_for_playlist(artificial_playlist_id, entry_num) metadata = PlaylistEntryMetaData.select_metadata_for_entry(entry.artificial_id)
metadata = PlaylistEntryMetaData.select_metadata_for_entry(entry.artificial_id) metadata.delete()
metadata.delete() entry.delete()
entry.delete() DB.session.commit()
session.commit()
@staticmethod @staticmethod
def count_entries(artificial_playlist_id): def count_entries(artificial_playlist_id):
""" """
Returns the count of all entries. Returns the count of all entries.
""" """
with DB.Session() as session: result = DB.session.query(PlaylistEntry).filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id).count()
return ( return result
session.query(PlaylistEntry)
.filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id)
.count()
)
@hybrid_property @hybrid_property
def entry_end(self): def entry_end(self):
...@@ -688,7 +624,7 @@ class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel): ...@@ -688,7 +624,7 @@ class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel):
artificial_entry_id = Column(Integer, ForeignKey("playlist_entry.artificial_id")) artificial_entry_id = Column(Integer, ForeignKey("playlist_entry.artificial_id"))
# Relationships # Relationships
entry = relationship("PlaylistEntry", uselist=False, back_populates="meta_data", lazy='subquery') entry = relationship("PlaylistEntry", uselist=False, back_populates="meta_data")
# Data # Data
artist = Column(String(256)) artist = Column(String(256))
...@@ -697,13 +633,7 @@ class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel): ...@@ -697,13 +633,7 @@ class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel):
@staticmethod @staticmethod
def select_metadata_for_entry(artificial_playlistentry_id): def select_metadata_for_entry(artificial_playlistentry_id):
with DB.Session() as session: return DB.session.query(PlaylistEntryMetaData).filter(PlaylistEntryMetaData.artificial_entry_id == artificial_playlistentry_id).first()
return (
session.query(PlaylistEntryMetaData)
.filter(PlaylistEntryMetaData.artificial_entry_id == artificial_playlistentry_id)
.first()
)
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment