/* * NOTE: This copyright does *not* cover user programs that use Hyperic * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2010], VMware, Inc. * This file is part of Hyperic. * * Hyperic is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.measurement.server.session; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.common.SystemException; import org.hyperic.hq.measurement.MeasurementConstants; import org.hyperic.hq.measurement.shared.AvailabilityManager; import org.hyperic.hq.stats.ConcurrentStatsCollector; import org.hyperic.util.stats.StatCollector; import org.hyperic.util.stats.StatUnreachableException; import org.hyperic.util.timer.StopWatch; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * This job is responsible for filling in missing availability metric values. */ @Service("availabilityCheckService") @Transactional public class AvailabilityCheckServiceImpl implements AvailabilityCheckService { private final Log log = LogFactory.getLog(AvailabilityCheckServiceImpl.class); private static final String AVAIL_BACKFILLER_TIME = ConcurrentStatsCollector.AVAIL_BACKFILLER_TIME; private long startTime = 0; private long wait = 5 * MeasurementConstants.MINUTE; private final Object IS_RUNNING_LOCK = new Object(); private final AtomicBoolean hasStarted = new AtomicBoolean(false); private boolean isRunning = false; private ConcurrentStatsCollector concurrentStatsCollector; private AvailabilityCache availabilityCache; private BackfillPointsService backfillPointsService; private AvailabilityFallbackCheckQue checkQue; private AvailabilityFallbackChecker fallbackChecker; @Autowired public AvailabilityCheckServiceImpl(ConcurrentStatsCollector concurrentStatsCollector, AvailabilityManager availabilityManager, AvailabilityCache availabilityCache, BackfillPointsService backfillPointsService, AvailabilityFallbackChecker fallbackChecker) { this.concurrentStatsCollector = concurrentStatsCollector; this.availabilityCache = availabilityCache; this.backfillPointsService = backfillPointsService; this.checkQue = availabilityManager.getFallbackCheckQue(); this.fallbackChecker = fallbackChecker; } @PostConstruct public void initStats() { concurrentStatsCollector.register(AVAIL_BACKFILLER_TIME); } public void backfillPlatformAvailability() { backfillPlatformAvailability(System.currentTimeMillis(), false); } public void testBackfill(long current) { backfillPlatformAvailability(current, true); } public void backfillPlatformAvailability(long current, boolean forceStart) { long start = now(); Map<Integer, ResourceDataPoint> backfillPoints = null; final boolean debug = log.isDebugEnabled(); final StopWatch watch = new StopWatch(); try { // Don't start backfilling immediately if (!forceStart && !canStart(current)) { log.info("not starting availability check"); return; } synchronized (IS_RUNNING_LOCK) { if (isRunning) { log.warn("Availability Check Service is already running, bailing out"); return; } else { isRunning = true; } } try { // PLEASE NOTE: This synchronized block directly affects the // throughput of the availability metrics, while this lock is // active no availability metric will be inserted. This is to // ensure the backfilled points will not be inserted after the // associated AVAIL_UP value from the agent. // The code must be extremely efficient or else it will have // a big impact on the performance of availability insertion. synchronized (availabilityCache) { log.info("starting availability check"); if (debug) watch.markTimeBegin("getBackfillPlatformPoints"); backfillPoints = backfillPointsService.getBackfillPlatformPoints(current); if (debug) watch.markTimeEnd("getBackfillPlatformPoints"); } if (backfillPoints.size() > 0 && debug) { log.debug("backfillPlatformAvailability: got " + backfillPoints.size() + " platforms to check. Adding to queue"); } checkQue.addToQue(backfillPoints); final List<ResourceDataPoint> availabilityDataPoints = pollWorkList(); if (debug) watch.markTimeBegin("fallbackChecker.checkAvailability"); fallbackChecker.checkAvailability(availabilityDataPoints, current); if (debug) watch.markTimeEnd("fallbackChecker.checkAvailability"); } finally { synchronized (IS_RUNNING_LOCK) { isRunning = false; } } } catch (Exception e) { throw new SystemException(e); } finally { if (debug) log.debug("availability check watch=" + watch); concurrentStatsCollector.addStat(now() - start, AVAIL_BACKFILLER_TIME); } } private List<ResourceDataPoint> pollWorkList() { checkQue.cleanQueFromNonExistant(); List<ResourceDataPoint> dataPointList = new ArrayList<ResourceDataPoint>(); ResourceDataPoint dp = checkQue.poll(); while (dp != null) { dataPointList.add(dp); dp = checkQue.poll(); } log.debug("setWorkList: current dataPointList size: " + dataPointList.size()); return dataPointList; } private long now() { return System.currentTimeMillis(); } private boolean canStart(long now) { if (!hasStarted.get()) { if (startTime == 0) { startTime = now; } else if ((startTime + wait) <= now) { // start after the initial wait time only if the // availability inserter is not "backlogged" if (getAvailabilityInserterQueueSize() < 1000) { hasStarted.set(true); } } } return hasStarted.get(); } private long getAvailabilityInserterQueueSize() { boolean debug = log.isDebugEnabled(); String statKey = "AvailabilityInserter_QUEUE_SIZE"; long currentQueueSize = -1; StatCollector stat = (StatCollector) concurrentStatsCollector.getStatKeys().get(statKey); if (stat == null) { if (debug) { log.debug(statKey + " is not registered in the ConcurrentStatsCollector"); } } else { try { currentQueueSize = stat.getVal(); } catch (StatUnreachableException e) { if (debug) { log.debug("Could not get value for " + statKey + ": " + e.getMessage(), e); } } } if (debug) { log.debug("Current availability inserter queue size = " + currentQueueSize); } return currentQueueSize; } }