From 856e4c82d9b5ff724777e1bdcd9d40314f039d21 Mon Sep 17 00:00:00 2001 From: Rokas Maciulaitis Date: Mon, 18 Nov 2019 13:36:11 +0100 Subject: [PATCH 1/2] docs: adds section about DB session management * Closes #123 --- docs/index.rst | 1 + docs/sessions_management.rst | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 docs/sessions_management.rst diff --git a/docs/index.rst b/docs/index.rst index 82d78c7..0376bda 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,6 +20,7 @@ Invenio-DB. configuration usage alembic + sessions_management examplesapp diff --git a/docs/sessions_management.rst b/docs/sessions_management.rst new file mode 100644 index 0000000..ea4ae2c --- /dev/null +++ b/docs/sessions_management.rst @@ -0,0 +1,101 @@ +.. + This file is part of Invenio. + Copyright (C) 2019 CERN. + + Invenio is free software; you can redistribute it and/or modify it + under the terms of the MIT License; see LICENSE file for more details. + +Database session management +=========================== + +Invenio uses `SQLAlchemy Toolkit and Object Relational Mapper `_ +for all database related operations. + + +Transactions are tied to requests +--------------------------------- +In Invenio, a transaction is tied to a HTTP request. By default, the transaction is +automatically rolled back unless you explicitly call ``db.session.commit()``. + +Exceptions causes rollback +-------------------------- +If an exception occurs during request handling, then the entire transaction will be +rolled back unless there has been an explicit call to ``db.session.commit()`` +before the exception was raised. This is because the default behavior is to rollback. + +Why are transactions tied to requests? +-------------------------------------- +Transactions are tied to requests, because the outer view, in charge of handling the +request, needs full control of when a transaction is committed. If the view was not +in charge, you could end up with inconsistent data in the database - for instance +persistent identifier may have been committed, but the associated record was not committed. +That is why Invenio makes use of SQLAlchemy’s version counter feature to provide `optimistic +concurrency control (OCC) `_ +on the records table when the database transaction isolation level is below repeatable read +isolation level. + +When are SQL statements sent to the database server. +---------------------------------------------------- +SQLAlchemy only sends the SQL statements (INSERT, UPDATE, SELECT, …) to the database +server when needed, or when explicitly requested via e.g. ``db.session.flush()`` or +``db.session.commit()``. + +This means that in many cases, SQL insert and update statements are not sent to the +server until a commit is called. + +What about nested transactions? +------------------------------- +Nested transactions are using database save points, which allow you to do a partial +rollback. Also, nested transactions causes a flush to the database, meaning that +the SQL statements are sent to the server. + +When are partial rollbacks useful? +---------------------------------- +Partial rollbacks can be useful for instance if you want to try to insert a user, +but the user already exists in the table. Then you can rollback the insert and +instead execute an update statement at the application level. + +When is flushing useful? +------------------------ +Explicitly forcing the flush of SQL statements to the database can be useful +if you need a value from the database (e.g. auto-incrementing primary keys), +and the application needs the primary key to continue. Also, they can be +useful to force integrity constraints to be checked by the database, +which may be needed by the database. + +What happens with exceptions in nested transactions? +---------------------------------------------------- +If an exception occurs in a nested transaction, first the save point will be +rolled back, and afterwards the entire transaction will be rolled back unless +the exception is caught. + +For instance in the following code example, the entire transaction will be rolled backed: + +.. code:: python + + @app.route('/') + def index(): + # db operations 1 .... + with db.session.begin_nested(): + # db operations 2 .... + raise Exception() + db.session.commit() + +On the other, in the following example, the propagation of the exception is stopped, +and only the db 2 operations are rolled back, while db operations 1 are committed to +the database. + + +.. code:: python + + @app.route('/') + def index(): + # db operations 1 .... + try: + db.session.begin_nested(): + # db operations 2 .... + raise Exception() + db.session.commit() + except Exception: + db.session.rollback() + db.session.commit() From cb9ef8484e8b349e14a445a581d23b77936bd9cb Mon Sep 17 00:00:00 2001 From: Rokas Maciulaitis Date: Mon, 18 Nov 2019 14:07:13 +0100 Subject: [PATCH 2/2] setup: bumps SQLAlchemy version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0c9faef..6d2ffd7 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ 'Flask>=0.11.1', 'Flask-Alembic>=2.0.1', 'Flask-SQLAlchemy>=2.1', - 'SQLAlchemy>=1.0', + 'SQLAlchemy>=1.1.0', 'SQLAlchemy-Utils>=0.33.1', ]