- Tl;dr We probably want to keep some level of manual flushing, but there are a lot of flushes we can remove!
-
Currently, we create Hibernate Sessions with FlushMode.MANUAL (code pointer and code pointer). This mode delegates all flushing operations to us.
- Initially I thought about changing our Sessions to have FlushMode.COMMIT or FlushMode.AUTO, both of which automatically flush before each Transaction commit. However, this looks to generate flushes that contain no actual changes, which we want to avoid.
-
The methods of
GenericHibernateDaoexecute their queries withGenericHibernateDao::execute.exectuewraps each query in a Transaction.GenericHibernateDao::flushitself callsexecute. So each call todao.flush()that we remove will save us a Transaction.
-
We only want to Flush on queries that update the db. All such queries will go through
GenericHibernateDao::saveorGenericHibernateDao::delete.- These methods have a
flushparameter that controlls if we flush or not. - Of the Public methods of
GenericHibernateDaowhich callsaveanddelete, onlysaveMapObjectanddeleteMapObjectcall withflush=False.
- These methods have a
-
Therefore, we can immediatley make the following changes:
- Remove
dao.flush()calls wheredaois always aSQLiteDao: Example.SQLiteDao::flushis a noop. - Remove
dao.flush()calls after calls to methods ofGenericHibernateDaothat are notsaveMapObjectordeleteMapObject. These methods will callGenericHibernateDao::[save/delete]withflush=True, so there is no need for a manual flush afterward. Example. - Confirmed with Hibernate logging. In these cases we're doing a Trasaction containing a Flush that has no updates.
- Ex: 2022-09-19 12:53:21 AbstractFlushingEventListener [DEBUG] Flushed: 0 insertions, 0 updates, 0 deletions to 12 objects 2022-09-19 12:53:21 AbstractFlushingEventListener [DEBUG] Flushed: 0 (re)creations, 0 updates, 0 removals to 87 collections
- Remove
-
What to do with
GenericHibernateDao::[save/delete]MapObject?- Consider the case were
[save/delete]MapObjectis called once, followed bydao.flush(). Example.- We can save a Transaction here by making a version of
saveMapObjectthat callssavewithflush=True.
- We can save a Transaction here by making a version of
- The cases that are more interesting are where we perform several
[save/delete]MapObjectcalls, followed by a flush afterward, Example.- This allows us "queue up" many updates in the Hibernate persistence context, then flush them at once.
- I think this is good behavior to keep as it reduces the number of flushes.
- There are some places we can cut down on flushes here as well.
- Another way to deal with this would be providing a mechanism to put all of these calls within one transaction. However, that change would be more work and I'm not sure how much gain would come of it.
- Consider the case were
- These Job classes all behave similarly. They create a session with FlushMode.COMMIT, start a transaction, call some dao methods, and commit a transaction.
- However, each dao method will run its own transaction within
executeand performs a flush for the update methods that are called - So, we can probably change the FlushMode to MANUAL here.
- Furthermore, since each individual query runs its own transaction, do we need to wrap everything in an outer transaction?
- Keep the
flushparameter and behavior of theGenericHibernateDao::[save/delete]methods to keep the "queuing" behaviour ofGenericHibernateDao::[save/delete]MapObject - Go ahead with creating a PR removing unnecessary flushes for both the non-job and job code.
- To address the question on the card. I don't think the related objects case of Map and Account is relevant. We don't save maps to the UserAccount object and instead get all maps in the CollaborativeMapObject table tagged with the current UserAccount.
- We setup the Hibernate Sessions within Spring using Spring's OpenSessionInViewFilter. Docs
- Quoting the docs, Spring assumes that OpenSessionInViewFilter will
be used in combination with service layer transactions that care for the flushing: The active transaction manager will temporarily change the flush mode to FlushMode.AUTO during a read-write transaction, with the flush mode reset to FlushMode.MANUAL at the end of each transaction.- However as far as I can tell we do not use Spring's transaction management system at all.
- If using their system, we'd annotate methods that update the db (i.e.
GenericHibernateDao::[save/delete]) with@Transactional(read-only=False)and ottherGenericHibernateDaomethods with@Transactional(read-only=True). That would achieve the same behaviour we want to achieve of only flushing on updates. - I don't think we need to do this at all, but I came across it in my investigation.
- Quoting the docs, Spring assumes that OpenSessionInViewFilter will