/*
* Copyright 2013 Rackspace
*
* 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.rackspacecloud.blueflood.io;
import com.rackspacecloud.blueflood.cache.LocatorCache;
import com.rackspacecloud.blueflood.io.astyanax.AstyanaxWriter;
import com.rackspacecloud.blueflood.io.datastax.DCassandraUtilsIO;
import com.rackspacecloud.blueflood.rollup.Granularity;
import com.rackspacecloud.blueflood.io.CassandraModel.MetricColumnFamily;
import com.rackspacecloud.blueflood.service.SingleRollupWriteContext;
import com.rackspacecloud.blueflood.types.*;
import com.rackspacecloud.blueflood.utils.DefaultClockImpl;
import com.rackspacecloud.blueflood.utils.TimeValue;
import org.junit.After;
import org.junit.Before;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class IntegrationTestBase {
private static final char[] STRING_SEEDS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_".toCharArray();
protected static final Random RAND = new Random(System.currentTimeMillis());
protected static final ConcurrentHashMap<Locator, String> locatorToUnitMap = new ConcurrentHashMap<Locator, String>();
@Before
public void setUp() throws Exception {
// really short lived connections for tests!
CassandraUtilsIO cassandraUtilsIO = new DCassandraUtilsIO();
for (String cf : CassandraModel.getAllColumnFamiliesNames())
cassandraUtilsIO.truncateColumnFamily(cf);
}
@After
public void clearInterruptedThreads() throws Exception {
// clear all interrupts! Why do we do this? The best I can come up with is that the test harness (junit) is
// interrupting threads. One test in particular is very bad about this: RollupRunnableIntegrationTest.
// Nothing in that test looks particularly condemnable other than the use of Metrics timers in the rollup
// itself. Anyway... this should clear up the travis build failures.
//
// Debugging this was an exceptional pain in the neck. It turns out that there can be a fair amount of time
// between when a thread is interrupted and an InterruptedException gets thrown. Minutes in our case. This is
// because the AstyanaxWriter singleton keeps its threadpools between test invocations.
//
// The semantics of Thread.interrupt() are such that calling it only sets an interrupt flag to true, but doesn't
// really interrupt the thread. Subsequent calls to Thread.sleep() end up throwing the exception because the
// thread is in an interrupted state.
Method clearInterruptPrivate = Thread.class.getDeclaredMethod("isInterrupted", boolean.class);
clearInterruptPrivate.setAccessible(true);
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.isInterrupted()) {
System.out.println(String.format("Clearing interrupted thread: " + thread.getName()));
clearInterruptPrivate.invoke(thread, true);
}
}
}
@After
public void tearDown() throws Exception {
// meh
}
/**
* create a number of test locators of numTenants x numMetricNames combination
* @param numTenants
* @param numMetricNames
* @return list of locators for testing
*/
protected List<Locator> generateTestLocators(String tenantIdPrefix, int numTenants, String metricNamePrefix, int numMetricNames) {
List<Locator> locators = new ArrayList<Locator>();
for (int i = 1; i <= numTenants; i++) {
for (int j = 1; j <= numMetricNames; j++) {
locators.add(Locator.createLocatorFromPathComponents(tenantIdPrefix + i, metricNamePrefix + j));
}
}
return locators;
}
protected Metric writeMetric(String name, Object value) throws Exception {
final List<IMetric> metrics = new ArrayList<IMetric>();
final Locator locator = Locator.createLocatorFromPathComponents("acctId", name);
Metric metric = new Metric(locator, value, System.currentTimeMillis(),
new TimeValue(1, TimeUnit.DAYS), "unknown");
metrics.add(metric);
AstyanaxWriter.getInstance().insertFull(metrics, false, new DefaultClockImpl());
LocatorCache.getInstance().resetInsertedLocatorsCache();
return metric;
}
protected PreaggregatedMetric getGaugeMetric(String name, String tenantid, long timestamp) {
final Locator locator = Locator.createLocatorFromPathComponents(tenantid, name);
return getGaugeMetric(locator, timestamp);
}
protected PreaggregatedMetric getGaugeMetric(Locator locator, long timestamp) {
BluefloodGaugeRollup rollup = new BluefloodGaugeRollup()
.withLatest(timestamp, new Long(getRandomIntMetricValue()));
return new PreaggregatedMetric(timestamp, locator, new TimeValue(1, TimeUnit.DAYS), rollup);
}
protected List<IMetric> makeRandomIntMetrics(int count) {
final String tenantId = "ac" + randString(8);
List<IMetric> metrics = new ArrayList<IMetric>();
final long now = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
final Locator locator = Locator.createLocatorFromPathComponents(tenantId, "met" + randString(8));
metrics.add(getRandomIntMetric(locator, now - 10000000));
}
return metrics;
}
protected static String randString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++)
sb.append( STRING_SEEDS[ RAND.nextInt( STRING_SEEDS.length ) ] );
return sb.toString();
}
protected int getRandomIntMetricValue() {
return RAND.nextInt();
}
protected String getRandomStringMetricValue() {
return "str" + String.valueOf(getRandomIntMetricValue());
}
protected Metric getRandomIntMetric(final Locator locator, long timestamp) {
locatorToUnitMap.putIfAbsent(locator, UNIT_ENUM.values()[new Random().nextInt(UNIT_ENUM.values().length)].unit);
return new Metric(locator, getRandomIntMetricValue(), timestamp, new TimeValue(1, TimeUnit.DAYS), locatorToUnitMap.get(locator));
}
protected Metric getRandomIntMetricMaxValue(final Locator locator, long timestamp, int max) {
locatorToUnitMap.putIfAbsent(locator, UNIT_ENUM.values()[new Random().nextInt(UNIT_ENUM.values().length)].unit);
return new Metric(locator, RAND.nextInt( max ), timestamp, new TimeValue(1, TimeUnit.DAYS), locatorToUnitMap.get(locator));
}
protected Metric getRandomStringmetric(final Locator locator, long timestamp) {
locatorToUnitMap.putIfAbsent(locator, UNIT_ENUM.UNKNOWN.unit);
return new Metric(locator, getRandomStringMetricValue(), timestamp, new TimeValue(1, TimeUnit.DAYS), locatorToUnitMap.get(locator));
}
protected static <T> Metric makeMetric(final Locator locator, long timestamp, T value) {
return new Metric(locator, value, timestamp, new TimeValue(1, TimeUnit.DAYS), "unknown");
}
private enum UNIT_ENUM {
SECS("seconds"),
MSECS("milliseconds"),
BYTES("bytes"),
KILOBYTES("kilobytes"),
UNKNOWN("unknown");
private String unit;
private UNIT_ENUM(String unitValue) {
this.unit = unitValue;
}
private String getUnit() {
return unit;
}
}
protected void generateRollups(Locator locator, long from, long to, Granularity destGranularity) throws Exception {
if (destGranularity == Granularity.FULL) {
throw new Exception("Can't roll up to FULL");
}
MetricsRW metricsRW = IOContainer.fromConfig().getBasicMetricsRW();
MetricColumnFamily destCF;
ArrayList<SingleRollupWriteContext> writeContexts = new ArrayList<SingleRollupWriteContext>();
for (Range range : Range.rangesForInterval(destGranularity, from, to)) {
destCF = CassandraModel.getColumnFamily(BasicRollup.class, destGranularity);
Points<SimpleNumber> input = metricsRW.getDataToRollup(locator, RollupType.BF_BASIC, range,
CassandraModel.CF_METRICS_FULL_NAME);
BasicRollup basicRollup = BasicRollup.buildRollupFromRawSamples(input);
writeContexts.add(new SingleRollupWriteContext(basicRollup, locator, destGranularity, destCF, range.start));
}
metricsRW.insertRollups(writeContexts);
}
protected Range getRangeFromMinAgoToNow(int deltaMin) {
// ask for 5 minutes back
long now = System.currentTimeMillis();
return new Range(now - (deltaMin*60*1000), now);
}
protected String getRandomTenantId() {
return String.valueOf( RAND.nextInt( 5 ) );
}
}