How to make several operations as one transaction

I need to develop a clan system with the leaderboards. The system consists of:

  • Group
  • Leaderboard between participants of the group
  • Leaderboard between all participants of all groups (world and regional)
  • Leaderboard between all groups (world and regional)

So when some user sends create group request, we need to:

  • Create a group
  • Create a regional leaderboard between participants (if it doesn’t exist)
  • Create a regional leaderboard between groups (if it doesn’t exist)
  • Write player’s score to the leaderboards (nk.LeaderboardRecordsWrite * 4, or maybe can you offer some better solution for regional+world leaderboard? But I think this is another question.)

But if some operation fails I need to discard all previous operations and return an error to the user because the state of the system is broken in this case. How can I do that?

One more example is when a user leaves a group

  • remove user’s records from the players leaderboards
  • subtract user’s score from the unions leaderboards
  • if the user is the last participant - delete the group
    else if the user is an admin - give this role to some another participant of the group.
    else leave the group

If some operation fails - the state will be broken. How to avoid such situations?

1 Like

This thread doesn’t answer your question but gives a bit of context: Rollback mechanism for storage objects and wallet - #2 by sesposito.

We do not expose transactions through the APIs so there is no way to rollback the operations or make them atomical at the DB level through them, but some of the operations are done within transactions internally (some examples are given in the thread).

First of all, you should wrap any sequence of operations that should be grouped together into custom RPCs to ensure these won’t fail midway due to potential network or client issues, making them logical blocks that while do not guarantee atomicity, should be good enough.

What you could do to keep the system in a consistent state in case of an error, is to attempt to delete any previously created entities up to the point of the failure, and just ignore error checking to make them no-ops in case the entity does not exist (although this may be a bit different case-by-case).

Invoking Leaderboard/Tournament create APIs if they already exist are no-ops, so need to check that.

To remove the scores from the leaderboard after LeaveGroup is successful, I’d take the same approach of attempting to delete the records but ignoring any failures due to no records existing.

Hope this helps

1 Like

Thanks for your reply!

I’ve grouped the sequences together, but if the database is on a different node with the nakama server, we can’t avoid potential network problems or problems with the node (this is a rare case, but if it is possible - one day it might happen :)). In this case we can’t access the db to fix the state immediately, so we need to develop some system that will wait the db recovery and then fix the state. In the case with leaderboards and groups - it is not critical, we will only have wrong leaderboard records for some users. But I afraid to face with other cases in future, when we can’t just ignore errors.

So, I hoped to find some universal system tool for resolving such issues, but it sounds like we need to develop some recovery subsystem for each issue.