/* * * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.netflix.ice.basic; import com.google.common.cache.*; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.netflix.ice.common.AwsUtils; import com.netflix.ice.common.ConsolidateType; import com.netflix.ice.common.Poller; import com.netflix.ice.reader.ReaderConfig; import com.netflix.ice.reader.ThroughputMetricService; import org.apache.commons.io.IOUtils; import org.joda.time.DateTime; import org.joda.time.Hours; import org.joda.time.Interval; import org.joda.time.PeriodType; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.Map; public class BasicThroughputMetricService extends Poller implements ThroughputMetricService { private String metricName; private String metricUnitName; private String factoredCostCurrencySign; private double factoredCostMultiply; private String filePrefix; private ReaderConfig config; private Map<DateTime, File> fileCache; private LoadingCache<DateTime, double[]> data; public BasicThroughputMetricService(String metricName, String metricUnitName, String factoredCostCurrencySign, double factoredCostMultiply, String filePrefix) { this.metricName = metricName; this.metricUnitName = metricUnitName; this.factoredCostCurrencySign = factoredCostCurrencySign; this.factoredCostMultiply = factoredCostMultiply; this.filePrefix = filePrefix; fileCache = Maps.newConcurrentMap(); } public void init() { config = ReaderConfig.getInstance(); data = CacheBuilder.newBuilder() .maximumSize(config.monthlyCacheSize) .removalListener(new RemovalListener<DateTime, double[]>() { public void onRemoval(RemovalNotification<DateTime, double[]> objectRemovalNotification) { fileCache.remove(objectRemovalNotification.getKey()); } }) .build( new CacheLoader<DateTime, double[]>() { public double[] load(DateTime monthDate) throws Exception { return loadData(monthDate); } }); this.start(); } @Override protected void poll() throws Exception { for (DateTime key: fileCache.keySet()) { File file = fileCache.get(key); boolean downloaded = AwsUtils.downloadFileIfChanged(config.workS3BucketName, config.workS3BucketPrefix, file, 0); if (downloaded) { logger.info("trying to re-read data for " + file); FileInputStream in = new FileInputStream(file); try { String[] strs = IOUtils.toString(in).split(","); double[] values = new double[strs.length]; for (int i = 0; i < strs.length; i++) values[i] = Double.parseDouble(strs[i]); data.put(key, values); logger.info("done re-read data for " + file); } finally { in.close(); } } } } private double[] loadData(DateTime monthDate) throws InterruptedException { while (true) { try { File file = new File(config.localDir, filePrefix + AwsUtils.monthDateFormat.print(monthDate)); AwsUtils.downloadFileIfChanged(config.workS3BucketName, config.workS3BucketPrefix, file, 0); FileInputStream in = new FileInputStream(file); try { String[] strs = IOUtils.toString(in).split(","); double[] values = new double[strs.length]; for (int i = 0; i < strs.length; i++) values[i] = Double.parseDouble(strs[i]); return values; } finally { in.close(); } } catch (Exception e) { logger.error("error in loading data for " + monthDate, e); Thread.sleep(1000*20L); } } } public String getMetricName() { return metricName; } public String getMetricUnitName() { return metricUnitName; } public String getFactoredCostCurrencySign() { return factoredCostCurrencySign; } public double getFactoredCostMultiply() { return factoredCostMultiply; } public double[] getData(Interval interval, ConsolidateType consolidateType) throws Exception { DateTime start = interval.getStart().withDayOfMonth(1).withMillisOfDay(0); DateTime end = interval.getEnd(); int num = interval.toPeriod(PeriodType.hours()).getHours(); if (interval.getStart().plusHours(num).isBefore(interval.getEnd())) num++; double[] hourly = new double[num]; List<Double> monthly = Lists.newArrayList(); do { double total = 0; int resultIndex = interval.getStart().isBefore(start) ? Hours.hoursBetween(interval.getStart(), start).getHours() : 0; int fromIndex = interval.getStart().isBefore(start) ? 0 : Hours.hoursBetween(start, interval.getStart()).getHours(); double[] data = this.data.get(start); while (resultIndex < num && fromIndex < data.length) { total += data[fromIndex]; hourly[resultIndex++] = data[fromIndex++]; } start = start.plusMonths(1); monthly.add(total); } while (start.isBefore(end)); int hoursInPeriod = (int) (consolidateType.millis / AwsUtils.hourMillis); num = consolidateType == ConsolidateType.monthly ? monthly.size() : (int) Math.ceil(1.0 * num / hoursInPeriod); double[] result = new double[num]; if (consolidateType == ConsolidateType.monthly) { for (int i = 0; i < num; i++) result[i] = monthly.get(i); } else { for (int i = 0; i < hourly.length; i++) result[i/hoursInPeriod] += hourly[i]; } return result; } }