Есть 3 таблицы: Users, Subscriptions, UsersSubscriptions(join table). При добавлении новой записи в UsersSubscriptions нужно проверить число подписок пользователя, если оно превышает определённое значение, то не делать insert.
Псевдокод:
If(!usersSubscriptions.userCanSubscribe()) throw new Exception('Subscriptions exceeded!'); /** ( SELECT COUNT(*) FROM UsersSubscriptions WHERE userId = currentUserId ) < maxSubscriptions */
UsersSubscriptions.addRecord(); /** INSERT INTO UsersSubscriptions (subscriptionId, userId)
VALUES (currentSubscriptionId, urrentUserId); */
Race Conditions: Если поток №1 и поток №2 успешно прошли условие if (первую строчку кода), а в количество подписок пользователя на тот момент было, скажем, 4 из 5 возможных, то оба потока выполнят UsersSubscriptions.addRecord() и кол-во подписок пользователя станет больше положенного.
Как можно избежать этого ?
P.S.: в NoSQL базах данных, например, MongoDB, можно было бы не делать join table UsersSubscriptions, а просто в таблице пользователей разместить массив id подписок и в таблице подписок разместить массив id пользователей. Тогда в операцию update можно было бы добавить условие, что длина массива меньше maxSubscriptions, то есть что-то вроде
Users.updateOne( {_id: userId, $expr: { $lt: [ { $size: '$subscriptions'}, maxSubscriptions] } } }, updateObject );
И то же самое, только для Subscriptions. Но при таком подходе потребуется добавлять
транзакции, так как нужно модифицировать более 1-го документа.