From ed1b8f239ee67b9af9899b2d4ebe35574e87de0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rajmund=20Hru=C5=A1ka?= <rajmund.hruska@cesnet.cz>
Date: Tue, 19 Oct 2021 16:57:21 +0200
Subject: [PATCH] Feature: Use SQLAlchemy from flask_sqlalchemy. (Redmine
 issue: #6205)

---
 lib/mentat/services/sqlstorage.py | 23 +++++++++++++++--------
 lib/mentat/services/whois.py      |  5 +----
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/lib/mentat/services/sqlstorage.py b/lib/mentat/services/sqlstorage.py
index 31a23155b..59a8d1981 100644
--- a/lib/mentat/services/sqlstorage.py
+++ b/lib/mentat/services/sqlstorage.py
@@ -10,7 +10,8 @@
 
 """
 Database storage abstraction layer. The current implementation is based on the
-awesome `SQLAlchemy <http://www.sqlalchemy.org/>`__ library.
+awesome `SQLAlchemy <http://www.sqlalchemy.org/>`__ library accessed by
+`flask_sqlalchemy <https://flask-sqlalchemy.palletsprojects.com/>`__.
 
 .. warning::
 
@@ -26,8 +27,9 @@ __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea
 
 
 import copy
-import sqlalchemy
-from sqlalchemy.orm import Query
+from flask import Flask
+import flask_sqlalchemy
+from sqlalchemy.exc import OperationalError
 
 #
 # Custom libraries
@@ -39,7 +41,7 @@ from mentat.datatype.sqldb import MODEL
 _MANAGER = None
 
 
-class RetryingQuery(Query):
+class RetryingQuery(flask_sqlalchemy.orm.Query):
     """
     An override of SQLAlchemy's Query class, allowing for recovery from a lost DB
     connection.
@@ -48,7 +50,7 @@ class RetryingQuery(Query):
         for _ in range(2):
             try:
                 return super()._execute_and_instances(querycontext)
-            except sqlalchemy.exc.OperationalError:
+            except OperationalError:
                 self.session.close()
                 continue
 
@@ -67,9 +69,14 @@ class StorageService:
 
         :param enginecfg: Connection arguments.
         """
-        self.dbengine     = sqlalchemy.engine_from_config(enginecfg, prefix = '')
-        self.sessionmaker = sqlalchemy.orm.sessionmaker(bind = self.dbengine)
-        self._session     = self.sessionmaker(query_cls = RetryingQuery)
+        self.app = Flask(__name__)
+        self.app.config['SQLALCHEMY_DATABASE_URI'] = enginecfg['url']
+        self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+
+        self.db = flask_sqlalchemy.SQLAlchemy(self.app)
+        self.dbengine = self.db.engine_from_config(enginecfg, prefix = '')
+        self.sessionmaker = flask_sqlalchemy.orm.sessionmaker(bind = self.dbengine)
+        self._session = self.sessionmaker(query_cls = RetryingQuery)
 
     def __del__(self):
         self.close()
diff --git a/lib/mentat/services/whois.py b/lib/mentat/services/whois.py
index 9fe799b7a..3a050ff07 100644
--- a/lib/mentat/services/whois.py
+++ b/lib/mentat/services/whois.py
@@ -270,10 +270,7 @@ class SqldbWhoisModule(WhoisModule):
             })
             networks.append(netw)
 
-        # Use rollback to close transaction, please see issue #4251 for details.
-        # In short: In SQLAlchemy commit immediatelly opens new transaction, which
-        # then keeps hanging in "transaction-idle" state.
-        storage.session.rollback()
+        storage.session.commit()
 
         # Let the parent implementation take care of loading internal lookup table.
         return super().setup(networks)
-- 
GitLab