简体   繁体   中英

Postgres design solution to user limiting in rooms (race condition)

I have a chatting application with rooms in which users can freely join/leave any time they want. Rooms are limited to 8 users at a single time.

For simplification purposes, I have this relationship right now:

User -> (Many to one) -> Room

I'm checking if the room is full by querying

SELECT COUNT(*) FROM users WHERE room_id = x

before inserting the user, which works in normal cases.

However, if everybody joins at the same time, then it gets to a race condition and the limit is bypassed. How should I tackle this issue? Is Postgres suitable for this kind of operation?

While not wishing to be unkind, the previous answer is far from optimal.

While you can do this...

LOCK TABLE users IN ACCESS EXCLUSIVE MODE;

This is clearly quite heavy handed and is preventing all updates to the users table whether related to room changes or not.

A lighter approach would be instead to lock just the data you care about.

-- Move user 456 from room 122 to room 123
BEGIN;

SELECT true FROM rooms WHERE id = 123 FOR UPDATE;
SELECT true FROM users WHERE id = 456 AND room_id = 122 FOR UPDATE;
-- If either of the above failed to return a row, the starting condition of your database is not what you thought. Rollback

SELECT count(*) FROM users WHERE room_id = 123;
-- check count looks ok

UPDATE users SET room_id = 123 WHERE id = 456;

COMMIT;

This will lock two crucial items:

  1. The new room being moved to.
  2. The user being moved between rooms.

Since you don't care about people being moved out of rooms, this should be enough.

If two separate connections try to move different people into the same room simultaneously, one will be forced to wait until the other transaction commits or rolls back and releases its locks. If two separate connections try to update the same person, the same will happen.

As far as the other answer is concerned

Of course during the period the table is locked, if some thread in your application is trying to write users, it will get back an error.

No, it won't. Not unless you hold the lock for long enough to time out or try to take your own lock and tell it not to wait.

I think that you should probably synchronise the access and the writing from your calling application

Relational databases have been expressly designed to handle concurrent access from multiple clients. It is one of the things they are inarguably good at. If you are already using a RDBMS and implementing your own concurrency control then you are either in an exceptional situation or aren't using your RDBMS properly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM