Async, ArrayLists, and SnakeYaml

Discussion in 'Plugin Development' started by bdubz4552, Mar 11, 2015.

Thread Status:
Not open for further replies.
  1. Offline


    So, I have been working on a new plugin feature, and with this new feature, there is an ArrayList of "FriendList" objects (These are simply containers for a UUID and an ArrayList of UUIDs, you can probably figure out how it works). Then there is also a file with all of these FriendList objects written to it for permanent storage. Now, when a player joins the server, the file is read, and if the player has a FriendList associated with them, it loads it. And if a player leaves, then the ArrayList containing all the FriendLists is checked, and if a match is found, it is removed from memory.

    This may sound all good, but my concern is, suppose we have a server of 120 people, all with FriendLists. How's it gonna do when its calling read/write/memory actions every single time someone joins or leaves? That's 120 objects to check, just when a player leaves, or in the case of a join, 120 YAML entries to check. And considering that players come and go all the time on large servers, that is going to be a LOT of read/write/memory operations over a little ArrayList. So, I have to think async might be a good idea...

    ... But is it going to be safe? The Bukkit wiki says Bukkit API should never be accessed, which is not a problem, I can make the UUIDs needed final local variables before the runnable is called, but it also says shared Collections should not be accessed in async. Slightly concerning, since ArrayList implements AbstractList, implements Collection. And I use SnakeYaml (comes with the API) for handling the storage file, too. I know the answer is likely going to be very subjective to what I am doing with my code, but I at least want to know if there is an absolute no, SnakeYaml in async will destroy everything or something like that.
  2. Offline


    You might want to take a look at java's HashMap. Using a Map instead of a list, with the uuid being the key and a list of uuids (or your friendlist object) being the value, you don't have to go through the whole list everytime you want to find the entry for a player.

    Regarding async usage of config loading/saving: you can do that, I think.

    And/or you might want to reduce the amount of file loads/saves, to happen periodically, for a bunch of players at once, and not everytime a player leaves. If the saved file isn't that large (for example if you give every player his own file) the performance impact from a single save/load shouldn't be too critical if you don't do it often.
  3. Offline


    @bdubz4552 First of all I'd like to introduce you to Maps, as these are exactly what you need. A Map allows you to associates keys to values, in your case, Players to FriendLists. It will also increase performance, if the player count is high.

    Secondly, the big question is: are you using 1 file for every player, or 1 separate file per player? I'm still thinking which one is the best, but here are a few arguments for each one:
    - How is the second option better? By making every file independent you can load/save/edit multiple files at the same time and load them dynamically. Here's what I mean by this. Imagine the following scenario: there are 100 players online and one quits; you do not longer need it's data and want to unload it. If you are using 1 big file, no ways to unload it; that would destroy everyone's data. However, if the data is spread out in multiple files, you can unload the said file without affecting the rest.
    - How is the first option better? Let's say you are using the second one. Imagine the following scenario: Bob is friends with Martin, and they are both logged in, until Bob logs out. Martin then removes Bob from his friends list. Since his data file is already loaded, you can simply remove Bob's UUID from the FriendsList. However, in order to remove Martin's UUID from Bob's FriendsList, you need to load Bob's file, edit it and save it, for just a minor change. If you were using the first option, you'd only have to remove the two entries, without worrying about loading/saving a file for almost nothing. ALSO, since it's not possible to unload the data when a player logs out, it's also not possible to load it when it logs in, since by that time it'll be already loaded. Therefore, you will almost never need to do I/O operations (except on startup and shutdown).
    - What I recommend: if you know what you're doing, go for option 2. The RAM is going to explode if you don't (un)load dynamically the data on a large server. It's more complicated, but it's worth it. If you're more of a beginner, sticking to option 1 is probably the best choice to make.

    Thirdly, reading and writing to memory will indeed be inevitable each time a player logs in/out. That said, if you know what you're doing, you can check, before saving, if a change has been made to the friends list since last time the player logged in. If not, then saving is not necessary.

    In any case I recommend you to go async for loading and saving the data. The only thing you MUST enforce, is that 1 thread at most can access the same data file at the same time. If you don't, you'll end up with some serious data corruption cases.

    Lastly, the only potential thread safety issue I can think of right now, is a loading/saving thread accessing your Map/ArrayList (if you choose to ignore my advice of above) or even someone's FriendsList while it is being modified by some code in the main Minecraft thread. How to fix that.. don't ask me, sorry, I don't know.

    Hope this helped ;D

    @blablubbabc I hate ninjas like you :mad:
  4. Offline


    @Totom3 @blablubbabc
    I think making a Map as to an ArrayList of objects may be useful, yes, but if my vague memory serves me right (damn you school, making me forget these things) I thought about a HashMap and for some reason decided against it. Will try it though. I do want to clear up a little it of ambiguity though. The ArrayList is in memory, and the storage file is not. The storage file is only given read operations on player join, and write operations when a user adds or removes a friend. The ArrayList is checked whenever an event where the question of "Is this player a friend of another" happens, when a friend is added or removed, or when a player leaves.
  5. Offline



    The FileConfiguration (and all of it's keys) are stored in memory backed by a HashMap. A HashMap is more or less the fastest general purpose structure, much faster than an ArrayList. File I/O operations liek you describe only happen when the config is loaded/saved (server start/shutdown).

    Also, SnakeYAML is not designed to be thread safe, and as such, should not be accessed from any other thread than the main thread of your server. Why do you have asynchronous tasks anyway? From what you have described, there's no reason I see to have them. Iterating over 120 entries in a list and performing a simple check on them is a relatively trivial operation, and I wouldn't bother with the time that takes unless you can prove that is an issue.

    I would say that you should have one file, as the size of your config will likely never get so great as to warrant anything else. I would load it at start up and create a HashMap that associates UUIDs to other data based on the values in config, that way the only comparisons and lookups that are done during normal operation are using a HashMap and they aren't doing String comparisons, which will give you lightening fast performance even with ~100,000 entries.
  6. Offline


    @mythbusterma I would have figured with 120 people online, running through a list be it in memory or a file every time someone joins or leaves (the nature of large servers like this is that people are joining and leaving very quickly) would be a bit taxing over time but if you insist, then I'll give it a go.
  7. Offline

    1Rogue Retired Staff

    Not really, you'd be surprised at the amount of work you can do in terms of nanoseconds.

    At any rate, as @mythbusterma pointed out Configurations are loaded into memory already. Look into storing player information either in a database or in separate files per-player.
Thread Status:
Not open for further replies.

Share This Page