// $Id: AlternateFlush.java,v 1.4 2006-12-15 15:38:07 tigran Exp $ package diskCacheV111.hsmControl.flush.driver ; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import diskCacheV111.hsmControl.flush.HsmFlushControlCore; import diskCacheV111.hsmControl.flush.HsmFlushSchedulable; import diskCacheV111.pools.PoolCellInfo; import diskCacheV111.pools.PoolCostInfo; import diskCacheV111.pools.StorageClassFlushInfo; import dmg.cells.nucleus.CellAdapter; import dmg.util.CommandInterpreter; import org.dcache.util.Args; /** * @author Patrick Fuhrmann patrick.fuhrmann@desy.de * @version 0.0, Dec 03, 2005 * */ public class AlternateFlush implements HsmFlushSchedulable { private static final Logger _log = LoggerFactory.getLogger(AlternateFlush.class); private HsmFlushControlCore _core; private CommandInterpreter _interpreter; private String _mode = "auto" ; private double _percentageToFlush = 0.5 ; private int _countToFlush = 5 ; private int _flushAtOnce; /** * Our Pool class. Contains things we need to remember. * Is stored with setDriverHandle to avoid our own * bookkeeping. */ private class Pool implements HsmFlushControlCore.DriverHandle { private String name; private int flushCounter; private boolean modeReady; private long totalSpace; private long preciousSpace; private int preciousFileCount; private HsmFlushControlCore.Pool pool; private Pool( String name , HsmFlushControlCore.Pool pool ){ this.name = name ; this.pool = pool ; update() ; } public void update(){ PoolCellInfo cellInfo = pool.getCellInfo() ; if( cellInfo == null ) { return; } PoolCostInfo costInfo = cellInfo.getPoolCostInfo() ; PoolCostInfo.PoolSpaceInfo spaceInfo = costInfo.getSpaceInfo() ; totalSpace = spaceInfo.getTotalSpace() ; preciousSpace = spaceInfo.getPreciousSpace() ; preciousFileCount = countTotalPending() ; } private void flush(){ flushCounter += flushPool( pool ); } private boolean isFlushing(){ // return countStorageClassesFlushing(pool) > 0 ; return flushCounter > 0 ; } private int countTotalPending(){ return countTotalPendingPool( pool ) ; } public String toString(){ return name ; } } private static class StackEntry { private long waitingSince = System.currentTimeMillis(); private StackEntry( int state ){ this.state = state ; } private int state; } private class EngineStack { private Stack<StackEntry> _stack = new Stack<>() ; public void push( StackEntry entry ){ _stack.push(entry) ; } public StackEntry pop(){ return _stack.pop(); } public boolean isEmpty(){ return _stack.empty() ; } public int getCurrentState(){ return _stack.empty() ? -1 : (_stack.peek()).state ; } } private EngineStack _engineStack = new EngineStack() ; private int _status; private static final int QUERY_ALL_POOLS_IO_MODE = 1 ; public AlternateFlush( CellAdapter cell , HsmFlushControlCore core ){ _log.info("AlternateFlush started"); _core = core ; _interpreter = new CommandInterpreter( this ) ; } //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // // call backs from the flush manager. // @Override public void init(){ if(_evt) { _log.info("EVENT : Initiating ..."); } Args args = _core.getDriverArgs() ; // // printout what we got from our master // for( int i = 0 ; i < args.argc() ; i++ ){ _log.info(" args "+i+" : "+args.argv(i)) ; } for( int i = 0 ; i < args.optc() ; i++ ){ _log.info(" opts "+args.optv(i)+"="+args.getOpt(args.optv(i))) ; } for (Object pool : _core.getConfiguredPools()) { _log.info(" configured pool : " + pool.toString()); } // // reset the pool modes of all pools we are responsible for. // As a side effect we get the actual pool modes. // for (Object o : _core.getConfiguredPools()) { HsmFlushControlCore.Pool pool = (HsmFlushControlCore.Pool) o; pool.setDriverHandle(new Pool(pool.getName(), pool)); pool.setReadOnly(false); _log.info("init : setting readonly=false : " + pool.getName()); } } @Override public void propertiesUpdated( Map<String,Object> properties ){ if(_evt) { _log.info("EVENT : propertiesUpdated : " + properties); } Set<String> keys = new HashSet<>( properties.keySet() ) ; // // for all properties we support, try to change the values // accordingly. // for (String key : keys) { switch (key) { case "mode": { // // mode is ok, so try to change it. // Object obj = properties.get(key); if (obj != null) { String mode = obj.toString(); if (mode.equals("auto")) { _mode = "auto"; } else if (mode.equals("manual")) { _mode = "manual"; } // else{ // just don't do anything if the value is invalid // the requestor will get the unmodified retrun. // } } break; } case "flush.count": { Object obj = properties.get(key); if (obj != null) { try { int count = Integer.parseInt(obj.toString()); if (count < 1) { throw new IllegalArgumentException("Value for " + key + " not supported " + obj); } _countToFlush = count; } catch (Exception ee) { _log.warn("Exception while seting " + key + " " + ee); } } break; } case "flush.atonce": { Object obj = properties.get(key); if (obj != null) { try { int count = Integer.parseInt(obj.toString()); if (count < 1) { throw new IllegalArgumentException("Value for " + key + " not supported " + obj); } _flushAtOnce = count; } catch (Exception ee) { _log.warn("Exception while seting " + key + " " + ee); } } break; } case "flush.percentage": { Object obj = properties.get(key); if (obj != null) { try { double percent = Double.parseDouble(obj.toString()); if (percent < 0.0) { throw new IllegalArgumentException("Value for " + key + " not supported " + obj); } _percentageToFlush = percent; } catch (Exception ee) { _log.warn("Exception while seting " + key + " " + ee); } } break; } default: // // remove the key to inform the requestor that we don't // support this property. // properties.remove(key); break; } } // // do as it would have been a query // properties.put( "mode" , _mode ) ; properties.put("flush.count" , String.valueOf(_countToFlush)) ; properties.put("flush.percentage" , String.valueOf(_percentageToFlush)) ; properties.put("flush.atonce" , String.valueOf(_flushAtOnce)) ; // } @Override public void poolIoModeUpdated( String poolName , HsmFlushControlCore.Pool pool ){ if(_evt) { _log.info("EVENT : poolIoModeUpdated : " + pool); } Pool ip = getInternalPool( pool ) ; ip.modeReady = true ; ip.update() ; } @Override public void flushingDone( String poolName , String storageClassName , HsmFlushControlCore.FlushInfo flushInfo ){ if(_evt) { _log.info("EVENT : flushingDone : pool =" + poolName + ";class=" + storageClassName /* + "flushInfo="+flushInfo */); } HsmFlushControlCore.Pool pool = _core.getPoolByName( poolName ) ; if( pool == null ){ _log.warn("flushingDone for a non configured pool : "+poolName); return ; } Pool ip = getInternalPool( pool ) ; ip.update() ; ip.flushCounter -- ; if( ip.flushCounter <= 0 ){ ip.flushCounter = 0 ; _log.info("flushingDone : pool finished all flushing : "+poolName+" ; setting back to readWrite mode"); pool.setReadOnly(false); } /* if( ! ip.isFlushing() ){ _log.info("flushingDone : pool finished all flushing : "+poolName+" ; setting back to readWrite mode"); pool.setReadOnly(false); } */ } @Override public void reset(){ if(_evt) { _log.info("EVENT : reset"); } } @Override public void timer(){ if(_evt) { _log.info("EVENT : timer"); } // // // check for the next pool to flush. // Collection<String> set = new HashSet<>() ; for( HsmFlushControlCore.Pool pool = nextToFlush() ; pool != null ; pool = nextToFlush() ){ String poolName = pool.getName() ; if( set.contains(poolName) ) { break; } set.add( poolName ) ; _log.info("timer : Good candidate to flush : "+poolName); Pool ip = getInternalPool( pool ) ; if( ! ip.modeReady ) { continue; } pool.setReadOnly(true); ip.flush( ) ; } } @Override public void poolFlushInfoUpdated( String poolName , HsmFlushControlCore.Pool pool ){ if(_evt) { _log.info("EVENT : poolFlushInfoUpdated : " + pool.getName()); } if( ! pool.isActive() ){ _log.info( "poolFlushInfoUpdated : Pool : "+poolName+" inactive"); return ; } // // make sure we store the incoming stuff in our internal structure. // getInternalPool( pool ).update() ; } /** * Executes the external command with CommandInterpreter (using our ac_xx) commands. */ @Override public void command( Args args ){ if(_evt) { _log.info("EVENT : command : " + args); } if (args.argc() == 0) { return; } try{ Object reply = _interpreter.command( args ) ; if( reply == null ) { throw new Exception("Null pointer from command call"); } _log.info("Command returns : "+reply.toString() ); }catch(Exception ee ){ _log.warn("Command returns an exception ("+ee.getClass().getName()+") : " + ee.toString()); } } @Override public void prepareUnload(){ if(_evt) { _log.info("EVENT : Preparing unload (ignoring)"); } } @Override public void configuredPoolAdded( String poolName ){ if(_evt) { _log.info("EVENT : Configured pool added : " + poolName); } HsmFlushControlCore.Pool pool = _core.getPoolByName( poolName ) ; if( pool == null ){ _log.warn("Pool not found in _core database : "+poolName); return ; } Pool ip = getInternalPool( pool ) ; pool.setReadOnly(false); } @Override public void poolSetupUpdated(){ if(_evt) { _log.info("EVENT : Pool Setup updated (ignoring)"); } } @Override public void configuredPoolRemoved( String poolName ){ if(_evt) { _log.info("EVENT : Configured pool removed : " + poolName + " (ignoring)"); } } //------------------------------------------------------------------------------------------- // // C O M M A N D S // public static final String hh_dummy = "# dummy call" ; public String ac_dummy_$_1_99( Args args ){ return args.toString(); } public String ac_list_pools( Args args ){ return null; } //------------------------------------------------------------------------------------------- // // C O N V E N I E N E N T F U N C T I O N S // private Pool getInternalPool( HsmFlushControlCore.Pool pool ){ Pool ip = (Pool)pool.getDriverHandle() ; if( ip == null ){ _log.warn("getInternalPool : Unconfigured pool arrived "+pool.getName()+"; configuring"); pool.setDriverHandle( ip = new Pool( pool.getName() , pool ) ) ; } return ip ; } /** * * Convenient method to flush all pending storage classes of the specified pool. * * @param ip Internal pool representation. */ private int flushPool( HsmFlushControlCore.Pool pool ){ int flushing = 0 ; for (Object o : pool.getFlushInfos()) { HsmFlushControlCore.FlushInfo info = (HsmFlushControlCore.FlushInfo) o; StorageClassFlushInfo flush = info.getStorageClassFlushInfo(); long size = flush.getTotalPendingFileSize(); _log.info("flushPool : class = " + info .getName() + " size = " + size + " flushing = " + info .isFlushing()); // // is precious size > 0 and are we not yet flushing ? // try { if ((size > 0L) && !info.isFlushing()) { _log.info("flushPool : !!! flushing " + pool .getName() + " " + info.getName()); info.flush(_flushAtOnce); flushing++; } } catch (Exception ee) { _log.warn("flushPool : Problem flushing " + pool .getName() + " " + info.getName() + " " + ee); } } return flushing ; } /** * Counts the number pending requests per pool. * * @return Number of flush requests per pool. */ private int countTotalActivePool( HsmFlushControlCore.Pool pool ){ int total = 0 ; for (Object o : pool.getFlushInfos()) { HsmFlushControlCore.FlushInfo info = (HsmFlushControlCore.FlushInfo) o; StorageClassFlushInfo flush = info.getStorageClassFlushInfo(); total += flush.getActiveCount(); } return total ; } /** * Counts the number pending requests per pool. * * @return Number of flush requests per pool. */ private int countTotalPendingPool( HsmFlushControlCore.Pool pool ){ int total = 0 ; for (Object o : pool.getFlushInfos()) { HsmFlushControlCore.FlushInfo info = (HsmFlushControlCore.FlushInfo) o; StorageClassFlushInfo flush = info.getStorageClassFlushInfo(); total += flush.getRequestCount(); } return total ; } private boolean _ntf = true ; private boolean _evt = true ; /** * Central place to decide whether or not we want to flush * a pool. If 'null' is returned there is no pool ready yet. * * @return Next pool to flush or 'null' if no pool is ready yet. */ private HsmFlushControlCore.Pool nextToFlush(){ List<HsmFlushControlCore.Pool> pools = _core.getConfiguredPools() ; // // Get all pools which are currently not flushing. // List<Pool> list = new ArrayList<>(); for (HsmFlushControlCore.Pool pool : pools) { if (_ntf) { _log.info("nextToFlush : checking pool " + pool); } if (!pool.isActive()) { continue; } Pool ip = (Pool) pool.getDriverHandle(); if (ip.isFlushing()) { if (_ntf) { _log.info("nextToFlush : is already flushing " + pool .getName()); } } else { list.add(ip); } } // // make sure we have at least one pool to write on // if( list.size() < 2 ){ if(_ntf) { _log.info("nextToFlush : currently not enough pools to write on (" + list .size() + ")"); } return null ; } if(_ntf) { _log.info("nextToFlush : possible candidates : " + list); } /** * Get pool with highest pending file count and pool with highest * precious/total space ratio. */ Pool poolWithHighestCounter = null , poolWithHighestPercentage = null ; int highestCounter = -1 ; double highestPercentage = -1.0 ; for (Pool ip : list) { if (ip.preciousFileCount > highestCounter) { poolWithHighestCounter = ip; highestCounter = ip.preciousFileCount; } double percentage = ((double) ip.preciousSpace) / ((double) ip.totalSpace); if (percentage > highestPercentage) { poolWithHighestPercentage = ip; highestPercentage = percentage; } } if(_ntf) { _log.info("nextToFlush : highest percentage found for : " + poolWithHighestPercentage .pool.getName() + " (" + highestPercentage + ")"); } if(_ntf) { _log.info("nextToFlush : highest counter found for : " + poolWithHighestCounter .pool.getName() + " (" + highestCounter + ")"); } if( highestPercentage > _percentageToFlush ) { return poolWithHighestPercentage.pool; } if( highestCounter > _countToFlush ) { return poolWithHighestCounter.pool; } return null ; } /** * Determines how many storage classes are in the process of being flushed on this pool. * * @return Number of storage class in progress of been flushed on this pool. */ private int countStorageClassesFlushing( HsmFlushControlCore.Pool pool ){ int flushing = 0 ; for (Object o : pool.getFlushInfos()) { if (((HsmFlushControlCore.FlushInfo) o).isFlushing()) { flushing++; } } return flushing ; } }