From: Sasa M. <sa...@us...> - 2004-08-31 07:20:28
|
Update of /cvsroot/jrobin/src/org/jrobin/core In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26888/org/jrobin/core Modified Files: RrdDbPool.java Log Message: major improvements, should run considerably faster Index: RrdDbPool.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/core/RrdDbPool.java,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** RrdDbPool.java 21 Jul 2004 08:27:40 -0000 1.10 --- RrdDbPool.java 31 Aug 2004 07:20:17 -0000 1.11 *************** *** 30,34 **** /** * Class to represent the pool of open RRD files.<p> ! * * To open already existing RRD file with JRobin, you have to create a * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path --- 30,34 ---- /** * Class to represent the pool of open RRD files.<p> ! * <p/> * To open already existing RRD file with JRobin, you have to create a * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path *************** *** 36,40 **** * especially with large RRD files with many datasources and * several long archives.<p> ! * * In a multithreaded environment you might probably need a reference to the * same RRD file from two different threads (RRD file updates are performed in --- 36,40 ---- * especially with large RRD files with many datasources and * several long archives.<p> ! * <p/> * In a multithreaded environment you might probably need a reference to the * same RRD file from two different threads (RRD file updates are performed in *************** *** 42,65 **** * the RrdDb construction process more efficient it might be convenient to open all * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p> ! * * How does it work? The typical usage scenario goes like this:<p> ! * * <pre> * // obtain instance to RrdDbPool object * RrdDbPool pool = RrdDbPool.getInstance(); ! * * // request a reference to RrdDb object * String path = "some_relative_or_absolute_path_to_any_RRD_file"; * RrdDb rrdDb = RrdDbPool.requestRrdDb(path); ! * * // reference obtained, do whatever you want with it... * ... * ... ! * * // once you don't need the reference, release it. * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool * pool.release(rrdDb); ! *</pre> ! * * It's that simple. When the reference is requested for * the first time, RrdDbPool will open the RRD file --- 42,65 ---- * the RrdDb construction process more efficient it might be convenient to open all * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p> ! * <p/> * How does it work? The typical usage scenario goes like this:<p> ! * <p/> * <pre> * // obtain instance to RrdDbPool object * RrdDbPool pool = RrdDbPool.getInstance(); ! * <p/> * // request a reference to RrdDb object * String path = "some_relative_or_absolute_path_to_any_RRD_file"; * RrdDb rrdDb = RrdDbPool.requestRrdDb(path); ! * <p/> * // reference obtained, do whatever you want with it... * ... * ... ! * <p/> * // once you don't need the reference, release it. * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool * pool.release(rrdDb); ! * </pre> ! * <p/> * It's that simple. When the reference is requested for * the first time, RrdDbPool will open the RRD file *************** *** 68,77 **** * reference will be returned, and its usage count will be increased by one. When the * reference is released its usage count will be decremented by one.<p> ! * * When the reference count drops to zero, RrdDbPool will not close the underlying * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'. * If someone request the same RRD file again (before it gets closed), the same * reference will be returned again.<p> ! * * RrdDbPool has a 'garbage collector' which runs in a separate * thread and gets activated only when the number of RRD files kept in the --- 68,77 ---- * reference will be returned, and its usage count will be increased by one. When the * reference is released its usage count will be decremented by one.<p> ! * <p/> * When the reference count drops to zero, RrdDbPool will not close the underlying * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'. * If someone request the same RRD file again (before it gets closed), the same * reference will be returned again.<p> ! * <p/> * RrdDbPool has a 'garbage collector' which runs in a separate * thread and gets activated only when the number of RRD files kept in the *************** *** 81,99 **** * RrdDbPool object keeps track of the time when each RRD file * becomes eligible for closing so that the oldest RRD file gets closed first.<p> ! * * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)} method to * change it at any time.<p> ! * * <b>WARNING:</b>Never use close() method on the reference returned from the pool. * When the reference is no longer needed, return it to the pool with the * {@link #release(RrdDb) release()} method.<p> ! * * However, you are not forced to use RrdDbPool methods to obtain RrdDb references * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class * offers serious performance improvement especially in complex applications with many * threads and many simultaneously open RRD files.<p> ! * * The pool is thread-safe.<p> ! * * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects * with {@link RrdBackend backends} different from default.<p> --- 81,99 ---- * RrdDbPool object keeps track of the time when each RRD file * becomes eligible for closing so that the oldest RRD file gets closed first.<p> ! * <p/> * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)} method to * change it at any time.<p> ! * <p/> * <b>WARNING:</b>Never use close() method on the reference returned from the pool. * When the reference is no longer needed, return it to the pool with the * {@link #release(RrdDb) release()} method.<p> ! * <p/> * However, you are not forced to use RrdDbPool methods to obtain RrdDb references * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class * offers serious performance improvement especially in complex applications with many * threads and many simultaneously open RRD files.<p> ! * <p/> * The pool is thread-safe.<p> ! * <p/> * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects * with {@link RrdBackend backends} different from default.<p> *************** *** 107,120 **** * which still does not force garbage collector (the process which closes RRD files) to run. */ ! public static final int INITIAL_CAPACITY = 100; ! private int capacity = INITIAL_CAPACITY; ! private Map rrdMap = new HashMap(); ! private List rrdGcList = new LinkedList(); private RrdBackendFactory factory; ! private int poolHitsCount, poolRequestsCount; /** * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM. * @return Instance to RrdDbPool object. */ --- 107,127 ---- * which still does not force garbage collector (the process which closes RRD files) to run. */ ! public static final int INITIAL_CAPACITY = 500; ! private int capacity = INITIAL_CAPACITY, maxUsedCapacity; ! /** ! * Constant to represent the priority of the background thread which closes excessive RRD files ! * which are no longer in use. ! */ ! public static final int GC_THREAD_PRIORITY = /** Thread.NORM_PRIORITY - */ 1; ! ! private HashMap rrdMap = new HashMap(INITIAL_CAPACITY); ! private LinkedHashMap rrdIdleMap = new LinkedHashMap(INITIAL_CAPACITY); private RrdBackendFactory factory; ! private int poolHitsCount = 0, poolRequestsCount = 0; /** * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM. + * * @return Instance to RrdDbPool object. */ *************** *** 128,135 **** --- 135,144 ---- private RrdDbPool() { + // just to satisfy the singleton pattern } private void startGarbageCollector() { Thread gcThread = new Thread(this); + gcThread.setPriority(GC_THREAD_PRIORITY); gcThread.setDaemon(true); gcThread.start(); *************** *** 143,167 **** * @param path Relative or absolute path to a RRD file. * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException { ! String keypath = getCanonicalPath(path); ! RrdDb rrdDbRequested; ! if (rrdMap.containsKey(keypath)) { // already open ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(keypath); ! reportUsage(rrdEntry); ! debug("EXISTING: " + rrdEntry.dump()); ! rrdDbRequested = rrdEntry.getRrdDb(); poolHitsCount++; } else { // not found, open it RrdDb rrdDb = new RrdDb(path, getFactory()); ! addRrdEntry(keypath, rrdDb); ! rrdDbRequested = rrdDb; } ! poolRequestsCount++; ! return rrdDbRequested; } --- 152,175 ---- * @param path Relative or absolute path to a RRD file. * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException { ! poolRequestsCount++; ! String canonicalPath = getCanonicalPath(path); ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(canonicalPath); ! if (rrdEntry != null) { // already open ! reportUsage(canonicalPath, rrdEntry); ! debug("CACHED: " + rrdEntry.dump()); poolHitsCount++; } else { // not found, open it RrdDb rrdDb = new RrdDb(path, getFactory()); ! rrdEntry = new RrdEntry(rrdDb); ! addRrdEntry(canonicalPath, rrdEntry); ! debug("ADDED: " + rrdEntry.dump()); } ! return rrdEntry.getRrdDb(); } *************** *** 170,186 **** * relative or absolute path, and its contents will be provided from the specified * XML file (RRDTool comaptible). ! * @param path Relative or absolute path to a new RRD file. * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible) * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(String path, String xmlPath) ! throws IOException, RrdException { ! String keypath = getCanonicalPath(path); ! prooveInactive(keypath); ! RrdDb rrdDb = new RrdDb(path, xmlPath, getFactory()); ! addRrdEntry(keypath, rrdDb); poolRequestsCount++; return rrdDb; } --- 178,197 ---- * relative or absolute path, and its contents will be provided from the specified * XML file (RRDTool comaptible). ! * ! * @param path Relative or absolute path to a new RRD file. * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible) * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(String path, String xmlPath) ! throws IOException, RrdException { poolRequestsCount++; + String canonicalPath = getCanonicalPath(path); + prooveInactive(canonicalPath); + RrdDb rrdDb = new RrdDb(path, xmlPath, getFactory()); + RrdEntry rrdEntry = new RrdEntry(rrdDb); + addRrdEntry(canonicalPath, rrdEntry); + debug("ADDED: " + rrdEntry.dump()); return rrdDb; } *************** *** 189,251 **** * Returns a reference to a new RRD file. The new file will be created based on the * definition contained in a RrdDef object. * @param rrdDef RRD definition object * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException { - String path = rrdDef.getPath(); - String keypath = getCanonicalPath(path); - prooveInactive(keypath); - RrdDb rrdDb = new RrdDb(rrdDef, getFactory()); - addRrdEntry(keypath, rrdDb); poolRequestsCount++; return rrdDb; } ! private void reportUsage(RrdEntry rrdEntry) { ! if(rrdEntry.reportUsage() == 1) { // must not be garbage collected ! rrdGcList.remove(rrdEntry); } } ! private void reportRelease(RrdEntry rrdEntry) { ! if(rrdEntry.reportRelease() == 0) { // ready to be garbage collected ! rrdGcList.add(rrdEntry); } } ! private void addRrdEntry(String keypath, RrdDb rrdDb) throws IOException { ! RrdEntry newEntry = new RrdEntry(rrdDb); ! reportUsage(newEntry); ! debug("NEW: " + newEntry.dump()); ! rrdMap.put(keypath, newEntry); // notify garbage collector notify(); } ! private void prooveInactive(String keypath) throws RrdException, IOException { ! if(rrdMap.containsKey(keypath)) { // already open, check if active (not released) ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(keypath); ! if(rrdEntry.isInUse()) { // not released, not allowed here throw new RrdException("VALIDATOR: Cannot create new RrdDb file. " + ! "File " + keypath + " already active in pool"); ! } ! else { // open but released... safe to close it debug("WILL BE RECREATED: " + rrdEntry.dump()); ! removeRrdEntry(rrdEntry); } } } ! private void removeRrdEntry(RrdEntry rrdEntry) throws IOException { rrdEntry.closeRrdDb(); ! rrdMap.values().remove(rrdEntry); ! rrdGcList.remove(rrdEntry); debug("REMOVED: " + rrdEntry.dump()); } --- 200,261 ---- * Returns a reference to a new RRD file. The new file will be created based on the * definition contained in a RrdDef object. + * * @param rrdDef RRD definition object * @return Reference to a RrdDb object (RRD file). ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException { poolRequestsCount++; + String canonicalPath = getCanonicalPath(rrdDef.getPath()); + prooveInactive(canonicalPath); + RrdDb rrdDb = new RrdDb(rrdDef, getFactory()); + RrdEntry rrdEntry = new RrdEntry(rrdDb); + addRrdEntry(canonicalPath, rrdEntry); + debug("ADDED: " + rrdEntry.dump()); return rrdDb; } ! private void reportUsage(String canonicalPath, RrdEntry rrdEntry) { ! if (rrdEntry.reportUsage() == 1) { // must not be garbage collected ! rrdIdleMap.remove(canonicalPath); } } ! private void reportRelease(String canonicalPath, RrdEntry rrdEntry) { ! if (rrdEntry.reportRelease() == 0) { // ready to be garbage collected ! rrdIdleMap.put(canonicalPath, rrdEntry); } } ! private void addRrdEntry(String canonicalPath, RrdEntry newRrdEntry) { ! rrdMap.put(canonicalPath, newRrdEntry); ! maxUsedCapacity = Math.max(rrdMap.size(), maxUsedCapacity); // notify garbage collector notify(); } ! private void prooveInactive(String canonicalPath) throws RrdException, IOException { ! if (rrdMap.containsKey(canonicalPath)) { // already open, check if active (not released) ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(canonicalPath); ! if (rrdEntry.isInUse()) { // not released, not allowed here throw new RrdException("VALIDATOR: Cannot create new RrdDb file. " + ! "File " + canonicalPath + " already active in pool"); ! } else { // open but released... safe to close it debug("WILL BE RECREATED: " + rrdEntry.dump()); ! removeRrdEntry(canonicalPath, rrdEntry); } } } ! private void removeRrdEntry(String canonicalPath, RrdEntry rrdEntry) throws IOException { rrdEntry.closeRrdDb(); ! rrdMap.remove(canonicalPath); ! rrdIdleMap.remove(canonicalPath); debug("REMOVED: " + rrdEntry.dump()); } *************** *** 256,279 **** * closing'. It will be eventually closed by the pool when the number of open RRD files * becomes too big. Most recently released files will be closed last. * @param rrdDb Reference to RRD file that is no longer needed. ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized void release(RrdDb rrdDb) throws IOException, RrdException { ! if(rrdDb == null) { // we don't want NullPointerException return; } ! if(rrdDb.isClosed()) { ! throw new RrdException("Cannot release: already closed"); } ! String keypath = rrdDb.getCanonicalPath(); ! if(rrdMap.containsKey(keypath)) { ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(keypath); ! reportRelease(rrdEntry); debug("RELEASED: " + rrdEntry.dump()); ! } ! else { ! throw new RrdException("RRD file " + keypath + " not in the pool"); } // notify garbage collector --- 266,289 ---- * closing'. It will be eventually closed by the pool when the number of open RRD files * becomes too big. Most recently released files will be closed last. + * * @param rrdDb Reference to RRD file that is no longer needed. ! * @throws IOException Thrown in case of I/O error. * @throws RrdException Thrown in case of JRobin specific error. */ public synchronized void release(RrdDb rrdDb) throws IOException, RrdException { ! if (rrdDb == null) { // we don't want NullPointerException return; } ! if (rrdDb.isClosed()) { ! throw new RrdException("File " + rrdDb.getPath() + " already closed"); } ! String canonicalPath = getCanonicalPath(rrdDb.getPath()); ! if (rrdMap.containsKey(canonicalPath)) { ! RrdEntry rrdEntry = (RrdEntry) rrdMap.get(canonicalPath); ! reportRelease(canonicalPath, rrdEntry); debug("RELEASED: " + rrdEntry.dump()); ! } else { ! throw new RrdException("RRD file " + rrdDb.getPath() + " not in the pool"); } // notify garbage collector *************** *** 290,317 **** public void run() { debug("GC: started"); ! synchronized (this) { ! for (; ;) { ! while (rrdMap.size() > capacity && rrdGcList.size() > 0) { try { ! RrdEntry oldestRrdEntry = (RrdEntry) rrdGcList.get(0); ! debug("GC: closing " + oldestRrdEntry.dump()); ! removeRrdEntry(oldestRrdEntry); } catch (IOException e) { e.printStackTrace(); } } ! ! try { ! debug("GC: waiting: " + ! rrdMap.size() + " open, " + ! rrdGcList.size() + " released, " + ! "capacity = " + capacity + ", " + ! "hits = " + poolHitsCount + ", " + ! "requests = " + poolRequestsCount); ! wait(); ! debug("GC: running"); ! } catch (InterruptedException e) { } } } } --- 300,328 ---- public void run() { debug("GC: started"); ! for (; ;) { ! synchronized (this) { ! if (rrdMap.size() > capacity && rrdIdleMap.size() > 0) { try { ! String canonicalPath = (String) rrdIdleMap.keySet().iterator().next(); ! RrdEntry rrdEntry = (RrdEntry) rrdIdleMap.get(canonicalPath); ! debug("GC: closing " + rrdEntry.dump()); ! removeRrdEntry(canonicalPath, rrdEntry); } catch (IOException e) { e.printStackTrace(); } } ! else { ! try { ! debug("GC: waiting: " + rrdMap.size() + " open, " + rrdIdleMap.size() + ! " released, " + "capacity = " + capacity + ", " + "hits = " + ! poolHitsCount + ", " + "requests = " + poolRequestsCount); ! wait(); ! debug("GC: running"); ! } catch (InterruptedException e) { ! e.printStackTrace(); ! } } } + Thread.yield(); } } *************** *** 323,336 **** /** * Clears the internal state of the pool entirely. All open RRD files are closed. * @throws IOException Thrown in case of I/O related error. */ public synchronized void reset() throws IOException { Iterator it = rrdMap.values().iterator(); ! while(it.hasNext()) { ! RrdEntry rrdEntry = (RrdEntry) it.next(); rrdEntry.closeRrdDb(); } rrdMap.clear(); ! rrdGcList.clear(); debug("Nothing left in the pool"); } --- 334,348 ---- /** * Clears the internal state of the pool entirely. All open RRD files are closed. + * * @throws IOException Thrown in case of I/O related error. */ public synchronized void reset() throws IOException { Iterator it = rrdMap.values().iterator(); ! while (it.hasNext()) { ! RrdEntry rrdEntry = (RrdEntry) it.next(); rrdEntry.closeRrdDb(); } rrdMap.clear(); ! rrdIdleMap.clear(); debug("Nothing left in the pool"); } *************** *** 341,345 **** private static void debug(String msg) { ! if(DEBUG) { System.out.println("POOL: " + msg); } --- 353,357 ---- private static void debug(String msg) { ! if (DEBUG) { System.out.println("POOL: " + msg); } *************** *** 348,362 **** /** * Returns the internal state of the pool. Useful for debugging purposes. * @return Internal pool state (list of open RRD files, with the number of usages for ! * each one). * @throws IOException Thrown in case of I/O error. */ public synchronized String dump() throws IOException { StringBuffer buff = new StringBuffer(); Iterator it = rrdMap.values().iterator(); ! while(it.hasNext()) { ! RrdEntry rrdEntry = (RrdEntry) it.next(); ! buff.append(rrdEntry.dump()); ! buff.append("\n"); } return buff.toString(); --- 360,378 ---- /** * Returns the internal state of the pool. Useful for debugging purposes. + * * @return Internal pool state (list of open RRD files, with the number of usages for ! * each one). * @throws IOException Thrown in case of I/O error. */ public synchronized String dump() throws IOException { StringBuffer buff = new StringBuffer(); + buff.append("POOL DUMP: " + rrdMap.size() + " open, " + rrdIdleMap.size() + " released\n"); + buff.append("capacity=" + capacity + ", " + "maxUsedCapacity=" + maxUsedCapacity + "\n"); + buff.append("hits=" + poolHitsCount + ", " + "requests=" + poolRequestsCount + "\n"); + buff.append("efficiency=" + getPoolEfficency() + "\n-------\n"); Iterator it = rrdMap.values().iterator(); ! while (it.hasNext()) { ! RrdEntry rrdEntry = (RrdEntry) it.next(); ! buff.append(rrdEntry.dump() + "\n"); } return buff.toString(); *************** *** 364,367 **** --- 380,398 ---- /** + * Returns paths to all open files currently held in the pool. + * @return An array containing open file paths. + */ + public synchronized String[] getCachedFilePaths() { + Set keySet = rrdMap.keySet(); + int n = keySet.size(), i = 0; + String[] files = new String[n]; + Iterator it = keySet.iterator(); + while(it.hasNext()) { + files[i++] = (String) it.next(); + } + return files; + } + + /** * Returns maximum number of internally open RRD files * which still does not force garbage collector to run. *************** *** 385,391 **** private RrdBackendFactory getFactory() throws RrdException { ! if(factory == null) { factory = RrdBackendFactory.getDefaultFactory(); ! if(!(factory instanceof RrdFileBackendFactory)) { factory = null; throw new RrdException( --- 416,422 ---- private RrdBackendFactory getFactory() throws RrdException { ! if (factory == null) { factory = RrdBackendFactory.getDefaultFactory(); ! if (!(factory instanceof RrdFileBackendFactory)) { factory = null; throw new RrdException( *************** *** 398,402 **** private class RrdEntry { private RrdDb rrdDb; ! private int usageCount; public RrdEntry(RrdDb rrdDb) { --- 429,433 ---- private class RrdEntry { private RrdDb rrdDb; ! private int usageCount = 1; public RrdEntry(RrdDb rrdDb) { *************** *** 427,432 **** String dump() throws IOException { ! String keypath = rrdDb.getCanonicalPath(); ! return keypath + " [" + usageCount + "]"; } } --- 458,463 ---- String dump() throws IOException { ! String canonicalPath = rrdDb.getCanonicalPath(); ! return canonicalPath + " [" + usageCount + "]"; } } *************** *** 436,444 **** * RrdDb requests served from the internal pool of open RRD files * with the number of total RrdDb requests. * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst). If no RrdDb reference ! * was ever requested, 1 would be returned. */ public synchronized double getPoolEfficency() { ! if(poolRequestsCount == 0) { return 1.0; } --- 467,476 ---- * RrdDb requests served from the internal pool of open RRD files * with the number of total RrdDb requests. + * * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst). If no RrdDb reference ! * was ever requested, 1 would be returned. */ public synchronized double getPoolEfficency() { ! if (poolRequestsCount == 0) { return 1.0; } *************** *** 450,453 **** --- 482,486 ---- /** * Returns the number of RRD requests served from the internal pool of open RRD files + * * @return The number of pool "hits". */ *************** *** 458,461 **** --- 491,495 ---- /** * Returns the total number of RRD requests successfully served by this pool. + * * @return Total number of RRD requests */ *************** *** 463,466 **** --- 497,509 ---- return poolRequestsCount; } + + /** + * Returns the maximum number of open RRD files over the lifetime + * of the pool. + * @return maximum number of open RRD files. + */ + public synchronized int getMaxUsedCapacity() { + return maxUsedCapacity; + } } |