run post login activities in a new transaction to avoid database locks

This commit is contained in:
Athou
2023-05-27 19:29:44 +02:00
parent 45eb436b8f
commit f100f3f91a
2 changed files with 33 additions and 22 deletions

View File

@@ -15,39 +15,40 @@ public class UnitOfWork {
} }
public static <T> T call(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) { public static <T> T call(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) {
final Session session = sessionFactory.openSession();
if (ManagedSessionContext.hasBind(sessionFactory)) {
throw new IllegalStateException("Already in a unit of work!");
}
T t = null; T t = null;
try {
ManagedSessionContext.bind(session); boolean sessionAlreadyBound = ManagedSessionContext.hasBind(sessionFactory);
session.beginTransaction(); try (Session session = sessionFactory.openSession()) {
if (!sessionAlreadyBound) {
ManagedSessionContext.bind(session);
}
Transaction tx = session.beginTransaction();
try { try {
t = sessionRunner.runInSession(); t = sessionRunner.runInSession();
commitTransaction(session); commitTransaction(tx);
} catch (Exception e) { } catch (Exception e) {
rollbackTransaction(session); rollbackTransaction(tx);
UnitOfWork.<RuntimeException> rethrow(e); UnitOfWork.rethrow(e);
} }
} finally { } finally {
session.close(); if (!sessionAlreadyBound) {
ManagedSessionContext.unbind(sessionFactory); ManagedSessionContext.unbind(sessionFactory);
}
} }
return t; return t;
} }
private static void rollbackTransaction(Session session) { private static void rollbackTransaction(Transaction tx) {
final Transaction txn = session.getTransaction(); if (tx != null && tx.isActive()) {
if (txn != null && txn.isActive()) { tx.rollback();
txn.rollback();
} }
} }
private static void commitTransaction(Session session) { private static void commitTransaction(Transaction tx) {
final Transaction txn = session.getTransaction(); if (tx != null && tx.isActive()) {
if (txn != null && txn.isActive()) { tx.commit();
txn.commit();
} }
} }

View File

@@ -6,8 +6,10 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.SessionFactory;
import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.dao.UserDAO; import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.model.User; import com.commafeed.backend.model.User;
import com.commafeed.backend.service.FeedSubscriptionService; import com.commafeed.backend.service.FeedSubscriptionService;
@@ -20,6 +22,7 @@ public class PostLoginActivities {
private final UserDAO userDAO; private final UserDAO userDAO;
private final FeedSubscriptionService feedSubscriptionService; private final FeedSubscriptionService feedSubscriptionService;
private final SessionFactory sessionFactory;
private final CommaFeedConfiguration config; private final CommaFeedConfiguration config;
public void executeFor(User user) { public void executeFor(User user) {
@@ -27,19 +30,26 @@ public class PostLoginActivities {
Date now = new Date(); Date now = new Date();
boolean saveUser = false; boolean saveUser = false;
// only update lastLogin field every hour in order to not // only update lastLogin field every hour in order to not
// invalidate the cache every time someone logs in // invalidate the cache every time someone logs in
if (lastLogin == null || lastLogin.before(DateUtils.addHours(now, -1))) { if (lastLogin == null || lastLogin.before(DateUtils.addHours(now, -1))) {
user.setLastLogin(now); user.setLastLogin(now);
saveUser = true; saveUser = true;
} }
if (config.getApplicationSettings().getHeavyLoad() && user.shouldRefreshFeedsAt(now)) {
if (Boolean.TRUE.equals(config.getApplicationSettings().getHeavyLoad()) && user.shouldRefreshFeedsAt(now)) {
feedSubscriptionService.refreshAll(user); feedSubscriptionService.refreshAll(user);
user.setLastFullRefresh(now); user.setLastFullRefresh(now);
saveUser = true; saveUser = true;
} }
if (saveUser) { if (saveUser) {
userDAO.saveOrUpdate(user); // Post login activites are susceptible to run for any webservice call.
// We update the user in a new transaction to update the user immediately.
// If we didn't and the webservice call takes time, subsequent webservice calls would have to wait for the first call to
// finish even if they didn't use the same database tables, because they updated the user too.
UnitOfWork.run(sessionFactory, () -> userDAO.saveOrUpdate(user));
} }
} }