2

I am trying to figure out how to manage a users game state using akka.

The game state will be persisted to mysql and this cannot change because we have other services that require this.

Anything that happens in a game is considered an "event".

Then you I have "Levels" which someone can achieve. A level is achieved when you complete all the "events" associated with it.

So you have:

Level
 - event1  e.g. reach a point in the game
 - event2  e.g. pickup a sword
 - event3  e.g. defeat a monster

So in a game there are many levels, and 100's of events that are linked to levels.

So all "events" are sent via HTTP to my backend, and I save the event in the database.

I then have to load the users game profile in memory, and then re-calculate the Level's achieved since there was a new event that happened. Note: This calculation cannot be done at the database level because it is a little more complicated that I am writing here.

The problem I see is that if I use akka, I can't have multiple actors processing the events for the same user, because the data can become stale.

Just to be clear, so when a new event arrives, I have to load the game profile in memory, loop through the levels and see if any of them have been achieved, if they have, update the database

e.g. update levels set achieved=true where level_id = 123 and user_id=234

e.g. actor1 loads the profile (all the levels and events for this user) and then processes the new event that just arrived in the inbox. at the same time, actor2 loads the profile (same as actor1), and then processes the new event. When it persists the changes to mysql, the data will be out of sych.

If I was using threads, I would have to lock during the game profile calculation and persisting to the db.

How can I do this using Akka and be able to handle things in parallel, or is this scenerio not allow for it?

cool breeze
  • 4,461
  • 5
  • 38
  • 67

1 Answers1

5

Let's think how you would manage it without actors. So, in nutshell, you have the following problem scenario:

  1. two (or more) update requests arrive at the same time, both are going to modify the same data
  2. both requests read some stable data state, then update it each in its own manner and persist to the DB
  3. the modifications from the request which checked in first are lost, more precisely - overridden by the later request.

This is a classical problem. There are at least two classical solutions of it:

  1. Optimistic locking
  2. Pessimistic locking: it's usually achieved by applying Serializable isolation level for transactions.

It worth reading this answer with a nice comparison of both worlds.

As you're using Akka, you most probably want to prefer better concurrency and occasional failures, which are easy to recover. It goes on par with Akka motto let it crash.

So, you need to make the next steps:

  1. Add version column to your table(s). It can be numeric or string (with hash). Numeric is the simplest one.
  2. When you insert new record - initialize versions.
  3. When you update the record - check version value has not changed. So, here's your update strategy:
    1. Read record and its version.
    2. Update record in memory.
    3. Execute update query with criteria where rec_id=$id and version=$version.
    4. If updated records count is 1 - you're good. If 0 - throw OptimisticLockException or smth like this.
  4. Finally, it's time for Akka to do its job: come up with appropriate supervision strategy (I'd pick something like try again in 1 second). In actor's preRestart method return the update message back to the actor's mailbox (see Restart Hooks chapter in Akka docs).

With this strategy, even if two requests try to update the same record at a time, one of them will fail and will be immediately processed again.

Community
  • 1
  • 1
Roman
  • 64,384
  • 92
  • 238
  • 332