Compare commits

...
Author SHA1 Message Date
Bill Oley f582e8ca1c Refactor SSL integration tests to use the same SslContext when possible 2020-04-27 09:29:05 -04:00
Bill Oley 6972c46a1a Merge pull request #204 from matthpeterson/issue-203
Closes issue #203 - avoid calling sizeInBytes if it's not expected to exist
2020-04-17 10:42:14 -04:00
matthpeterson 4d7bee7566 Closes issue #203 - Stop calling sizeInBytes if it's not expected to exist 2020-04-16 15:17:08 +00:00
Bill Oley 4b11b3d50f closes #201 Create balancer assignment file if it doesn't exist 2020-04-10 10:27:18 -04:00
Bill Oley 84b9856ec7 closes #199 Fix issues when using a generated (self-signed) server certificate 2020-04-10 10:20:25 -04:00
Bill Oley c9548a9b9a closes #198 Cleaner shutdown of resources for Timely server, balancer, grafana-auth 2020-04-10 10:01:47 -04:00
Bill Oley aec7bd4c38 closes #197 Fix scan range in MetaCacheImpl 2020-04-09 11:58:30 -04:00
Bill Oley e2b8b643f2 closes #200 Initialize non-cached metrics stored in zookeeper 2020-04-09 11:32:03 -04:00
Bill Oley c19e1d8b2f Set version to 1.2.8-SNAPSHOT 2020-02-21 10:20:48 -05:00
Bill Oley cb73e8e75c Tag version 1.2.7 2020-02-21 10:02:31 -05:00
Bill Oley 4c508c1a7d Merge pull request #195 from dtspence/TIMELY-194
Fixed #194, corrected datasource.js to look at wildcard tag value
2020-02-21 08:55:37 -05:00
dtspence c6c2c0d41e Fixed #194, corrected datasource.js to look at wildcard tag value
The alignment process to match tag query results with the metric definition
looks at the tag k/v pairs and makes a special consideration for a wildcard tag value.
The change adjusts datasource.js so that the '.*' notation will match a query result
and metric definition.
2020-02-05 19:56:03 +00:00
Christopher Tubbs a24cef78b3 Update jackson (multiple CVEs) (#193) 2019-11-07 23:05:38 -05:00
Bill Oley 08f99f6540 Set version to 1.2.7-SNAPSHOT 2019-10-30 12:33:49 -04:00
Bill Oley be11da7d15 Tag version 1.2.6 2019-10-30 12:30:33 -04:00
Bill Oley 4bc5753328 fixes #192 Thread deadlock in BalancedMetricResolver 2019-10-29 12:24:10 -04:00
Bill Oley 2ccb89a06d re #191 Correct typos 2019-10-18 09:16:03 -04:00
Bill Oley dfc3840ad4 re #191 Delete cached metrics when they are no longer being updated 2019-10-17 17:49:24 -04:00
Bill Oley 6235034d8b BalancedMetricResolver in the non-leader balancer should read assignments at intervals 2019-10-17 17:49:24 -04:00
Bill Oley d8de6ac06f Make zookeeper timeout configurable 2019-10-17 17:49:24 -04:00
Christopher Tubbs f381946abe Merge pull request #190 from NationalSecurityAgency/dependabot/maven/io.netty-netty-all-4.1.42.Final
Bump netty-all from 4.1.35.Final to 4.1.42.Final
2019-10-17 09:35:13 -07:00
dependabot[bot] 48bbe6a561 Bump netty-all from 4.1.35.Final to 4.1.42.Final
Bumps [netty-all](https://github.com/netty/netty) from 4.1.35.Final to 4.1.42.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.1.35.Final...netty-4.1.42.Final)

Signed-off-by: dependabot[bot] <[email protected]>
2019-10-11 18:58:57 +00:00
Christopher Tubbs 0f6110ea59 Update jackson to 2.9.10
* Fix CVE-2019-16335
* Fix CVE-2019-14540
2019-09-26 23:40:08 -04:00
Bill Oley 461b5e7e78 Pom version to 1.2.6-SNAPSHOT 2019-08-22 12:17:45 -04:00
Bill Oley 4ec7d94c28 Tag version 1.2.5 2019-08-22 12:12:49 -04:00
Bill Oley 77e910ae06 Use BufferedOutputWriter instead of PrintWriter, pass Exceptions instead of having to check error condition 2019-08-22 10:09:27 -04:00
Bill Oley a4998af887 Move jackson-databind version to the properties section 2019-08-08 13:50:08 -04:00
Bill Oley 2858549ede Merge pull request #188 from ctubbsii/jdk11-build-support
Bump versions / permit build with JDK11
2019-08-08 13:35:54 -04:00
Christopher Tubbs 57f0e1408f Bump versions / permit build with JDK11
Support builds on newer JDKs
* Update tycho-compiler-jdt in analytics/pom.xml
* Ignore spotbugs RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE for JDK11
* Bump several plugins to latest (maven-compiler-plugin, etc.)
* Add profile for using -release flag in Eclipse and with JDK9 and later

Fix recently added warnings
* Add try-with-resources for TreeCache objects to remove recent warnings
* Suppress generics warning in MetricCompactionStrategy
* Update TabletMetadataConsole to avoid deprecated Accumulo
  ClientConfiguration API

Other
* Bump jackson-databind for CVE-2019-14439, CVE-2019-14379
* Bump hadoop 2.7 to latest in series (2.7.3->2.7.7) for build
2019-08-06 16:14:13 -04:00
Bill Oley 151adc025a Merge pull request #185 from dtspence/TIMELY-184
Fixes #184 Metric compaction strategy and summary implementation
2019-08-06 15:15:45 -04:00
Bill Oley 5abd9d7200 closes #187 Prevent deadlock in balancer distributed read/write lock 2019-08-06 12:00:57 -04:00
Bill Oley fa2e211c72 closes #186 Make balancer websocket incomingBufferSize its own setting 2019-08-06 11:47:36 -04:00
dtspence a21438976e TIMELY-184: Metric compaction strategy and summary implementation. 2019-08-01 14:05:35 +00:00
Christopher Tubbs ba37047f3a Update jackson-databind (CVE-2019-12814) 2019-07-23 20:38:46 -04:00
Bill Oley 9305470c69 re #183 Check tagKey not null or blank in Meta.toKeys 2019-06-19 09:08:20 -04:00
Bill Oley 200233542e re #183 Add timestamps to meta tags to allow proper age off 2019-06-17 15:36:51 -04:00
Bill Oley 3cd78dc624 Merge branch 'drewfarris-config-fixup' 2019-05-30 12:55:35 -04:00
96 changed files with 3834 additions and 908 deletions

No files matched your search

+3 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-analytics</artifactId>
<!--
@@ -68,6 +68,7 @@
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>jdt</compilerId>
@@ -76,7 +77,7 @@
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>0.21.0</version>
<version>1.4.0</version>
</dependency>
</dependencies>
</plugin>
+1
View File
@@ -111,6 +111,7 @@ timely-balancer:
flush-interval-seconds: 30
scanner-read-ahead: 1
subscription-batch-size: 5000
incomingBufferSize: 50000000
wsClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-balancer</artifactId>
<dependencies>
@@ -1,7 +1,7 @@
package timely.balancer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import com.google.common.util.concurrent.AtomicDouble;
@@ -20,38 +20,26 @@ public class ArrivalRate {
private AtomicDouble longTermRate = new AtomicDouble(0);
private AtomicLong longTermLastReset = new AtomicLong(0);
public ArrivalRate(Timer timer) {
public ArrivalRate(ScheduledExecutorService arrivalRateExecutor) {
long now = System.currentTimeMillis();
shortTermLastReset.set(now);
mediumTermLastReset.set(now);
longTermLastReset.set(now);
// 2 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetShort();
}
}, 120000, 120000);
arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetShort();
}, 2, 2, TimeUnit.MINUTES);
// 5 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetMedium();
}
}, 300000, 300000);
arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetMedium();
}, 5, 5, TimeUnit.MINUTES);
// 15 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetLong();
}
}, 900000, 900000);
arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetLong();
}, 15, 15, TimeUnit.MINUTES);
}
private void resetShort() {
@@ -57,6 +57,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -144,8 +145,11 @@ public class Balancer {
this.balancerConfig = balancerConf;
}
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
protected static ConfigurableApplicationContext initializeContext(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
}
private void shutdownHook() {
@@ -177,8 +181,9 @@ public class Balancer {
channelFutures.add(wsChannelHandle.close());
}
int udpChannel = 1;
for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle");
LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close());
}
@@ -187,14 +192,13 @@ public class Balancer {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
}
});
Integer quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod();
int quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -239,9 +243,7 @@ public class Balancer {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
}
});
@@ -252,14 +254,29 @@ public class Balancer {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
}
if (this.metricResolver != null) {
try {
LOG.info("Closing metricResolver");
this.metricResolver.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (curatorFramework != null) {
try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (applicationContext != null) {
LOG.info("Closing applicationContext");
applicationContext.close();
}
if (curatorFramework != null) {
curatorFramework.close();
}
this.shutdown = true;
LOG.info("Server shut down.");
}
@@ -360,7 +377,9 @@ public class Balancer {
// start curator framework
RetryPolicy retryPolicy = new RetryForever(1000);
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), 30000, 1000,
int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(balancerConfig.getZooKeeper().getTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), timeout, 10000,
retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
@@ -478,12 +497,12 @@ public class Balancer {
public static void main(String[] args) throws Exception {
Balancer.applicationContext = Balancer.initializeConfiguration(args);
BalancerConfiguration balancerConf = applicationContext.getBean(BalancerConfiguration.class);
Balancer.applicationContext = Balancer.initializeContext(args);
BalancerConfiguration balancerConf = Balancer.applicationContext.getBean(BalancerConfiguration.class);
Balancer b = new Balancer(balancerConf);
Balancer balancer = new Balancer(balancerConf);
try {
b.run();
balancer.run();
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
@@ -491,7 +510,7 @@ public class Balancer {
LOG.error("Error running server.", e);
} finally {
try {
b.shutdown();
balancer.shutdown();
} catch (Exception e) {
System.exit(1);
}
@@ -515,11 +534,13 @@ public class Balancer {
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(
balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("login",
new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler());
ch.pipeline().addLast("error", new TimelyExceptionHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
}
};
}
@@ -16,4 +16,14 @@ public class BalancerWebsocket extends Websocket {
public GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> getWsClientPool() {
return wsClientPool;
}
public int incomingBufferSize = 50000000;
public int getIncomingBufferSize() {
return incomingBufferSize;
}
public void setIncomingBufferSize(int incomingBufferSize) {
this.incomingBufferSize = incomingBufferSize;
}
}
@@ -1,9 +1,14 @@
package timely.balancer.configuration;
import javax.validation.constraints.NotBlank;
public class ZooKeeper {
private String servers;
@NotBlank
private String timeout = "120s";
public String getServers() {
return servers;
}
@@ -11,4 +16,12 @@ public class ZooKeeper {
public void setServers(String servers) {
this.servers = servers;
}
public String getTimeout() {
return timeout;
}
public void setTimeout(String timeout) {
this.timeout = timeout;
}
}
@@ -1,7 +1,5 @@
package timely.balancer.connection;
import java.util.Timer;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -13,7 +11,6 @@ import timely.balancer.configuration.BalancerConfiguration;
public class TimelyBalancedHost {
private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class);
static private Timer arrivalRateTimer = new Timer("ArrivalRateTimerTBH", true);
private String host;
private int tcpPort;
@@ -24,34 +21,25 @@ public class TimelyBalancedHost {
private BalancerConfiguration balancerConfig;
private int failures = 0;
private int successes = 0;
private ArrivalRate arrivalRate = new ArrivalRate(arrivalRateTimer);
private ArrivalRate arrivalRate = null;
public TimelyBalancedHost() {
private TimelyBalancedHost() {
}
public TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
private TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
this.host = host;
this.tcpPort = tcpPort;
this.httpPort = httpPort;
this.wsPort = wsPort;
this.udpPort = udpPort;
this.arrivalRate = arrivalRate;
}
public TimelyBalancedHost(String host, int basePort) {
this.host = host;
this.tcpPort = basePort;
this.httpPort = basePort + 1;
this.wsPort = basePort + 2;
this.udpPort = basePort + 3;
}
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort);
}
static public TimelyBalancedHost of(String host, int basePort) {
return new TimelyBalancedHost(host, basePort);
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort, arrivalRate);
}
public void setBalancerConfig(BalancerConfiguration balancerConfig) {
@@ -141,11 +129,17 @@ public class TimelyBalancedHost {
}
public void arrived() {
arrivalRate.arrived();
if (this.arrivalRate != null) {
this.arrivalRate.arrived();
}
}
public double getArrivalRate() {
return arrivalRate.getRate();
if (this.arrivalRate == null) {
return 0;
} else {
return this.arrivalRate.getRate();
}
}
@Override
@@ -22,7 +22,7 @@ public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedH
@Override
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
int bufferSize = balancerConfig.getWebsocket().getSubscriptionBatchSize() * 500;
int bufferSize = balancerConfig.getWebsocket().getIncomingBufferSize();
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
balancerConfig.isLoginRequired(), "", "",
@@ -2,8 +2,9 @@ package timely.balancer.healthcheck;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,12 +16,12 @@ public class HealthChecker {
private static final Logger LOG = LoggerFactory.getLogger(HealthChecker.class);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private Timer timer;
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final private TCPServerHealthCheck tcpServerHealthCheck;
private TimerTask check = new TimerTask() {
@Override
public void run() {
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.executorService.scheduleAtFixedRate(() -> {
synchronized (timelyHosts) {
for (TimelyBalancedHost h : timelyHosts) {
try {
@@ -35,13 +36,7 @@ public class HealthChecker {
}
}
}
}
};
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.timer = new Timer("HealthCheckerThread", true);
this.timer.scheduleAtFixedRate(check, 0, config.getCheckServerHealthInterval());
}, 0, config.getCheckServerHealthInterval(), TimeUnit.MILLISECONDS);
}
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) {
@@ -50,4 +45,17 @@ public class HealthChecker {
this.timelyHosts.addAll(timelyHosts);
}
}
public void close() throws Exception {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
}
@@ -7,6 +7,7 @@ import static timely.balancer.Balancer.LEADER_LATCH_PATH;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
@@ -19,10 +20,10 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -71,8 +72,8 @@ public class BalancedMetricResolver implements MetricResolver {
private List<TimelyBalancedHost> serverList = new ArrayList<>();
private Random r = new Random();
final private HealthChecker healthChecker;
private Timer timer = new Timer("RebalanceTimer", true);
private Timer arrivalRateTimer = new Timer("ArrivalRateTimerResolver", true);
private ScheduledExecutorService assignmentExecutor = Executors.newScheduledThreadPool(2);
private ScheduledExecutorService arrivalRateExecutor = Executors.newScheduledThreadPool(2);
private int roundRobinCounter = 0;
private Set<String> nonCachedMetrics = new HashSet<>();
private DistributedAtomicValue nonCachedMetricsIP;
@@ -108,22 +109,12 @@ public class BalancedMetricResolver implements MetricResolver {
LOG.debug("Handling assignments event {}. assignmentsLastUpdatedInHdfs:{}", event.getType().toString(),
new Date(assignmentsLastUpdatedInHdfs.get().postValue()));
if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastHdfsUpdate > lastLocalUpdate) {
LOG.debug("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
readAssignmentsFromHdfs();
} else {
LOG.debug("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
}
readAssignmentsFromHdfs(true);
}
}
};
try {
TreeCache assignmentTreeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH);
try (TreeCache assignmentTreeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH)) {
assignmentTreeCache.getListenable().addListener(assignmentListener);
assignmentTreeCache.start();
} catch (Exception e) {
@@ -144,8 +135,7 @@ public class BalancedMetricResolver implements MetricResolver {
}
};
try {
TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS);
try (TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS)) {
nonCachedMetricsTreeCache.getListenable().addListener(nonCachedMetricsListener);
nonCachedMetricsTreeCache.start();
} catch (Exception e) {
@@ -160,55 +150,55 @@ public class BalancedMetricResolver implements MetricResolver {
}
readNonCachedMetricsIP();
readAssignmentsFromHdfs();
readAssignmentsFromHdfs(false);
timer.schedule(new TimerTask() {
@Override
public void run() {
if (isLeader.get()) {
try {
balance();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
}, 900000, 900000);
timer.schedule(new TimerTask() {
@Override
public void run() {
assignmentExecutor.scheduleAtFixedRate(() -> {
if (isLeader.get()) {
try {
if (isLeader.get()) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.debug("Writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
writeAssigmentsToHdfs();
}
}
balance();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}, 10000, 60000);
}, 15, 15, TimeUnit.MINUTES);
assignmentExecutor.scheduleAtFixedRate(() -> {
try {
if (isLeader.get()) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.debug("Leader writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
writeAssignmentsToHdfs();
} else {
LOG.trace("Leader not writing assignments to hdfs lastLocalUpdate ({}) <= lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
}
} else {
readAssignmentsFromHdfs(true);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, 10, 60, TimeUnit.SECONDS);
}
private void readNonCachedMetricsIP() {
Set<String> currentNonCachedMetricsDistributed = new TreeSet<>();
try {
nonCachedMetricsIPRWLock.readLock().acquire();
boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
if (currentNonCachedMetricsDistributedBytes != null) {
try {
currentNonCachedMetricsDistributed = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes);
} catch (Exception e) {
LOG.error(e.getMessage());
LOG.error(e.getMessage(), e);
currentNonCachedMetricsDistributed = new TreeSet<>();
}
}
@@ -249,7 +239,7 @@ public class BalancedMetricResolver implements MetricResolver {
public void isLeader() {
LOG.info("this balancer is the leader");
isLeader.set(true);
writeAssigmentsToHdfs();
writeAssignmentsToHdfs();
}
@Override
@@ -275,7 +265,7 @@ public class BalancedMetricResolver implements MetricResolver {
for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(),
pl.getWsPort(), pl.getUdpPort());
pl.getWsPort(), pl.getUdpPort(), new ArrivalRate(arrivalRateExecutor));
LOG.info("adding service {} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", si.getId(),
pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort());
tbh.setBalancerConfig(balancerConfig);
@@ -300,7 +290,8 @@ public class BalancedMetricResolver implements MetricResolver {
for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(),
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort());
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
tbh.setBalancerConfig(balancerConfig);
availableHosts.add(tbh);
}
@@ -342,14 +333,16 @@ public class BalancedMetricResolver implements MetricResolver {
assignMetric(s, h);
LOG.debug("Assigned server removed. Assigning {} to server {}:{}", s, h.getTcpPort());
}
writeAssigmentsToHdfs();
}
} finally {
balancerLock.writeLock().unlock();
}
if (isLeader.get() && rebalanceNeeded) {
balance();
if (isLeader.get()) {
if (rebalanceNeeded) {
balance();
}
writeAssignmentsToHdfs();
}
}
@@ -643,7 +636,10 @@ public class BalancedMetricResolver implements MetricResolver {
}
try {
nonCachedMetricsIPRWLock.writeLock().acquire();
boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
Set<String> currentNonCachedMetricsIP;
if (currentNonCachedMetricsDistributedBytes == null) {
@@ -690,7 +686,7 @@ public class BalancedMetricResolver implements MetricResolver {
try {
lock.writeLock().release();
} catch (Exception e3) {
LOG.error(e3.getMessage());
LOG.error(e3.getMessage(), e3);
}
}
}
@@ -744,7 +740,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (chooseMetricSpecificHost) {
ArrivalRate rate = metricMap.get(metric);
if (rate == null) {
rate = new ArrivalRate(arrivalRateTimer);
rate = new ArrivalRate(arrivalRateExecutor);
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
@@ -875,11 +871,44 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh;
}
private void readAssignmentsFromHdfs() {
private void createAssignmentFile() {
try {
LOG.info("Creating assignment file: " + balancerConfig.getAssignmentFile());
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
fs.create(assignmentFile);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
private void readAssignmentsFromHdfs(boolean checkIfNecessary) {
try {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (checkIfNecessary) {
if (lastHdfsUpdate <= lastLocalUpdate) {
LOG.debug("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
return;
}
}
LOG.debug("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
// proceed with reading from HDFS
Map<String, TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>();
try {
assignmentsIPRWLock.readLock().acquire();
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.writeLock().lock();
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
@@ -912,8 +941,14 @@ public class BalancedMetricResolver implements MetricResolver {
metricToHostMap.clear();
for (Map.Entry<String, TimelyBalancedHost> e : assignedMetricToHostMap.entrySet()) {
if (shouldCache(e.getKey())) {
metricToHostMap.put(e.getKey(), e.getValue());
if (StringUtils.isNotBlank(e.getKey()) && e.getValue() != null) {
if (shouldCache(e.getKey())) {
metricToHostMap.put(e.getKey(), e.getValue());
}
} else {
Exception e1 = new IllegalStateException(
"Bad assignment metric:" + e.getKey() + " host:" + e.getValue());
LOG.warn(e1.getMessage(), e1);
}
}
@@ -932,6 +967,9 @@ public class BalancedMetricResolver implements MetricResolver {
assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue());
LOG.info("Read {} assignments from hdfs lastHdfsUpdate = lastLocalUpdate ({})", metricToHostMap.size(),
new Date(assignmentsLastUpdatedLocal.get()));
} catch (FileNotFoundException e) {
createAssignmentFile();
metricToHostMap.clear();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
@@ -944,11 +982,14 @@ public class BalancedMetricResolver implements MetricResolver {
}
}
private void writeAssigmentsToHdfs() {
private void writeAssignmentsToHdfs() {
CsvWriter writer = null;
try {
assignmentsIPRWLock.writeLock().acquire();
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.readLock().lock();
nonCachedMetricsLocalLock.readLock().lock();
try {
@@ -1006,13 +1047,50 @@ public class BalancedMetricResolver implements MetricResolver {
private void assignMetric(String metric, TimelyBalancedHost tbh) {
if (isLeader.get()) {
balancerLock.writeLock().lock();
try {
metricToHostMap.put(metric, tbh);
assignmentsLastUpdatedLocal.set(System.currentTimeMillis());
} finally {
balancerLock.writeLock().unlock();
if (StringUtils.isNotBlank(metric) && tbh != null) {
balancerLock.writeLock().lock();
try {
metricToHostMap.put(metric, tbh);
assignmentsLastUpdatedLocal.set(System.currentTimeMillis());
} finally {
balancerLock.writeLock().unlock();
}
} else {
Exception e = new IllegalStateException("Bad assignment metric:" + metric + " host:" + tbh);
LOG.warn(e.getMessage(), e);
}
}
}
@Override
public void close() throws Exception {
this.assignmentExecutor.shutdown();
try {
this.assignmentExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.assignmentExecutor.isTerminated()) {
this.assignmentExecutor.shutdownNow();
}
}
this.arrivalRateExecutor.shutdown();
try {
this.arrivalRateExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.arrivalRateExecutor.isTerminated()) {
this.arrivalRateExecutor.shutdownNow();
}
}
try {
this.healthChecker.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
@@ -7,4 +7,6 @@ public interface MetricResolver {
public TimelyBalancedHost getHostPortKeyIngest(String metric);
public TimelyBalancedHost getHostPortKey(String metric);
public void close() throws Exception;
}
+2 -2
View File
@@ -4,13 +4,13 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-client</artifactId>
<properties>
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
<!-- use .dev0 instead of -SNAPSHOT -->
<python.client.version>1.2.5.dev0</python.client.version>
<python.client.version>1.2.8.dev0</python.client.version>
</properties>
<dependencies>
<dependency>
@@ -1,7 +1,8 @@
package timely.client.tcp;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.net.Socket;
import org.slf4j.Logger;
@@ -14,7 +15,7 @@ public class TcpClient implements AutoCloseable {
private final String host;
private final int port;
private Socket sock = null;
private PrintWriter out = null;
private BufferedWriter out = null;
private long connectTime = 0L;
private long backoff = 2000;
private int bufferSize = -1;
@@ -59,7 +60,7 @@ public class TcpClient implements AutoCloseable {
}
}
public synchronized void flush() {
public synchronized void flush() throws IOException {
if (null != out) {
out.flush();
writesSinceFlush = 0;
@@ -93,12 +94,12 @@ public class TcpClient implements AutoCloseable {
}
private synchronized int connect() {
if (null == sock || !sock.isConnected() || out.checkError()) {
if (null == sock || !sock.isConnected()) {
if (System.currentTimeMillis() > (connectTime + backoff)) {
try {
connectTime = System.currentTimeMillis();
sock = new Socket(host, port);
out = new PrintWriter(sock.getOutputStream(), false);
out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
backoff = 2000;
LOG.trace("Connected to Timely at {}:{}", host, port);
} catch (IOException e) {
@@ -22,7 +22,7 @@ import timely.model.parse.TagParser;
@JsonRootName("metric")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "name" })
public class Metric implements Serializable {
public class Metric implements Serializable, ObjectSizeOf {
private static final long serialVersionUID = 1L;
@@ -186,4 +186,9 @@ public class Metric implements Serializable {
return this.metric;
}
}
@Override
public long sizeInBytes() {
return 0;
}
}
@@ -2,13 +2,16 @@ package timely.model;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
/**
* A simple interface that objects can implement to return the object size.
*/
@@ -22,15 +25,29 @@ public interface ObjectSizeOf {
public static class ObjectInstance {
private Object o;
private String name = "";
public ObjectInstance(Object _o) {
this.o = _o;
}
public ObjectInstance(Object _o, String name) {
this.o = _o;
this.name = name;
}
public Object getObject() {
return o;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return System.identityHashCode(o);
@@ -64,110 +81,134 @@ public interface ObjectSizeOf {
*
* @return an approximation of the object size
*/
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(),
useSizeInBytesMethod, debug);
return estSize;
}
public static long getObjectSize(Object o) {
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(), true, false);
return estSize;
return getObjectSize(o, true, false);
}
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack,
boolean useSizeInBytesMethod, boolean debug) {
long totalSize = 0;
stack.add(new ObjectInstance(o));
while (!stack.isEmpty()) {
long size = 0;
ObjectInstance oi = stack.pop();
o = oi.getObject();
if (o != null && !visited.contains(oi)) {
visited.add(oi);
try {
if (useSizeInBytesMethod) {
try {
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
} else {
Method sizeInBytes = o.getClass().getMethod("sizeInBytes", (Class<?>[]) null);
size = (Long) sizeInBytes.invoke(o);
}
} catch (Throwable t) {
// ok, lets do this the hard way...
}
}
if (size == 0) {
// the hard way...
// do not include Class related objects or
// reflection objects
if ((o instanceof Class) || (o instanceof ClassLoader) || (o.getClass().getPackage() != null
&& o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
size = 0;
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
size = NUMBER_SIZE;
} else {
// lets do a simple sizing
Class<?> c = o.getClass();
if (c.isArray()) {
size = ARRAY_OVERHEAD;
int length = Array.getLength(o);
if (c.getComponentType().isPrimitive()) {
size += length * getPrimitiveObjectSize(c.getComponentType());
} else {
size += length * REFERENCE;
for (int i = 0; i < length; i++) {
Object element = Array.get(o, i);
if (element != null) {
stack.add(new ObjectInstance(element));
}
}
}
} else {
size += OBJECT_OVERHEAD;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (field.getType().isPrimitive()) {
size += getPrimitiveObjectSize(field.getType());
} else {
size += REFERENCE;
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
Object fieldObject = field.get(o);
if (fieldObject != null) {
stack.push(new ObjectInstance(fieldObject));
}
} catch (Exception e) {
// cannot get to field, so
// ignore it in this size
// calculation
e.printStackTrace();
}
field.setAccessible(accessible);
}
}
c = c.getSuperclass();
}
}
size = roundUp(size);
}
}
} catch (Throwable t) {
log.warning("Unable to determine object size for " + o);
}
if (debug) {
System.out.println(o.getClass().getCanonicalName() + "=" + size);
}
}
totalSize += size;
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
List<String> output = null;
if (debug) {
output = new ArrayList<>();
}
long size = getObjectSize(new ObjectInstance(o, o.getClass().getSimpleName()), new HashSet<>(), 0,
useSizeInBytesMethod, output);
if (debug) {
Collections.reverse(output);
for (String s : output)
System.out.println(s);
}
return size;
}
return totalSize;
public static long getObjectSize(ObjectInstance oi, Set<ObjectInstance> visited, int indent,
boolean useSizeInBytesMethod, List<String> output) {
long size = 0;
Object o = oi.getObject();
int arrayOverhead = 0;
int objectOverhead = 0;
int reference = 0;
boolean roundUp = false;
if (o != null && !visited.contains(oi)) {
visited.add(oi);
try {
if (useSizeInBytesMethod) {
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
if (output != null && size > 0) {
output.add(StringUtils.repeat(' ', indent) + oi.getName() + " [" + size + "] ("
+ o.getClass().getCanonicalName() + ") [ObjectSizeOf]");
}
}
}
if (size == 0) {
// the hard way...
// do not include Class related objects or
// reflection objects
if ((o instanceof Class) || (o instanceof ClassLoader) || (o.getClass().getPackage() != null
&& o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
size = 0;
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
size = NUMBER_SIZE;
} else {
// lets do a simple sizing
Class<?> c = o.getClass();
if (c.isArray()) {
arrayOverhead++;
int length = Array.getLength(o);
if (c.getComponentType().isPrimitive()) {
size += length * getPrimitiveObjectSize(c.getComponentType());
} else {
reference += length;
for (int i = 0, index = length - 1; i < length; i++, index--) {
Object element = Array.get(o, index);
if (element != null) {
size += getObjectSize(
new ObjectInstance(element, oi.getName() + "[" + index + "]"),
visited, indent + 2, useSizeInBytesMethod, output);
}
}
}
} else {
objectOverhead++;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (field.getType().isPrimitive()) {
long primSize = getPrimitiveObjectSize(field.getType());
if (output != null) {
output.add(StringUtils.repeat(' ', indent + 2) + field.getName() + " ["
+ primSize + "] (" + field.getType().getCanonicalName() + ")");
}
size += primSize;
} else {
reference++;
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
Object fieldObject = field.get(o);
if (fieldObject != null) {
size += getObjectSize(
new ObjectInstance(fieldObject, field.getName()), visited,
indent + 2, useSizeInBytesMethod, output);
}
} catch (Exception e) {
// cannot get to field, so
// ignore it in this size
// calculation
e.printStackTrace();
}
field.setAccessible(accessible);
}
}
c = c.getSuperclass();
}
}
roundUp = true;
}
}
long underlyingObjects = size;
long overheads = REFERENCE * reference + OBJECT_OVERHEAD * objectOverhead
+ ARRAY_OVERHEAD * arrayOverhead;
long addedOverheads = size + overheads;
if (roundUp) {
size = roundUp(addedOverheads);
} else {
size = addedOverheads;
}
if (output != null) {
output.add(StringUtils.repeat(' ', indent) + oi.getName() + " [" + size + "] ("
+ o.getClass().getCanonicalName() + ") = " + underlyingObjects + "+" + overheads + "+"
+ (size - addedOverheads) + " REF=" + reference + " OBJ=" + objectOverhead + " ARRAY="
+ arrayOverhead + " ROUND=" + (size - addedOverheads));
}
} catch (Throwable t) {
log.warning("Unable to determine object size for " + o);
}
}
return size;
}
public static long roundUp(long size) {
@@ -191,6 +232,42 @@ public interface ObjectSizeOf {
return 0;
}
}
public static long getStringSize(String s) {
long size = 0;
size += ObjectSizeOf.Sizer.OBJECT_OVERHEAD;
size += ObjectSizeOf.Sizer.REFERENCE;
size += ObjectSizeOf.Sizer.getPrimitiveObjectSize(int.class);
try {
// change with Java 9
if (String.class.getDeclaredField("coder") != null) {
size += ObjectSizeOf.Sizer.getPrimitiveObjectSize(byte.class);
}
} catch (Exception e) {
}
size += roundUp(ARRAY_OVERHEAD + (s.length() * ObjectSizeOf.Sizer.getPrimitiveObjectSize(byte.class)));
return roundUp(size);
}
}
public static void main(String[] args) {
Metric m = new Metric("mymetric", 100000l, 32);
m.addTag(new Tag("key1", "value1"));
m.addTag(new Tag("key2", "value2"));
m.addTag(new Tag("key3", "value3"));
m.addTag(new Tag("key4", "value4"));
m.addTag(new Tag("key5", "value5"));
long newSize1 = ObjectSizeOf.Sizer.getObjectSize(m, false, true);
System.out.println("new size #1: " + newSize1);
System.out.println();
System.out.println();
System.out.println();
long newSize2 = ObjectSizeOf.Sizer.getObjectSize(m, true, true);
System.out.println("new size #2: " + newSize2);
}
}
+32 -1
View File
@@ -18,7 +18,7 @@ import timely.model.parse.TagParser;
* Tag consists of key value pair
*/
@JsonRootName("tag")
public class Tag implements Comparable<Tag>, Serializable {
public class Tag implements Comparable<Tag>, ObjectSizeOf, Serializable {
private static final long serialVersionUID = 1L;
@@ -27,6 +27,12 @@ public class Tag implements Comparable<Tag>, Serializable {
private String key;
private String value;
@JsonIgnore
private boolean mustRecalculateLength = true;
@JsonIgnore
private long length;
public Tag() {
}
@@ -55,6 +61,7 @@ public class Tag implements Comparable<Tag>, Serializable {
public void set(String key, String value) {
this.key = key;
this.value = value;
this.mustRecalculateLength = true;
}
@JsonIgnore
@@ -64,6 +71,7 @@ public class Tag implements Comparable<Tag>, Serializable {
public void setKey(String key) {
this.key = key;
this.mustRecalculateLength = true;
}
@JsonIgnore
@@ -73,6 +81,7 @@ public class Tag implements Comparable<Tag>, Serializable {
public void setValue(String value) {
this.value = value;
this.mustRecalculateLength = true;
}
/**
@@ -106,4 +115,26 @@ public class Tag implements Comparable<Tag>, Serializable {
public int compareTo(Tag other) {
return new CompareToBuilder().append(this.key, other.key).append(this.value, other.value).toComparison();
}
@JsonIgnore
@Override
public long sizeInBytes() {
if (this.mustRecalculateLength) {
calculateLength();
this.mustRecalculateLength = false;
}
return this.length;
}
private void calculateLength() {
long l = 0;
l += 2 * Sizer.REFERENCE;
l += Sizer.OBJECT_OVERHEAD;
l += Sizer.getPrimitiveObjectSize(long.class);
l += Sizer.getPrimitiveObjectSize(boolean.class);
l += Sizer.getStringSize(key);
l += Sizer.getStringSize(value);
this.length = Sizer.roundUp(l);
}
}
+13 -1
View File
@@ -5,13 +5,14 @@ import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Objects;
/**
* Value consists of a long timestamp and a double measurement
*/
public class Value implements Comparator<Value>, Serializable {
public class Value implements Comparator<Value>, ObjectSizeOf, Serializable {
private static final long serialVersionUID = 1L;
@@ -90,4 +91,15 @@ public class Value implements Comparator<Value>, Serializable {
public String toString() {
return "Value{" + "timestamp=" + timestamp + ", measure=" + measure + "}";
}
@JsonIgnore
@Override
public long sizeInBytes() {
long l = 0;
l += Sizer.OBJECT_OVERHEAD;
l += 2 * Sizer.REFERENCE;
l += Sizer.NUMBER_SIZE;
l += Sizer.NUMBER_SIZE;
return Sizer.roundUp(l);
}
}
+5
View File
@@ -5,4 +5,9 @@
<Method name="connect" />
<Class name="timely.client.tcp.TcpClient"/>
</Match>
<Match>
<!-- Must ignore these everywhere, because of a javac byte code generation bug -->
<!-- https://github.com/spotbugs/spotbugs/issues/756 -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>
</FindBugsFilter>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>collectd-nsq-plugin</artifactId>
<dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>collectd-plugin-parent</artifactId>
<dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>collectd-timely-plugin</artifactId>
<dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-common</artifactId>
<dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>grafana-auth</artifactId>
<dependencies>
@@ -81,8 +81,11 @@ public class GrafanaAuth {
this.grafanaAuthConfig = grafanaAuthConfig;
}
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
protected static ConfigurableApplicationContext initializeContext(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
}
private void shutdownHook() {
@@ -109,13 +112,11 @@ public class GrafanaAuth {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
}
});
Integer quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod();
int quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>();
if (httpBossGroup != null) {
@@ -132,9 +133,7 @@ public class GrafanaAuth {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
}
});
@@ -272,12 +271,13 @@ public class GrafanaAuth {
public static void main(String[] args) throws Exception {
GrafanaAuth.applicationContext = GrafanaAuth.initializeConfiguration(args);
GrafanaAuthConfiguration balancerConf = applicationContext.getBean(GrafanaAuthConfiguration.class);
GrafanaAuth.applicationContext = GrafanaAuth.initializeContext(args);
GrafanaAuthConfiguration grafanaAuthConf = GrafanaAuth.applicationContext
.getBean(GrafanaAuthConfiguration.class);
GrafanaAuth b = new GrafanaAuth(balancerConf);
GrafanaAuth grafanaAuth = new GrafanaAuth(grafanaAuthConf);
try {
b.run();
grafanaAuth.run();
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
@@ -285,7 +285,7 @@ public class GrafanaAuth {
LOG.error("Error running server.", e);
} finally {
try {
b.shutdown();
grafanaAuth.shutdown();
} catch (Exception e) {
System.exit(1);
}
@@ -314,7 +314,8 @@ public class GrafanaAuth {
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("httpRelay", new GrafanaRelayHandler(config, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler());
ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
}
};
}
@@ -87,7 +87,7 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
String protocol = config.getGrafana().getProtocol();
String host = config.getGrafana().getHost();
Integer port = config.getGrafana().getPort();
k = TimelyBalancedHost.of(host, -1, port, -1, -1);
k = TimelyBalancedHost.of(host, -1, port, -1, -1, null);
client = httpClientPool.borrowObject(k);
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-app</artifactId>
<packaging>pom</packaging>
+1 -1
View File
@@ -509,7 +509,7 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
} else {
return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this));
}
}.bind(this));
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","responsePromises","reqBody","showQuery","_addCredentialOptions","headers","Promise","all","queryResponses","combinedResponse","x","concat","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","toPairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","every","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GLine truncated
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","responsePromises","reqBody","showQuery","_addCredentialOptions","headers","Promise","all","queryResponses","combinedResponse","x","concat","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","toPairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","every","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GLine truncated
+2 -2
View File
@@ -23,8 +23,8 @@
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
],
"version": "1.2.5-SNAPSHOT",
"updated": "2019-05-30"
"version": "1.2.8-SNAPSHOT",
"updated": "2020-02-21"
},
"includes": [
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "timely-app",
"private": true,
"version": "1.2.5-SNAPSHOT",
"version": "1.2.8-SNAPSHOT",
"description": "",
"main": "index.js",
"scripts": {
+2 -2
View File
@@ -23,8 +23,8 @@
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
],
"version": "1.2.5-SNAPSHOT",
"updated": "2019-05-30"
"version": "1.2.8-SNAPSHOT",
"updated": "2020-02-21"
},
"includes": [
@@ -446,7 +446,7 @@ export class TimelyDatasource {
} else {
return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this));
}
}.bind(this));
+40 -20
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Timely</name>
<description>A secure and scalable time series database based on Apache Accumulo</description>
@@ -50,37 +50,39 @@
<version.commons-pool2>2.6.1</version.commons-pool2>
<version.curator>4.1.0</version.curator>
<version.disruptor>3.4.2</version.disruptor>
<version.easymock>4.0.2</version.easymock>
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
<version.fast-classpath-scanner>3.1.13</version.fast-classpath-scanner>
<version.flink>1.1.4</version.flink>
<version.google-flatbuffers>1.10.0</version.google-flatbuffers>
<version.guava>27.1-jre</version.guava>
<version.hadoop>2.7.3</version.hadoop>
<version.hadoop>2.7.7</version.hadoop>
<version.httpcomponents>4.5.6</version.httpcomponents>
<version.impsort>1.2.0</version.impsort>
<version.jackson>2.9.9</version.jackson>
<version.jacoco>0.7.6.201602180812</version.jacoco>
<version.impsort>1.3.0</version.impsort>
<version.jackson>2.10.0</version.jackson>
<version.jackson.databind>2.10.0</version.jackson.databind>
<version.jacoco>0.8.4</version.jacoco>
<version.javacsv>2.0</version.javacsv>
<version.javassist>3.24.1-GA</version.javassist>
<version.jcip-annotations>1.0</version.jcip-annotations>
<version.jsoup>1.11.3</version.jsoup>
<version.junit>4.12</version.junit>
<version.log4j>2.11.2</version.log4j>
<version.maven-assembly>2.6</version.maven-assembly>
<version.maven-build-helper>1.12</version.maven-build-helper>
<version.maven-compiler>3.3</version.maven-compiler>
<version.maven-dependency>2.10</version.maven-dependency>
<version.maven-jar>3.0.1</version.maven-jar>
<version.maven-pmd>3.6</version.maven-pmd>
<version.maven-rpm>2.1.4</version.maven-rpm>
<version.maven-shade>2.4.2</version.maven-shade>
<version.maven-surefire>2.19.1</version.maven-surefire>
<version.netty>4.1.35.Final</version.netty>
<version.maven-assembly>3.1.1</version.maven-assembly>
<version.maven-build-helper>3.0.0</version.maven-build-helper>
<version.maven-compiler>3.8.1</version.maven-compiler>
<version.maven-dependency>3.1.1</version.maven-dependency>
<version.maven-jar>3.1.2</version.maven-jar>
<version.maven-pmd>3.12.0</version.maven-pmd>
<version.maven-rpm>2.2.0</version.maven-rpm>
<version.maven-shade>3.2.1</version.maven-shade>
<version.maven-surefire>3.0.0-M3</version.maven-surefire>
<version.netty>4.1.42.Final</version.netty>
<version.netty-tcnative>2.0.25.Final</version.netty-tcnative>
<version.os-maven>1.5.0.Final</version.os-maven>
<version.powermock>1.6.4</version.powermock>
<version.slf4j>1.7.26</version.slf4j>
<version.spotbugs-maven>3.1.7</version.spotbugs-maven>
<version.spotbugs-maven>3.1.12.1</version.spotbugs-maven>
<version.spring-boot>2.1.3.RELEASE</version.spring-boot>
<version.tyrus>1.15</version.tyrus>
</properties>
@@ -99,7 +101,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${version.jackson}</version>
<version>${version.jackson.databind}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
@@ -396,6 +398,12 @@
<version>${version.jcip-annotations}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${version.easymock}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
@@ -411,7 +419,7 @@
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.8.0</version>
<version>2.10.0</version>
<configuration>
<compilerCompliance>${maven.compiler.source}</compilerCompliance>
<compilerSource>${maven.compiler.source}</compilerSource>
@@ -429,7 +437,7 @@
<plugin>
<groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId>
<version>2.8.0</version>
<version>2.10.0</version>
<configuration>
<predefinedSortOrder>recommended_2008_06</predefinedSortOrder>
<createBackupFile>false</createBackupFile>
@@ -573,7 +581,7 @@
<configuration>
<rules>
<requireMavenVersion>
<version>3.0.5</version>
<version>3.5.0</version>
</requireMavenVersion>
<requireJavaVersion>
<version>[${maven.compiler.target},)</version>
@@ -744,6 +752,9 @@
<name>m2e.version</name>
</property>
</activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
<build>
<pluginManagement>
<plugins>
@@ -804,5 +815,14 @@
</pluginManagement>
</build>
</profile>
<profile>
<id>jdk-release-flag</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
</profile>
</profiles>
</project>
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
if [[ `uname` == "Darwin" ]]; then
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
else
THIS_SCRIPT=`readlink -f $0`
fi
THIS_DIR="${THIS_SCRIPT%/*}"
BASE_DIR=${THIS_DIR}/..
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1024m -Xms128m"
JVM_ARGS="${JVM_ARGS} -Dlogging.config=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely
+12 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.5-SNAPSHOT</version>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>timely-server</artifactId>
<dependencies>
@@ -177,11 +177,22 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
+61 -35
View File
@@ -60,6 +60,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -75,7 +76,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.api.response.TimelyException;
import timely.auth.AuthCache;
import timely.auth.VisibilityCache;
import timely.configuration.Configuration;
@@ -132,6 +132,7 @@ public class Server {
private static final String OS_VERSION = "os.version";
private static final String WS_PATH = "/websocket";
private SslContext sslCtx = null;
private CuratorFramework curatorFramework;
protected static final CountDownLatch LATCH = new CountDownLatch(1);
static ConfigurableApplicationContext applicationContext;
@@ -233,11 +234,12 @@ public class Server {
public static void main(String[] args) throws Exception {
Configuration conf = initializeConfiguration(args);
Server.applicationContext = initializeContext(args);
Configuration conf = Server.applicationContext.getBean(Configuration.class);
Server s = new Server(conf);
Server server = new Server(conf);
try {
s.run();
server.run(createSSLContext(conf));
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
@@ -245,16 +247,18 @@ public class Server {
LOG.error("Error running server.", e);
} finally {
try {
s.shutdown();
server.shutdown();
} catch (Exception e) {
System.exit(1);
}
}
}
protected static Configuration initializeConfiguration(String[] args) {
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args);
return applicationContext.getBean(Configuration.class);
protected static ConfigurableApplicationContext initializeContext(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
}
private void shutdownHook() {
@@ -286,8 +290,9 @@ public class Server {
channelFutures.add(wsChannelHandle.close());
}
int udpChannel = 1;
for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle");
LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close());
}
@@ -296,14 +301,13 @@ public class Server {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
}
});
Integer quietPeriod = config.getServer().getShutdownQuietPeriod();
int quietPeriod = config.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -348,40 +352,58 @@ public class Server {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString());
LOG.error("{}", e.getMessage());
e.printStackTrace();
LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
}
});
try {
LOG.info("Flushing datastore.");
dataStore.flush();
} catch (TimelyException e) {
LOG.error("Error flushing to server during shutdown", e);
if (dataStore != null) {
try {
LOG.info("Closing dataStore");
dataStore.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (dataStoreCache != null) {
try {
LOG.info("Closing dataStoreCache");
dataStoreCache.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
try {
LOG.info("Closing MetaCacheFactory");
LOG.info("Closing metaCacheFactory");
MetaCacheFactory.close();
} catch (Exception e) {
LOG.error("Error closing MetaCacheFactory during shutdown", e);
LOG.error(e.getMessage(), e);
}
try {
LOG.info("Closing WebSocketRequestDecoder");
LOG.info("Closing webSocketRequestDecoder subscriptions");
WebSocketRequestDecoder.close();
} catch (Exception e) {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
LOG.error(e.getMessage(), e);
}
if (curatorFramework != null) {
curatorFramework.close();
try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (applicationContext != null) {
LOG.info("Closing applicationContext");
applicationContext.close();
try {
LOG.info("Closing applicationContext");
applicationContext.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
this.shutdown = true;
LOG.info("Server shut down.");
@@ -414,10 +436,12 @@ public class Server {
}
}
public void run() throws Exception {
public void run(SslContext sslCtx) throws Exception {
RetryPolicy retryPolicy = new RetryForever(1000);
curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(), 30000, 10000,
int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(config.getAccumulo().getZookeeperTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(), timeout, 10000,
retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
@@ -479,7 +503,6 @@ public class Server {
final int httpPort = config.getHttp().getPort();
final String httpIp = config.getHttp().getIp();
SslContext sslCtx = createSSLContext(config);
if (sslCtx instanceof OpenSslServerContext) {
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
String application = "Timely_" + httpPort;
@@ -536,7 +559,7 @@ public class Server {
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
}
protected SslContext createSSLContext(Configuration config) throws Exception {
public static SslContext createSSLContext(Configuration config) throws Exception {
ServerSsl sslCfg = config.getSecurity().getServerSsl();
Boolean generate = sslCfg.isUseGeneratedKeypair();
@@ -544,7 +567,7 @@ public class Server {
if (generate) {
LOG.warn("Using generated self signed server certificate");
Date begin = new Date();
Date end = new Date(begin.getTime() + 86400000);
Date end = new Date(begin.getTime() + TimeUnit.DAYS.toMillis(7));
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else {
@@ -611,7 +634,8 @@ public class Server {
ch.pipeline().addLast("cors", new CorsHandler(cors));
ch.pipeline().addLast("queryDecoder",
new timely.netty.http.HttpRequestDecoder(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("strict", new StrictTransportHandler(config));
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("doLogin",
@@ -624,7 +648,9 @@ public class Server {
ch.pipeline().addLast("version", new HttpVersionRequestHandler());
ch.pipeline().addLast("cache", new HttpCacheRequestHandler(dataStoreCache));
ch.pipeline().addLast("put", new HttpMetricPutHandler(dataStore));
ch.pipeline().addLast("error", new TimelyExceptionHandler());
ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
}
};
}
@@ -52,7 +52,8 @@ public class StandaloneServer extends Server {
System.exit(1);
}
Configuration conf = initializeConfiguration(args);
StandaloneServer.applicationContext = initializeContext(args);
Configuration conf = StandaloneServer.applicationContext.getBean(Configuration.class);
File accumuloDir = new File(tmp, "accumulo");
MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret");
@@ -84,7 +85,7 @@ public class StandaloneServer extends Server {
try {
LOG.info("Starting StandaloneServer");
StandaloneServer s = new StandaloneServer(conf);
s.run();
s.run(createSSLContext(conf));
} catch (Exception e) {
System.err.println("Error starting server");
e.printStackTrace();
@@ -8,6 +8,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.base.Joiner;
@@ -39,6 +40,8 @@ public class MetricAdapter {
private static final TagParser tagParser = new TagParser();
private static final TagListParser tagListParser = new TagListParser();
private static Pattern equalsPattern = Pattern.compile("=");
private static Pattern commaPattern = Pattern.compile(",");
public static final ColumnVisibility EMPTY_VISIBILITY = new ColumnVisibility();
public static final String VISIBILITY_TAG = "viz";
@@ -59,10 +62,10 @@ public class MetricAdapter {
// are used as a delimiter
String k = t.getKey();
String v = t.getValue();
k = k.replaceAll("=", "\\=");
k = k.replaceAll(",", "\\,");
v = v.replaceAll("=", "\\=");
v = v.replaceAll(",", "\\,");
k = equalsPattern.matcher(k).replaceAll("\\=");
k = commaPattern.matcher(k).replaceAll("\\,");
v = equalsPattern.matcher(v).replaceAll("\\=");
v = commaPattern.matcher(v).replaceAll("\\,");
newTags.add(new Tag(k, v));
}
return newTags;
@@ -75,10 +78,10 @@ public class MetricAdapter {
// are used as a delimiter
String k = t.getKey();
String v = t.getValue();
k = k.replaceAll("=", "\\=");
k = k.replaceAll(",", "\\,");
v = v.replaceAll("=", "\\=");
v = v.replaceAll(",", "\\,");
k = equalsPattern.matcher(k).replaceAll("\\=");
k = commaPattern.matcher(k).replaceAll("\\,");
v = equalsPattern.matcher(v).replaceAll("\\=");
v = commaPattern.matcher(v).replaceAll("\\,");
newTags.put(k, v);
}
return newTags;
+33 -42
View File
@@ -5,6 +5,10 @@ import java.util.List;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Meta implements Comparable<Meta> {
@@ -49,9 +53,15 @@ public class Meta implements Comparable<Meta> {
public List<Key> toKeys() {
List<Key> keys = new ArrayList<>();
keys.add(new Key(METRIC_PREFIX + metric));
keys.add(new Key(TAG_PREFIX + metric, tagKey));
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue));
if (StringUtils.isNotBlank(metric)) {
keys.add(new Key(METRIC_PREFIX + metric));
}
if (StringUtils.isNotBlank(tagKey)) {
keys.add(new Key(TAG_PREFIX + metric, tagKey));
}
if (StringUtils.isNotBlank(tagKey) && StringUtils.isNotBlank(tagValue)) {
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue));
}
return keys;
}
@@ -97,39 +107,25 @@ public class Meta implements Comparable<Meta> {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((metric == null) ? 0 : metric.hashCode());
result = prime * result + ((tagKey == null) ? 0 : tagKey.hashCode());
result = prime * result + ((tagValue == null) ? 0 : tagValue.hashCode());
return result;
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(metric);
builder.append(tagKey);
builder.append(tagValue);
return builder.toHashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
public boolean equals(Object o) {
if (o instanceof Meta) {
Meta obj = (Meta) o;
EqualsBuilder builder = new EqualsBuilder();
builder.append(this.metric, obj.metric);
builder.append(this.tagKey, obj.tagKey);
builder.append(this.tagValue, obj.tagValue);
return builder.isEquals();
} else {
return false;
if (getClass() != obj.getClass())
return false;
Meta other = (Meta) obj;
if (metric == null) {
if (other.metric != null)
return false;
} else if (!metric.equals(other.metric))
return false;
if (tagKey == null) {
if (other.tagKey != null)
return false;
} else if (!tagKey.equals(other.tagKey))
return false;
if (tagValue == null) {
if (other.tagValue != null)
return false;
} else if (!tagValue.equals(other.tagValue))
return false;
return true;
}
}
@Override
@@ -139,15 +135,10 @@ public class Meta implements Comparable<Meta> {
@Override
public int compareTo(Meta o) {
int result = metric.compareTo(o.metric);
if (result != 0) {
return result;
}
result = tagKey.compareTo(o.tagKey);
if (result != 0) {
return result;
}
return tagValue.compareTo(o.tagValue);
CompareToBuilder builder = new CompareToBuilder();
builder.append(this.metric, o.metric);
builder.append(this.tagKey, o.tagKey);
builder.append(this.tagValue, o.tagValue);
return builder.toComparison();
}
}
@@ -156,8 +156,10 @@ public class MetricsResponse {
b.append(TR_END);
String prevMetric = null;
StringBuilder tags = new StringBuilder();
Map<String, Long> tagMap = new HashMap<>();
for (Meta m : tree) {
if (prevMetric != null && !m.getMetric().equals(prevMetric)) {
tagMap.clear();
b.append(TR_START);
b.append(TD_START).append(prevMetric).append(TD_END);
b.append(TD_START).append(tags.toString()).append(TD_END);
@@ -168,8 +170,12 @@ public class MetricsResponse {
if (prevMetric == null) {
prevMetric = m.getMetric();
}
long numValues = tagMap.getOrDefault(m.getTagKey(), 0l);
if (!(this.ignoredTags.contains(m.getTagKey()))) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" ");
if (numValues <= conf.getMetaCache().getMaxTagValues()) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" ");
tagMap.put(m.getTagKey(), ++numValues);
}
}
}
b.append(TR_START);
@@ -12,6 +12,8 @@ public class Accumulo {
@NotBlank
private String instanceName;
@NotBlank
private String zookeeperTimeout = "120s";
@NotBlank
private String username;
@NotBlank
private String password;
@@ -46,6 +48,14 @@ public class Accumulo {
this.instanceName = instanceName;
}
public String getZookeeperTimeout() {
return zookeeperTimeout;
}
public void setZookeeperTimeout(String zookeeperTimeout) {
this.zookeeperTimeout = zookeeperTimeout;
}
public String getUsername() {
return username;
}
@@ -13,6 +13,7 @@ public class Cache {
private List<String> nonCachedMetrics = new ArrayList<>();
private long maxUniqueTagSets = 50000;
private long flushInterval = 5000;
private long staleCacheExpiration = 1800000;
public HashMap<String, Integer> getMetricAgeOffHours() {
return metricAgeOffHours;
@@ -57,4 +58,12 @@ public class Cache {
public void setFlushInterval(long flushInterval) {
this.flushInterval = flushInterval;
}
public long getStaleCacheExpiration() {
return staleCacheExpiration;
}
public void setStaleCacheExpiration(long staleCacheExpiration) {
this.staleCacheExpiration = staleCacheExpiration;
}
}
@@ -1,6 +1,7 @@
package timely.configuration;
import java.util.List;
import java.util.Objects;
import com.google.common.collect.Lists;
import timely.validator.NotEmptyIfFieldSet;
@@ -76,4 +77,24 @@ public class ServerSsl {
public void setUseCiphers(List<String> useCiphers) {
this.useCiphers = useCiphers;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ServerSsl serverSsl = (ServerSsl) o;
return useGeneratedKeypair == serverSsl.useGeneratedKeypair && useOpenssl == serverSsl.useOpenssl
&& Objects.equals(certificateFile, serverSsl.certificateFile)
&& Objects.equals(keyFile, serverSsl.keyFile) && Objects.equals(keyPassword, serverSsl.keyPassword)
&& Objects.equals(trustStoreFile, serverSsl.trustStoreFile)
&& Objects.equals(useCiphers, serverSsl.useCiphers);
}
@Override
public int hashCode() {
return Objects.hash(certificateFile, keyFile, keyPassword, trustStoreFile, useGeneratedKeypair, useOpenssl,
useCiphers);
}
}
@@ -48,6 +48,7 @@ import java.util.TimeZone;
import java.util.regex.Pattern;
import javax.activation.MimetypesFileTypeMap;
import javax.net.ssl.SSLHandshakeException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -123,6 +124,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
public static final int HTTP_CACHE_SECONDS = 60;
private boolean ignoreSslHandshakeErrors = false;
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
@@ -221,6 +223,10 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// ignore SSLHandshakeException when using a self-signed server certificate
if (ignoreSslHandshakeErrors && cause.getCause() instanceof SSLHandshakeException) {
return;
}
LOG.error("Error serving file", cause);
if (ctx.channel().isActive()) {
sendError(ctx, INTERNAL_SERVER_ERROR);
@@ -379,4 +385,9 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
}
public HttpStaticFileServerHandler setIgnoreSslHandshakeErrors(boolean ignoreSslHandshakeErrors) {
this.ignoreSslHandshakeErrors = ignoreSslHandshakeErrors;
return this;
}
}
@@ -1,5 +1,7 @@
package timely.netty.http;
import javax.net.ssl.SSLHandshakeException;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -12,6 +14,7 @@ import timely.api.response.TimelyException;
public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyException> implements TimelyHttpHandler {
private static final Logger LOG = LoggerFactory.getLogger(TimelyExceptionHandler.class);
private boolean ignoreSslHandshakeErrors = false;
@Override
protected void channelRead0(ChannelHandlerContext ctx, TimelyException msg) throws Exception {
@@ -20,6 +23,10 @@ public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyEx
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// ignore SSLHandshakeException when using a self-signed server certificate
if (ignoreSslHandshakeErrors && cause.getCause() instanceof SSLHandshakeException) {
return;
}
LOG.error("Unhandled exception in pipeline", cause);
if (cause instanceof TimelyException) {
this.sendHttpError(ctx, (TimelyException) cause);
@@ -32,4 +39,8 @@ public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyEx
}
}
public TimelyExceptionHandler setIgnoreSslHandshakeErrors(boolean ignoreSslHandshakeErrors) {
this.ignoreSslHandshakeErrors = ignoreSslHandshakeErrors;
return this;
}
}
@@ -109,7 +109,6 @@ public class WebSocketRequestDecoder extends MessageToMessageDecoder<WebSocketFr
}
public static void close() {
LOG.info("Closing subscriptions");
SubscriptionRegistry.get().forEach((k, v) -> v.close());
}
@@ -29,8 +29,6 @@ public interface DataStore {
List<QueryResponse> query(QueryRequest msg) throws TimelyException;
void flush() throws TimelyException;
long getAgeOffForMetric(String metricName);
List<Range> getQueryRanges(String metric, long start, long end, Set<Tag> colFamValues);
@@ -45,4 +43,6 @@ public interface DataStore {
DataStoreCache getCache();
InternalMetrics getInternalMetrics();
void close() throws Exception;
}
@@ -22,10 +22,10 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -127,7 +127,7 @@ public class DataStoreImpl implements DataStore {
private final String metricsTable;
private final String metaTable;
private final InternalMetrics internalMetrics;
private final Timer internalMetricsTimer = new Timer("InternalMetricsTimer", true);
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private final int scannerThreads;
private final long maxDownsampleMemory;
private final BatchWriterConfig bwConfig;
@@ -147,6 +147,7 @@ public class DataStoreImpl implements DataStore {
Accumulo accumuloConf = conf.getAccumulo();
properties.put("instance.name", accumuloConf.getInstanceName());
properties.put("instance.zookeeper.host", accumuloConf.getZookeepers());
properties.put("instance.zookeeper.timeout", accumuloConf.getZookeeperTimeout());
final ClientConfiguration aconf = ClientConfiguration.fromMap(properties);
final Instance instance = new ZooKeeperInstance(aconf);
connector = instance.getConnector(accumuloConf.getUsername(),
@@ -243,14 +244,9 @@ public class DataStoreImpl implements DataStore {
}
internalMetrics = new InternalMetrics(conf);
internalMetricsTimer.schedule(new TimerTask() {
@Override
public void run() {
internalMetrics.getMetricsAndReset().forEach(m -> store(m, false));
}
}, METRICS_PERIOD, METRICS_PERIOD);
executorService.scheduleAtFixedRate(
() -> internalMetrics.getMetricsAndReset().forEach(m -> store(m, false)), METRICS_PERIOD,
METRICS_PERIOD, TimeUnit.MILLISECONDS);
this.metaCache = MetaCacheFactory.getCache(conf);
} catch (Exception e2) {
@@ -354,10 +350,7 @@ public class DataStoreImpl implements DataStore {
internalMetrics.incrementMetricsReceived(1);
List<Meta> toCache = new ArrayList<>(metric.getTags().size());
for (final Tag tag : metric.getTags()) {
Meta key = new Meta(metric.getName(), tag.getKey(), tag.getValue());
if (!metaCache.contains(key)) {
toCache.add(key);
}
toCache.add(new Meta(metric.getName(), tag.getKey(), tag.getValue()));
}
if (!toCache.isEmpty()) {
final Set<Mutation> muts = new TreeSet<>(new Comparator<Mutation>() {
@@ -378,7 +371,7 @@ public class DataStoreImpl implements DataStore {
MetaKeySet mks = new MetaKeySet();
toCache.forEach(m -> mks.addAll(m.toKeys()));
internalMetrics.incrementMetaKeysInserted(mks.size());
muts.addAll(mks.toMutations());
muts.addAll(mks.toMutations(metric.getValue().getTimestamp()));
try {
metaWriter.get().addMutations(muts);
} catch (MutationsRejectedException e) {
@@ -486,19 +479,6 @@ public class DataStoreImpl implements DataStore {
return result;
}
@Override
public void flush() {
internalMetricsTimer.cancel();
writers.forEach(w -> {
try {
w.close();
} catch (final Exception ex) {
LOG.warn("Error shutting down batchwriter", ex);
}
});
}
@Override
public SearchLookupResponse lookup(SearchLookupRequest msg) throws TimelyException {
long startMillis = System.currentTimeMillis();
@@ -1056,4 +1036,24 @@ public class DataStoreImpl implements DataStore {
}
}
@Override
public void close() throws Exception {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
writers.forEach(w -> {
try {
w.close();
} catch (final Exception ex) {
LOG.warn("Error shutting down batchwriter: ", ex.getMessage());
}
});
}
}
@@ -5,16 +5,20 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.ClientConfiguration;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Instance;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.ZooKeeperInstance;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.data.Key;
@@ -34,7 +38,7 @@ public class MetaCacheImpl implements MetaCache {
private volatile boolean closed = false;
private Cache<Meta, Object> cache = null;
private Configuration configuration;
private Timer timer = new Timer("MetaCacheImpl", true);
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
@Override
public void init(Configuration config) {
@@ -42,13 +46,8 @@ public class MetaCacheImpl implements MetaCache {
cache = Caffeine.newBuilder().initialCapacity(1000).build();
long cacheRefreshMinutes = configuration.getMetaCache().getCacheRefreshMinutes();
if (cacheRefreshMinutes > 0) {
timer.schedule(new TimerTask() {
@Override
public void run() {
refreshCache(config.getMetaCache().getExpirationMinutes());
}
}, 60000, cacheRefreshMinutes * 60 * 1000);
executorService.scheduleAtFixedRate(() -> refreshCache(config.getMetaCache().getExpirationMinutes()), 1,
cacheRefreshMinutes, TimeUnit.MINUTES);
}
}
@@ -63,12 +62,16 @@ public class MetaCacheImpl implements MetaCache {
Map<String, Map<String, Long>> metricMap = new HashMap<>();
Scanner scanner = null;
try {
Map<Meta, Object> newCache = new HashMap<>();
final Instance instance = new ZooKeeperInstance(aConf);
Connector connector = instance.getConnector(accumuloConf.getUsername(),
new PasswordToken(accumuloConf.getPassword()));
scanner = connector.createScanner(metaTable, Authorizations.EMPTY);
LOG.debug("Begin scanning " + metaTable);
Range metricRange = new Range(new Key(Meta.METRIC_PREFIX), true, new Key(Meta.METRIC_PREFIX + '\0'), false);
Key metricPrefixBeginKey = new Key(Meta.METRIC_PREFIX);
int firstChar = Meta.METRIC_PREFIX.charAt(0);
Key metricPrefixEndKey = new Key(String.valueOf((char) (firstChar + 1)) + ':');
Range metricRange = new Range(metricPrefixBeginKey, true, metricPrefixEndKey, false);
scanner.setRange(metricRange);
Set<String> allMetrics = new TreeSet<>();
for (Map.Entry<Key, Value> entry : scanner) {
@@ -96,20 +99,20 @@ public class MetaCacheImpl implements MetaCache {
if (entry.getKey().getTimestamp() > oldestTimestamp) {
tagMap.put(tagKey, ++numTagValues);
if (numTagValues <= configuration.getMetaCache().getMaxTagValues()) {
cache.put(meta, DUMMY);
newCache.put(meta, DUMMY);
} else {
// found maxTagValues on this refresh, so remove this key
cache.invalidate(meta);
// found maxTagValues on this refresh
maxedOutValues = true;
}
} else {
// this entry is more than expirationMinutes old, so remove it
cache.invalidate(meta);
}
}
}
synchronized (cache) {
cache.invalidateAll();
cache.putAll(newCache);
}
LOG.debug("Finished scanning " + metaTable);
} catch (Exception e) {
} catch (AccumuloException | AccumuloSecurityException | TableNotFoundException e) {
LOG.error(e.getMessage(), e);
} finally {
if (scanner != null) {
@@ -141,6 +144,16 @@ public class MetaCacheImpl implements MetaCache {
@Override
public void close() {
this.closed = true;
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
@Override
+137 -61
View File
@@ -11,9 +11,9 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
@@ -71,6 +71,7 @@ public class DataStoreCache {
private Set<String> nonCachedMetrics = Collections.synchronizedSet(new HashSet<>());
private DistributedAtomicValue nonCachedMetricsIP = null;
private InterProcessReadWriteLock nonCachedMetricsIPRWLock;
private long staleCacheExpiration;
private long maxUniqueTagSets;
private final Security security;
private Map<String, Long> minimumAgeOff;
@@ -78,7 +79,7 @@ public class DataStoreCache {
private int flushBatch = 0;
private int numBatches = 5;
private InternalMetrics internalMetrics;
private Timer maintenanceTimer = new Timer("DataStoreCacheTimer");
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(6);
public DataStoreCache(CuratorFramework curatorFramework, Configuration conf) {
if (curatorFramework != null) {
@@ -89,6 +90,7 @@ public class DataStoreCache {
LOG.info("Reading initial values from nonCachedMetricsIP");
readNonCachedMetricsIP();
maxUniqueTagSets = conf.getCache().getMaxUniqueTagSets();
staleCacheExpiration = conf.getCache().getStaleCacheExpiration();
security = conf.getSecurity();
Map<String, Integer> cacheAgeOff = conf.getCache().getMetricAgeOffHours();
Map<String, Integer> accumuloAgeOff = conf.getMetricAgeOffDays();
@@ -101,70 +103,58 @@ public class DataStoreCache {
minimumAgeOffForIterator = getAgeOffForIterator(minimumAgeOff);
maintenanceTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
flushCaches(flushBatch);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
flushBatch++;
if (flushBatch == numBatches) {
flushBatch = 0;
}
executorService.scheduleAtFixedRate(() -> {
try {
flushCaches(flushBatch);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
flushBatch++;
if (flushBatch == numBatches) {
flushBatch = 0;
}
}
}, conf.getCache().getFlushInterval(), conf.getCache().getFlushInterval());
}, conf.getCache().getFlushInterval(), conf.getCache().getFlushInterval(), TimeUnit.MILLISECONDS);
maintenanceTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
ageOffGorillaStores();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
executorService.scheduleAtFixedRate(() -> {
try {
DataStoreCache.this.removeStaleMetrics();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, (600 * 1000), (600 * 1000));
}, 5, 5, TimeUnit.MINUTES);
maintenanceTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
archiveGorillaStoreCurrentCompressors();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
executorService.scheduleAtFixedRate(() -> {
try {
ageOffGorillaStores();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, (3600 * 1000), (3600 * 1000));
}, 10, 10, TimeUnit.MINUTES);
maintenanceTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
pruneStats();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
executorService.scheduleAtFixedRate(() -> {
try {
archiveGorillaStoreCurrentCompressors();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, (600 * 1000), (600 * 1000));
}, 60, 60, TimeUnit.MINUTES);
maintenanceTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
reportInternalMetrics();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
executorService.scheduleAtFixedRate(() -> {
try {
pruneStats();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, (60 * 1000), (60 * 1000));
}, 10, 10, TimeUnit.MINUTES);
executorService.scheduleAtFixedRate(() -> {
try {
reportInternalMetrics();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, 1, 1, TimeUnit.MINUTES);
}
private void addNonCachedMetrics(Collection<String> nonCachedMetricsUpdate) {
@@ -216,6 +206,11 @@ public class DataStoreCache {
nonCachedMetricsIPRWLock = new InterProcessReadWriteLock(curatorFramework, NON_CACHED_METRICS_LOCK_PATH);
testIPRWLock(curatorFramework, nonCachedMetricsIPRWLock, NON_CACHED_METRICS_LOCK_PATH);
nonCachedMetricsIP = new DistributedAtomicValue(curatorFramework, NON_CACHED_METRICS, new RetryForever(1000));
try {
nonCachedMetricsIP.initialize(SerializationUtils.serialize(new TreeSet<>()));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
TreeCacheListener nonCachedMetricsListener = new TreeCacheListener() {
@Override
@@ -227,8 +222,7 @@ public class DataStoreCache {
}
};
try {
TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS);
try (TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS)) {
nonCachedMetricsTreeCache.getListenable().addListener(nonCachedMetricsListener);
nonCachedMetricsTreeCache.start();
} catch (Exception e) {
@@ -437,6 +431,68 @@ public class DataStoreCache {
}
}
private void removeStaleMetrics() {
Set<String> metricsToRemove = new HashSet<>();
long stamp = gorillaMapLock.readLock();
try {
long now = System.currentTimeMillis();
for (Map.Entry<String, Map<TaggedMetric, GorillaStore>> entry : gorillaMap.entrySet()) {
String metricName = entry.getKey();
long totalMsecToMetric = 0;
long numStoresWithMultipleEntries = 0;
int tagVariations = entry.getValue().values().size();
for (GorillaStore store : entry.getValue().values()) {
long numEntries = store.getNumEntries();
long oldestTimestamp = store.getOldestTimestamp();
long newestTimestamp = store.getNewestTimestamp();
if (numEntries > 1) {
long msecToMetric = (newestTimestamp - oldestTimestamp) / (numEntries - 1);
totalMsecToMetric += msecToMetric;
numStoresWithMultipleEntries++;
}
}
if (numStoresWithMultipleEntries > 0) {
long avgMsecToMetric = totalMsecToMetric / numStoresWithMultipleEntries;
long ageOfNewest = now - getNewestTimestamp(metricName, stamp);
// do not delete slow-arriving metrics based on this criteria
// only consider metrics that should have arrived in staleCacheExpiration * 0.5
LOG.trace("metric:{} tagVariations:{} avgMsecToMetric:{}", metricName, tagVariations,
avgMsecToMetric);
if (avgMsecToMetric < (staleCacheExpiration * 0.5)) {
if (ageOfNewest > staleCacheExpiration) {
LOG.info(
"Removing metric:{} tagVariations:{} avgMsecToMetric:{} ageOfNewestEntry:{} > staleCacheExpiration:{}",
metricName, tagVariations, avgMsecToMetric, ageOfNewest, staleCacheExpiration);
metricsToRemove.add(metricName);
}
} else {
LOG.trace(
"Skipping staleness evaluation of metric:{} tagVariations:{} avgMsecToMetric:{} < (0.5 * staleCacheExpiration):{}",
metricName, tagVariations, avgMsecToMetric, (staleCacheExpiration / 2));
}
}
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
gorillaMapLock.unlockRead(stamp);
}
if (metricsToRemove.size() > 0) {
stamp = gorillaMapLock.writeLock();
try {
for (String metricName : metricsToRemove) {
gorillaMap.remove(metricName);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
gorillaMapLock.unlockWrite(stamp);
}
}
}
public long getAgeOffForMetric(String metricName) {
if (this.minimumAgeOff.containsKey(metricName)) {
return this.minimumAgeOff.get(metricName);
@@ -512,6 +568,7 @@ public class DataStoreCache {
try {
metricMap = gorillaMap.get(metric);
if (metricMap == null) {
LOG.info("Creating new cache for metric:{}", metric);
metricMap = new HashMap<>();
needWrite = true;
}
@@ -769,9 +826,13 @@ public class DataStoreCache {
}
public long getNewestTimestamp(String metric) {
return getNewestTimestamp(metric, 0);
}
public long getNewestTimestamp(String metric, long lockStamp) {
long newest = 0;
long stamp = gorillaMapLock.readLock();
long stamp = lockStamp == 0 ? gorillaMapLock.readLock() : lockStamp;
try {
Map<TaggedMetric, GorillaStore> gorillaStoreMap = gorillaMap.get(metric);
for (Map.Entry<TaggedMetric, GorillaStore> entry : gorillaStoreMap.entrySet()) {
@@ -780,7 +841,9 @@ public class DataStoreCache {
}
}
} finally {
gorillaMapLock.unlockRead(stamp);
if (lockStamp == 0) {
gorillaMapLock.unlockRead(stamp);
}
}
return newest;
}
@@ -848,4 +911,17 @@ public class DataStoreCache {
}
return response;
}
public void close() {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
}
@@ -0,0 +1,189 @@
package timely.store.compaction;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.TabletId;
import org.apache.accumulo.server.fs.FileRef;
import org.apache.accumulo.tserver.compaction.CompactionPlan;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
import org.apache.accumulo.tserver.compaction.MajorCompactionRequest;
import org.apache.commons.collections4.trie.PatriciaTrie;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.MapConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.store.MetricAgeOffIterator;
public class MetricCompactionStrategy extends CompactionStrategy {
private static final Logger LOG = LoggerFactory.getLogger(MetricCompactionStrategy.class);
private boolean hasMinAgeOff;
private MetricAgeOffConfiguration minAgeOffConfig;
private boolean logOnly;
private String filterPrefix;
private String majcIteratorName;
private long explicitTimeMillis;
private boolean useExplicitTime;
public static final String LOG_ONLY_KEY = "logonly";
public static final String MAJC_ITERATOR_NAME_KEY = "majc.name";
public static final String DEFAULT_MAJC_ITERATOR_NAME = "ageoffmetrics";
public static final String AGE_OFF_PREFIX = MetricAgeOffIterator.AGE_OFF_PREFIX;
public static final String FILTER_PREFIX_KEY = AGE_OFF_PREFIX + "filter";
public static final String MIN_AGEOFF_KEY = AGE_OFF_PREFIX + "min";
public static final String DEFAULT_AGEOFF_KEY_SUFFIX = MetricAgeOffIterator.DEFAULT_AGEOFF_KEY;
public static final String DEFAULT_AGEOFF_KEY = AGE_OFF_PREFIX + DEFAULT_AGEOFF_KEY_SUFFIX;
@Override
public void init(Map<String, String> options) {
// options:
// logonly - disables compaction and will only log result
// majciterator - the iterator name to gather age-off values
// filter - limit the compaction to just a subset of prefixes
// min - minimum age-off which may override the metric age-off;
// allows more data to accumulate in tablets without being removed
logOnly = options.containsKey(LOG_ONLY_KEY) && Boolean.parseBoolean(options.get(LOG_ONLY_KEY));
majcIteratorName = options.getOrDefault(MAJC_ITERATOR_NAME_KEY, DEFAULT_MAJC_ITERATOR_NAME);
filterPrefix = options.get(FILTER_PREFIX_KEY);
if (options.containsKey(MIN_AGEOFF_KEY)) {
long minAgeOff = Long.parseLong(options.get(MIN_AGEOFF_KEY));
hasMinAgeOff = true;
minAgeOffConfig = MetricAgeOffConfiguration.newFromDefaultingMinimum(minAgeOff);
}
}
@Override
public boolean shouldCompact(MajorCompactionRequest request) {
TabletId tabletId = request.getTabletId();
if (null == tabletId.getEndRow() || null == tabletId.getPrevEndRow()) {
return false;
} else if (request.getFiles().isEmpty()) {
return false;
}
// check if prev/end key are both at age-off intervals
// skip tablet if there is no prev-end key, or previous age-off is not at max;
// other age-off will compact
Optional<String> endName = TabletRowAdapter.decodeRowPrefix(tabletId.getEndRow());
OptionalLong endOffset = TabletRowAdapter.decodeRowOffset(tabletId.getEndRow());
Optional<String> prevEndName = TabletRowAdapter.decodeRowPrefix(tabletId.getPrevEndRow());
if (!endName.isPresent() || !prevEndName.isPresent() || !endOffset.isPresent()) {
return false;
}
// compute age-off from majc iterator
// then maximize on the min-default-ageoff so that the
// configuration with largest age-off is used
MetricAgeOffConfiguration ageOffConfig = MetricAgeOffConfiguration.newFromRequest(request, majcIteratorName);
if (hasMinAgeOff) {
ageOffConfig = MetricAgeOffConfiguration.maximizeDefaultAgeOff(ageOffConfig, minAgeOffConfig);
}
long currentTime = useExplicitTime ? explicitTimeMillis : System.currentTimeMillis();
long ageOffComputed = currentTime - ageOffConfig.computeAgeOff(endName.get());
boolean shouldCompact = (ageOffComputed > endOffset.getAsLong() && endName.equals(prevEndName))
&& (null == filterPrefix || endName.get().startsWith(filterPrefix));
if (LOG.isDebugEnabled() && logOnly && shouldCompact) {
LOG.debug("Tablet will be metric age-off compacted {threshold: {}, endKey: {}, offset: {}, prevKey: {}}",
ageOffComputed, endName, endOffset.getAsLong(), prevEndName);
} else if (LOG.isTraceEnabled()) {
LOG.trace("Tablet check metric compaction {threshold: {}, endKey: {}, offset: {}, prevKey: {}, result: {}}",
ageOffComputed, endName, endOffset.getAsLong(), prevEndName, shouldCompact);
}
return shouldCompact && !logOnly;
}
@Override
public CompactionPlan getCompactionPlan(MajorCompactionRequest request) {
CompactionPlan plan = null;
if (shouldCompact(request)) {
plan = new CompactionPlan();
for (FileRef ref : request.getFiles().keySet()) {
plan.inputFiles.add(ref);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Tablet selected for age-off: " + TabletRowAdapter.toDebugOutput(request.getTabletId()));
}
}
return plan;
}
void setExplicitTime(long timeMillis) {
explicitTimeMillis = timeMillis;
useExplicitTime = true;
}
private static class MetricAgeOffConfiguration {
private final PatriciaTrie<Long> ageoffs;
private final long defaultAgeOff;
private MetricAgeOffConfiguration(PatriciaTrie<Long> ageoffs, long defaultAgeOff) {
this.ageoffs = ageoffs;
this.defaultAgeOff = defaultAgeOff;
}
public static MetricAgeOffConfiguration maximizeDefaultAgeOff(MetricAgeOffConfiguration x,
MetricAgeOffConfiguration y) {
return (x.defaultAgeOff > y.defaultAgeOff) ? x : y;
}
public static MetricAgeOffConfiguration newFromDefaultingMinimum(long minAgeOff) {
return new MetricAgeOffConfiguration(new PatriciaTrie<>(), minAgeOff);
}
public static MetricAgeOffConfiguration newFromRequest(MajorCompactionRequest request,
String majcIteratorName) {
Configuration config = new MapConfiguration(request.getTableProperties());
String majcIteratorKey = Property.TABLE_ITERATOR_MAJC_PREFIX.getKey() + majcIteratorName;
if (LOG.isTraceEnabled()) {
LOG.trace("Using key lookup for iterator: {}", majcIteratorKey);
}
Configuration configAgeOff = config.subset((majcIteratorKey + ".opt"));
if (null == configAgeOff.getString(DEFAULT_AGEOFF_KEY)) {
throw new IllegalArgumentException(
DEFAULT_AGEOFF_KEY_SUFFIX + " must be configured for " + majcIteratorKey);
}
PatriciaTrie<Long> ageoffs = new PatriciaTrie<>();
long configureMinAgeOff = Long.MAX_VALUE;
long configureMaxAgeOff = Long.MIN_VALUE;
@SuppressWarnings("unchecked")
Iterator<String> keys = configAgeOff.getKeys();
while (keys.hasNext()) {
String k = keys.next();
String v = configAgeOff.getString(k);
if (k.startsWith((AGE_OFF_PREFIX))) {
String name = k.substring(AGE_OFF_PREFIX.length());
if (LOG.isTraceEnabled()) {
LOG.trace("Adding {} to Trie with value {}", name, Long.parseLong(v));
}
long ageoff = Long.parseLong(v);
configureMinAgeOff = Math.min(configureMinAgeOff, ageoff);
configureMaxAgeOff = Math.max(configureMaxAgeOff, ageoff);
ageoffs.put(name, ageoff);
}
}
long defaultAgeOff = ageoffs.get(DEFAULT_AGEOFF_KEY_SUFFIX);
return new MetricAgeOffConfiguration(ageoffs, defaultAgeOff);
}
public long computeAgeOff(String metricName) {
return ageoffs.getOrDefault(metricName, defaultAgeOff);
}
}
}
@@ -0,0 +1,111 @@
package timely.store.compaction;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.StringJoiner;
import org.apache.accumulo.core.client.lexicoder.Lexicoder;
import org.apache.accumulo.core.client.lexicoder.LongLexicoder;
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
import org.apache.accumulo.core.client.lexicoder.impl.ByteUtils;
import org.apache.accumulo.core.data.TabletId;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.hadoop.io.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TabletRowAdapter {
private static final Logger LOG = LoggerFactory.getLogger(TabletRowAdapter.class);
private static final Lexicoder<String> PREFIX_DECODER = new StringLexicoder();
private static final Lexicoder<Long> OFFSET_DECODER = new LongLexicoder();
private static final byte DELIMETER = 0x00;
private static final int MIN_OFFSET_LEN = 9;
public static OptionalLong decodeRowOffset(Text row) {
OptionalLong offset = OptionalLong.empty();
byte[] bytes = row.getBytes();
int delimidx = findRowDelimiterIndex(bytes);
if (delimidx > 0) {
try {
byte[] buffer = Arrays.copyOfRange(bytes, delimidx + 1, bytes.length);
buffer = ByteUtils.unescape(buffer);
// key might not have all bytes for long decoding
// ensure of sufficient length
int extendBy = (MIN_OFFSET_LEN - (buffer.length));
if (extendBy > 0) {
buffer = ArrayUtils.addAll(buffer, new byte[extendBy]);
}
long decodedOffset = OFFSET_DECODER.decode(buffer);
if (decodedOffset > 0) {
offset = OptionalLong.of(decodedOffset);
} else if (LOG.isTraceEnabled()) {
Optional<String> prefix = decodeRowPrefix(row);
LOG.trace(
"Delimiter identified but offset could not parse { prefix: {}, byte-len: {}, "
+ "offset-len: {}, offset-bytes: {}, row-bytes: {} }",
prefix, bytes.length, buffer.length, Hex.encodeHex(buffer), Hex.encodeHex(bytes));
}
} catch (IllegalArgumentException e) {
if (LOG.isTraceEnabled()) {
LOG.trace("Unable to parse offset: " + e.getMessage(), e);
}
}
}
return offset;
}
public static Optional<String> decodeRowPrefix(Text row) {
byte[] bytes = row.getBytes();
int delimidx = findRowDelimiterIndex(bytes);
byte[] prefixBytes = (delimidx >= 0) ? Arrays.copyOfRange(bytes, 0, delimidx) : bytes;
Optional<String> tabletName = Optional.empty();
try {
tabletName = Optional.of(PREFIX_DECODER.decode(ByteUtils.unescape(prefixBytes)));
} catch (IllegalArgumentException e) {
if (LOG.isTraceEnabled()) {
LOG.trace("Unable to parse offset: " + e.getMessage(), e);
}
}
return tabletName;
}
public static String toDebugOutput(TabletId tid) {
StringJoiner joiner = new StringJoiner(", ", "[", "]");
Text endRow = tid.getEndRow();
if (null != endRow) {
Optional<String> prefix = decodeRowPrefix(endRow);
OptionalLong offset = decodeRowOffset(endRow);
prefix.ifPresent(s -> joiner.add("prefix: " + s));
if (offset.isPresent()) {
LocalDateTime date = Instant.ofEpochMilli(offset.getAsLong()).atZone(ZoneId.of("UTC"))
.toLocalDateTime();
joiner.add("date: " + date.toString());
joiner.add("millis: " + offset.getAsLong());
}
}
return joiner.toString();
}
private static int findRowDelimiterIndex(byte[] bytes) {
int delimidx = -1;
for (int i = bytes.length - 1; i >= 0; i--) {
if (bytes[i] == DELIMETER) {
delimidx = i;
break;
}
}
return delimidx;
}
}
@@ -0,0 +1,158 @@
package timely.store.compaction;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
import org.apache.accumulo.tserver.compaction.CompactionPlan;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
import org.apache.accumulo.tserver.compaction.MajorCompactionRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TieredCompactionStrategy extends CompactionStrategy {
public static final String TIERED_PREFIX = "tier.";
public static final String TIERED_CLASS = "class";
public static final String TIERED_OPTS = "opts";
protected final Collection<TieredCompactorSupplier> compactors;
public TieredCompactionStrategy() {
compactors = new ArrayList<>();
}
public Collection<TieredCompactorSupplier> getCompactors() {
return Collections.unmodifiableCollection(compactors);
}
@Override
public void init(Map<String, String> config) {
// example:
// <table-compaction-prefix>.tiered.0.class=<majcompaction-class>
// <table-compaction-prefix>.tiered.0.opts.key=value
// <table-compaction-prefix>.tiered.1.class=<majcompaction-class>
// <table-compaction-prefix>.tiered.1.opts.key_a=value
// <table-compaction-prefix>.tiered.1.opts.key_b=value
//
// the key=value pairs will be sent to the specific tiered compaction
// strategy as arguments
compactors.clear();
createSuppliers(config);
}
@Override
public void gatherInformation(MajorCompactionRequest request) throws IOException {
for (TieredCompactorSupplier compactor : compactors) {
CompactionStrategy strategy = compactor.get(request.getTableProperties());
strategy.gatherInformation(request);
}
}
@Override
public boolean shouldCompact(MajorCompactionRequest request) throws IOException {
boolean shouldCompact = false;
for (TieredCompactorSupplier supplier : compactors) {
CompactionStrategy strategy = supplier.get(request.getTableProperties());
shouldCompact = strategy.shouldCompact(request);
if (shouldCompact) {
break;
}
}
return shouldCompact;
}
@Override
public CompactionPlan getCompactionPlan(MajorCompactionRequest request) throws IOException {
CompactionPlan plan = null;
Iterator<TieredCompactorSupplier> iter = compactors.iterator();
while ((null == plan || plan.inputFiles.isEmpty()) && iter.hasNext()) {
CompactionStrategy strategy = iter.next().get(request.getTableProperties());
plan = strategy.getCompactionPlan(request);
}
return plan;
}
protected void createSuppliers(Map<String, String> config) {
int tier = 0;
boolean continueLoop = true;
while (continueLoop) {
String clazzKey = TIERED_PREFIX + tier + "." + TIERED_CLASS;
String clazzName = config.get(clazzKey);
if (null != clazzName) {
compactors.add(new TieredConfiguredSupplier(clazzName, tier, config));
tier++;
} else {
continueLoop = false;
}
}
}
static class TieredConfiguredSupplier implements TieredCompactorSupplier {
private static final Logger LOG = LoggerFactory.getLogger(TieredConfiguredSupplier.class);
private final String clazzName;
private final int tier;
private final Map<String, String> initConfig;
private CompactionStrategy strategy;
private Map<String, String> options;
TieredConfiguredSupplier(String clazzName, int tier, Map<String, String> initConfig) {
this.strategy = null;
this.clazzName = clazzName;
this.tier = tier;
this.initConfig = initConfig;
}
public Map<String, String> options() {
if (null == options) {
options = new HashMap<>();
String tierCompare = Integer.toString(tier);
int tierIdx = TIERED_PREFIX.length();
int optsIdx = TIERED_PREFIX.length() + tierCompare.length() + 1;
for (Map.Entry<String, String> entry : initConfig.entrySet()) {
String key = entry.getKey();
if (key.regionMatches(tierIdx, tierCompare, 0, tierCompare.length())
&& key.regionMatches(optsIdx, TIERED_OPTS, 0, TIERED_OPTS.length())) {
options.put(key.substring(optsIdx + TIERED_OPTS.length() + 1), entry.getValue());
}
}
}
return Collections.unmodifiableMap(options);
}
public CompactionStrategy get(Map<String, String> tableProperties) {
if (null == strategy) {
try {
String tableContext = tableProperties.get(Property.TABLE_CLASSPATH.getKey());
Class<? extends CompactionStrategy> clazz;
if (null != tableContext && !tableContext.equals("")) {
clazz = AccumuloVFSClassLoader.getContextManager().loadClass(tableContext, clazzName,
CompactionStrategy.class);
} else {
clazz = AccumuloVFSClassLoader.loadClass(clazzName, CompactionStrategy.class);
}
strategy = clazz.newInstance();
strategy.init(options());
} catch (Exception e) {
LOG.debug("Failed to load class for tiered compaction: " + e.toString());
throw new RuntimeException(e);
}
}
return strategy;
}
}
}
@@ -0,0 +1,12 @@
package timely.store.compaction;
import java.util.Map;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
public interface TieredCompactorSupplier {
Map<String, String> options();
CompactionStrategy get(Map<String, String> tableProperties);
}
@@ -0,0 +1,130 @@
package timely.store.compaction.util;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.io.Text;
import timely.store.compaction.TabletRowAdapter;
public class MetadataAccumulator {
private final List<Entry> list;
private Entry currentEntry;
public MetadataAccumulator() {
this.list = new LinkedList<>();
}
public Entry checkEntryState(Text tablet) {
// entry may be null on initial invocation or after the pushOrDiscard operation
// check to see if the tablet has changed, if so then
// push or discard the current state
if (currentEntry != null && !tablet.equals(currentEntry.getTablet())) {
pushOrDiscardEntry();
}
if (currentEntry == null) {
currentEntry = new Entry(tablet);
}
return currentEntry;
}
public Collection<Entry> getEntries() {
return Collections.unmodifiableCollection(list);
}
public void ensureState() {
pushOrDiscardEntry();
}
private void pushOrDiscardEntry() {
if (currentEntry != null && currentEntry.hasFiles()) {
list.add(currentEntry);
}
currentEntry = null;
}
public static class Entry {
private final Text tablet;
private final String tabletPrefix;
private final boolean hasTabletOffset;
private final long tabletOffset;
private Text tabletPrev;
private long milliseconds;
private long totalBytes;
private int totalFiles;
public Entry(Text tablet) {
this.tablet = tablet;
Optional<String> prefixOptional = TabletRowAdapter.decodeRowPrefix(tablet);
OptionalLong offsetOptional = TabletRowAdapter.decodeRowOffset(tablet);
tabletPrefix = prefixOptional.orElseGet(tablet::toString);
if (offsetOptional.isPresent()) {
hasTabletOffset = true;
tabletOffset = offsetOptional.getAsLong();
} else {
hasTabletOffset = false;
tabletOffset = -1;
}
}
public String getTabletPrefix() {
return tabletPrefix;
}
public Text getTablet() {
return tablet;
}
public Text getTabletPrev() {
return tabletPrev;
}
public long getMilliseconds() {
return milliseconds;
}
public long getTotalFileBytes() {
return totalBytes;
}
public int getTotalFiles() {
return totalFiles;
}
public OptionalLong getTabletOffset() {
return hasTabletOffset ? OptionalLong.of(tabletOffset) : OptionalLong.empty();
}
public boolean hasFiles() {
return totalFiles > 0;
}
public void addFile(long size) {
totalFiles++;
totalBytes += size;
}
public void setTablePrev(Text prev) {
tabletPrev = prev;
}
public void setMilliseconds(long val) {
milliseconds = val;
}
@Override
public String toString() {
return String.format("metadata.entry {tablet: %s, millis: %d, offset-ms: %d, age-days: %d, files %d}",
tablet, milliseconds, tabletOffset,
(hasTabletOffset ? TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - tabletOffset) : -1),
totalFiles);
}
}
}
@@ -0,0 +1,40 @@
package timely.store.compaction.util;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.client.ClientConfiguration;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Instance;
import org.apache.accumulo.core.client.ZooKeeperInstance;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.configuration.Accumulo;
import timely.configuration.Configuration;
import timely.configuration.SpringBootstrap;
public class TabletMetadataConsole {
public static void main(String[] args) throws Exception {
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SpringBootstrap.class)
.bannerMode(Banner.Mode.OFF).web(WebApplicationType.NONE).run(args)) {
Configuration conf = ctx.getBean(Configuration.class);
HashMap<String, String> apacheConf = new HashMap<>();
Accumulo accumuloConf = conf.getAccumulo();
apacheConf.put("instance.name", accumuloConf.getInstanceName());
apacheConf.put("instance.zookeeper.host", accumuloConf.getZookeepers());
ClientConfiguration aconf = ClientConfiguration.fromMap(apacheConf);
Instance instance = new ZooKeeperInstance(aconf);
Connector con = instance.getConnector(accumuloConf.getUsername(),
new PasswordToken(accumuloConf.getPassword()));
TabletMetadataQuery query = new TabletMetadataQuery(con, conf.getMetricsTable());
TabletMetadataView view = query.run();
System.out.println(view.toText(TimeUnit.DAYS));
}
}
}
@@ -0,0 +1,31 @@
package timely.store.compaction.util;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.util.format.AggregatingFormatter;
public class TabletMetadataFormatter extends AggregatingFormatter {
private final TabletMetadataView view;
public TabletMetadataFormatter() {
this(new TabletMetadataView());
}
public TabletMetadataFormatter(TabletMetadataView view) {
this.view = view;
}
@Override
public void aggregateStats(Map.Entry<Key, Value> entry) {
view.addEntry(entry);
}
@Override
protected String getStats() {
return view.toText(TimeUnit.DAYS);
}
}
@@ -0,0 +1,44 @@
package timely.store.compaction.util;
import java.util.Map;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.metadata.MetadataTable;
public class TabletMetadataQuery {
private final Connector connector;
private final String tableName;
public TabletMetadataQuery(Connector connector, String tableName) {
this.connector = connector;
this.tableName = tableName;
}
public TabletMetadataView run() throws AccumuloException, AccumuloSecurityException, TableNotFoundException {
TabletMetadataView view = new TabletMetadataView();
Map<String, String> tableMap = connector.tableOperations().tableIdMap();
String tableId = tableMap.get(tableName);
if (null == tableId) {
throw new IllegalStateException("Unable to find " + MetadataTable.NAME);
}
try (Scanner s = connector.createScanner(MetadataTable.NAME,
connector.securityOperations().getUserAuthorizations(connector.whoami()))) {
// s.setRange(new Range(tableId, true, tableId, true));
s.setRange(Range.prefix(tableId));
for (Map.Entry<Key, Value> e : s) {
view.addEntry(e);
}
}
return view;
}
}
@@ -0,0 +1,144 @@
package timely.store.compaction.util;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.metadata.schema.MetadataSchema;
import org.apache.commons.io.FileUtils;
import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
import org.apache.hadoop.io.Text;
public class TabletMetadataView {
private final MetadataAccumulator accumulator;
private static final int DEFAULT_TOP_COUNT = 25;
public TabletMetadataView() {
accumulator = new MetadataAccumulator();
}
public void addEntry(Collection<Map.Entry<Key, Value>> entries) {
for (Map.Entry<Key, Value> entry : entries) {
addEntry(entry);
}
}
public void addEntry(Map.Entry<Key, Value> entry) {
KeyExtent ke = new KeyExtent(entry.getKey().getRow(), (Text) null);
if (ke.getEndRow() == null) {
return;
}
Text cf = entry.getKey().getColumnFamily();
Text cq = entry.getKey().getColumnQualifier();
MetadataAccumulator.Entry candidate = accumulator.checkEntryState(ke.getEndRow());
if (cf.equals(MetadataSchema.TabletsSection.DataFileColumnFamily.NAME)) {
String val = entry.getValue().toString();
String[] valSplit = val.split(",");
if (valSplit.length == 2) {
long size = Long.parseLong(valSplit[0]);
candidate.addFile(size);
}
} else if (cf.equals(MetadataSchema.TabletsSection.ServerColumnFamily.NAME)) {
if (cq.equals(MetadataSchema.TabletsSection.ServerColumnFamily.TIME_COLUMN.getColumnQualifier())) {
String timeVal = entry.getValue().toString();
if (timeVal.startsWith("M")) {
long ms = Long.parseLong(timeVal.substring(1));
candidate.setMilliseconds(ms);
}
}
} else if (cf.equals(MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.getColumnFamily())
&& cq.equals(MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.getColumnQualifier())) {
ke = new KeyExtent(entry.getKey().getRow(), entry.getValue());
candidate.setTablePrev(ke.getPrevEndRow());
}
}
public Collection<MetadataAccumulator.Entry> getEntries() {
accumulator.ensureState();
return accumulator.getEntries();
}
public TabletSummary.Builder computeSummary() {
return TabletSummary.newBuilder(getEntries());
}
public String toText() {
return toText(TimeUnit.MILLISECONDS);
}
public String toText(TimeUnit unit) {
if (getEntries().isEmpty()) {
return "No results";
}
StringBuilder sb = new StringBuilder();
TabletSummary summary = computeSummary().build();
Map<TabletStatisticType, StatisticalSummary> tabletAggregate = summary.aggregateSummary();
StatisticalSummary tabletSizes = tabletAggregate.get(TabletStatisticType.SIZE);
StatisticalSummary tabletTime = tabletAggregate.get(TabletStatisticType.TIME);
long tabletMax = unit.convert((long) tabletTime.getMax(), TimeUnit.MILLISECONDS);
long tabletMean = unit.convert((long) tabletTime.getMean(), TimeUnit.MILLISECONDS);
Collection<TabletStatistic> topOldestMetrics = summary.findTabletPrefixesByOldest(DEFAULT_TOP_COUNT);
Collection<TabletStatistic> topSizeMetrics = summary.findTabletPrefixesByLargest(DEFAULT_TOP_COUNT);
sb.append(String.format("Tablets { prefix: %d, total: %d } %s", summary.totalTabletPrefixes(),
summary.totalTablets(), System.lineSeparator()));
sb.append(System.lineSeparator());
sb.append(String.format("Size: { sum: %s, avg: %s } %s",
FileUtils.byteCountToDisplaySize((long) tabletSizes.getSum()),
FileUtils.byteCountToDisplaySize((long) tabletSizes.getMean()), System.lineSeparator()));
sb.append(String.format("Age: { max: %d, avg: %d } %s", tabletMax, tabletMean, System.lineSeparator()));
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
sb.append(String.format("Top %d Tablets (by-age) %s", DEFAULT_TOP_COUNT, System.lineSeparator()));
sb.append("---");
sb.append(System.lineSeparator());
for (TabletStatistic entry : topOldestMetrics) {
sb.append(String.format("%s = { tablets: %d, max-age: %d } %s", entry.getKeyName(), entry.getKeyCount(),
unit.convert(entry.maxAge(), TimeUnit.MILLISECONDS), System.lineSeparator()));
}
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
sb.append(String.format("Top %d Tablets (by-size) %s", DEFAULT_TOP_COUNT, System.lineSeparator()));
sb.append("---");
sb.append(System.lineSeparator());
for (TabletStatistic entry : topSizeMetrics) {
long totalSize = (long) entry.getSummary(TabletStatisticType.SIZE).getSum();
sb.append(String.format("%s = { tablets: %d, size: %s } %s", entry.getKeyName(), entry.getKeyCount(),
FileUtils.byteCountToDisplaySize(totalSize), System.lineSeparator()));
}
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
sb.append(String.format("Tablets (by-name) %s", System.lineSeparator()));
sb.append("---");
sb.append(System.lineSeparator());
for (TabletStatistic entry : summary.getSummary()) {
long totalSize = (long) entry.getSummary(TabletStatisticType.SIZE).getSum();
sb.append(String.format("%s = { tablets: %d, size: %s, max-age: %d } %s", entry.getKeyName(),
entry.getKeyCount(), FileUtils.byteCountToDisplaySize(totalSize),
unit.convert(entry.maxAge(), TimeUnit.MILLISECONDS), System.lineSeparator()));
}
sb.append(System.lineSeparator());
sb.append(System.lineSeparator());
return sb.toString();
}
}
@@ -0,0 +1,53 @@
package timely.store.compaction.util;
import java.util.Map;
import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
public class TabletStatistic {
private final String keyName;
private final int keyCount;
private final Map<TabletStatisticType, SummaryStatistics> stats;
private final SummaryStatistics timeStat;
private final SummaryStatistics sizeStat;
TabletStatistic(String keyName, int keyCount, Map<TabletStatisticType, SummaryStatistics> stats) {
this.keyName = keyName;
this.keyCount = keyCount;
this.stats = stats;
timeStat = stats.getOrDefault(TabletStatisticType.TIME, new SummaryStatistics());
sizeStat = stats.getOrDefault(TabletStatisticType.SIZE, new SummaryStatistics());
}
public String getKeyName() {
return keyName;
}
public int getKeyCount() {
return keyCount;
}
public StatisticalSummary getSummary(TabletStatisticType statType) {
return this.stats.getOrDefault(statType, new SummaryStatistics());
}
public Map<TabletStatisticType, SummaryStatistics> getSummaryMap() {
return stats;
}
public long maxAge() {
return (long) timeStat.getMax();
}
public long totalSize() {
return (long) sizeStat.getSum();
}
@Override
public String toString() {
return String.format("key: %s, count: %d, age: %d, size %d", keyName, keyCount, maxAge(), totalSize());
}
}
@@ -0,0 +1,5 @@
package timely.store.compaction.util;
public enum TabletStatisticType {
SIZE, TIME
}
@@ -0,0 +1,155 @@
package timely.store.compaction.util;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.math3.stat.descriptive.AggregateSummaryStatistics;
import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import timely.store.compaction.TabletRowAdapter;
public class TabletSummary {
private final Collection<TabletStatistic> tabletStats;
private TabletSummary(Collection<TabletStatistic> tabletStats) {
this.tabletStats = tabletStats;
}
public static Builder newBuilder(Collection<MetadataAccumulator.Entry> entries) {
return new Builder(entries);
}
public Map<TabletStatisticType, StatisticalSummary> aggregateSummary() {
// @formatter:off
return tabletStats.stream()
.flatMap(m -> m.getSummaryMap().entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> AggregateSummaryStatistics.aggregate(v.getValue())));
}
public long totalTabletPrefixes() {
return tabletStats.size();
}
public long totalTablets() {
// @formatter:off
return tabletStats.stream()
.mapToLong(TabletStatistic::getKeyCount)
.sum();
}
public Collection<TabletStatistic> findTabletPrefixesByOldest(int count) {
// @formatter:off
return tabletStats.stream()
.sorted(Comparator.comparingLong(TabletStatistic::maxAge)
.reversed()
.thenComparing(TabletStatistic::getKeyName))
.limit(count)
.collect(Collectors.toList());
}
public Collection<TabletStatistic> findTabletPrefixesByLargest(int count) {
// @formatter:off
return tabletStats.stream()
.sorted(Comparator.comparingLong(TabletStatistic::totalSize)
.reversed()
.thenComparing(TabletStatistic::getKeyName))
.limit(count)
.collect(Collectors.toList());
}
public Collection<TabletStatistic> getSummary() {
return tabletStats;
}
public static class Builder {
private final Collection<MetadataAccumulator.Entry> entries;
private long explictTimeMillis;
private boolean useExplicitTime;
private boolean disableTabletRowCheckFilter;
public Builder(Collection<MetadataAccumulator.Entry> entries) {
this.entries = entries;
}
public Builder currentTime(long millis) {
explictTimeMillis = millis;
useExplicitTime = true;
return this;
}
public Builder disableTabletRowCheckFilter() {
disableTabletRowCheckFilter = true;
return this;
}
public TabletSummary build() {
Stream<MetadataAccumulator.Entry> stream = entries.stream();
if (!disableTabletRowCheckFilter) {
stream = stream.filter(Builder::tabletEndAnPrevAreSame);
}
// @formatter:off
Collection<TabletStatistic> tabletStats = stream
.collect(Collectors.groupingBy(MetadataAccumulator.Entry::getTabletPrefix)).entrySet()
.stream()
.map(m -> computeStatistic(m.getValue(), m.getKey()))
.sorted(Comparator.comparing(TabletStatistic::getKeyName))
.collect(Collectors.toList());
// @formatter:on
return new TabletSummary(tabletStats);
}
private TabletStatistic computeStatistic(Collection<MetadataAccumulator.Entry> entries, String keyName) {
Map<TabletStatisticType, SummaryStatistics> stats = new HashMap<>(TabletStatisticType.values().length);
int maxSize = entries.size();
long timeMillis = useExplicitTime ? explictTimeMillis : System.currentTimeMillis();
for (TabletStatisticType statType : TabletStatisticType.values()) {
SummaryStatistics summaryStat = new SummaryStatistics();
Iterator<MetadataAccumulator.Entry> iterator = entries.iterator();
int idx = 0;
while (iterator.hasNext() && idx < maxSize) {
MetadataAccumulator.Entry e = iterator.next();
double d;
switch (statType) {
case SIZE:
d = e.getTotalFileBytes();
break;
case TIME:
// check the row key offset first; this was done to support
// tests so that they age could function from the row-key
// the tablets that do not have a key will use the modification time
d = timeMillis - e.getTabletOffset().orElse(e.getMilliseconds());
break;
default:
throw new IllegalStateException("Undefined switch case: " + statType.name());
}
summaryStat.addValue(d);
idx++;
}
stats.put(statType, summaryStat);
}
return new TabletStatistic(keyName, entries.size(), stats);
}
private static boolean tabletEndAnPrevAreSame(MetadataAccumulator.Entry entry) {
if ((null == entry.getTablet()) || (null == entry.getTabletPrev())) {
return false;
}
Optional<String> o1 = TabletRowAdapter.decodeRowPrefix(entry.getTablet());
Optional<String> o2 = TabletRowAdapter.decodeRowPrefix(entry.getTabletPrev());
return o1.equals(o2);
}
}
}
@@ -41,7 +41,7 @@ public class MetaKeySet extends TreeSet<Key> {
super(new MetaKeyComparator());
}
public List<Mutation> toMutations() {
public List<Mutation> toMutations(long ts) {
List<Mutation> results = new ArrayList<>();
String prevRow = null;
Iterator<Key> keys = this.iterator();
@@ -55,7 +55,7 @@ public class MetaKeySet extends TreeSet<Key> {
m = new Mutation(next.getRow());
prevRow = next.getRow().toString();
}
m.put(next.getColumnFamily(), next.getColumnQualifier(), NULL_VALUE);
m.put(next.getColumnFamily(), next.getColumnQualifier(), ts, NULL_VALUE);
}
results.add(m);
return results;
+5
View File
@@ -13,4 +13,9 @@
<Method name="getCompressorOutput" />
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<!-- Must ignore these everywhere, because of a javac byte code generation bug -->
<!-- https://github.com/spotbugs/spotbugs/issues/756 -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>
</FindBugsFilter>
@@ -4,8 +4,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.data.ColumnUpdate;
@@ -251,6 +258,52 @@ public class DownsampleIteratorTest {
assertEquals((elapsedTime / sampleInterval) * numTagVariations, totalBuckets);
}
@Test
public void test() {
String hello = new String("this is a longer string");
long maybe = ObjectSizeOf.Sizer.OBJECT_OVERHEAD + // for String
ObjectSizeOf.Sizer.REFERENCE + // for array of char
ObjectSizeOf.Sizer.getPrimitiveObjectSize(int.class) +
ObjectSizeOf.Sizer.ARRAY_OVERHEAD + // for array of char
hello.length() * ObjectSizeOf.Sizer.getPrimitiveObjectSize(char.class);
maybe = ObjectSizeOf.Sizer.roundUp(maybe);
long size = ObjectSizeOf.Sizer.getObjectSize(hello, true, true);
System.out.println("size = " + size);
System.out.println("maybe = " + maybe);
}
@Test
public void test2() {
System.out.println(ObjectSizeOf.Sizer.getObjectSize(new Long(100)));
}
@Test
public void testDownsampleMemoryCalculation() throws Exception {
int numTagVariations = 1;
int sampleInterval = 50;
int elapsedTime = 100;
int skipInterval = 10;
SortedMap<Key, Value> testData3 = createTestData3(elapsedTime, skipInterval, numTagVariations);
DownsampleIterator iter = new DownsampleIterator();
Map<Set<Tag>, Downsample> samples = runQuery(iter, testData3, sampleInterval, 1000);
assertEquals(numTagVariations, samples.size());
long totalBuckets = 0;
for (Entry<Set<Tag>, Downsample> entry : samples.entrySet()) {
Set<Tag> tagSet = entry.getKey();
Downsample downsample = entry.getValue();
for (Tag tag : tagSet) {
assertEquals(ObjectSizeOf.Sizer.getObjectSize(tag, true, true),
ObjectSizeOf.Sizer.getObjectSize(tag, false, true));
}
}
}
private Map<Set<Tag>, Downsample> runQuery(SortedKeyValueIterator<Key, Value> iter, SortedMap<Key, Value> testData,
long period, long maxDownsampleMemory) throws Exception {
IteratorSetting is = new IteratorSetting(100, DownsampleIterator.class);
@@ -0,0 +1,290 @@
package timely.store.compaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.tserver.compaction.CompactionPlan;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
import org.apache.accumulo.tserver.compaction.MajorCompactionRequest;
import org.junit.Test;
import timely.test.CompactionRequestBuilder;
public class MetricCompactionStrategyTest {
private static final long TIME_MILLIS = new GregorianCalendar(2019, Calendar.JANUARY, 1).getTimeInMillis();
@Test(expected = IllegalArgumentException.class)
public void initThrowsWhenNoDefaultAgeOff() {
Map<String, String> config = new HashMap<>();
MetricCompactionStrategy strategy = new MetricCompactionStrategy();
strategy.init(config);
CompactionRequestBuilder builder = new CompactionRequestBuilder()
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
MajorCompactionRequest request = builder.build();
strategy.shouldCompact(request);
}
@Test
public void minAgeOffOverridesMajcDefaultWillUseMaximum() throws IOException {
Map<String, String> config = new HashMap<>();
config.put(MetricCompactionStrategy.MIN_AGEOFF_KEY, Long.toString(TimeUnit.DAYS.toMillis(10)));
CompactionStrategy strategy = newStrategy(config);
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(13)
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void minAgeOffOverridesMajcDefaultNotCompact() throws IOException {
Map<String, String> config = new HashMap<>();
config.put(MetricCompactionStrategy.MIN_AGEOFF_KEY, Long.toString(TimeUnit.DAYS.toMillis(13)));
CompactionStrategy strategy = newStrategy(config);
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void compactFilesIfKeyIsPastAgeOff() throws IOException {
String f1 = "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf";
String f2 = "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf";
String f3 = "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao6.rf";
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys", TimeUnit.DAYS.toMillis(12))
.file(f1, 12, 1)
.file(f2, 11, 1)
.file(f3, 16, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertTrue(shouldCompact);
assertNotNull(plan);
assertEquals(3, plan.inputFiles.size());
assertEquals(f1, plan.inputFiles.get(0).path().toString());
assertEquals(f2, plan.inputFiles.get(1).path().toString());
assertEquals(f3, plan.inputFiles.get(2).path().toString());
}
@Test
public void noCompactionIfPrevKeyIsDifferent() throws IOException {
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys.mem", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void noCompactionIfPrevKeyIsNull() throws IOException {
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys", TimeUnit.DAYS.toMillis(11))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void noCompactionWhenExceedsButDoesNotHaveFiles() throws IOException {
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys", TimeUnit.DAYS.toMillis(12));
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void noCompactionIfKeyIsNotOffset() throws IOException {
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetricNonOffset("sys.mem", null)
.prevEndKeyMetricNonOffset("sys.cpu", null)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void noCompactionIfKeyIsWrongOffset() throws IOException {
CompactionStrategy strategy = newStrategy();
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetricNonOffset("sys.cpu", "test2")
.prevEndKeyMetricNonOffset("sys.cpu", "test1")
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void compactWithLogOnlyDoesNotCompact() throws IOException {
Map<String, String> defaults = new HashMap<>();
defaults.put(MetricCompactionStrategy.LOG_ONLY_KEY, "true");
CompactionStrategy strategy = newStrategy(defaults);
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
@Test
public void compactWithFilterPrefixWillCompact() throws IOException {
Map<String, String> defaults = new HashMap<>();
defaults.put(MetricCompactionStrategy.FILTER_PREFIX_KEY, "sys.cp");
CompactionStrategy strategy = newStrategy(defaults);
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertTrue(shouldCompact);
assertNotNull(plan);
assertEquals(2, plan.inputFiles.size());
}
@Test
public void compactWithoutFilterPrefixWillNotCompact() throws IOException {
Map<String, String> defaults = new HashMap<>();
defaults.put(MetricCompactionStrategy.FILTER_PREFIX_KEY, "sys.mem");
CompactionStrategy strategy = newStrategy(defaults);
// @formatter:off
CompactionRequestBuilder builder = newRequestBuilder(10)
.endKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(11))
.prevEndKeyMetric("sys.cpu", TimeUnit.DAYS.toMillis(12))
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", 1, 1)
.file("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao7.rf", 1, 1);
// @formatter:on
MajorCompactionRequest request = builder.build();
boolean shouldCompact = strategy.shouldCompact(request);
CompactionPlan plan = strategy.getCompactionPlan(request);
assertFalse(shouldCompact);
assertNull(plan);
}
private static CompactionRequestBuilder newRequestBuilder(int defaultAgeOffDays) {
return new CompactionRequestBuilder(TIME_MILLIS).tableProperties(
Property.TABLE_ITERATOR_MAJC_PREFIX.getKey() + "ageoffmetrics.opt.ageoff.default",
Long.toString(TimeUnit.DAYS.toMillis(defaultAgeOffDays)));
}
private static MetricCompactionStrategy newStrategy() {
return newStrategy(new TreeMap<>());
}
private static MetricCompactionStrategy newStrategy(Map<String, String> config) {
if (!config.containsKey(MetricCompactionStrategy.LOG_ONLY_KEY)) {
config.put(MetricCompactionStrategy.LOG_ONLY_KEY, "false");
}
MetricCompactionStrategy strategy = new MetricCompactionStrategy();
strategy.setExplicitTime(TIME_MILLIS);
strategy.init(config);
return strategy;
}
}
@@ -0,0 +1,79 @@
package timely.store.compaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.OptionalLong;
import org.apache.accumulo.core.client.lexicoder.Lexicoder;
import org.apache.accumulo.core.client.lexicoder.LongLexicoder;
import org.apache.accumulo.core.client.lexicoder.PairLexicoder;
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
import org.apache.accumulo.core.data.TabletId;
import org.apache.accumulo.core.util.ComparablePair;
import org.apache.hadoop.io.Text;
import org.easymock.EasyMock;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
public class TabletRowAdapterTest {
private static final Lexicoder<String> prefixCoder = new StringLexicoder();
private static final Lexicoder<Long> offsetCoder = new LongLexicoder();
private static final PairLexicoder<String, Long> pairCoder = new PairLexicoder<>(prefixCoder, offsetCoder);
@Test
public void decodePrefix() {
ComparablePair<String, Long> p = new ComparablePair<>("test", 1L);
byte[] bytes = pairCoder.encode(p);
OptionalLong decoded = TabletRowAdapter.decodeRowOffset(new Text(bytes));
assertTrue(decoded.isPresent());
}
@Test
public void decodeOffsetOnly() {
ComparablePair<String, Long> p = new ComparablePair<>("test", 1L);
byte[] bytes = pairCoder.encode(p);
OptionalLong decoded = TabletRowAdapter.decodeRowOffset(new Text(bytes));
assertTrue(decoded.isPresent());
assertEquals(1L, decoded.getAsLong());
}
@Test
public void decodeOffsetWithTruncatedOffset() {
// offset is truncated on tablet extents
long truncateOffset = 1554962382848L;
ComparablePair<String, Long> p = new ComparablePair<>("sys.disk.disk_octets", truncateOffset);
byte[] bytes = pairCoder.encode(p);
bytes = Arrays.copyOf(bytes, bytes.length - 4);
OptionalLong decoded = TabletRowAdapter.decodeRowOffset(new Text(bytes));
assertTrue(decoded.isPresent());
assertEquals(truncateOffset, decoded.getAsLong());
}
@Test
public void decodeOffsetWithBadDataWillSet() {
ComparablePair<String, String> p = new ComparablePair<>("test", "<");
byte[] bytes = new PairLexicoder<>(prefixCoder, prefixCoder).encode(p);
OptionalLong decoded = TabletRowAdapter.decodeRowOffset(new Text(bytes));
assertFalse(decoded.isPresent());
}
@Test
public void debugOutputTest() {
long offset = 1554962382848L;
ComparablePair<String, Long> p = new ComparablePair<>("sys.disk.disk_octets", offset);
byte[] bytes = pairCoder.encode(p);
TabletId tid = EasyMock.createMock(TabletId.class);
EasyMock.expect(tid.getEndRow()).andReturn(new Text(bytes));
EasyMock.replay(tid);
String debug = TabletRowAdapter.toDebugOutput(tid);
assertThat(debug, CoreMatchers.containsString("prefix: sys.disk.disk_octets"));
assertThat(debug, CoreMatchers.containsString("date: 2019-04-11T05:59:42.848"));
assertThat(debug, CoreMatchers.containsString("millis: 1554962382848"));
}
}
@@ -0,0 +1,236 @@
package timely.store.compaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import org.apache.accumulo.server.fs.FileRef;
import org.apache.accumulo.tserver.compaction.CompactionPlan;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
import org.apache.accumulo.tserver.compaction.DefaultCompactionStrategy;
import org.apache.accumulo.tserver.compaction.MajorCompactionRequest;
import org.easymock.EasyMock;
import org.junit.Test;
import timely.test.CompactionRequestBuilder;
public class TieredCompactionStrategyTest {
@Test
public void initConfiguresWithMultipleTiers() {
Map<String, String> config = new HashMap<>();
config.put("tier.0.class", DefaultCompactionStrategy.class.getName());
config.put("tier.0.opts.arg1", "foo");
config.put("tier.0.opts.arg2", "sys");
config.put("tier.1.class", DefaultCompactionStrategy.class.getName());
config.put("tier.1.opts.arg1", "bar");
config.put("tier.2.class", DefaultCompactionStrategy.class.getName());
TieredCompactionStrategy strategy = new TieredCompactionStrategy();
strategy.init(config);
List<TieredCompactorSupplier> suppliers = Lists.newArrayList(strategy.getCompactors());
assertEquals(3, suppliers.size());
assertEquals(2, suppliers.get(0).options().size());
assertEquals(1, suppliers.get(1).options().size());
assertEquals(0, suppliers.get(2).options().size());
assertEquals("foo", suppliers.get(0).options().get("arg1"));
assertEquals("sys", suppliers.get(0).options().get("arg2"));
assertEquals("bar", suppliers.get(1).options().get("arg1"));
}
@Test
public void initConfiguresWithSingleTierNoArguments() {
Map<String, String> config = new HashMap<>();
config.put("tier.0.class", DefaultCompactionStrategy.class.getName());
TieredCompactionStrategy strategy = new TieredCompactionStrategy();
strategy.init(config);
List<TieredCompactorSupplier> suppliers = Lists.newArrayList(strategy.getCompactors());
assertEquals(1, suppliers.size());
assertEquals(0, suppliers.get(0).options().size());
}
@Test
public void gatherInformationAcrossTiers() throws IOException {
CompactionStrategy c1 = EasyMock.createMock(CompactionStrategy.class);
CompactionStrategy c2 = EasyMock.createMock(CompactionStrategy.class);
c1.gatherInformation(EasyMock.anyObject());
EasyMock.expectLastCall();
c2.gatherInformation(EasyMock.anyObject());
EasyMock.expectLastCall();
EasyMock.replay(c1, c2);
TieredCompactionStrategy strategy = newStrategy(c1, c2);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
strategy.gatherInformation(request);
EasyMock.verify(c1, c2);
}
@Test
public void shouldCompactStopsAtTier() throws IOException {
CompactionStrategy c1 = EasyMock.createMock("c1", CompactionStrategy.class);
CompactionStrategy c2 = EasyMock.createMock("c2", CompactionStrategy.class);
CompactionStrategy c3 = EasyMock.createMock("c3", CompactionStrategy.class);
EasyMock.expect(c1.shouldCompact(EasyMock.anyObject())).andReturn(false);
EasyMock.expect(c2.shouldCompact(EasyMock.anyObject())).andReturn(true);
EasyMock.replay(c1, c2, c3);
TieredCompactionStrategy strategy = newStrategy(c1, c2, c3);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
boolean result = strategy.shouldCompact(request);
assertTrue(result);
EasyMock.verify(c1, c2, c3);
}
@Test
public void shouldCompactWithSingleTier() throws Exception {
CompactionStrategy c1 = EasyMock.createMock("c1", CompactionStrategy.class);
EasyMock.expect(c1.shouldCompact(EasyMock.anyObject())).andReturn(false);
EasyMock.replay(c1);
TieredCompactionStrategy strategy = newStrategy(c1);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
boolean result = strategy.shouldCompact(request);
assertFalse(result);
EasyMock.verify(c1);
}
@Test
public void getCompactionPlanSkipsAndStopsAtTier() throws IOException {
CompactionPlan p1 = new CompactionPlan();
p1.inputFiles.add(new FileRef("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf"));
CompactionStrategy c1 = EasyMock.createMock("c1", CompactionStrategy.class);
CompactionStrategy c2 = EasyMock.createMock("c2", CompactionStrategy.class);
CompactionStrategy c3 = EasyMock.createMock("c3", CompactionStrategy.class);
EasyMock.expect(c1.getCompactionPlan(EasyMock.anyObject())).andReturn(null);
EasyMock.expect(c2.getCompactionPlan(EasyMock.anyObject())).andReturn(p1);
EasyMock.replay(c1, c2, c3);
TieredCompactionStrategy strategy = newStrategy(c1, c2, c3);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
CompactionPlan actualPlan = strategy.getCompactionPlan(request);
assertNotNull(actualPlan);
assertSame(p1, actualPlan);
EasyMock.verify(c1, c2, c3);
}
@Test
public void getCompactionPlanSkipsWhenEmptyInputAndStopsAtTier() throws IOException {
CompactionPlan p1 = new CompactionPlan();
p1.inputFiles.add(new FileRef("hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf"));
CompactionStrategy c1 = EasyMock.createMock("c1", CompactionStrategy.class);
CompactionStrategy c2 = EasyMock.createMock("c2", CompactionStrategy.class);
CompactionStrategy c3 = EasyMock.createMock("c3", CompactionStrategy.class);
EasyMock.expect(c1.getCompactionPlan(EasyMock.anyObject())).andReturn(new CompactionPlan());
EasyMock.expect(c2.getCompactionPlan(EasyMock.anyObject())).andReturn(p1);
EasyMock.replay(c1, c2, c3);
TieredCompactionStrategy strategy = newStrategy(c1, c2, c3);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
CompactionPlan actualPlan = strategy.getCompactionPlan(request);
assertNotNull(actualPlan);
assertSame(p1, actualPlan);
EasyMock.verify(c1, c2, c3);
}
@Test
public void getCompactionPlanWillReturnNullIfNonCompact() throws IOException {
CompactionStrategy c1 = EasyMock.createMock("c1", CompactionStrategy.class);
CompactionStrategy c2 = EasyMock.createMock("c2", CompactionStrategy.class);
CompactionStrategy c3 = EasyMock.createMock("c3", CompactionStrategy.class);
EasyMock.expect(c1.getCompactionPlan(EasyMock.anyObject())).andReturn(null);
EasyMock.expect(c2.getCompactionPlan(EasyMock.anyObject())).andReturn(null);
EasyMock.expect(c3.getCompactionPlan(EasyMock.anyObject())).andReturn(null);
EasyMock.replay(c1, c2, c3);
TieredCompactionStrategy strategy = newStrategy(c1, c2, c3);
CompactionRequestBuilder builder = new CompactionRequestBuilder();
MajorCompactionRequest request = builder.build();
CompactionPlan actualPlan = strategy.getCompactionPlan(request);
assertNull(actualPlan);
EasyMock.verify(c1, c2, c3);
}
private static TieredCompactionStrategy newStrategy(CompactionStrategy... strategies) {
Collection<TieredCompactorSupplier> mockSuppliers = new ArrayList<>();
for (CompactionStrategy s : strategies) {
mockSuppliers.add(new MockSupplier(s));
}
// @formatter:off
TieredCompactionStrategy strategy = EasyMock.partialMockBuilder(TieredCompactionStrategy.class)
.withConstructor()
.addMockedMethod("createSuppliers")
.createMock();
// @formatter:on
strategy.createSuppliers(EasyMock.anyObject());
EasyMock.expectLastCall().andAnswer(() -> strategy.compactors.addAll(mockSuppliers));
EasyMock.replay(strategy);
strategy.init(Collections.emptyMap());
return strategy;
}
static class MockSupplier implements TieredCompactorSupplier {
private final CompactionStrategy strategy;
MockSupplier(CompactionStrategy strategy) {
this.strategy = strategy;
}
@Override
public Map<String, String> options() {
return Collections.emptyMap();
}
@Override
public CompactionStrategy get(Map<String, String> tableProperites) {
return strategy;
}
}
}
@@ -0,0 +1,42 @@
package timely.store.compaction.util;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.util.format.FormatterConfig;
import org.apache.hadoop.io.Text;
import org.junit.Test;
import timely.adapter.accumulo.MetricAdapter;
import timely.test.TestTabletMetadata;
public class TabletMetadataFormatterTest {
@Test
public void simpleTextOutput() {
Long a1 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
Long a2 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(29);
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", a1));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", a2));
// @formatter:off
Collection<Map.Entry<Key, Value>> metadata = new TestTabletMetadata("2")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.prev(r1, null)
.time(r1, a1)
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao0.rf", "451,2")
.time(r2, a2)
.prev(r2, r1)
.entries();
// @formatter:on
TabletMetadataFormatter f = new TabletMetadataFormatter();
f.initialize(metadata, new FormatterConfig());
System.out.println(f.next());
}
}
@@ -0,0 +1,106 @@
package timely.store.compaction.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Lists;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.hadoop.io.Text;
import org.junit.Test;
import timely.adapter.accumulo.MetricAdapter;
import timely.test.TestTabletMetadata;
public class TabletMetadataViewTest {
@Test
public void tabletAccumulate() {
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", 1L));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.mem", 1L));
Text r3 = new Text(MetricAdapter.encodeRowKey("sys.disk", 1L));
// @formatter:off
Collection<Map.Entry<Key, Value>> metadata = new TestTabletMetadata("2")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.prev(r1, null)
.time(r1, 1)
.time(r2, 2)
.prev(r2, r3)
.file(r3, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r3, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.file(r3, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao0.rf", "451,2")
.time(r3, 3)
.prev(r3, r1)
.entries();
// assert that r1/r2 are in the output correctly
// r2 does not have any files, it should not appear in the output
// @formatter:on
TabletMetadataView view = new TabletMetadataView();
view.addEntry(metadata);
List<MetadataAccumulator.Entry> accumulated = Lists.newArrayList(view.getEntries());
assertEquals(2, accumulated.size());
// validate that it skipped over non-file
assertEquals(r1, accumulated.get(0).getTablet());
assertEquals("sys.cpu", accumulated.get(0).getTabletPrefix());
assertEquals(1, accumulated.get(0).getMilliseconds());
assertEquals(2, accumulated.get(0).getTotalFiles());
assertEquals(r3, accumulated.get(1).getTablet());
assertEquals("sys.disk", accumulated.get(1).getTabletPrefix());
assertEquals(3, accumulated.get(1).getTotalFiles());
assertEquals(3, accumulated.get(1).getMilliseconds());
assertNull(accumulated.get(0).getTabletPrev());
assertEquals(r1, accumulated.get(1).getTabletPrev());
}
@Test
public void noRowsOnView() {
Collection<Map.Entry<Key, Value>> metadata = Collections.emptyList();
TabletMetadataView view = new TabletMetadataView();
view.addEntry(metadata);
List<MetadataAccumulator.Entry> accumulated = Lists.newArrayList(view.getEntries());
TabletSummary summary = view.computeSummary().build();
String text = view.toText();
assertEquals(0, accumulated.size());
assertEquals(summary.totalTablets(), 0);
assertNotNull(text);
}
@Test
public void simpleTextOutput() {
Long a1 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
Long a2 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(29);
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", a1));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", a2));
// @formatter:off
Collection<Map.Entry<Key, Value>> metadata = new TestTabletMetadata("2")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r1, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.prev(r1, null)
.time(r1, a1)
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao8.rf", "800,3")
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao9.rf", "431,1")
.file(r2, "hdfs://hdfs-name/accumulo/tables/2/default_tablet/C0000ao0.rf", "451,2")
.time(r2, a2)
.prev(r2, r1)
.entries();
// @formatter:on
TabletMetadataView view = new TabletMetadataView();
view.addEntry(metadata);
String output = view.toText(TimeUnit.DAYS);
System.out.println(output);
}
}
@@ -0,0 +1,171 @@
package timely.store.compaction.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Lists;
import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
import org.apache.hadoop.io.Text;
import org.junit.Test;
import timely.adapter.accumulo.MetricAdapter;
public class TabletSummaryTest {
private static final long DEFAULT_TIME_MILLIS = new GregorianCalendar(2019, Calendar.JANUARY, 1).getTimeInMillis();
@Test
public void totalsMatch() {
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", DEFAULT_TIME_MILLIS));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", DEFAULT_TIME_MILLIS));
Text r3 = new Text(MetricAdapter.encodeRowKey("sys.mem", DEFAULT_TIME_MILLIS));
MetadataAccumulator.Entry e1 = new MetadataAccumulator.Entry(r1);
MetadataAccumulator.Entry e2 = new MetadataAccumulator.Entry(r2);
MetadataAccumulator.Entry e3 = new MetadataAccumulator.Entry(r3);
e1.addFile(800);
e1.addFile(100);
e2.addFile(100);
e2.addFile(200);
e3.addFile(500);
// @formatter:off
TabletSummary summary = TabletSummary.newBuilder(Lists.newArrayList(e1, e2, e3))
.disableTabletRowCheckFilter()
.build();
// @formatter:on
assertEquals(3, summary.totalTablets());
assertEquals(2, summary.totalTabletPrefixes());
List<TabletStatistic> largest = Lists.newArrayList(summary.findTabletPrefixesByLargest(3));
assertEquals(2, largest.size());
assertEquals(1200, largest.get(0).totalSize());
assertEquals(500, largest.get(1).totalSize());
}
@Test
public void statsComputedCorrectly() {
long a1 = TimeUnit.HOURS.toMillis(1);
long a2 = TimeUnit.HOURS.toMillis(2);
long ts1 = DEFAULT_TIME_MILLIS - a1;
long ts2 = DEFAULT_TIME_MILLIS - a2;
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts1));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts2));
Text r3 = new Text(MetricAdapter.encodeRowKey("sys.mem", ts1));
MetadataAccumulator.Entry e1 = new MetadataAccumulator.Entry(r1);
MetadataAccumulator.Entry e2 = new MetadataAccumulator.Entry(r2);
MetadataAccumulator.Entry e3 = new MetadataAccumulator.Entry(r3);
e1.addFile(800);
e1.addFile(100);
e2.addFile(100);
e2.addFile(200);
e3.addFile(500);
// @formatter:off
TabletSummary summary = TabletSummary.newBuilder(Lists.newArrayList(e1, e2, e3))
.currentTime(DEFAULT_TIME_MILLIS)
.disableTabletRowCheckFilter()
.build();
// @formatter:on
List<TabletStatistic> largest = Lists.newArrayList(summary.findTabletPrefixesByLargest(3));
List<TabletStatistic> oldest = Lists.newArrayList(summary.findTabletPrefixesByOldest(3));
assertEquals(2, largest.size());
assertEquals(1200, largest.get(0).totalSize());
assertEquals(500, largest.get(1).totalSize());
assertEquals(2, oldest.size());
assertEquals(a2, oldest.get(0).maxAge());
assertEquals(a1, oldest.get(1).maxAge());
}
@Test
public void aggregateComputes() {
long a1 = TimeUnit.HOURS.toMillis(1);
long a2 = TimeUnit.HOURS.toMillis(2);
long ts1 = DEFAULT_TIME_MILLIS - a1;
long ts2 = DEFAULT_TIME_MILLIS - a2;
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts1));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts2));
Text r3 = new Text(MetricAdapter.encodeRowKey("sys.mem", ts1));
MetadataAccumulator.Entry e1 = new MetadataAccumulator.Entry(r1);
MetadataAccumulator.Entry e2 = new MetadataAccumulator.Entry(r2);
MetadataAccumulator.Entry e3 = new MetadataAccumulator.Entry(r3);
e1.addFile(800);
e1.addFile(100);
e2.addFile(100);
e2.addFile(200);
e1.addFile(500);
// @formatter:off
TabletSummary summary = TabletSummary.newBuilder(Lists.newArrayList(e1, e2, e3))
.currentTime(DEFAULT_TIME_MILLIS)
.disableTabletRowCheckFilter()
.build();
// @formatter:on
Map<TabletStatisticType, StatisticalSummary> map = summary.aggregateSummary();
StatisticalSummary size = map.get(TabletStatisticType.SIZE);
StatisticalSummary time = map.get(TabletStatisticType.TIME);
assertEquals(1700, (long) size.getSum());
assertEquals(a2, (long) time.getMax());
assertEquals(a1, (long) time.getMin());
}
@Test
public void initialTabletsArePrunedByDefault() {
// test that the filter does not include
// the differing tablet rows; the compaction strategy
// should ignore
long a1 = TimeUnit.DAYS.toMillis(365);
long a2 = TimeUnit.HOURS.toMillis(2);
long ts1 = DEFAULT_TIME_MILLIS - a1;
long ts2 = DEFAULT_TIME_MILLIS - a2;
Text r1 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts1));
Text r2 = new Text(MetricAdapter.encodeRowKey("sys.cpu", ts2));
MetadataAccumulator.Entry e1 = new MetadataAccumulator.Entry(r1);
MetadataAccumulator.Entry e2 = new MetadataAccumulator.Entry(r2);
e1.addFile(800);
e2.addFile(100);
e2.setTablePrev(r1);
// @formatter:off
TabletSummary summary = TabletSummary.newBuilder(Lists.newArrayList(e1, e2))
.currentTime(DEFAULT_TIME_MILLIS)
.build();
// @formatter:on
assertEquals(1, summary.totalTablets());
assertEquals(1, summary.totalTabletPrefixes());
Optional<TabletStatistic> stat = summary.getSummary().stream().findAny();
// verify that initial stat was pruned from results
// this is the default behavior
assertTrue(stat.isPresent());
assertEquals(a2, stat.get().maxAge());
}
}
@@ -0,0 +1,119 @@
package timely.test;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import org.apache.accumulo.core.client.lexicoder.Lexicoder;
import org.apache.accumulo.core.client.lexicoder.PairLexicoder;
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.TabletId;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.data.impl.TabletIdImpl;
import org.apache.accumulo.core.metadata.schema.DataFileValue;
import org.apache.accumulo.core.util.ComparablePair;
import org.apache.accumulo.server.fs.FileRef;
import org.apache.accumulo.server.fs.VolumeManager;
import org.apache.accumulo.tserver.compaction.MajorCompactionReason;
import org.apache.accumulo.tserver.compaction.MajorCompactionRequest;
import org.easymock.EasyMock;
import timely.adapter.accumulo.MetricAdapter;
public class CompactionRequestBuilder {
private static final long DEFAULT_TIME_MILLIS = new GregorianCalendar(2019, Calendar.JANUARY, 1).getTimeInMillis();
private static final Lexicoder<String> stringEncoder = new StringLexicoder();
private static final PairLexicoder<String, String> badPairEncoder = new PairLexicoder<>(stringEncoder,
stringEncoder);
private final Map<FileRef, DataFileValue> refs = new LinkedHashMap<>();
private final Map<String, String> tableProperties;
private final long timeMillis;
private Key endKeyMetric;
private Key prevKeyMetric;
public CompactionRequestBuilder() {
this(DEFAULT_TIME_MILLIS);
}
public CompactionRequestBuilder(long timeMillis) {
this.timeMillis = timeMillis;
this.tableProperties = new TreeMap<>();
}
public CompactionRequestBuilder endKeyMetricNonOffset(String name, String offset) {
byte[] rowKey = offset != null ? badPairEncoder.encode(new ComparablePair<>(name, offset))
: stringEncoder.encode(name);
endKeyMetric = new Key(rowKey);
return this;
}
public CompactionRequestBuilder endKeyMetric(String name, long offset) {
long ts = timeMillis - offset;
byte[] rowKey = MetricAdapter.encodeRowKey(name, ts);
endKeyMetric = new Key(rowKey);
return this;
}
public CompactionRequestBuilder prevEndKeyMetric(String name, long offset) {
long ts = timeMillis - offset;
byte[] rowKey = MetricAdapter.encodeRowKey(name, ts);
prevKeyMetric = new Key(rowKey);
return this;
}
public CompactionRequestBuilder prevEndKeyMetricNonOffset(String name, String offset) {
byte[] rowKey = offset != null ? badPairEncoder.encode(new ComparablePair<>(name, offset))
: stringEncoder.encode(name);
prevKeyMetric = new Key(rowKey);
return this;
}
public CompactionRequestBuilder file(String path, long size, long entries) {
refs.put(new FileRef(path), new DataFileValue(size, entries));
return this;
}
public CompactionRequestBuilder tableProperties(String key, String value) {
tableProperties.put(key, value);
return this;
}
public MajorCompactionRequest build() {
String tableName = "1";
KeyExtent ke = new KeyExtent();
ke.setTableId(tableName);
if (null != endKeyMetric) {
ke.setEndRow(endKeyMetric.getRow());
}
if (null != prevKeyMetric) {
ke.setPrevEndRow(prevKeyMetric.getRow());
}
TabletId tid = new TabletIdImpl(ke);
// @formatter:off
MajorCompactionRequest req = EasyMock.partialMockBuilder(MajorCompactionRequest.class)
.withConstructor(KeyExtent.class, MajorCompactionReason.class, VolumeManager.class,
AccumuloConfiguration.class)
.withArgs(ke, MajorCompactionReason.NORMAL, null, AccumuloConfiguration.getDefaultConfiguration())
.addMockedMethod("getTabletId")
.addMockedMethod("getTableProperties")
.createMock();
// @formatter:on
req.setFiles(refs);
EasyMock.expect(req.getTabletId()).andStubReturn(tid);
EasyMock.expect(req.getTableProperties()).andStubReturn(tableProperties);
EasyMock.replay(req);
return req;
}
}
@@ -0,0 +1,62 @@
package timely.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.BatchWriterConfig;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.data.Mutation;
import timely.adapter.accumulo.MetricAdapter;
import timely.model.Metric;
public class TestMetricWriter {
private static final int DEFAULT_BATCHES = 10000;
private final Connector connector;
private final TestMetrics metrics;
private final String tableName;
public TestMetricWriter(String tableName, Connector connector, TestMetrics metrics) {
this.connector = connector;
this.metrics = metrics;
this.tableName = tableName;
}
public long ingestRandomDuration(long duration, TimeUnit durationUnit, long timestampInitial, int decrementMillis) {
long timestampCurrent = timestampInitial;
long runUntilMillis = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, durationUnit);
while (System.currentTimeMillis() < runUntilMillis) {
timestampCurrent = ingestRandom(timestampCurrent, decrementMillis);
}
return timestampCurrent;
}
private long ingestRandom(long timestampInitial, int decrementMillis) {
long ts = timestampInitial;
Collection<Mutation> mutations = new ArrayList<>(TestMetricWriter.DEFAULT_BATCHES);
int idx = 0;
while (idx++ < TestMetricWriter.DEFAULT_BATCHES) {
ts = ts - decrementMillis;
Metric m = metrics.randomMetric(ts);
Mutation mutation = MetricAdapter.toMutation(m);
mutations.add(mutation);
}
try {
BatchWriterConfig cfg = new BatchWriterConfig().setMaxWriteThreads(4);
try (BatchWriter writer = connector.createBatchWriter(tableName, cfg)) {
writer.addMutations(mutations);
}
connector.tableOperations().flush(tableName, null, null, true);
} catch (AccumuloException | AccumuloSecurityException | TableNotFoundException e) {
throw new RuntimeException(e);
}
return ts;
}
}
@@ -0,0 +1,48 @@
package timely.test;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import timely.model.Metric;
import timely.model.Tag;
public class TestMetrics {
private final List<String> prefixes;
private final List<String> suffixes;
private final List<Tag> tags;
public TestMetrics() {
prefixes = Lists.newArrayList("sys", "statsd");
suffixes = createRandomSuffixes();
tags = new ArrayList<>(2);
tags.add(new Tag("hostname", "localhost"));
tags.add(new Tag("ip", "127.0.0.1"));
}
public Metric randomMetric(long timestamp) {
int prefixIdx = RandomUtils.nextInt(0, prefixes.size());
int suffixIdx = RandomUtils.nextInt(0, suffixes.size());
// @formatter:off
return Metric.newBuilder()
.name(prefixes.get(prefixIdx) + "." + suffixes.get(suffixIdx))
.tags(tags)
.value(timestamp, RandomUtils.nextDouble(0, 1.0))
.build();
}
private static List<String> createRandomSuffixes() {
int SUFFIX_MIN = 5;
int SUFFIX_MAX = 10;
int SUFFIX_NUM = 4;
List<String> list = new ArrayList<>(SUFFIX_NUM);
for (int i = 0; i < SUFFIX_NUM; i++) {
list.add(RandomStringUtils.randomAlphabetic(SUFFIX_MIN, SUFFIX_MAX).toLowerCase());
}
return list;
}
}
@@ -0,0 +1,58 @@
package timely.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Maps;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.metadata.schema.MetadataSchema;
import org.apache.accumulo.core.util.ColumnFQ;
import org.apache.hadoop.io.Text;
public class TestTabletMetadata {
private final String tableId;
private final List<Map.Entry<Key, Value>> entries;
public TestTabletMetadata(String tableId) {
this.tableId = tableId;
entries = new ArrayList<>();
}
public TestTabletMetadata file(Text row, String path, String value) {
Text cq = new Text(path);
addEntry(row, MetadataSchema.TabletsSection.DataFileColumnFamily.NAME, cq, value);
return this;
}
public TestTabletMetadata prev(Text row, Text prevRow) {
ColumnFQ cfq = MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN;
addEntry(row, cfq.getColumnFamily(), cfq.getColumnQualifier(), KeyExtent.encodePrevEndRow(prevRow));
return this;
}
public TestTabletMetadata time(Text row, long millis) {
ColumnFQ cfq = MetadataSchema.TabletsSection.ServerColumnFamily.TIME_COLUMN;
addEntry(row, cfq.getColumnFamily(), cfq.getColumnQualifier(), "M" + millis);
return this;
}
public Collection<Map.Entry<Key, Value>> entries() {
return entries;
}
private void addEntry(Text row, Text cf, Text cq, String value) {
addEntry(row, cf, cq, new Value(value));
}
private void addEntry(Text row, Text cf, Text cq, Value value) {
Text rowEntry = KeyExtent.getMetadataEntry(tableId, row);
Value val = new Value(value);
Key k = new Key(rowEntry, cf, cq);
entries.add(Maps.immutableEntry(k, val));
}
}
@@ -59,7 +59,7 @@ public class DataStoreIT extends OneWaySSLBase {
conf.setMetricAgeOffDays(ageOffSettings);
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + ONE_HOUR) + " 3.0 tag1=value1 tag2=value2",
@@ -106,11 +106,24 @@ public class DataStoreIT extends OneWaySSLBase {
conf.setMetricAgeOffDays(ageOffSettings);
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
// @formatter:off
/*
* TEST_TIME = System.currentTimeMillis() - ONE_DAY
* Age off for all metrics is one day = 24 hours
*
* lines 1, 2, 3 will age off immediately as they are >= 2 days old
* line 4 is there because otherwise the meta tags would also age off and we would get a 400 - No Tags Found
* line 5 is one day old and will age off immediately
* lines 6 & 7 should be returned as they are not aged off and are within the query range
*
*/
// @formatter:on
try {
put("sys.cpu.idle " + (TEST_TIME - ONE_DAY - (2 * ONE_HOUR)) + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME - ONE_DAY - ONE_HOUR) + " 3.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME - ONE_DAY) + " 2.0 tag1=value1 tag3=value3",
"sys.cpu.idle " + (TEST_TIME + (ONE_DAY * 2)) + " 2.0 tag1=value1 tag3=value3",
"sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + ONE_HOUR) + " 3.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + (ONE_HOUR * 2)) + " 2.0 tag1=value1 tag3=value3");
@@ -5,6 +5,8 @@ import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
@@ -12,8 +14,10 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Server;
import timely.auth.AuthCache;
import timely.configuration.Configuration;
import timely.configuration.ServerSsl;
import timely.store.MetricAgeOffIterator;
import timely.test.TestConfiguration;
@@ -24,6 +28,11 @@ public class MacITBase {
private static final Logger LOG = LoggerFactory.getLogger(MacITBase.class);
protected static ServerSsl serverSsl = null;
protected static File clientTrustStoreFile = null;
protected static SslContext sslCtx = null;
protected static SelfSignedCertificate serverCert = null;
private static final File tempDir;
protected static final String MAC_ROOT_USER = "root";
@@ -50,8 +59,6 @@ public class MacITBase {
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
conf.getSecurity().getServerSsl().setUseOpenssl(false);
conf.getSecurity().getServerSsl().setUseGeneratedKeypair(true);
conf.getWebsocket().setFlushIntervalSeconds(TestConfiguration.WAIT_SECONDS);
HashMap<String, Integer> ageOffSettings = new HashMap<>();
ageOffSettings.put(MetricAgeOffIterator.DEFAULT_AGEOFF_KEY, 7);
@@ -88,4 +95,52 @@ public class MacITBase {
public void initializeAuthCache() {
AuthCache.clear();
}
protected static void setupSSL() throws Exception {
setupSSL(TestConfiguration.createMinimalConfigurationForTest(), false);
}
protected static void setupSSL(Configuration config, boolean twoWaySsl) throws Exception {
setupSSL(config, twoWaySsl, null);
}
protected static void setupSSL(Configuration config, boolean twoWaySsl, SelfSignedCertificate serverCert)
throws Exception {
ServerSsl serverSsl = config.getSecurity().getServerSsl();
boolean generateNewSsl = MacITBase.serverSsl == null || serverCert != null
|| !MacITBase.serverSsl.equals(serverSsl);
if (generateNewSsl) {
// new serverCert and trustStoreFile
if (serverCert == null) {
MacITBase.serverCert = new SelfSignedCertificate();
} else {
MacITBase.serverCert = serverCert;
}
MacITBase.clientTrustStoreFile = MacITBase.serverCert.certificate().getAbsoluteFile();
MacITBase.serverSsl = serverSsl;
MacITBase.serverSsl.setCertificateFile(MacITBase.serverCert.certificate().getAbsolutePath());
MacITBase.serverSsl.setKeyFile(MacITBase.serverCert.privateKey().getAbsolutePath());
}
config.getSecurity().getServerSsl().setCertificateFile(MacITBase.serverSsl.getCertificateFile());
config.getSecurity().getServerSsl().setKeyFile(MacITBase.serverSsl.getKeyFile());
config.getSecurity().getServerSsl().setUseOpenssl(false);
config.getSecurity().getServerSsl().setUseGeneratedKeypair(false);
if (twoWaySsl) {
// Needed for 2way SSL
config.getSecurity().getServerSsl().setTrustStoreFile(MacITBase.serverSsl.getCertificateFile());
config.getSecurity().setAllowAnonymousHttpAccess(false);
} else {
config.getSecurity().setAllowAnonymousHttpAccess(true);
}
if (generateNewSsl) {
// new SslContext
OneWaySSLBase.sslCtx = Server.createSSLContext(config);
}
}
protected static SslContext getSslContext() {
return MacITBase.sslCtx;
}
}
@@ -0,0 +1,254 @@
package timely.test.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.iterators.IteratorUtil;
import org.apache.accumulo.tserver.compaction.CompactionStrategy;
import org.apache.accumulo.tserver.compaction.DefaultCompactionStrategy;
import org.apache.hadoop.io.Text;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.store.MetricAgeOffIterator;
import timely.store.compaction.MetricCompactionStrategy;
import timely.store.compaction.TabletRowAdapter;
import timely.store.compaction.util.TabletMetadataQuery;
import timely.store.compaction.util.TabletMetadataView;
import timely.store.compaction.util.TabletStatistic;
import timely.store.compaction.util.TabletSummary;
import timely.test.IntegrationTest;
import timely.test.TestMetricWriter;
import timely.test.TestMetrics;
@Category(IntegrationTest.class)
public class MetricCompactionIT extends MacITBase {
private final static Logger LOG = LoggerFactory.getLogger(MetricCompactionIT.class);
private static final String AGE_OFF_ITERATOR_NAME = "ageoffmetrics";
private static final EnumSet<IteratorUtil.IteratorScope> AGEOFF_SCOPES = EnumSet
.allOf(IteratorUtil.IteratorScope.class);
private Connector connector;
private MetricConfigurationAdapter adapter;
@BeforeClass
public static void setupClass() throws Exception {
Connector connector = mac.getConnector(MAC_ROOT_USER, MAC_ROOT_PASSWORD);
connector.instanceOperations().setProperty(Property.TSERV_MAJC_DELAY.getKey(), "3s");
}
@Before
public void setup() throws Exception {
connector = mac.getConnector(MAC_ROOT_USER, MAC_ROOT_PASSWORD);
String metricsTable = conf.getMetricsTable();
if (!metricsTable.contains(".")) {
throw new IllegalArgumentException("Expected to find namespace in table name");
}
String[] parts = metricsTable.split("\\.", 2);
String namespace = parts[0];
if (!connector.namespaceOperations().exists(namespace)) {
connector.namespaceOperations().create(namespace);
}
connector.tableOperations().create(metricsTable);
adapter = new MetricConfigurationAdapter(connector, conf.getMetricsTable());
connector.tableOperations().setProperty(conf.getMetricsTable(), Property.TABLE_SPLIT_THRESHOLD.getKey(), "50K");
adapter.resetState();
}
@After
public void teardown() throws Exception {
adapter.resetState();
}
@AfterClass
public static void teardownClass() throws Exception {
Connector connector = mac.getConnector(MAC_ROOT_USER, MAC_ROOT_PASSWORD);
connector.instanceOperations().setProperty(Property.TSERV_MAJC_DELAY.getKey(), "30s");
}
@Test
public void tableMetadataEnumeratesTablets() throws Exception {
String metricsTable = conf.getMetricsTable();
TestMetricWriter writer = new TestMetricWriter(metricsTable, connector, new TestMetrics());
writer.ingestRandomDuration(60, TimeUnit.SECONDS, System.currentTimeMillis(), 250);
Collection<Text> splits = connector.tableOperations().listSplits(conf.getMetricsTable());
TabletMetadataQuery query = new TabletMetadataQuery(connector, metricsTable);
TabletMetadataView view = query.run();
// @formatter:off
TabletSummary summary = view.computeSummary()
.disableTabletRowCheckFilter()
.build();
Set<String> splitsPrefixed = splits.stream()
.map(row -> TabletRowAdapter.decodeRowPrefix(row).orElse("unknown"))
.collect(Collectors.toSet());
Set<String> tabletsPrefixed = summary.getSummary().stream()
.map(TabletStatistic::getKeyName)
.collect(Collectors.toSet());
// difference both sides
// should be empty
// @formatter:on
Set<String> i1 = Sets.difference(tabletsPrefixed, splitsPrefixed);
Set<String> i2 = Sets.difference(splitsPrefixed, tabletsPrefixed);
assertTrue("tablets-diff" + i1.toString(), i1.isEmpty());
assertTrue("splits-diff" + i2.toString(), i2.isEmpty());
// dump data to screen as summary
System.out.println(view.toText());
}
@Test
public void tabletsCompactedUntilAgeOff() throws Exception {
// test will replicated problem in time-series tablets where data is
// split automatically and the resulting tablets will not be cleaned up
// by the default compaction strategy
//
// metric compaction strategy will age-off tablets by date on tablets
// but ignore differing end/prev rows
//
// the mini-accumulo cluster should not have any age-off
// iterators on timely.metrics
String metricsTable = conf.getMetricsTable();
// check to make sure the table has only one iterator
Map<String, EnumSet<IteratorUtil.IteratorScope>> itrs = connector.tableOperations().listIterators(metricsTable);
assertEquals(1, itrs.size());
assertTrue(itrs.containsKey("vers"));
long timestampMax = System.currentTimeMillis();
TestMetricWriter writer = new TestMetricWriter(conf.getMetricsTable(), connector, new TestMetrics());
long timestampMin = writer.ingestRandomDuration(30, TimeUnit.SECONDS, timestampMax, 250);
// invoke compact operation to flush everything through
// without this call the compaction strategy later on
// was not compacting all of the tablets
adapter.runCompaction();
// figure out what should be targeted based on an ageoff date
// cut the delta by 1/3
long ageoff = (timestampMax - timestampMin) / 3;
// run check to verify there are pre-existing splits for a prefix
// @formatter:off
TabletMetadataQuery query = new TabletMetadataQuery(connector, metricsTable);
TabletSummary summary1 = query.run()
.computeSummary()
.build();
Set<String> tabletsBefore = summary1.getSummary().stream()
.filter(t -> t.maxAge() > ageoff)
.map(TabletStatistic::getKeyName)
.collect(Collectors.toSet());
// @formatter:on
assertFalse("Tablets were empty before compaction, expected test data", tabletsBefore.isEmpty());
long compactBeforeMillis = System.currentTimeMillis();
// invoke the age-off compactor
// after this runs it should have gotten rid o fdata
// have pre-age-off tablets
adapter.applyCompactionConfiguration(MetricCompactionStrategy.class, ageoff);
adapter.runCompaction();
long compactDeltaMillis = System.currentTimeMillis() - compactBeforeMillis;
// re-run check on tablet metadata
// check that there aren't new tablets past the predicted age-off
// any tablets with differing end/prev rows are filtered by the summary
// @formatter:off
TabletSummary summary2 = query.run()
.computeSummary()
.build();
List<TabletStatistic> tabletsFilterAfter = summary2.getSummary().stream()
.filter(t -> ((t.maxAge() - compactDeltaMillis) > ageoff) && t.getKeyCount() > 1)
.collect(Collectors.toList());
// @formatter:on
if (!tabletsFilterAfter.isEmpty()) {
LOG.info("tablets-before: {}", tabletsBefore.toString());
LOG.info("tablets-after: {}", tabletsFilterAfter.toString());
}
// should not have found tablets past the age-off and should also
// have found existing tablets still in system (i.e. did not compact everything)
assertTrue("Tablets still had unexpected after compaction", tabletsFilterAfter.isEmpty());
assertTrue("Some tablets should exist, but did not find any", summary2.totalTablets() > 0);
}
private static class MetricConfigurationAdapter {
private final Connector connector;
private final String tableName;
private static String[] CLEAR_PROPERTY_KEYS = {
Property.TABLE_COMPACTION_STRATEGY_PREFIX.getKey() + MetricCompactionStrategy.MIN_AGEOFF_KEY };
public MetricConfigurationAdapter(Connector connector, String tableName) {
this.connector = connector;
this.tableName = tableName;
}
public void applyCompactionConfiguration(Class<? extends CompactionStrategy> clazz, long ageOff)
throws Exception {
Map<String, String> ageOffs = new HashMap<>();
ageOffs.put("ageoff.default", Long.toString(ageOff));
IteratorSetting ageOffIteratorSettings = new IteratorSetting(100, AGE_OFF_ITERATOR_NAME,
MetricAgeOffIterator.class, ageOffs);
connector.tableOperations().attachIterator(tableName, ageOffIteratorSettings, AGEOFF_SCOPES);
connector.tableOperations().setProperty(tableName, Property.TABLE_COMPACTION_STRATEGY.getKey(),
clazz.getName());
}
public void runCompaction() throws Exception {
connector.tableOperations().compact(tableName, null, null, true, true);
}
public void resetState() throws Exception {
Map<String, EnumSet<IteratorUtil.IteratorScope>> iters = connector.tableOperations()
.listIterators(tableName);
for (String name : iters.keySet()) {
if (name.startsWith("ageoff")) {
connector.tableOperations().removeIterator(tableName, name, AGEOFF_SCOPES);
}
}
// reset compaction strategy to default
connector.tableOperations().setProperty(tableName, Property.TABLE_COMPACTION_STRATEGY.getKey(),
DefaultCompactionStrategy.class.getName());
// reset any properties
for (String s : CLEAR_PROPERTY_KEYS) {
connector.tableOperations().removeProperty(tableName, s);
}
}
}
}
@@ -30,7 +30,7 @@ public class OneWaySSLAnonAccessIT extends OneWaySSLBase {
@Test
public void testQueryWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1000) + " 3.0 tag1=value1 tag2=value2",
@@ -1,6 +1,5 @@
package timely.test.integration;
import java.io.File;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
@@ -12,25 +11,21 @@ import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import timely.auth.AuthCache;
import timely.configuration.Configuration;
/**
* Base test class for SSL with anonymous access
*/
public class OneWaySSLBase extends QueryBase {
protected static File clientTrustStoreFile = null;
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
builder.sslProvider(SslProvider.JDK);
builder.trustManager(clientTrustStoreFile); // Trust the server cert
builder.trustManager(MacITBase.clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertTrue(ctx.isClient());
Assert.assertTrue(ctx instanceof JdkSslContext);
@@ -39,19 +34,9 @@ public class OneWaySSLBase extends QueryBase {
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(Configuration config) throws Exception {
SelfSignedCertificate serverCert = new SelfSignedCertificate();
config.getSecurity().getServerSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
config.getSecurity().getServerSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
config.getSecurity().getServerSsl().setUseOpenssl(false);
config.getSecurity().getServerSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousHttpAccess(true);
}
@Before
public void configureSSL() throws Exception {
setupSSL(conf);
setupSSL(conf, false);
}
@Override
@@ -93,7 +93,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
@Test
public void testBasicAuthLogin() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String metrics = "https://localhost:54322/api/metrics";
query(metrics);
@@ -105,7 +105,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
@Test(expected = UnauthorizedUserException.class)
public void testBasicAuthLoginFailure() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String metrics = "https://localhost:54322/api/metrics";
query("test", "test2", metrics);
@@ -117,7 +117,7 @@ public class OneWaySSLBasicAuthAccessIT extends OneWaySSLBase {
@Test
public void testQueryWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1000) + " 3.0 tag1=value1 tag2=value2",
@@ -13,7 +13,7 @@ public class StaticFileServerIT extends OneWaySSLBase {
@Before
public void before() throws Exception {
s = new Server(conf);
s.run();
s.run(getSslContext());
}
@After
@@ -0,0 +1,92 @@
package timely.test.integration;
import java.net.URL;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import org.junit.After;
import org.junit.Assert;
import timely.auth.AuthCache;
import timely.netty.Constants;
/**
* Base test class for SSL with anonymous access
*/
public class TwoWaySSLBase extends QueryBase {
@After
public void tearDown() throws Exception {
AuthCache.resetConfiguration();
}
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
// Use server cert / key on client side
builder.keyManager(MacITBase.serverCert.key(), (String) null, MacITBase.serverCert.cert());
builder.sslProvider(SslProvider.JDK);
builder.trustManager(MacITBase.clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertTrue(ctx.isClient());
Assert.assertTrue(ctx instanceof JdkSslContext);
JdkSslContext jdk = (JdkSslContext) ctx;
SSLContext jdkSslContext = jdk.context();
return jdkSslContext.getSocketFactory();
}
@Override
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
// Username and password not used in 2way SSL case
return getUrlConnection(null, null, url);
}
@Override
protected HttpsURLConnection getUrlConnection(String username, String password, URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setRequestMethod("GET");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
con.connect();
int responseCode = con.getResponseCode();
if (401 == responseCode) {
throw new UnauthorizedUserException();
}
Assert.assertEquals(200, responseCode);
List<String> cookies = con.getHeaderFields().get(HttpHeaderNames.SET_COOKIE.toString());
Assert.assertEquals(1, cookies.size());
Cookie sessionCookie = ClientCookieDecoder.STRICT.decode(cookies.get(0));
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(HttpHeaderNames.COOKIE.toString(), sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
return con;
}
}
@@ -1,30 +1,11 @@
package timely.test.integration;
import java.io.File;
import java.net.URL;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -33,10 +14,9 @@ import org.junit.rules.TemporaryFolder;
import timely.Server;
import timely.auth.AuthCache;
import timely.configuration.Configuration;
import timely.netty.Constants;
import timely.test.TestConfiguration;
public class TwoWaySSLFailureIT extends QueryBase {
public class TwoWaySSLFailureIT extends TwoWaySSLBase {
@ClassRule
public static final TemporaryFolder temp = new TemporaryFolder();
@@ -44,87 +24,6 @@ public class TwoWaySSLFailureIT extends QueryBase {
private static MiniAccumuloCluster mac = null;
private static Configuration conf = null;
private static SelfSignedCertificate serverCert = null;
private static File clientTrustStoreFile = null;
static {
try {
// This fqdn does not match what is in security.xml
serverCert = new SelfSignedCertificate("CN=bad.example.com");
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
} catch (Exception e) {
throw new RuntimeException("Error creating self signed certificate", e);
}
}
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
// Use server cert / key on client side
builder.keyManager(serverCert.key(), (String) null, serverCert.cert());
builder.sslProvider(SslProvider.JDK);
builder.trustManager(clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertTrue(ctx.isClient());
Assert.assertTrue(ctx instanceof JdkSslContext);
JdkSslContext jdk = (JdkSslContext) ctx;
SSLContext jdkSslContext = jdk.context();
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getServerSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.getSecurity().getServerSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setUseOpenssl(false);
config.getSecurity().getServerSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousHttpAccess(false);
}
@Override
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
// Username and password not used in 2way SSL case
return getUrlConnection(null, null, url);
}
@Override
protected HttpsURLConnection getUrlConnection(String username, String password, URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setRequestMethod("GET");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
con.connect();
int responseCode = con.getResponseCode();
if (401 == responseCode) {
throw new UnauthorizedUserException();
}
Assert.assertEquals(200, responseCode);
List<String> cookies = con.getHeaderFields().get(HttpHeaderNames.SET_COOKIE.toString());
Assert.assertEquals(1, cookies.size());
Cookie sessionCookie = ClientCookieDecoder.STRICT.decode(cookies.get(0));
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(HttpHeaderNames.COOKIE.toString(), sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
return con;
}
@BeforeClass
public static void beforeClass() throws Exception {
temp.create();
@@ -134,7 +33,8 @@ public class TwoWaySSLFailureIT extends QueryBase {
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
// This fqdn does not match what is in security.xml
setupSSL(conf, true, new SelfSignedCertificate("CN=bad.example.com"));
}
@AfterClass
@@ -163,7 +63,7 @@ public class TwoWaySSLFailureIT extends QueryBase {
@Test(expected = UnauthorizedUserException.class)
public void testBasicAuthLoginFailure() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String metrics = "https://localhost:54322/api/metrics";
query(metrics);
@@ -3,33 +3,17 @@ package timely.test.integration;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -41,7 +25,6 @@ import timely.api.request.timeseries.QueryRequest;
import timely.api.response.timeseries.QueryResponse;
import timely.auth.AuthCache;
import timely.configuration.Configuration;
import timely.netty.Constants;
import timely.test.IntegrationTest;
import timely.test.TestConfiguration;
@@ -51,7 +34,7 @@ import timely.test.TestConfiguration;
*
*/
@Category(IntegrationTest.class)
public class TwoWaySSLIT extends QueryBase {
public class TwoWaySSLIT extends TwoWaySSLBase {
private static final Long TEST_TIME = System.currentTimeMillis();
@@ -61,74 +44,6 @@ public class TwoWaySSLIT extends QueryBase {
private static MiniAccumuloCluster mac = null;
private static Configuration conf = null;
protected static SelfSignedCertificate serverCert = null;
protected static File clientTrustStoreFile = null;
static {
try {
serverCert = new SelfSignedCertificate();
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
} catch (Exception e) {
throw new RuntimeException("Error creating self signed certificate", e);
}
}
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
// Use server cert / key on client side.
builder.keyManager(serverCert.key(), (String) null, serverCert.cert());
builder.sslProvider(SslProvider.JDK);
builder.trustManager(clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertTrue(ctx.isClient());
Assert.assertTrue(ctx instanceof JdkSslContext);
JdkSslContext jdk = (JdkSslContext) ctx;
SSLContext jdkSslContext = jdk.context();
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getServerSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.getSecurity().getServerSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setUseOpenssl(false);
config.getSecurity().getServerSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousHttpAccess(false);
}
@Override
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
// Username and password not used in 2way SSL case
return getUrlConnection(null, null, url);
}
@Override
protected HttpsURLConnection getUrlConnection(String username, String password, URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier((host, session) -> true);
con.setRequestMethod("GET");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
con.connect();
int responseCode = con.getResponseCode();
if (401 == responseCode) {
throw new UnauthorizedUserException();
}
Assert.assertEquals(200, responseCode);
List<String> cookies = con.getHeaderFields().get(HttpHeaderNames.SET_COOKIE.toString());
Assert.assertEquals(1, cookies.size());
Cookie sessionCookie = ClientCookieDecoder.STRICT.decode(cookies.get(0));
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(HttpHeaderNames.COOKIE.toString(), sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier((host, session) -> true);
return con;
}
@BeforeClass
public static void beforeClass() throws Exception {
final MiniAccumuloConfig macConfig = new MiniAccumuloConfig(temp.newFolder("mac"), "secret");
@@ -137,7 +52,6 @@ public class TwoWaySSLIT extends QueryBase {
conf = TestConfiguration.createMinimalConfigurationForTest();
conf.getAccumulo().setInstanceName(mac.getInstanceName());
conf.getAccumulo().setZookeepers(mac.getZooKeepers());
setupSSL(conf);
}
@AfterClass
@@ -157,6 +71,7 @@ public class TwoWaySSLIT extends QueryBase {
}
}
});
setupSSL(conf, true);
}
@After
@@ -167,7 +82,7 @@ public class TwoWaySSLIT extends QueryBase {
@Test
public void testBasicAuthLogin() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String metrics = "https://localhost:54322/api/metrics";
query(metrics);
@@ -190,7 +105,7 @@ public class TwoWaySSLIT extends QueryBase {
public void testQueryWithVisibility(Configuration conf) throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
@@ -3,32 +3,14 @@ package timely.test.integration;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.security.Authorizations;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -36,8 +18,6 @@ import timely.Server;
import timely.api.request.timeseries.QueryRequest;
import timely.api.response.timeseries.QueryResponse;
import timely.auth.AuthCache;
import timely.configuration.Configuration;
import timely.netty.Constants;
import timely.test.IntegrationTest;
import timely.test.TestConfiguration;
@@ -48,97 +28,13 @@ import timely.test.TestConfiguration;
*
*/
@Category(IntegrationTest.class)
public class TwoWaySSLOpenSSLIT extends QueryBase {
public class TwoWaySSLOpenSSLIT extends TwoWaySSLBase {
private static final Long TEST_TIME = System.currentTimeMillis();
protected static SelfSignedCertificate serverCert = null;
protected static File clientTrustStoreFile = null;
static {
try {
serverCert = new SelfSignedCertificate();
clientTrustStoreFile = serverCert.certificate().getAbsoluteFile();
} catch (Exception e) {
throw new RuntimeException("Error creating self signed certificate", e);
}
}
protected SSLSocketFactory getSSLSocketFactory() throws Exception {
SslContextBuilder builder = SslContextBuilder.forClient();
builder.applicationProtocolConfig(ApplicationProtocolConfig.DISABLED);
// Use server cert / key on client side.
builder.keyManager(serverCert.key(), (String) null, serverCert.cert());
builder.sslProvider(SslProvider.JDK);
builder.trustManager(clientTrustStoreFile); // Trust the server cert
SslContext ctx = builder.build();
Assert.assertTrue(ctx.isClient());
Assert.assertTrue(ctx instanceof JdkSslContext);
JdkSslContext jdk = (JdkSslContext) ctx;
SSLContext jdkSslContext = jdk.context();
return jdkSslContext.getSocketFactory();
}
protected static void setupSSL(Configuration config) throws Exception {
config.getSecurity().getServerSsl().setCertificateFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setKeyFile(serverCert.privateKey().getAbsolutePath());
// Needed for 2way SSL
config.getSecurity().getServerSsl().setTrustStoreFile(serverCert.certificate().getAbsolutePath());
config.getSecurity().getServerSsl().setUseOpenssl(false);
config.getSecurity().getServerSsl().setUseGeneratedKeypair(false);
config.getSecurity().setAllowAnonymousHttpAccess(false);
}
@Before
public void configureSSL() throws Exception {
setupSSL(conf);
}
@Override
protected HttpsURLConnection getUrlConnection(URL url) throws Exception {
// Username and password not used in 2way SSL case
return getUrlConnection(null, null, url);
}
@Override
protected HttpsURLConnection getUrlConnection(String username, String password, URL url) throws Exception {
HttpsURLConnection.setDefaultSSLSocketFactory(getSSLSocketFactory());
URL loginURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/login");
HttpsURLConnection con = (HttpsURLConnection) loginURL.openConnection();
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
con.setRequestMethod("GET");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "application/json");
con.connect();
int responseCode = con.getResponseCode();
if (401 == responseCode) {
throw new UnauthorizedUserException();
}
Assert.assertEquals(200, responseCode);
List<String> cookies = con.getHeaderFields().get(HttpHeaderNames.SET_COOKIE.toString());
Assert.assertEquals(1, cookies.size());
Cookie sessionCookie = ClientCookieDecoder.STRICT.decode(cookies.get(0));
Assert.assertEquals(Constants.COOKIE_NAME, sessionCookie.name());
con = (HttpsURLConnection) url.openConnection();
con.setRequestProperty(HttpHeaderNames.COOKIE.toString(), sessionCookie.name() + "=" + sessionCookie.value());
con.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
return con;
}
@Before
public void setup() throws Exception {
setupSSL(conf, true);
Connector con = mac.getConnector("root", "secret");
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
}
@@ -151,7 +47,7 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
@Test
public void testBasicAuthLogin() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String metrics = "https://localhost:54322/api/metrics";
query(metrics);
@@ -163,7 +59,7 @@ public class TwoWaySSLOpenSSLIT extends QueryBase {
@Test
public void testQueryWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1000) + " 3.0 tag1=value1 tag2=value2",
@@ -49,7 +49,7 @@ public class TcpClientIT extends OneWaySSLBase {
@Test
public void testPut() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (TcpClient client = new TcpClient("127.0.0.1", 54321)) {
client.open();
client.write("put sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2\n");
@@ -80,7 +80,7 @@ public class TcpClientIT extends OneWaySSLBase {
public void testPutMultiple() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (TcpClient client = new TcpClient("127.0.0.1", 54321)) {
client.open();
// @formatter:off
@@ -124,7 +124,7 @@ public class TcpClientIT extends OneWaySSLBase {
@Test
public void testPutInvalidTimestamp() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (TcpClient client = new TcpClient("127.0.0.1", 54321)) {
client.open();
client.write("put sys.cpu.user " + TEST_TIME + "Z" + " 1.0 tag1=value1 tag2=value2\n");
@@ -139,7 +139,7 @@ public class TcpClientIT extends OneWaySSLBase {
@Test
public void testPersistence() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
@@ -177,13 +177,13 @@ public class TcpClientIT extends OneWaySSLBase {
LOG.info("Meta no vers iter: " + entry);
count++;
}
assertEquals(10, count);
assertEquals(15, count);
}
@Test
public void testPersistenceWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4 viz=(a|b)",
@@ -53,6 +53,7 @@ public class UdpClientIT extends MacITBase {
}
}
});
setupSSL();
}
@After
@@ -63,7 +64,7 @@ public class UdpClientIT extends MacITBase {
@Test
public void testPut() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (UdpClient client = new UdpClient("127.0.0.1", 54325)) {
client.open();
client.write(("put sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2\n"));
@@ -93,7 +94,7 @@ public class UdpClientIT extends MacITBase {
public void testPutMultiple() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (UdpClient client = new UdpClient("127.0.0.1", 54325)) {
client.open();
client.write(("put sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2\n" + "put sys.cpu.idle "
@@ -135,7 +136,7 @@ public class UdpClientIT extends MacITBase {
@Test
public void testPutInvalidTimestamp() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (UdpClient client = new UdpClient("127.0.0.1", 54325)) {
client.open();
client.write(("put sys.cpu.user " + TEST_TIME + "Z" + " 1.0 tag1=value1 tag2=value2\n"));
@@ -149,7 +150,7 @@ public class UdpClientIT extends MacITBase {
@Test
public void testPersistence() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
@@ -187,13 +188,13 @@ public class UdpClientIT extends MacITBase {
LOG.info("Meta no vers iter: " + entry);
count++;
}
assertEquals(10, count);
assertEquals(15, count);
}
@Test
public void testPersistenceWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4 viz=(a|b)",
@@ -65,7 +65,7 @@ public class WebSocketClientIT extends OneWaySSLBase {
Connector con = mac.getConnector("root", "secret");
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
s = new Server(conf);
s.run();
s.run(getSslContext());
setupSslCtx();
}
@@ -23,7 +23,7 @@ public class HTTPStrictTransportSecurityIT extends OneWaySSLBase {
@Before
public void before() throws Exception {
s = new Server(conf);
s.run();
s.run(getSslContext());
}
@After
@@ -58,12 +58,11 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testSuggest() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
"sys.cpu.idle " + (TEST_TIME + 2) + " 1.0 tag3=value3 tag4=value4",
"zzzz 1234567892 1.0 host=localhost");
"sys.cpu.idle " + (TEST_TIME + 2) + " 1.0 tag3=value3 tag4=value4");
// Latency in TestConfiguration is 2s, wait for it
sleepUninterruptibly(TestConfiguration.WAIT_SECONDS, TimeUnit.SECONDS);
@@ -78,11 +77,7 @@ public class HttpApiIT extends OneWaySSLBase {
// Test empty query
result = query(suggest + "type=metrics&max=10");
assertEquals("[\"sys.cpu.idle\",\"sys.cpu.user\",\"zzzz\"]", result);
// Test skipping over initial metrics
result = query(suggest + "type=metrics&q=z&max=10");
assertEquals("[\"zzzz\"]", result);
assertEquals("[\"sys.cpu.idle\",\"sys.cpu.user\"]", result);
} finally {
s.shutdown();
}
@@ -91,7 +86,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testMetrics() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
@@ -125,7 +120,7 @@ public class HttpApiIT extends OneWaySSLBase {
// @formatter:off
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
@@ -180,7 +175,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testLookup() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3",
@@ -216,7 +211,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithMsResolution() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3",
@@ -264,7 +259,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithoutMsResolution() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3",
@@ -303,7 +298,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithNoTags() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3",
@@ -339,7 +334,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithNoTagsMultipleSeries() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 host=h1",
"sys.cpu.user " + TEST_TIME + " 2.0 tag1=value1 tag2=value2 host=h2",
@@ -380,7 +375,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test(expected = NotSuccessfulException.class)
public void testQueryWithNoMatchingTags() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3 rack=r2",
@@ -404,7 +399,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithTagWildcard() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
// @formatter:off
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -469,7 +464,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithTagWildcard2() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
// @formatter:off
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -529,7 +524,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithTagOr() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -613,7 +608,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithTagRegex() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
System.out.println("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1");
System.out.println("sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3 rack=r2");
@@ -679,7 +674,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testQueryWithTagRegex2() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2 rack=r1",
"sys.cpu.user " + (TEST_TIME + 1) + " 1.0 tag3=value3 rack=r2",
@@ -735,7 +730,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testRateQuery() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + (TEST_TIME) + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -787,7 +782,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testRateCounterQuery() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + (TEST_TIME) + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -841,7 +836,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testRateCounterWithOptions() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
// @formatter:off
put("sys.cpu.user " + (TEST_TIME) + " 1.0 tag1=value1 tag2=value2 rack=r1",
@@ -897,7 +892,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testGetVersion() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
String response = query("https://127.0.0.1:54322/version", "application/json");
assertNotNull(response);
@@ -910,7 +905,7 @@ public class HttpApiIT extends OneWaySSLBase {
@Test
public void testPutMetric() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
Metric m = Metric.newBuilder().name("sys.cpu.user").value(TEST_TIME, 1.0D).tag(new Tag("tag1", "value1"))
.build();
@@ -56,7 +56,7 @@ public class TimelyTcpIT extends MacITBase {
@Test
public void testVersion() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (Socket sock = new Socket("127.0.0.1", 54321);
PrintWriter writer = new PrintWriter(sock.getOutputStream(), true);) {
writer.write("version\n");
@@ -76,7 +76,7 @@ public class TimelyTcpIT extends MacITBase {
@Test
public void testPut() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (Socket sock = new Socket("127.0.0.1", 54321);
PrintWriter writer = new PrintWriter(sock.getOutputStream(), true);) {
writer.write("put sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2\n");
@@ -107,7 +107,7 @@ public class TimelyTcpIT extends MacITBase {
public void testPutMultiple() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (Socket sock = new Socket("127.0.0.1", 54321);
PrintWriter writer = new PrintWriter(sock.getOutputStream(), true)) {
// @formatter:off
@@ -190,7 +190,7 @@ public class TimelyTcpIT extends MacITBase {
LOG.debug("Sending {} bytes", data.length);
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (Socket sock = new Socket("127.0.0.1", 54321);) {
sock.getOutputStream().write(data);
sock.getOutputStream().flush();
@@ -233,7 +233,7 @@ public class TimelyTcpIT extends MacITBase {
@Test
public void testPutInvalidTimestamp() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
try (Socket sock = new Socket("127.0.0.1", 54321);
PrintWriter writer = new PrintWriter(sock.getOutputStream(), true);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));) {
@@ -249,7 +249,7 @@ public class TimelyTcpIT extends MacITBase {
@Test
public void testPersistence() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
@@ -287,13 +287,13 @@ public class TimelyTcpIT extends MacITBase {
LOG.info("Meta no vers iter: " + entry);
count++;
}
assertEquals(10, count);
assertEquals(15, count);
}
@Test
public void testPersistenceWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4 viz=(a|b)",
@@ -60,6 +60,7 @@ public class TimelyUdpIT extends MacITBase {
}
}
});
setupSSL();
}
@After
@@ -70,7 +71,7 @@ public class TimelyUdpIT extends MacITBase {
@Test
public void testPut() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 54325);
DatagramPacket packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), 54325);
try (DatagramSocket sock = new DatagramSocket()) {
@@ -102,7 +103,7 @@ public class TimelyUdpIT extends MacITBase {
public void testPutMultiple() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 54325);
DatagramPacket packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), 54325);
// @formatter:off
@@ -185,7 +186,7 @@ public class TimelyUdpIT extends MacITBase {
LOG.debug("Sending {} bytes", data.length);
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 54325);
DatagramPacket packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), 54325);
try (DatagramSocket sock = new DatagramSocket()) {
@@ -229,7 +230,7 @@ public class TimelyUdpIT extends MacITBase {
@Test
public void testPutInvalidTimestamp() throws Exception {
final TestServer m = new TestServer(conf);
m.run();
m.run(getSslContext());
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 54325);
DatagramPacket packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), 54325);
try (DatagramSocket sock = new DatagramSocket();) {
@@ -245,7 +246,7 @@ public class TimelyUdpIT extends MacITBase {
@Test
public void testPersistence() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4",
@@ -283,13 +284,13 @@ public class TimelyUdpIT extends MacITBase {
LOG.info("Meta no vers iter: " + entry);
count++;
}
assertEquals(10, count);
assertEquals(15, count);
}
@Test
public void testPersistenceWithVisibility() throws Exception {
final Server s = new Server(conf);
s.run();
s.run(getSslContext());
try {
put("sys.cpu.user " + TEST_TIME + " 1.0 tag1=value1 tag2=value2",
"sys.cpu.idle " + (TEST_TIME + 1) + " 1.0 tag3=value3 tag4=value4 viz=(a|b)",
@@ -189,7 +189,7 @@ public class WebSocketIT extends OneWaySSLBase {
@Before
public void setup() throws Exception {
s = new Server(conf);
s.run();
s.run(getSslContext());
Connector con = mac.getConnector("root", "secret");
con.securityOperations().changeUserAuthorizations("root", new Authorizations("A", "B", "C", "D", "E", "F"));
@@ -38,17 +38,18 @@ public class MetaKeySetTest {
mks.addAll(one.toKeys());
mks.addAll(two.toKeys());
mks.addAll(three.toKeys());
List<Mutation> muts = mks.toMutations();
long ts = System.currentTimeMillis();
List<Mutation> muts = mks.toMutations(ts);
Mutation e1 = new Mutation("m:sys.cpu.user");
e1.put("", "", MetaKeySet.NULL_VALUE);
e1.put("", "", ts, MetaKeySet.NULL_VALUE);
Mutation e2 = new Mutation("t:sys.cpu.user");
e2.put("tag1", "", MetaKeySet.NULL_VALUE);
e2.put("tag2", "", MetaKeySet.NULL_VALUE);
e2.put("tag3", "", MetaKeySet.NULL_VALUE);
e2.put("tag1", "", ts, MetaKeySet.NULL_VALUE);
e2.put("tag2", "", ts, MetaKeySet.NULL_VALUE);
e2.put("tag3", "", ts, MetaKeySet.NULL_VALUE);
Mutation e3 = new Mutation("v:sys.cpu.user");
e3.put("tag1", "value1", MetaKeySet.NULL_VALUE);
e3.put("tag2", "value2", MetaKeySet.NULL_VALUE);
e3.put("tag3", "value3", MetaKeySet.NULL_VALUE);
e3.put("tag1", "value1", ts, MetaKeySet.NULL_VALUE);
e3.put("tag2", "value2", ts, MetaKeySet.NULL_VALUE);
e3.put("tag3", "value3", ts, MetaKeySet.NULL_VALUE);
Assert.assertEquals(3, muts.size());
Assert.assertTrue(muts.contains(e1));
Assert.assertTrue(muts.contains(e2));