User load / save concurrency problem

This describes how possible save / load concurrency and race condition problems are dealt with


Top

1 The overview of the system

To be able to talk about the save / load concurrency problem, we first need to briefly describe the system and the save / load operations.

When client connects to the server, and authenticates with credentials, the server executes select queries upon the database to load all the saved items, stats and data.
When client, which has already connected and authenticated disconnects from server, the server deletes all user item and saves data from database, and then inserts all the new data, which he has in memory.

2 Why delete and then insert

Allthough there are other, more profficient ways to do this, they are much more complicated, and not worth implementing, as they would increase the entire game code's complexity greatly.

Obviously, the drawback of this solution is, that if server crashes between the delete and insert operations, then the user data is irreversibly lost.
However, it is aspect which can be avoided by creating flawless server and take care of every single crash possibility. The only crashes that could endanger the server then are crashes from outside (process gets killed, earthquake which shuts power off and so on)

3 The security risk during user loading / saving

Let's start with some load and save schema:

In that case, all would be working, and well defined. However, this was the idealized case.
Let's imagine, that client will disconnect sooner than the data is loaded:

So obviously here, you get to undefined behavior. Moreover, because the queries sent to database can arrive in different order, so this can result in mixing select and (insert, delete).
On database side, it can result in (for example) following sequence:
Select, Delete, Select, Insert
and so on.
Surprisingly, even if you would implement some mutex mechanism in database (for example save there bit, that user is being loaded / saved right now), it is not sufficient, sice communication with database is done via network, and there is NOT guaranteed, when the query packet will arrive to database, and therefore it is IMPOSSIBLE to create working mutex on database side!

4 The solution

The solution is to create mutex on server side, which will get rid of all possible security issues related to the discussed topic. There are, however, few exceptions, discussed later.
Since nodeJS is single threaded event based, all we have to do is use something like mutex (aka critical section) for that one thread that we control.
The idea is to save username of player for whom data is being loaded / saved or login requested, and clear the flag once the operation was finished.

The situation is depicted by following sequence diagrams:

For loading:

For saving: