diff --git a/src/com/massivecraft/mcore/cmd/arg/ARColl.java b/src/com/massivecraft/mcore/cmd/arg/ARColl.java new file mode 100644 index 00000000..095efbfc --- /dev/null +++ b/src/com/massivecraft/mcore/cmd/arg/ARColl.java @@ -0,0 +1,40 @@ +package com.massivecraft.mcore.cmd.arg; + +import java.util.Collection; + +import org.bukkit.command.CommandSender; + +import com.massivecraft.mcore.store.Coll; + +public class ARColl extends ARAbstractSelect> +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static ARColl i = new ARColl(); + public static ARColl get() { return i; } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public String typename() + { + return "coll"; + } + + @Override + public Coll select(String arg, CommandSender sender) + { + return Coll.getMap().get(arg); + } + + @Override + public Collection altNames(CommandSender sender) + { + return Coll.getNames(); + } + +} diff --git a/src/com/massivecraft/mcore/mcorecmd/CmdMCoreMStoreStats.java b/src/com/massivecraft/mcore/mcorecmd/CmdMCoreMStoreStats.java index 3c87b6f7..ea38e453 100644 --- a/src/com/massivecraft/mcore/mcorecmd/CmdMCoreMStoreStats.java +++ b/src/com/massivecraft/mcore/mcorecmd/CmdMCoreMStoreStats.java @@ -1,11 +1,14 @@ package com.massivecraft.mcore.mcorecmd; import java.util.List; +import java.util.Map.Entry; import com.massivecraft.mcore.MCorePerm; +import com.massivecraft.mcore.cmd.arg.ARColl; import com.massivecraft.mcore.cmd.req.ReqHasPerm; import com.massivecraft.mcore.store.Coll; import com.massivecraft.mcore.store.ExamineThread; +import com.massivecraft.mcore.util.MUtil; import com.massivecraft.mcore.util.Txt; public class CmdMCoreMStoreStats extends MCoreCommand @@ -14,21 +17,70 @@ public class CmdMCoreMStoreStats extends MCoreCommand { super(aliases); + this.addOptionalArg("coll", Coll.TOTAL); + this.addRequirements(ReqHasPerm.get(MCorePerm.CMD_MCORE_MSTORE_STATS.node)); } @Override public void perform() { - msg(Txt.titleize("MStore Statistics")); + if (!this.argIsSet(0) || this.arg(0).equalsIgnoreCase(Coll.TOTAL)) + { + this.performTotal(); + } + else + { + Coll coll = this.arg(0, ARColl.get()); + if (coll == null) return; + this.performColl(coll); + } + } + + public void performTotal() + { + msg(Txt.titleize("MStore Total Statistics")); msg("Last Examine Duration: %dms", ExamineThread.get().getLastDurationMillis()); msg("== Coll | Sync Count In | Sync Count Out =="); - for (String name : Coll.getNames()) + for (Entry> entry : Coll.getMap().entrySet()) { - long in = Coll.getSyncCount(name, true); - long out = Coll.getSyncCount(name, false); + String name = entry.getKey(); + Coll coll = entry.getValue(); + long in = coll.getSyncCount(Coll.TOTAL, true); + long out = coll.getSyncCount(Coll.TOTAL, false); + msg("%s | %d | %d", name, in, out); } } + public void performColl(Coll coll) + { + msg(Txt.titleize("MStore "+coll.getName()+" Statistics")); + + msg("Entity Count: %d", coll.getIds().size()); + msg("Basename: %s", coll.getBasename()); + msg("Universe: %s", coll.getUniverse()); + msg("Entity Class: %s", coll.getEntityClass().getName()); + msg("Plugin: %s", coll.getPlugin().getDescription().getFullName()); + msg("Database: %s", coll.getDb().getName()); + msg("Driver: %s", coll.getDriver().getName()); + + int limit; + + msg("== Sync Count In =="); + limit = 30; + for (Entry entry : MUtil.entriesSortedByValues(coll.getSyncMap(true), false)) + { + if (limit-- == 0) break; + msg("%s %d", entry.getKey(), entry.getValue()); + } + + msg("== Sync Count Out =="); + limit = 30; + for (Entry entry : MUtil.entriesSortedByValues(coll.getSyncMap(false), false)) + { + if (limit-- == 0) break; + msg("%s %d", entry.getKey(), entry.getValue()); + } + } } diff --git a/src/com/massivecraft/mcore/store/Coll.java b/src/com/massivecraft/mcore/store/Coll.java index 2d04e49f..b479f8d0 100644 --- a/src/com/massivecraft/mcore/store/Coll.java +++ b/src/com/massivecraft/mcore/store/Coll.java @@ -4,15 +4,12 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.CopyOnWriteArrayList; import org.bukkit.plugin.Plugin; @@ -30,31 +27,18 @@ public class Coll implements CollInterface // GLOBAL REGISTRY // -------------------------------------------- // + public final static String TOTAL = "*total*"; + // All instances registered here are considered inited. - private static List> instances = new CopyOnWriteArrayList>(); - public static List> getInstances() { return instances; } + private static Map> name2instance = new ConcurrentSkipListMap>(NaturalOrderComparator.get()); - private static TreeSet names = new TreeSet(NaturalOrderComparator.get()); - public static TreeSet getNames() { return names; } + private static Map> umap = Collections.unmodifiableMap(name2instance); + private static Set unames = Collections.unmodifiableSet(name2instance.keySet()); + private static Collection> uinstances = Collections.unmodifiableCollection(name2instance.values()); - // Log database syncronization for display in the "/mcore mstore stats" command. - private static Map name2out = new TreeMap(String.CASE_INSENSITIVE_ORDER); - private static Map name2in = new TreeMap(String.CASE_INSENSITIVE_ORDER); - - public static long getSyncCount(String name, boolean in) - { - Long count = (in ? name2in.get(name) : name2out.get(name)); - if (count == null) return 0; - return count; - } - - public static void addSyncCount(String name, boolean in) - { - long count = getSyncCount(name, in); - count++; - Map map = (in ? name2in : name2out); - map.put(name, count); - } + public static Map> getMap() { return umap; } + public static Set getNames() { return unames; } + public static Collection> getInstances() { return uinstances; } // -------------------------------------------- // // WHAT DO WE HANDLE? @@ -364,6 +348,29 @@ public class Coll implements CollInterface this.lastDefault.remove(id); } + // Log database syncronization for display in the "/mcore mstore stats" command. + private Map id2out = new TreeMap(); + private Map id2in = new TreeMap(); + + public Map getSyncMap(boolean in) + { + return in ? this.id2in : this.id2out; + } + + public long getSyncCount(String id, boolean in) + { + Long count = this.getSyncMap(in).get(id); + if (count == null) return 0; + return count; + } + + public void addSyncCount(String id, boolean in) + { + long count = this.getSyncCount(id, in); + count++; + this.getSyncMap(in).put(id, count); + } + // -------------------------------------------- // // SYNC LOWLEVEL IO ACTIONS // -------------------------------------------- // @@ -542,21 +549,37 @@ public class Coll implements CollInterface { case LOCAL_ALTER: case LOCAL_ATTACH: - this.saveToRemote(id); - if (this.inited()) addSyncCount(this.getName(), false); + this.saveToRemote(id); + if (this.inited()) + { + this.addSyncCount(TOTAL, false); + this.addSyncCount(id, false); + } break; case LOCAL_DETACH: this.removeAtRemote(id); - if (this.inited()) addSyncCount(this.getName(), false); + if (this.inited()) + { + this.addSyncCount(TOTAL, false); + this.addSyncCount(id, false); + } break; case REMOTE_ALTER: case REMOTE_ATTACH: this.loadFromRemote(id); - if (this.inited()) addSyncCount(this.getName(), true); + if (this.inited()) + { + this.addSyncCount(TOTAL, true); + this.addSyncCount(id, true); + } break; case REMOTE_DETACH: this.removeAtLocal(id); - if (this.inited()) addSyncCount(this.getName(), true); + if (this.inited()) + { + this.addSyncCount(TOTAL, true); + this.addSyncCount(id, true); + } break; default: this.clearIdentifiedChanges(id); @@ -688,25 +711,27 @@ public class Coll implements CollInterface public void init() { if (this.inited()) return; + // TODO: Could this be more efficient by considering it's the first sync? this.syncAll(); - instances.add(this); - names.add(this.getName()); + + name2instance.put(this.getName(), this); } @Override public void deinit() { if (!this.inited()) return; + // TODO: Save outwards only? We may want to avoid loads at this stage... this.syncAll(); - instances.remove(this); - names.remove(this.getName()); + + name2instance.remove(this.getName()); } @Override public boolean inited() { - return instances.contains(this); + return name2instance.containsKey(this.getName()); } } diff --git a/src/com/massivecraft/mcore/store/CollInterface.java b/src/com/massivecraft/mcore/store/CollInterface.java index f516e558..d6ae72a1 100644 --- a/src/com/massivecraft/mcore/store/CollInterface.java +++ b/src/com/massivecraft/mcore/store/CollInterface.java @@ -107,6 +107,10 @@ public interface CollInterface public void clearSynclog(Object oid); */ + public Map getSyncMap(boolean in); + public long getSyncCount(String name, boolean in); + public void addSyncCount(String name, boolean in); + // -------------------------------------------- // // SYNC LOWLEVEL IO ACTIONS // -------------------------------------------- //