billy1234567892 / inquisitor-mc

Automatically exported from code.google.com/p/inquisitor-mc
0 stars 0 forks source link

Do MySQL tasks in seperate thread #27

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
It averages a bit more than 50ms for the onplayerjoin event. When i have 10-20 
people joining at the same time this causes major lag. Since you are only 
saving data it would make sense to run it in a seperate thread.

Thanks,
Sam

Original issue reported on code.google.com by Sam...@samistine.com on 19 Oct 2014 at 4:11

GoogleCodeExporter commented 9 years ago
Inquisitor v2.28 Total: 430.113 s Pct: 0.29%

Pct Total Pct Tick    Total      Avg     PerTick  Count                         
 Event

  0.09%   149.58%   128.12 s   74.79 ms    0.0    1.7k        PlayerListenerImpl::onPlayerJoin(PlayerJoinEvent)
  0.08%   0.08%     119.83 s   0.04 ms     6.3    18,246.8k   PlayerListenerImpl::onPlayerMove(PlayerMoveEvent)
  0.08%   16.48%    119.43 s   8.24 ms     0.0    14.5k       Task: StatisticsManager$4(Single)
  0.03%   52.04%    44.39 s    26.02 ms    0.0    1.7k        PlayerListenerImpl::onPlayerQuit(PlayerQuitEvent)
  0.00%   43.45%    6.28 s     21.72 ms    0.0    0.3k        PlayerListenerImpl::onPlayerDeath(PlayerDeathEvent)

Original comment by Sam...@samistine.com on 20 Oct 2014 at 3:59

GoogleCodeExporter commented 9 years ago
This was originally designed as a multi-server plugin. Working with bungeecord 
and such. As a result, the design calls for anytime a user disconnects or 
connects that the inventory must be completely flushed to disk (on Quit) or 
read in and set (On Join) each time, ensuring that no inventory is lost and 
nothing gets out of sync.

The main writes to the SQL DB under any other circumstance happen on another 
thread.

Original comment by snarfatt...@gmail.com on 20 Oct 2014 at 7:09

GoogleCodeExporter commented 9 years ago
Is there anything i can do to improve my timings? I use a different database 
for each server. My mysql is run on a hdd, but i could run a seperate instance 
on an ssd.

Original comment by Sam...@samistine.com on 21 Oct 2014 at 3:51

GoogleCodeExporter commented 9 years ago
Im now using this method for login
    public static void onPlayerJoin(final Player player) {
        Global.plugin.getServer().getScheduler().runTaskAsynchronously(Global.plugin, new Runnable() {

            String puuidst = player.getUniqueId().toString();
            String pname = player.getName();
            Date date = new Date();
            String servername = Global.plugin.getServer().getServerName();

            public void run() {

                if (!isStatsPlayer(player))
                    return;
                if (ignoredPlayerJoins.contains(pname)) {
                    Utils.debug("ignored join for player '%s'", pname);
                    return;
                }
                Utils.debug("onPlayerJoin '%s'", pname);

                try {
                    //Very simply update statement that will allow players to add their UUIDs to the db during the
                    // conversion process, then once a name change happens it will update it based on the uuid matching.
                    PreparedStatement stmt = null;
                    StringBuilder sql = new StringBuilder();
                    sql.append("UPDATE ").append(DB.tableName(group.getName())).append(" SET `");
                    sql.append(group.getKeyName()).append("`=?, `uuid`=? WHERE `");
                    sql.append(group.getKeyName()).append("`=? OR `uuid`=?");
                    stmt = DB.prepare(sql.toString());
                    stmt.setString(1, pname);
                    stmt.setString(2, puuidst);
                    stmt.setString(3, pname);
                    stmt.setString(4, puuidst);
                    stmt.execute();

                    Statistics stats = group.getStatistics(pname);
                    stats.set("uuid", puuidst);
                    stats.incr("joins");
                    stats.set("lastJoin", date);
                    stats.set("sessionTime", 0);
                    stats.set("online", true);
                    if (!stats.isInDB())
                        stats.set("firstJoin", date);
                    String bedServer = stats.getString("bedServer");
                    if ((bedServer != null)
                            && bedServer.equals(servername))
                        bedOwners.add(pname);
                    playerStates.put(pname,
                            new PlayerState(stats.getFloat("totalTime")));

                    Utils.debug("totalTime: " + stats.getFloat("totalTime"));

                    stats.flushSync();
                } catch (Exception ex) {
                    Utils.severe("OnPlayerJoin Exception message: " + ex.getMessage());
                    StringWriter sw = new StringWriter();
                    ex.printStackTrace(new PrintWriter(sw));
                    Utils.severe("Stack Trace: " + sw.toString());
                }

            }});

    }

Original comment by Sam...@samistine.com on 27 Oct 2014 at 12:40

GoogleCodeExporter commented 9 years ago
I think all the lag is being caused by     public void flushSync() {

Original comment by Sam...@samistine.com on 29 Oct 2014 at 6:09

GoogleCodeExporter commented 9 years ago
Okay hopefully im not destroying anything but this is what im currently doing 
on my servers:

I replaced all "stats.flushSync();" in PlayerStats too:
        Global.plugin.getServer().getScheduler().runTaskAsynchronously(Global.plugin, new Runnable() {
            public void run() {
                stats.flushSync();
            }

And for the playerlogin i kept pretty much what i had abouve except i also 
added the stats.flushSync(); thingy

Advise me if im doing anything that can cause issues. So far none of 
Inquisitors Timings have gone above 1ms

Original comment by Sam...@samistine.com on 29 Oct 2014 at 6:25

GoogleCodeExporter commented 9 years ago
Under normal circumstances it's already running all the PlayerStats async. If 
you only have a single server, I don't see too much risk with what you are 
doing. Multiple servers you are going to see glitches, could create dupe bugs, 
etc.

Someone could log in, throw all their diamonds away, a second later inquisitor 
will give them back, pickup what they dropped. It's a very small window they've 
have to hit, and the server would have to be lagging pretty bad as well. Just 
be aware of the possibility.

Original comment by snarfatt...@gmail.com on 29 Oct 2014 at 12:51

GoogleCodeExporter commented 9 years ago
I am using different databases for each server. Can there still be dupe bugs? 
Also I don't know why but the stats.flushSync(); must not be running async in 
the original code if your saying it should be.

Original comment by Sam...@samistine.com on 29 Oct 2014 at 4:15

GoogleCodeExporter commented 9 years ago
I worry cause one of my server has had issues with plugins causing it to 
consistently run at 2tps, you could still log in an play but everything moved 
very slowly.

Original comment by Sam...@samistine.com on 29 Oct 2014 at 4:18

GoogleCodeExporter commented 9 years ago
I can understand why you are trying to reduce timings... I haven't spent too 
much time looking through what you've done, just noting some of the reasons why 
I've not made those async. Multi worlds with different inventory could cause 
some issues as well. I honestly haven't had much time to think through all the 
ramifications of your changes and if they are solid or not. A wee bit busy 
lately with work. 

With Bukkit and Spigot issues I've kinda put this project on the backburner 
until I have an idea of what's happening there. If Spigot 1.8 comes out and is 
stable, I'll jump back in to this and update it to fully support 1.8 with a 
bunch of other patches that are required. I will have some time to fully vet 
your suggestions here as well.

Original comment by snarfatt...@gmail.com on 29 Oct 2014 at 4:28

GoogleCodeExporter commented 9 years ago
Thank you, one more thing. Is there any reason why I can't just call 
stats.flushsync only when a player logs out?

Original comment by Sam...@samistine.com on 29 Oct 2014 at 8:00

GoogleCodeExporter commented 9 years ago
And how does inquisitor know if I'm only running one server?

Original comment by Sam...@samistine.com on 29 Oct 2014 at 8:00

GoogleCodeExporter commented 9 years ago
Also what does flushsync() do?

Original comment by Sam...@samistine.com on 29 Oct 2014 at 8:32

GoogleCodeExporter commented 9 years ago
Sorry for all my questions,
So far this is what i have done, http://pastebin.com/qedW6CW2
I am currently running that on multiple servers each with there own database.
Hopefully nothing goes wrong ;)

Original comment by Sam...@samistine.com on 29 Oct 2014 at 9:10