Race Issues

Discussion in 'Spigot Plugin Development' started by JesseMcCullough, May 31, 2017.

  1. Overview
    I have a plugin that is getting a prefix, set by any player (custom per-player prefix) from a MySQL database. When the prefix is retrieved, it is then stored into a cache system. The prefix can be edited at anytime, which just refreshes the already-cached prefix. When the player leaves, the cached prefix is stored back into a MySQL database.

    Now, here's the problem - Server A and Server B:
    When Server A asks for the prefix of a player from the MySQL database, it retrieves the prefix, then stores it into the cache system. This happens on the AsyncPlayerPreLoginEvent (also tested on PlayerLoginEvent).

    At any given moment, the player can change the prefix.

    When a player leaves Server A to go to Server B, Server A starts to put its cache in the MySQL database. This happens on the PlayerQuitEvent.

    But, Server B starts to get the player's prefix from the MySQL database before Server A's PlayerQuitEvent fully executes, whereby leads Server B to get the old, unchanged version of a player's prefix from the outdated MySQL database.

    Server A to Server B relationship event order:
    1. AsyncPlayerPreLoginEvent (also tested on PlayerLoginEvent)
    2. -Any random prefix editing via command-
    3. *On leave* Server A's PlayerQuitEvent execution after Server B's full AsyncPlayerPreLoginEvent execution.

    Finally, the question:
    What should I do in order to let Server A fully execute its cache-to-database process, then have Server B do its thing? I thought about delaying the cache - which could be the only way, - but I want to try to avoid the possibility of a player sending a message in chat only to be met with disappointment by the realization of their prefix has not yet been updated on Server B.

    Any suggestions will be gladly appreciated. I have no attachments to my current code design, therefore I am more than willing to destroy each line of code with the hopes of rebuilding something more robust.
  2. I'd personally just broadcast a message from server A to any other known server (B in this case) "Hey I just set the player's prefix to this value, at this timestamp", and have each server figure out what to do with it.
    • If the player is unknown to the server, ignore the message (if it's going to load it later, it'll fetch the latest prefix anyway)
    • If the player is currently logging in, store the message and process it after the data has been fetched (see next point)
    • If the player has logged in, compare the last updated time of the prefix (which would have to be stored with the data), and change it if the broadcast from A contains a newer prefix.
    It will still be possible for the player to see their old prefix, albeit the time frame is small, and unlikely to occur. This won't be anything you can completely solve anyway, just patch it the best you can.

    The broadcasting of said message can be done through any means: own socket server, Redis, RabbitMQ, etc.
  3. ScarabCoder

    ScarabCoder Retired Resource Staff

    I'm fairly sure you've probably already considered this, but why not just set the prefix on the MySQL server every time it's changed ingame, rather than caching it? If it's performance, try multithreading/async.
  4. Are you in control of server switching? If so you can simply delay it until you've written to MySQL, right?

    So: player wants to switch servers -> save prefix async -> send them to other server

    If you don't have control of server switching, then this won't work out.
  5. Definitely an out-of-the-box approach. I like it. I'll run some tests and see if the outcome is desirable. Thank you!

    I'm a bit new to working with SQL and multithreading, yet I have decent knowledge of them both. Would updating the database async-ly every time the prefix changed be looked down upon, performance-wise?

    That would be a perfect fix. But I don't have the ability to control that flow without causing A) havoc B) player "lag" rage C) all of the above/to-the-left
  6. It doesn't ensure the next server reads it in time, f.e. a connection pool can be too busy handling other queries at that given moment, whereas the other server is just idling and fetching it immediately (thus getting some old values)
    • Agree Agree x 1
    • Informative Informative x 1