Add index on (user_id, shown) in notification table to optimize markNotificationsShown queries and prevent deadlocks. Implement transaction handling in markNotificationsShown method for atomic updates.

This commit is contained in:
Torsten Schulz (local)
2025-12-18 15:04:37 +01:00
parent 8e1e0968ae
commit 6a9b2b8d1d
2 changed files with 50 additions and 5 deletions

View File

@@ -0,0 +1,30 @@
"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
// Create index on (user_id, shown) to optimize markNotificationsShown queries
// This prevents deadlocks by allowing fast lookups and reducing lock contention
await queryInterface.sequelize.query(`
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'i'
AND c.relname = 'idx_notification_user_id_shown'
AND n.nspname = 'falukant_log'
) THEN
CREATE INDEX idx_notification_user_id_shown
ON falukant_log.notification (user_id, shown);
END IF;
END$$;
`);
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(`
DROP INDEX IF EXISTS falukant_log.idx_notification_user_id_shown;
`);
}
};

View File

@@ -4382,11 +4382,26 @@ class FalukantService extends BaseService {
async markNotificationsShown(hashedUserId) { async markNotificationsShown(hashedUserId) {
const user = await getFalukantUserOrFail(hashedUserId); const user = await getFalukantUserOrFail(hashedUserId);
// Use transaction to prevent deadlocks and ensure atomicity
const transaction = await sequelize.transaction({
isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
});
try {
const [count] = await Notification.update( const [count] = await Notification.update(
{ shown: true }, { shown: true },
{ where: { userId: user.id, shown: false } } {
where: { userId: user.id, shown: false },
transaction
}
); );
await transaction.commit();
return { updated: count }; return { updated: count };
} catch (error) {
await transaction.rollback();
throw error;
}
} }
async getPoliticalOfficeHolders(hashedUserId) { async getPoliticalOfficeHolders(hashedUserId) {