Compare commits

..
135 changed files with 545 additions and 17583 deletions

No files matched your search

-1
View File
@@ -6,5 +6,4 @@ target/
*.iml
logs/
server/pki/
client/src/main/python/setup.py
+1 -1
View File
@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
</parent>
<properties>
<flink.version>1.1.4</flink.version>
File diff suppressed because it is too large. Load diff
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
export TRUSTSTORE_FILE=
export TRUSTSTORE_TYPE="jks";
export TRUSTSTORE_PASS=
export KEYSTORE_FILE=
export KEYSTORE_TYPE="pkcs12";
export KEYSTORE_PASS=
export NUM_QUERY_THREADS=4
export NUM_INGEST_THREADS=4
-21
View File
@@ -1,21 +0,0 @@
#!/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}/../..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
. ${THIS_DIR}/load-test-env.sh
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1G -Xms1G"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*
-21
View File
@@ -1,21 +0,0 @@
#!/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}/../..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
. ${THIS_DIR}/load-test-env.sh
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1G -Xms1G"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*
-11
View File
@@ -1,11 +0,0 @@
#!/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%/*}"
sed -i -- "s/#.*#/#$1#/g" $THIS_DIR/load-test-data.txt
-45
View File
@@ -1,45 +0,0 @@
#!/bin/bash -vx
if [[ `uname` == "Darwin" ]]; then
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
TCNATIVE_SUFFIX="tcnative.jnilib"
else
THIS_SCRIPT=`readlink -f $0`
TCNATIVE_SUFFIX="tcnative.so"
fi
# netty tcnative file reference
#netty-tcnative-1.1.33.Fork18-linux-x86_64-fedora.jar -> libnetty-tcnative.so
#netty-tcnative-1.1.33.Fork18-linux-x86_64.jar -> libnetty-tcnative.so
#netty-tcnative-1.1.33.Fork18-osx-x86_64.jar -> libnetty-tcnative.jnilib
THIS_DIR="${THIS_SCRIPT%/*}"
NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=${THIS_DIR}/..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
if [[ -e ${TMP_DIR} ]]; then
rm -rf ${TMP_DIR}
fi
mkdir ${TMP_DIR}
if [[ -e ${NATIVE_DIR} ]]; then
rm -rf ${NATIVE_DIR}
fi
mkdir -p ${NATIVE_DIR}
pushd ${BASE_DIR}/bin
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty_${TCNATIVE_SUFFIX}
popd
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty_${TCNATIVE_SUFFIX}"
JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="60">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error" includeLocation="false">
<AppenderRef ref="Console" level="error" />
</Root>
</Loggers>
</Configuration>
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="60">
<Properties>
<Property name="PID">????</Property>
<Property name="LOG_PATTERN">%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%wEx</Property>
</Properties>
<Appenders>
<RollingRandomAccessFile name="Rolling" fileName="/srv/logs/timely/timely-balancer.log"
filePattern="/srv/logs/timely/$${date:yyyy-MM}/timely-balancer-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1024 MB" />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingRandomAccessFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace" includeLocation="false">
<AppenderRef ref="Console" level="warn" />
<AppenderRef ref="Rolling" level="trace" />
</Root>
</Loggers>
</Configuration>
-49
View File
@@ -1,49 +0,0 @@
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
</beans:bean>
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
<beans:property name="users">
<util:map key-type="java.lang.String">
<beans:entry key="example.com">
<util:list>
<beans:value>D</beans:value>
<beans:value>E</beans:value>
<beans:value>F</beans:value>
</util:list>
</beans:entry>
</util:map>
</beans:property>
</beans:bean>
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
</beans:bean>
<user-service id="usernamePasswordService">
<user name="test" password="test1" authorities="A,B,C" />
</user-service>
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<!-- X509 PreAuthenticated authorization provider -->
<authentication-provider ref="x509AuthorizationProvider"/>
<!-- Basic authorization provider, validates provided username and password -->
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
</authentication-manager>
</beans:beans>
-91
View File
@@ -1,91 +0,0 @@
timely:
balancer:
timelyHosts:
-
regex: ^[a-g].*
host: localhost
tcpPort: 4242
httpPort: 4243
wsPort: 4244
udpPort: 4245
-
regex: ^[h-n].*
host: localhost
tcpPort: 4342
httpPort: 4343
wsPort: 4244
udpPort: 4245
-
regex: ^[o-z].*
host: localhost
tcpPort: 4442
httpPort: 4443
wsPort: 4444
udpPort: 4445
tcpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 8
maxTotalPerKey: 8
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
httpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 8
maxTotalPerKey: 8
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
wsClientPool:
minIdlePerKey: 0
maxIdlePerKey: 8
maxTotalPerKey: 8
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
udpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 8
maxTotalPerKey: 8
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
clientSsl:
keyFile:
keyType: pkcs12
keyPassword:
trustStoreFile:
trustStoreType: jks
trustStorePassword:
-54
View File
@@ -1,54 +0,0 @@
timely:
metrics-table: timely.metrics
meta-table: timely.meta
metric-age-off-days:
default: 7
accumulo:
instance-name: dummy
zookeepers: localhost
username: dummy
password: dummy
write:
latency: 5s
threads: 3
bufferSize: 52428800
scan:
threads: 4
max-downsample-memory: 10000000
security:
session-max-age: 86400
allow-anonymous-access: true
ssl:
certificate-file:
key-file:
key-password:
use-generated-keypair: false
trust-store-file:
use-openssl: true
#use-ciphers:
server:
ip: 127.0.0.1
tcp-port: 5242
udp-port: 5245
http:
host: localhost
ip: 127.0.0.1
port: 5243
redirect-path: /secure-me
strict-transport-max-age: 608400
cors:
allow-any-origin: false
allow-null-origin: false
allow-credentials: true
allowed-origins:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type
websocket:
ip: 127.0.0.1
port: 5244
timeout: 60
subscription-lag: 120
scanner-batch-size: 10000
flush-interval-seconds: 30
scanner-read-ahead: 1
subscription-batch-size: 5000
-331
View File
@@ -1,331 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>timely-balancer</artifactId>
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-server</artifactId>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-client</artifactId>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-common</artifactId>
<version>${parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.google.flatbuffers</groupId>
<artifactId>flatbuffers-java</artifactId>
<scope>system</scope>
<systemPath>${project.basedir}/../server/lib/flatbuffers-java-1.4.0.jar</systemPath>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>fi.iki.yak</groupId>
<artifactId>compression-gorilla</artifactId>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative</artifactId>
<version>${version.netty-tcnative}</version>
<classifier>${os.detected.classifier}</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-minicluster</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<exclusions>
<exclusion>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
<!-- Use os-maven-plugin to initialize the "os.detected" properties -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>${version.findbugs-maven}</version>
<configuration>
<effort>Max</effort>
<threshold>Medium</threshold>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/dist/assembly.xml</descriptor>
</descriptors>
<tarLongFileMode>posix</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<excludeArtifactIds>junit</excludeArtifactIds>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
<execution>
<id>unpack-native</id>
<goals>
<goal>unpack</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<artifactItems>
<artifactItem>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative</artifactId>
<version>${version.netty-tcnative}</version>
<classifier>${os.detected.classifier}</classifier>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/native</outputDirectory>
<includes>**/*.so</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/generated-java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>formatter-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-54
View File
@@ -1,54 +0,0 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>dist</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
<include>NOTICE*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>timely*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/bin</directory>
<outputDirectory>bin</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/resources/webapp</directory>
<outputDirectory>bin/webapp</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
</fileSets>
</assembly>
@@ -1,141 +0,0 @@
package timely.balancer;
import java.util.Timer;
import java.util.TimerTask;
public class ArrivalRate {
private long shortTermArrivals = 0l;
private double shortTermRate = 0;
private long shortTermLastReset = 0l;
private long mediumTermArrivals = 0l;
private double mediumTermRate = 0;
private long mediumTermLastReset = 0l;
private long longTermArrivals = 0l;
private double longTermRate = 0;
private long longTermLastReset = 0l;
private double rate = 0;
private boolean recalculateEveryUpdate = true;
private long created = System.currentTimeMillis();
private Timer timer = new Timer("ArrivalRateTimer");
public ArrivalRate() {
shortTermLastReset = mediumTermLastReset = longTermLastReset = System.currentTimeMillis();
// 5 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetShort();
}
}, 300000, 300000);
// 5 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetMedium();
}
}, 900000, 900000);
// 5 minute
timer.schedule(new TimerTask() {
@Override
public void run() {
resetLong();
}
}, 3600000, 3600000);
}
synchronized private void resetShort() {
long now = System.currentTimeMillis();
shortTermRate = shortTermArrivals / (((double) (now - shortTermLastReset)) / 1000);
shortTermArrivals = 0;
shortTermLastReset = now;
}
synchronized private void resetMedium() {
long now = System.currentTimeMillis();
mediumTermRate = mediumTermArrivals / (((double) (now - mediumTermLastReset)) / 1000);
mediumTermArrivals = 0;
mediumTermLastReset = now;
}
synchronized private void resetLong() {
long now = System.currentTimeMillis();
longTermRate = longTermArrivals / (((double) (now - longTermLastReset)) / 1000);
longTermArrivals = 0;
longTermLastReset = now;
}
synchronized public void arrived() {
shortTermArrivals++;
mediumTermArrivals++;
longTermArrivals++;
if (recalculateEveryUpdate) {
calculateRate();
if ((System.currentTimeMillis() - created) > 600000) {
recalculateEveryUpdate = false;
}
}
}
synchronized private double getShortRate(long now) {
if (shortTermRate > 0) {
return shortTermRate;
} else {
return shortTermArrivals / (((double) (now - shortTermLastReset)) / 1000);
}
}
synchronized private double getMediumRate(long now) {
if (mediumTermRate > 0) {
return mediumTermRate;
} else {
return mediumTermArrivals / (((double) (now - mediumTermLastReset)) / 1000);
}
}
synchronized private double getLongRate(long now) {
if (longTermRate > 0) {
return longTermRate;
} else {
return longTermArrivals / (((double) (now - longTermLastReset)) / 1000);
}
}
synchronized public void calculateRate() {
long now = System.currentTimeMillis();
// 1 minute
if (now - shortTermLastReset > 60000) {
// use live
shortTermRate = 0;
}
// 3 minute
if (now - mediumTermLastReset > 180000) {
// use live
mediumTermRate = 0;
}
// 12 minute
if (now - longTermLastReset > 720000) {
// use live
longTermRate = 0;
}
rate = 0.50 * getLongRate(now) + 0.30 * getMediumRate(now) + 0.20 * getShortRate(now);
}
synchronized public double getRate() {
if (rate == 0) {
calculateRate();
}
return rate;
}
}
@@ -1,525 +0,0 @@
package timely.balancer;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.OpenSslServerSessionContext;
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 io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.Configuration;
import timely.SpringBootstrap;
import timely.balancer.connection.http.HttpClientPool;
import timely.balancer.connection.tcp.TcpClientPool;
import timely.balancer.connection.udp.UdpClientPool;
import timely.balancer.connection.ws.WsClientPool;
import timely.balancer.healthcheck.HealthChecker;
import timely.balancer.netty.http.HttpRelayHandler;
import timely.balancer.netty.tcp.TcpRelayHandler;
import timely.balancer.netty.udp.UdpRelayHandler;
import timely.balancer.netty.ws.WsRelayHandler;
import timely.balancer.resolver.BalancedMetricResolver;
import timely.balancer.resolver.MetricResolver;
import timely.client.http.HttpClient;
import timely.netty.http.HttpStaticFileServerHandler;
import timely.netty.http.NonSslRedirectHandler;
import timely.netty.http.TimelyExceptionHandler;
import timely.netty.http.auth.X509LoginRequestHandler;
import timely.netty.tcp.MetricsBufferDecoder;
import timely.netty.tcp.TcpDecoder;
import timely.netty.tcp.TcpVersionHandler;
import timely.netty.udp.UdpDecoder;
import timely.netty.udp.UdpPacketToByteBuf;
import timely.netty.websocket.WebSocketHttpCookieHandler;
import timely.netty.websocket.WebSocketRequestDecoder;
import timely.netty.websocket.subscription.WSTimelyExceptionHandler;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class Balancer {
private static final Logger LOG = LoggerFactory.getLogger(Balancer.class);
private static final int EPOLL_MIN_MAJOR_VERSION = 2;
private static final int EPOLL_MIN_MINOR_VERSION = 6;
private static final int EPOLL_MIN_PATCH_VERSION = 32;
private static final String OS_NAME = "os.name";
private static final String OS_VERSION = "os.version";
private static final String WS_PATH = "/websocket";
protected static final CountDownLatch LATCH = new CountDownLatch(1);
static ConfigurableApplicationContext applicationContext;
private final Configuration config;
private final BalancerConfiguration balancerConfig;
private EventLoopGroup tcpWorkerGroup = null;
private EventLoopGroup tcpBossGroup = null;
private EventLoopGroup httpWorkerGroup = null;
private EventLoopGroup httpBossGroup = null;
private EventLoopGroup wsWorkerGroup = null;
private EventLoopGroup wsBossGroup = null;
private EventLoopGroup udpBossGroup = null;
private EventLoopGroup udpWorkerGroup = null;
private MetricResolver metricResolver;
protected Channel tcpChannelHandle = null;
protected Channel httpChannelHandle = null;
protected Channel wsChannelHandle = null;
protected List<Channel> udpChannelHandleList = new ArrayList<>();
protected volatile boolean shutdown = false;
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
}
public Balancer(Configuration conf, BalancerConfiguration balancerConf) throws Exception {
this.config = conf;
this.balancerConfig = balancerConf;
}
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args);
}
private void shutdownHook() {
final Runnable shutdownRunner = () -> {
if (!shutdown) {
shutdown();
}
};
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
Runtime.getRuntime().addShutdownHook(hook);
}
public void shutdown() {
List<ChannelFuture> channelFutures = new ArrayList<>();
if (tcpChannelHandle != null) {
LOG.info("Closing tcpChannelHandle");
channelFutures.add(tcpChannelHandle.close());
}
if (httpChannelHandle != null) {
LOG.info("Closing httpChannelHandle");
channelFutures.add(httpChannelHandle.close());
}
if (wsChannelHandle != null) {
LOG.info("Closing wsChannelHandle");
channelFutures.add(wsChannelHandle.close());
}
for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle");
channelFutures.add(c.close());
}
// wait for the channels to shutdown
channelFutures.forEach(f -> {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config());
LOG.error("{}", e.getMessage());
e.printStackTrace();
}
});
Integer 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));
}
if (tcpWorkerGroup != null) {
LOG.info("Shutting down tcpWorkerGroup");
groupFutures.add(tcpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (httpBossGroup != null) {
LOG.info("Shutting down httpBossGroup");
groupFutures.add(httpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (httpWorkerGroup != null) {
LOG.info("Shutting down httpWorkerGroup");
groupFutures.add(httpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (wsBossGroup != null) {
LOG.info("Shutting down wsBossGroup");
groupFutures.add(wsBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (wsWorkerGroup != null) {
LOG.info("Shutting down wsWorkerGroup");
groupFutures.add(wsWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (udpBossGroup != null) {
LOG.info("Shutting down udpBossGroup");
groupFutures.add(udpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (udpWorkerGroup != null) {
LOG.info("Shutting down udpWorkerGroup");
groupFutures.add(udpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
groupFutures.parallelStream().forEach(f -> {
try {
f.get();
} catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString());
LOG.error("{}", e.getMessage());
e.printStackTrace();
}
});
try {
LOG.info("Closing WebSocketRequestDecoder");
WebSocketRequestDecoder.close();
} catch (Exception e) {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
}
if (applicationContext != null) {
LOG.info("Closing applicationContext");
applicationContext.close();
}
this.shutdown = true;
LOG.info("Server shut down.");
}
private static boolean useEpoll() {
// Should we just return true if this is Linux and if we get an error
// during Epoll
// setup handle it there?
final String os = SystemPropertyUtil.get(OS_NAME).toLowerCase().trim();
final String[] version = SystemPropertyUtil.get(OS_VERSION).toLowerCase().trim().split("\\.");
if (os.startsWith("linux") && version.length >= 3) {
final int major = Integer.parseInt(version[0]);
if (major > EPOLL_MIN_MAJOR_VERSION) {
return true;
} else if (major == EPOLL_MIN_MAJOR_VERSION) {
final int minor = Integer.parseInt(version[1]);
if (minor > EPOLL_MIN_MINOR_VERSION) {
return true;
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
final int patch = Integer.parseInt(version[2].substring(0, 2));
return patch >= EPOLL_MIN_PATCH_VERSION;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
protected SslContext createSSLContext(Configuration config) throws Exception {
Configuration.Ssl sslCfg = config.getSecurity().getSsl();
Boolean generate = sslCfg.isUseGeneratedKeypair();
SslContextBuilder ssl;
if (generate) {
LOG.warn("Using generated self signed server certificate");
Date begin = new Date();
Date end = new Date(begin.getTime() + 86400000);
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else {
String cert = sslCfg.getCertificateFile();
String key = sslCfg.getKeyFile();
String keyPass = sslCfg.getKeyPassword();
if (null == cert || null == key) {
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
}
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
}
ssl.ciphers(sslCfg.getUseCiphers());
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
ssl.clientAuth(ClientAuth.OPTIONAL);
Boolean useOpenSSL = sslCfg.isUseOpenssl();
if (useOpenSSL) {
ssl.sslProvider(SslProvider.OPENSSL);
} else {
ssl.sslProvider(SslProvider.JDK);
}
String trustStore = sslCfg.getTrustStoreFile();
if (null != trustStore) {
if (!trustStore.isEmpty()) {
ssl.trustManager(new File(trustStore));
}
}
return ssl.build();
}
protected SSLContext createSSLClientContext(BalancerConfiguration config) throws Exception {
BalancerConfiguration.ClientSsl ssl = config.getClientSsl();
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(),
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
}
public void run() throws Exception {
final boolean useEpoll = useEpoll();
Class<? extends ServerSocketChannel> channelClass;
Class<? extends Channel> datagramChannelClass;
if (useEpoll) {
tcpWorkerGroup = new EpollEventLoopGroup();
tcpBossGroup = new EpollEventLoopGroup();
httpWorkerGroup = new EpollEventLoopGroup();
httpBossGroup = new EpollEventLoopGroup();
wsWorkerGroup = new EpollEventLoopGroup();
wsBossGroup = new EpollEventLoopGroup();
udpWorkerGroup = new EpollEventLoopGroup();
udpBossGroup = new EpollEventLoopGroup();
channelClass = EpollServerSocketChannel.class;
datagramChannelClass = EpollDatagramChannel.class;
} else {
tcpWorkerGroup = new NioEventLoopGroup();
tcpBossGroup = new NioEventLoopGroup();
httpWorkerGroup = new NioEventLoopGroup();
httpBossGroup = new NioEventLoopGroup();
wsWorkerGroup = new NioEventLoopGroup();
wsBossGroup = new NioEventLoopGroup();
udpWorkerGroup = new NioEventLoopGroup();
udpBossGroup = new NioEventLoopGroup();
channelClass = NioServerSocketChannel.class;
datagramChannelClass = NioDatagramChannel.class;
}
LOG.info("Using channel class {}", channelClass.getSimpleName());
TcpClientPool tcpClientPool = new TcpClientPool(this.balancerConfig);
HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPool);
this.metricResolver = new BalancedMetricResolver(this.balancerConfig, healthChecker);
final ServerBootstrap tcpServer = new ServerBootstrap();
tcpServer.group(tcpBossGroup, tcpWorkerGroup);
tcpServer.channel(channelClass);
tcpServer.handler(new LoggingHandler());
tcpServer.childHandler(setupTcpChannel(metricResolver, tcpClientPool));
tcpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
tcpServer.option(ChannelOption.SO_BACKLOG, 128);
tcpServer.option(ChannelOption.SO_KEEPALIVE, true);
final int tcpPort = config.getServer().getTcpPort();
final String tcpIp = config.getServer().getIp();
tcpChannelHandle = tcpServer.bind(tcpIp, tcpPort).sync().channel();
final String tcpAddress = ((InetSocketAddress) tcpChannelHandle.localAddress()).getAddress().getHostAddress();
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;
OpenSslServerSessionContext opensslCtx = openssl.sessionContext();
opensslCtx.setSessionCacheEnabled(true);
opensslCtx.setSessionCacheSize(128);
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
opensslCtx.setSessionTimeout(config.getSecurity().getSessionMaxAge());
}
SSLContext clientSSLContext = createSSLClientContext(balancerConfig);
final ServerBootstrap httpServer = new ServerBootstrap();
httpServer.group(httpBossGroup, httpWorkerGroup);
httpServer.channel(channelClass);
httpServer.handler(new LoggingHandler());
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig, clientSSLContext);
httpServer.childHandler(setupHttpChannel(config, balancerConfig, sslCtx, metricResolver, httpClientPool));
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
httpServer.option(ChannelOption.SO_BACKLOG, 128);
httpServer.option(ChannelOption.SO_KEEPALIVE, true);
httpChannelHandle = httpServer.bind(httpIp, httpPort).sync().channel();
final String httpAddress = ((InetSocketAddress) httpChannelHandle.localAddress()).getAddress().getHostAddress();
final int wsPort = config.getWebsocket().getPort();
final String wsIp = config.getWebsocket().getIp();
final ServerBootstrap wsServer = new ServerBootstrap();
wsServer.group(wsBossGroup, wsWorkerGroup);
wsServer.channel(channelClass);
wsServer.handler(new LoggingHandler());
WsClientPool wsClientPool = new WsClientPool(config, balancerConfig, clientSSLContext);
wsServer.childHandler(setupWSChannel(config, balancerConfig, sslCtx, metricResolver, wsClientPool));
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
wsServer.option(ChannelOption.SO_BACKLOG, 128);
wsServer.option(ChannelOption.SO_KEEPALIVE, true);
/* Not sure if next three lines are necessary */
wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860);
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
final int udpPort = config.getServer().getUdpPort();
final String udpIp = config.getServer().getIp();
UdpClientPool udpClientPool = new UdpClientPool(balancerConfig);
for (int n = 1; n < DEFAULT_EVENT_LOOP_THREADS; n++) {
final Bootstrap udpServer = new Bootstrap();
udpServer.group(udpBossGroup);
udpServer.channel(datagramChannelClass);
udpServer.handler(setupUdpChannel(metricResolver, udpClientPool));
udpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
udpServer.option(EpollChannelOption.SO_REUSEADDR, true);
udpServer.option(EpollChannelOption.SO_REUSEPORT, true);
udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel());
}
shutdownHook();
LOG.info(
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
}
public static void main(String[] args) throws Exception {
Balancer.applicationContext = Balancer.initializeConfiguration(args);
Configuration conf = applicationContext.getBean(Configuration.class);
BalancerConfiguration balancerConf = applicationContext.getBean(BalancerConfiguration.class);
Balancer b = new Balancer(conf, balancerConf);
try {
b.run();
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
} catch (Exception e) {
LOG.error("Error running server.", e);
} finally {
try {
b.shutdown();
} catch (Exception e) {
System.exit(1);
}
}
}
protected ChannelHandler setupHttpChannel(Configuration config, BalancerConfiguration balancerConfig,
SslContext sslCtx, MetricResolver metricResolver, HttpClientPool httpClientPool) {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(config, sslCtx));
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
ch.pipeline().addLast("compressor", new HttpContentCompressor());
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(config));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("login", new X509LoginRequestHandler(config));
ch.pipeline()
.addLast("httpRelay", new HttpRelayHandler(balancerConfig, metricResolver, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler());
}
};
}
protected ChannelHandler setupUdpChannel(MetricResolver metricResolver, UdpClientPool udpClientPool) {
return new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) throws Exception {
ch.pipeline().addLast("logger", new LoggingHandler());
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
ch.pipeline().addLast("putDecoder", new UdpDecoder());
ch.pipeline().addLast("udpRelayHandler", new UdpRelayHandler(metricResolver, udpClientPool));
}
};
}
protected ChannelHandler setupTcpChannel(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
ch.pipeline().addLast("putDecoder", new TcpDecoder());
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPool));
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
}
};
}
protected ChannelHandler setupWSChannel(Configuration conf, BalancerConfiguration balancerConfig,
SslContext sslCtx, MetricResolver metricResolver, WsClientPool wsClientPool) {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
ch.pipeline().addLast("httpServer", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
ch.pipeline().addLast("sessionExtractor", new WebSocketHttpCookieHandler(config));
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config));
ch.pipeline().addLast("httpRelay",
new WsRelayHandler(balancerConfig, config, metricResolver, wsClientPool));
ch.pipeline().addLast("error", new WSTimelyExceptionHandler());
}
};
}
}
@@ -1,174 +0,0 @@
package timely.balancer;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import timely.balancer.connection.TimelyBalancedHost;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "timely.balancer")
public class BalancerConfiguration {
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfig udpClientPool = new GenericKeyedObjectPoolConfig();
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfig tcpClientPool = new GenericKeyedObjectPoolConfig();
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfig httpClientPool = new GenericKeyedObjectPoolConfig();
@Valid
@NestedConfigurationProperty
private GenericKeyedObjectPoolConfig wsClientPool = new GenericKeyedObjectPoolConfig();
@Valid
@NestedConfigurationProperty
private ClientSsl clientSsl = new ClientSsl();
private boolean loginRequired = false;
@Valid
@NestedConfigurationProperty
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private long checkServerHealthInterval = 10000;
private int serverFailuresBeforeDown = 3;
private int serverSuccessesBeforeUp = 3;
@Valid
@NestedConfigurationProperty
private List<TimelyBalancedHost> wsHosts = new ArrayList<>();
public void setCheckServerHealthInterval(long checkServerHealthInterval) {
this.checkServerHealthInterval = checkServerHealthInterval;
}
public long getCheckServerHealthInterval() {
return checkServerHealthInterval;
}
public int getServerFailuresBeforeDown() {
return serverFailuresBeforeDown;
}
public void setServerFailuresBeforeDown(int serverFailuresBeforeDown) {
this.serverFailuresBeforeDown = serverFailuresBeforeDown;
}
public int getServerSuccessesBeforeUp() {
return serverSuccessesBeforeUp;
}
public void setServerSuccessesBeforeUp(int serverSuccessesBeforeUp) {
this.serverSuccessesBeforeUp = serverSuccessesBeforeUp;
}
public GenericKeyedObjectPoolConfig getUdpClientPool() {
return udpClientPool;
}
public GenericKeyedObjectPoolConfig getTcpClientPool() {
return tcpClientPool;
}
public GenericKeyedObjectPoolConfig getHttpClientPool() {
return httpClientPool;
}
public GenericKeyedObjectPoolConfig getWsClientPool() {
return wsClientPool;
}
public ClientSsl getClientSsl() {
return clientSsl;
}
public void setLoginRequired(boolean loginRequired) {
this.loginRequired = loginRequired;
}
public boolean isLoginRequired() {
return loginRequired;
}
public List<TimelyBalancedHost> getTimelyHosts() {
return timelyHosts;
}
public class ClientSsl {
private String keyFile;
private String keyType;
private String keyPassword;
private String trustStoreFile;
private String trustStoreType;
private String trustStorePassword;
public String getKeyFile() {
return keyFile;
}
public BalancerConfiguration setKeyFile(String keyFile) {
this.keyFile = keyFile;
return BalancerConfiguration.this;
}
public String getKeyType() {
return keyType;
}
public BalancerConfiguration setKeyType(String keyType) {
this.keyType = keyType;
return BalancerConfiguration.this;
}
public String getKeyPassword() {
return keyPassword;
}
public BalancerConfiguration setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
return BalancerConfiguration.this;
}
public String getTrustStoreFile() {
return trustStoreFile;
}
public BalancerConfiguration setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
return BalancerConfiguration.this;
}
public String getTrustStoreType() {
return trustStoreType;
}
public BalancerConfiguration setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
return BalancerConfiguration.this;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public BalancerConfiguration setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
return BalancerConfiguration.this;
}
}
}
@@ -1,8 +0,0 @@
package timely.balancer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import timely.SpringBootstrap;
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
public class BalancerSpringBootstrap {
}
@@ -1,157 +0,0 @@
package timely.balancer.connection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.ArrivalRate;
import timely.balancer.BalancerConfiguration;
public class TimelyBalancedHost {
private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class);
private String regex;
private String host;
private int tcpPort;
private int httpPort;
private int wsPort;
private int udpPort;
private boolean isUp = true;
private BalancerConfiguration config;
private int failures = 0;
private int successes = 0;
private ArrivalRate arrivalRate = new ArrivalRate();
public TimelyBalancedHost() {
}
public TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
this.host = host;
this.tcpPort = tcpPort;
this.httpPort = httpPort;
this.wsPort = wsPort;
this.udpPort = udpPort;
}
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);
}
public void setConfig(BalancerConfiguration config) {
this.config = config;
}
synchronized public boolean isUp() {
return isUp;
}
public void reportSuccess() {
int serverSuccessesBeforeUp = config.getServerSuccessesBeforeUp();
String h = host;
int p = tcpPort;
synchronized (this) {
if (LOG.isTraceEnabled()) {
LOG.trace("success reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p,
isUp, (successes + 1), serverSuccessesBeforeUp);
}
if (!isUp) {
if (++successes >= serverSuccessesBeforeUp) {
isUp = true;
successes = 0;
LOG.info("host up host:{} port:{}", h, p);
}
}
}
}
public void reportFailure() {
int serverFailuresBeforeDown = config.getServerFailuresBeforeDown();
String h = host;
int p = tcpPort;
synchronized (this) {
if (LOG.isTraceEnabled()) {
LOG.trace("failure reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p,
isUp, (failures + 1), serverFailuresBeforeDown);
}
if (isUp) {
if (++failures >= serverFailuresBeforeDown) {
isUp = false;
failures = 0;
LOG.info("host down host:{} port:{}", h, p);
}
}
}
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getUdpPort() {
return udpPort;
}
public void setUdpPort(int udpPort) {
this.udpPort = udpPort;
}
public int getTcpPort() {
return tcpPort;
}
public void setTcpPort(int tcpPort) {
this.tcpPort = tcpPort;
}
public int getHttpPort() {
return httpPort;
}
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
public int getWsPort() {
return wsPort;
}
public void setWsPort(int wsPort) {
this.wsPort = wsPort;
}
public void arrived() {
arrivalRate.arrived();
}
public double getArrivalRate() {
return arrivalRate.getRate();
}
public void calculateRate() {
arrivalRate.calculateRate();
}
}
@@ -1,49 +0,0 @@
package timely.balancer.connection.http;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import timely.balancer.connection.TimelyBalancedHost;
import javax.net.ssl.SSLContext;
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
private final SSLContext sslContext;
public HttpClientFactory(SSLContext sslContext) {
this.sslContext = sslContext;
}
@Override
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
BasicCookieStore cookieJar = new BasicCookieStore();
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, cookieJar, false));
}
@Override
public void destroyObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
}
@Override
public boolean validateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) {
if (k.isUp()) {
return true;
} else {
return false;
}
}
@Override
public void activateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
}
@Override
public void passivateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
}
}
@@ -1,58 +0,0 @@
package timely.balancer.connection.http;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import javax.net.ssl.SSLContext;
//import java.util.Map;
//import java.util.concurrent.ConcurrentHashMap;
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
private static final Logger LOG = LoggerFactory.getLogger(HttpClientPool.class);
public HttpClientPool(BalancerConfiguration config, SSLContext sslContext) {
super(new HttpClientFactory(sslContext), config.getHttpClientPool());
}
// private Map<TimelyBalancedHost, Integer> checkedOut = new
// ConcurrentHashMap<>();
// @Override
// public CloseableHttpClient borrowObject(TimelyBalancedHost key) throws
// Exception {
// CloseableHttpClient client = super.borrowObject(key);
// Integer n;
// synchronized (checkedOut) {
// n = checkedOut.get(key);
// if (n == null) {
// n = 0;
// }
// n++;
// checkedOut.put(key, n);
// }
// LOG.trace("Borrowing HttpClient for key " + key.getHost() + ":" +
// key.getHttpPort() + " " + getNumActive(key)
// + ":" + getNumIdle(key) + ":" + n);
// return client;
// }
//
// @Override
// public void returnObject(TimelyBalancedHost key, CloseableHttpClient obj)
// {
// super.returnObject(key, obj);
// Integer n;
// synchronized (checkedOut) {
// n = checkedOut.get(key);
// n--;
// checkedOut.put(key, n);
// }
// LOG.trace("Returning HttpClient for key " + key.getHost() + ":" +
// key.getHttpPort() + " " + getNumActive(key)
// + ":" + getNumIdle(key) + ":" + n);
// }
}
@@ -1,58 +0,0 @@
package timely.balancer.connection.tcp;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.tcp.TcpClient;
import java.io.IOException;
public class TcpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, TcpClient> {
public TcpClientFactory() {
}
@Override
public PooledObject<TcpClient> makeObject(TimelyBalancedHost k) throws Exception {
return new DefaultPooledObject<>(new TcpClient(k.getHost(), k.getTcpPort()));
}
@Override
public void destroyObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
try {
o.getObject().flush();
o.getObject().close();
} catch (IOException e) {
throw new IOException("Error closing connection to " + k.getHost() + ":" + k.getTcpPort());
}
}
@Override
public boolean validateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) {
if (k.isUp()) {
return true;
} else {
return false;
}
}
@Override
public void activateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
try {
o.getObject().open();
} catch (Exception e) {
throw new IOException("Unable to connect to " + k.getHost() + ":" + k.getTcpPort(), e);
}
}
@Override
public void passivateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
try {
o.getObject().flush();
} catch (Exception e) {
throw new IOException("Error flushing connection to " + k.getHost() + ":" + k.getTcpPort());
}
}
}
@@ -1,13 +0,0 @@
package timely.balancer.connection.tcp;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.tcp.TcpClient;
public class TcpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, TcpClient> {
public TcpClientPool(BalancerConfiguration config) {
super(new TcpClientFactory(), config.getTcpClientPool());
}
}
@@ -1,57 +0,0 @@
package timely.balancer.connection.udp;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.udp.UdpClient;
import java.io.IOException;
public class UdpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, UdpClient> {
public UdpClientFactory() {
}
@Override
public PooledObject<UdpClient> makeObject(TimelyBalancedHost k) throws Exception {
return new DefaultPooledObject<>(new UdpClient(k.getHost(), k.getUdpPort()));
}
@Override
public void destroyObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
try {
o.getObject().flush();
o.getObject().close();
} catch (IOException e) {
throw new IOException("Error closing connection to " + k.getHost() + ":" + k.getUdpPort());
}
}
@Override
public boolean validateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) {
if (k.isUp()) {
return true;
} else {
return false;
}
}
@Override
public void activateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
try {
o.getObject().open();
} catch (Exception e) {
throw new IOException("Unable to connect to " + k.getHost() + ":" + k.getUdpPort(), e);
}
}
@Override
public void passivateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
try {
o.getObject().flush();
} catch (Exception e) {
throw new IOException("Error flushing connection to " + k.getHost() + ":" + k.getUdpPort());
}
}
}
@@ -1,13 +0,0 @@
package timely.balancer.connection.udp;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.udp.UdpClient;
public class UdpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, UdpClient> {
public UdpClientPool(BalancerConfiguration config) {
super(new UdpClientFactory(), config.getUdpClientPool());
}
}
@@ -1,77 +0,0 @@
package timely.balancer.connection.ws;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.BasicCookieStore;
import timely.Configuration;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import javax.net.ssl.SSLContext;
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> {
private final SSLContext sslContext;
private final boolean doLogin;
private Configuration config;
public WsClientFactory(Configuration config, SSLContext sslContext, boolean doLogin) {
this.sslContext = sslContext;
this.doLogin = doLogin;
this.config = config;
}
@Override
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
int bufferSize = config.getWebsocket().getSubscriptionBatchSize() * 500;
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), doLogin, "", "", false, bufferSize);
return new DefaultPooledObject<>(client);
}
@Override
public void destroyObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
}
@Override
public boolean validateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) {
if (k.isUp()) {
return true;
} else {
return false;
}
}
@Override
public void activateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
// try {
// o.getObject().open();
// } catch (IOException e) {
// throw new IOException("Unable to connect to " + k.getHost() + ":" +
// k.getHttpPort());
// }
}
@Override
public void passivateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
// try {
// o.getObject().flush();
// } catch (Exception e) {
// throw new IOException("Error flushing connection to " + k.getHost() +
// ":" + k.getHttpPort());
// }
// try {
// o.getObject().flush();
// o.getObject().close();
// } catch (IOException e) {
// throw new IOException("Error closing connection to " + k.getHost() +
// ":" + k.getHttpPort());
// }
}
}
@@ -1,18 +0,0 @@
package timely.balancer.connection.ws;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.http.client.HttpClient;
import timely.Configuration;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import javax.net.ssl.SSLContext;
public class WsClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, WebSocketSubscriptionClient> {
public WsClientPool(Configuration config, BalancerConfiguration balancerConfig, SSLContext sslContext) {
super(new WsClientFactory(config, sslContext, balancerConfig.isLoginRequired()), balancerConfig
.getWsClientPool());
}
}
@@ -1,38 +0,0 @@
package timely.balancer.healthcheck;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.tcp.TcpClientPool;
import timely.balancer.resolver.MetricResolver;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class HealthChecker {
private List<TimelyBalancedHost> timelyHosts;
private Timer timer;
final private TCPServerHealthCheck tcpServerHealthCheck;
private TimerTask check = new TimerTask() {
@Override
public void run() {
for (TimelyBalancedHost h : timelyHosts) {
if (tcpServerHealthCheck.isServerHealthy(h)) {
h.reportSuccess();
} else {
h.reportFailure();
}
}
}
};
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.timelyHosts = config.getTimelyHosts();
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.timer = new Timer("HealthCheckerThread", true);
this.timer.scheduleAtFixedRate(check, 0, config.getCheckServerHealthInterval());
}
}
@@ -1,8 +0,0 @@
package timely.balancer.healthcheck;
import timely.balancer.connection.TimelyBalancedHost;
public interface ServerHealthCheck {
public boolean isServerHealthy(TimelyBalancedHost timelyBalancedHost);
}
@@ -1,33 +0,0 @@
package timely.balancer.healthcheck;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.tcp.TcpClientPool;
import timely.client.tcp.TcpClient;
public class TCPServerHealthCheck implements ServerHealthCheck {
final private TcpClientPool tcpClientPool;
public TCPServerHealthCheck(TcpClientPool tcpClientPool) {
this.tcpClientPool = tcpClientPool;
}
@Override
public boolean isServerHealthy(TimelyBalancedHost timelyBalancedHost) {
boolean healthy = true;
TcpClient client = null;
try {
client = tcpClientPool.borrowObject(timelyBalancedHost);
client.open();
} catch (Exception e) {
healthy = false;
} finally {
if (client != null) {
tcpClientPool.returnObject(timelyBalancedHost, client);
}
}
return healthy;
}
}
@@ -1,222 +0,0 @@
package timely.balancer.netty.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import timely.api.request.HttpGetRequest;
import timely.api.request.HttpPostRequest;
import timely.api.request.MetricRequest;
import timely.api.request.timeseries.HttpRequest;
import timely.api.request.timeseries.QueryRequest;
import timely.api.response.TimelyException;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.http.HttpClientPool;
import timely.balancer.resolver.MetricResolver;
import timely.netty.http.TimelyHttpHandler;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
private static final Logger LOG = LoggerFactory.getLogger(HttpRelayHandler.class);
private static final String LOG_RECEIVED_REQUEST = "Received HTTP request {}";
private static final String LOG_PARSED_REQUEST = "Parsed request {}";
private static final String NO_AUTHORIZATIONS = "";
private final BalancerConfiguration conf;
private final HttpClientPool httpClientPool;
private MetricResolver metricResolver;
public HttpRelayHandler(BalancerConfiguration config, MetricResolver metricResolver, HttpClientPool httpClientPool) {
this.conf = config;
this.metricResolver = metricResolver;
this.httpClientPool = httpClientPool;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
CloseableHttpClient client = null;
TimelyBalancedHost k = null;
CloseableHttpResponse relayedResponse = null;
FullHttpRequest request = null;
try {
try {
request = msg.getHttpRequest();
String originalURI = request.getUri();
originalURI = encodeURI(originalURI);
String metric = null;
if (msg instanceof QueryRequest) {
Collection<QueryRequest.SubQuery> subqueries = ((QueryRequest) msg).getQueries();
if (subqueries != null) {
Iterator<QueryRequest.SubQuery> itr = subqueries.iterator();
if (itr.hasNext()) {
metric = itr.next().getMetric();
}
}
k = metricResolver.getHostPortKey(metric);
} else if (msg instanceof MetricRequest) {
metric = ((MetricRequest) msg).getMetric().getName();
k = metricResolver.getHostPortKeyIngest(metric);
} else {
k = metricResolver.getHostPortKey(null);
}
k = metricResolver.getHostPortKey(metric);
client = httpClientPool.borrowObject(k);
List<Map.Entry<String, String>> headerList = request.headers().entries();
List<Header> relayedHeaderList = new ArrayList<>();
for (Map.Entry<String, String> h : headerList) {
if (!h.getKey().equals(CONTENT_LENGTH)) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
}
}
Header[] headerArray = new Header[relayedHeaderList.size()];
relayedHeaderList.toArray(headerArray);
String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
if (request.getMethod().equals(HttpMethod.GET)) {
HttpUriRequest relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.POST)) {
HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content);
relayedRequest.setEntity(entity);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
}
} catch (Exception e) {
String message = e.getMessage();
if (message == null) {
LOG.error("", e);
} else {
if (message.contains("No matching tags")) {
LOG.trace(message);
} else {
LOG.error(message, e);
}
}
this.sendHttpError(
ctx,
new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), e.getMessage(), e
.getLocalizedMessage(), e));
return;
} finally {
if (request != null && request instanceof AutoCloseable) {
ReferenceCountUtil.release(request);
}
}
FullHttpResponse response = null;
if (relayedResponse == null) {
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
"Method not allowed", "Method not allowed"));
} else {
// buf = JsonUtil.getObjectMapper().writeValueAsBytes(msg);
ByteArrayOutputStream baos = new MyByteArrayOutputStream();
StreamUtils.copy(relayedResponse.getEntity().getContent(), baos);
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(relayedResponse
.getStatusLine().getStatusCode()), Unpooled.copiedBuffer(baos.toByteArray()));
for (Header h : relayedResponse.getAllHeaders()) {
response.headers().add(h.getName(), h.getValue());
}
// response.headers().set(HttpHeaders.Names.CONTENT_TYPE,
// Constants.JSON_TYPE);
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response);
}
} finally {
if (relayedResponse != null) {
relayedResponse.close();
}
if (client != null && k != null) {
httpClientPool.returnObject(k, client);
} else {
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri());
}
}
}
static public class MyByteArrayOutputStream extends ByteArrayOutputStream {
public MyByteArrayOutputStream() {
}
public MyByteArrayOutputStream(int size) {
super(size);
}
public int getCount() {
return count;
}
public byte[] getBuf() {
return buf;
}
}
static public String encodeURI(String s) {
try {
s = s.replaceAll("\\{", URLEncoder.encode("{", "UTF-8"));
s = s.replaceAll("\\}", URLEncoder.encode("}", "UTF-8"));
} catch (Exception e) {
}
return s;
}
public static void main(String[] args) {
String s = "https://127.0.0.1:4243/api/query?start=1356998400000&end=1528398304000&m=sum:rate{false,100,0}:sys.thermal.gauge{class=aws}";
URI uri = URI.create(encodeURI(s));
System.out.println(uri.toString());
}
}
@@ -1,90 +0,0 @@
package timely.balancer.netty.tcp;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.request.MetricRequest;
import timely.api.request.TcpRequest;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.tcp.TcpClientPool;
import timely.balancer.resolver.MetricResolver;
import timely.client.tcp.TcpClient;
import timely.netty.Constants;
import java.nio.charset.StandardCharsets;
public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
private static final Logger LOG = LoggerFactory.getLogger(TcpRelayHandler.class);
private static final String LOG_ERR_MSG = "Error storing put metric: {}";
private static final String ERR_MSG = "Error storing put metric: ";
private MetricResolver metricResolver;
private TcpClientPool tcpClientPool;
public TcpRelayHandler(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
this.metricResolver = metricResolver;
this.tcpClientPool = tcpClientPool;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, TcpRequest msg) throws Exception {
LOG.trace("Received {}", msg);
try {
String line;
Pair<TimelyBalancedHost, TcpClient> keyClientPair = null;
try {
if (msg instanceof MetricRequest) {
String metricName = ((MetricRequest) msg).getMetric().getName();
line = ((MetricRequest) msg).getLine();
keyClientPair = getClient(metricName, true);
} else {
// Version request
line = "version";
keyClientPair = getClient(null, false);
}
keyClientPair.getRight().write(line + "\n");
keyClientPair.getRight().flush();
} finally {
if (keyClientPair != null && keyClientPair.getLeft() != null && keyClientPair.getRight() != null) {
tcpClientPool.returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
}
}
} catch (Exception e) {
LOG.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n")
.getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
private Pair<TimelyBalancedHost, TcpClient> getClient(String metric, boolean metricRequest) {
TcpClient client = null;
TimelyBalancedHost k = null;
int failures = 0;
while (client == null) {
try {
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver
.getHostPortKey(metric);
client = tcpClientPool.borrowObject(k);
} catch (Exception e1) {
failures++;
client = null;
LOG.error(e1.getMessage(), e1);
try {
Thread.sleep(failures < 10 ? 500 : 60000);
} catch (InterruptedException e2) {
// nothing
}
}
}
return Pair.of(k, client);
}
}
@@ -1,83 +0,0 @@
package timely.balancer.netty.udp;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.request.MetricRequest;
import timely.api.request.UdpRequest;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.udp.UdpClientPool;
import timely.balancer.resolver.MetricResolver;
import timely.client.udp.UdpClient;
import timely.netty.Constants;
import java.nio.charset.StandardCharsets;
public class UdpRelayHandler extends SimpleChannelInboundHandler<UdpRequest> {
private static final Logger LOG = LoggerFactory.getLogger(UdpRelayHandler.class);
private static final String LOG_ERR_MSG = "Error storing put metric: {}";
private static final String ERR_MSG = "Error storing put metric: ";
private MetricResolver metricResolver;
private UdpClientPool udpClientPool;
public UdpRelayHandler(MetricResolver metricResolver, UdpClientPool udpClientPool) {
this.metricResolver = metricResolver;
this.udpClientPool = udpClientPool;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, UdpRequest msg) throws Exception {
LOG.trace("Received {}", msg);
try {
MetricRequest metricRequest = (MetricRequest) msg;
String metricName = metricRequest.getMetric().getName();
Pair<TimelyBalancedHost, UdpClient> keyClientPair = null;
try {
keyClientPair = getClient(metricName, true);
keyClientPair.getRight().write(metricRequest.getLine() + "\n");
keyClientPair.getRight().flush();
} finally {
if (keyClientPair != null && keyClientPair.getLeft() != null && keyClientPair.getRight() != null) {
udpClientPool.returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
}
}
} catch (Exception e) {
LOG.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n")
.getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
private Pair<TimelyBalancedHost, UdpClient> getClient(String metric, boolean metricRequest) {
UdpClient client = null;
TimelyBalancedHost k = null;
int failures = 0;
while (client == null) {
try {
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver
.getHostPortKey(metric);
client = udpClientPool.borrowObject(k);
} catch (Exception e1) {
failures++;
client = null;
LOG.error(e1.getMessage(), e1);
try {
Thread.sleep(failures < 10 ? 500 : 60000);
} catch (InterruptedException e2) {
// nothing
}
}
}
return Pair.of(k, client);
}
}
@@ -1,65 +0,0 @@
package timely.balancer.netty.ws;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.response.TimelyException;
import timely.client.websocket.ClientHandler;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import java.util.concurrent.TimeUnit;
public class WsClientHandler extends ClientHandler {
private static final Logger LOG = LoggerFactory.getLogger(WsClientHandler.class);
private final ChannelHandlerContext ctx;
private ScheduledFuture<?> ping;
public WsClientHandler(ChannelHandlerContext ctx, int pingRate) {
this.ctx = ctx;
this.ping = this.ctx.executor().scheduleAtFixedRate(() -> {
LOG.trace("Sending ping on channel {}", ctx.channel());
ctx.writeAndFlush(new PingWebSocketFrame());
}, pingRate, pingRate, TimeUnit.SECONDS);
}
@Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
ctx.writeAndFlush(new TextWebSocketFrame(message));
LOG.debug("Message received on Websocket session {}: {}", session.getId(), message);
});
}
@Override
public void onClose(Session session, CloseReason reason) {
super.onClose(session, reason);
if (!reason.getCloseCode().equals(CloseReason.CloseCodes.NORMAL_CLOSURE)) {
LOG.error("Abnormal close: " + reason.getReasonPhrase());
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
reason.getReasonPhrase(), "");
WsRelayHandler.sendErrorResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e);
}
if (!ping.isCancelled()) {
ping.cancel(false);
}
}
@Override
public void onError(Session session, Throwable t) {
super.onError(session, t);
LOG.error(t.getMessage(), t);
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), t.getMessage(), "");
WsRelayHandler.sendErrorResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e);
if (!ping.isCancelled()) {
ping.cancel(false);
}
}
}
@@ -1,42 +0,0 @@
package timely.balancer.netty.ws;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.ws.WsClientPool;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import java.io.IOException;
public class WsClientHolder {
private static final Logger LOG = LoggerFactory.getLogger(WsClientHolder.class);
private WebSocketSubscriptionClient client = null;
private TimelyBalancedHost host = null;
public WsClientHolder(TimelyBalancedHost host, WebSocketSubscriptionClient client) {
this.host = host;
this.client = client;
}
synchronized public WebSocketSubscriptionClient getClient() {
return client;
}
synchronized public TimelyBalancedHost getHost() {
return host;
}
synchronized public void close(WsClientPool wsClientPool) {
if (client != null && host != null) {
try {
client.close();
} catch (IOException e) {
LOG.error("Error closing web socket client", e);
}
wsClientPool.returnObject(host, client);
client = null;
host = null;
}
}
}
@@ -1,204 +0,0 @@
package timely.balancer.netty.ws;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.Configuration;
import timely.api.request.subscription.AddSubscription;
import timely.api.request.subscription.CloseSubscription;
import timely.api.request.subscription.CreateSubscription;
import timely.api.request.subscription.RemoveSubscription;
import timely.api.request.subscription.SubscriptionRequest;
import timely.api.response.TimelyException;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.ws.WsClientPool;
import timely.balancer.resolver.MetricResolver;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import timely.netty.http.TimelyHttpHandler;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequest> implements TimelyHttpHandler {
private static final Logger LOG = LoggerFactory.getLogger(WsRelayHandler.class);
private final BalancerConfiguration balancerConfig;
private final Configuration config;
private final WsClientPool wsClientPool;
private MetricResolver metricResolver;
static private ObjectMapper mapper;
Map<String, Map<String, WsClientHolder>> wsClients = new ConcurrentHashMap<>();
Map<String, ChannelHandlerContext> wsSubscriptions = new ConcurrentHashMap<>();
static {
mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module());
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.configure(SerializationFeature.WRAP_EXCEPTIONS, true);
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
}
public WsRelayHandler(BalancerConfiguration balancerConfig, Configuration config, MetricResolver metricResolver,
WsClientPool wsClientPool) {
this.balancerConfig = balancerConfig;
this.config = config;
this.metricResolver = metricResolver;
this.wsClientPool = wsClientPool;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, SubscriptionRequest msg) throws Exception {
TimelyBalancedHost k;
ChannelHandlerContext origContext = null;
String subscriptionId;
try {
String metric = null;
if (msg instanceof CreateSubscription) {
CreateSubscription create = (CreateSubscription) msg;
final String currentSubscriptionId = create.getSubscriptionId();
subscriptionId = currentSubscriptionId;
wsSubscriptions.put(subscriptionId, ctx);
// WebSocketSubscriptionClient sends the createSubscription when
// we call open
// We don't know where to send it to yet anyway because that
// depends on the metric.
ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Map<String, WsClientHolder> metricToClientMap = wsClients.get(currentSubscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
// close all clients for this subscriptionId
LOG.info("Channel closed, closing subscriptions for subscriptionId:%s",
currentSubscriptionId);
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
entry.getValue().close(wsClientPool);
}
wsClients.remove(currentSubscriptionId);
wsSubscriptions.remove(currentSubscriptionId);
}
}
}
});
} else if (msg instanceof AddSubscription) {
AddSubscription add = (AddSubscription) msg;
metric = add.getMetric();
subscriptionId = add.getSubscriptionId();
origContext = wsSubscriptions.get(subscriptionId);
if (origContext == null) {
// send error because user never sent a CreateSubscription
// and we have nowhere to send the results
LOG.info("ChannelHandlerContext not found for subscriptionId:%s - createSubscription not called?",
subscriptionId);
sendErrorResponse(ctx, HttpResponseStatus.BAD_REQUEST, new IllegalArgumentException(
"Must call create first"));
}
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap == null) {
metricToClientMap = new HashMap<>();
wsClients.put(subscriptionId, metricToClientMap);
}
WebSocketSubscriptionClient client = null;
synchronized (metricToClientMap) {
WsClientHolder hostClientHolder = metricToClientMap.get(metric);
if (hostClientHolder == null) {
k = metricResolver.getHostPortKey(metric);
client = wsClientPool.borrowObject(k);
client.open(new WsClientHandler(origContext, (config.getWebsocket().getTimeout() / 2)));
metricToClientMap.put(metric, new WsClientHolder(k, client));
} else {
client = hostClientHolder.getClient();
}
}
Map<String, String> tags = null;
Long startTime = 0L;
Long endTime = 0L;
Long delayTime = 5000L;
if (add.getTags().isPresent()) {
tags = add.getTags().get();
}
if (add.getStartTime().isPresent()) {
startTime = add.getStartTime().get();
}
if (add.getEndTime().isPresent()) {
endTime = add.getEndTime().get();
}
if (add.getDelayTime().isPresent()) {
delayTime = add.getDelayTime().get();
}
client.addSubscription(add.getMetric(), tags, startTime, endTime, delayTime);
} else if (msg instanceof RemoveSubscription) {
RemoveSubscription remove = (RemoveSubscription) msg;
metric = remove.getMetric();
subscriptionId = remove.getSubscriptionId();
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
WsClientHolder hostClientHolder = metricToClientMap.get(metric);
if (hostClientHolder != null) {
hostClientHolder.getClient().removeSubscription(metric);
} else {
LOG.info("client not found for subscriptionId:%s metric:%s", subscriptionId, metric);
}
}
}
} else if (msg instanceof CloseSubscription) {
CloseSubscription close = (CloseSubscription) msg;
subscriptionId = close.getSubscriptionId();
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
// close all clients for this subscriptionId
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
entry.getValue().close(wsClientPool);
}
}
wsClients.remove(subscriptionId);
wsSubscriptions.remove(subscriptionId);
}
ctx.writeAndFlush(new CloseWebSocketFrame(1000, "Client requested close."));
}
} catch (ConnectException e1) {
LOG.error(e1.getMessage(), e1);
ChannelHandlerContext errorCtx = origContext == null ? ctx : origContext;
sendErrorResponse(errorCtx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e1);
} catch (Exception e2) {
LOG.error(e2.getMessage(), e2);
ChannelHandlerContext errorCtx = origContext == null ? ctx : origContext;
sendErrorResponse(errorCtx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e2);
}
}
static public void sendErrorResponse(ChannelHandlerContext ctx, HttpResponseStatus status, Exception ex) {
try {
TimelyException te = new TimelyException(status.code(), ex.getMessage(), ex.getMessage());
String json = mapper.writeValueAsString(te);
ctx.writeAndFlush(new TextWebSocketFrame(json));
} catch (JsonProcessingException e) {
LOG.error("Error serializing exception");
}
}
}
@@ -1,297 +0,0 @@
package timely.balancer.resolver;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.ArrivalRate;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.healthcheck.HealthChecker;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
public class BalancedMetricResolver implements MetricResolver {
private static final Logger LOG = LoggerFactory.getLogger(BalancedMetricResolver.class);
private Map<String, TimelyBalancedHost> metricToHostMap = new HashMap<>();
private Map<Integer, TimelyBalancedHost> serverMap = new HashMap<>();
private Map<String, ArrivalRate> metricMap = new HashMap<>();
private Random r = new Random();
final private HealthChecker healthChecker;
private Timer timer = new Timer("RebalanceTimer", true);
private long balanceUntil = System.currentTimeMillis() + 1800000l;
public BalancedMetricResolver(BalancerConfiguration config, HealthChecker healthChecker) {
int n = 0;
synchronized (this) {
for (TimelyBalancedHost h : config.getTimelyHosts()) {
h.setConfig(config);
serverMap.put(n++, h);
}
}
this.healthChecker = healthChecker;
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
rebalanceAllMetrics();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}, 300000);
timer.schedule(new TimerTask() {
@Override
public void run() {
if (System.currentTimeMillis() < balanceUntil) {
try {
balance();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
}, 600000, 120000);
}
private TimelyBalancedHost getLeastUsedHost() {
Map<Double, TimelyBalancedHost> rateSortedHosts = new TreeMap<>();
for (Map.Entry<Integer, TimelyBalancedHost> e : serverMap.entrySet()) {
rateSortedHosts.put(e.getValue().getArrivalRate(), e.getValue());
}
Iterator<Map.Entry<Double, TimelyBalancedHost>> itr = rateSortedHosts.entrySet().iterator();
TimelyBalancedHost tbh = null;
while (itr.hasNext() && tbh == null) {
TimelyBalancedHost currentTBH = itr.next().getValue();
if (currentTBH.isUp()) {
tbh = currentTBH;
}
}
return tbh;
}
private TimelyBalancedHost getRandomHost(TimelyBalancedHost notThisOne) {
TimelyBalancedHost tbh = null;
for (int x = 0; tbh == null && x < serverMap.size(); x++) {
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
if (!tbh.isUp()) {
tbh = null;
} else if (notThisOne != null && tbh.equals(notThisOne)) {
tbh = null;
}
}
return tbh;
}
private TimelyBalancedHost getRoundRobinHost() {
TimelyBalancedHost tbh;
int x = metricToHostMap.size() % serverMap.size();
tbh = serverMap.get(x);
if (tbh.isUp()) {
return tbh;
} else {
return getRandomHost(null);
}
}
synchronized public void rebalanceAllMetrics() {
Map<Double, String> rateSortedMetrics = new TreeMap<>();
for (Map.Entry<String, ArrivalRate> e : metricMap.entrySet()) {
rateSortedMetrics.put(e.getValue().getRate(), e.getKey());
}
metricToHostMap.clear();
for (String m : rateSortedMetrics.values()) {
metricToHostMap.put(m, getRoundRobinHost());
}
}
synchronized public void balance() {
LOG.info("rebalancing begin");
Map<Double, TimelyBalancedHost> ratedSortedHosts = new TreeMap<>();
double totalArrivalRate = 0;
for (Map.Entry<Integer, TimelyBalancedHost> e : serverMap.entrySet()) {
ratedSortedHosts.put(e.getValue().getArrivalRate(), e.getValue());
totalArrivalRate += e.getValue().getArrivalRate();
}
Iterator<Map.Entry<Double, TimelyBalancedHost>> itr = ratedSortedHosts.entrySet().iterator();
TimelyBalancedHost mostUsed = null;
TimelyBalancedHost leastUsed = null;
while (itr.hasNext()) {
TimelyBalancedHost currentTBH = itr.next().getValue();
if (currentTBH.isUp()) {
if (leastUsed == null) {
leastUsed = currentTBH;
mostUsed = currentTBH;
} else {
// should end up with the last server that is up
mostUsed = currentTBH;
}
}
}
double averageArrivalRate = totalArrivalRate / serverMap.size();
double highestArrivalRate = mostUsed.getArrivalRate();
double lowestArrivalRate = leastUsed.getArrivalRate();
LOG.info("rebalancing high:{} avg:{} low:{}", highestArrivalRate, averageArrivalRate, lowestArrivalRate);
// 5% over average
int numReassigned = 0;
if (highestArrivalRate > averageArrivalRate * 1.05) {
LOG.info("rebalancing: high > 5% higher than average");
// sort metrics by rate
Map<Double, String> rateSortedMetrics = new TreeMap<>();
for (Map.Entry<String, ArrivalRate> e : metricMap.entrySet()) {
rateSortedMetrics.put(e.getValue().getRate(), e.getKey());
}
double desiredDeltaHighest = (highestArrivalRate - averageArrivalRate) * 0.1;
double desiredDeltaLowest = (averageArrivalRate - lowestArrivalRate) * 0.1;
Iterator<Map.Entry<Double, String>> metricItr = rateSortedMetrics.entrySet().iterator();
boolean done = false;
LOG.info("rebalancing: desiredDeltaHighest:{} desiredDeltaLowest:{} rateSortedMetrics.size():{}",
desiredDeltaHighest, desiredDeltaLowest, rateSortedMetrics.size());
// advance to halfway
for (int numMetric = 0; metricItr.hasNext() && numMetric <= rateSortedMetrics.size() / 2; numMetric++) {
metricItr.next();
}
long maxToReassign = Math.round(((double) metricMap.size() / serverMap.size()) * 0.20);
while (!done && metricItr.hasNext() && numReassigned < maxToReassign) {
Map.Entry<Double, String> current = metricItr.next();
Double currentRate = current.getKey();
String currentMetric = current.getValue();
if (desiredDeltaHighest > 0) {
if (metricToHostMap.get(currentMetric).equals(mostUsed)) {
LOG.debug("rebalancing: trying to reassign metric {} from server {}:{}", currentMetric,
mostUsed.getHost(), mostUsed.getTcpPort());
if (desiredDeltaHighest > 0) {
numReassigned++;
metricToHostMap.put(currentMetric, leastUsed);
LOG.debug("rebalancing: reassigning metric {} from server {}:{} to {}:{}", currentMetric,
mostUsed.getHost(), mostUsed.getTcpPort(), leastUsed.getHost(),
leastUsed.getTcpPort());
desiredDeltaLowest -= currentRate;
desiredDeltaHighest -= currentRate;
} else {
TimelyBalancedHost tbh = getRandomHost(mostUsed);
if (tbh != null) {
numReassigned++;
metricToHostMap.put(currentMetric, tbh);
LOG.info("rebalancing: reassigning metric {} from server {}:{} to {}:{}",
currentMetric, mostUsed.getHost(), mostUsed.getTcpPort(), tbh.getHost(),
tbh.getTcpPort());
desiredDeltaHighest -= currentRate;
} else {
LOG.debug(
"rebalancing: unable to reassign metric {} from server {}:{} - getRandomHost returned null",
currentMetric, mostUsed.getHost(), mostUsed.getTcpPort());
}
}
}
} else {
done = true;
}
}
}
LOG.info("rebalancing end - reassigned {}", numReassigned);
}
@Override
public TimelyBalancedHost getHostPortKeyIngest(String metric) {
if (StringUtils.isNotBlank(metric)) {
ArrivalRate rate;
rate = metricMap.get(metric);
if (rate == null) {
rate = new ArrivalRate();
metricMap.put(metric, rate);
}
rate.arrived();
}
TimelyBalancedHost tbh;
if (StringUtils.isBlank(metric)) {
tbh = getRandomHost(null);
} else {
tbh = metricToHostMap.get(metric);
if (tbh == null) {
tbh = getRoundRobinHost();
metricToHostMap.put(metric, tbh);
} else if (!tbh.isUp()) {
TimelyBalancedHost oldTbh = tbh;
tbh = getLeastUsedHost();
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}",
metric, oldTbh.getHost(), oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort());
metricToHostMap.put(metric, tbh);
}
}
// if all else fails
if (tbh == null || !tbh.isUp()) {
for (TimelyBalancedHost h : serverMap.values()) {
if (h.isUp()) {
tbh = h;
break;
}
}
if (tbh != null && StringUtils.isNotBlank(metric)) {
metricToHostMap.put(metric, tbh);
}
}
if (tbh != null) {
tbh.arrived();
tbh.calculateRate();
}
return tbh;
}
@Override
public TimelyBalancedHost getHostPortKey(String metric) {
TimelyBalancedHost tbh = null;
if (StringUtils.isNotBlank(metric)) {
tbh = metricToHostMap.get(metric);
}
if (tbh == null || !tbh.isUp()) {
for (int x = 0; tbh == null && x < serverMap.size(); x++) {
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
if (!tbh.isUp()) {
tbh = null;
}
}
}
// if all else fails
if (tbh == null || !tbh.isUp()) {
for (TimelyBalancedHost h : serverMap.values()) {
if (h.isUp()) {
tbh = h;
break;
}
}
if (tbh != null && StringUtils.isNotBlank(metric)) {
metricToHostMap.put(metric, tbh);
}
}
return tbh;
}
}
@@ -1,10 +0,0 @@
package timely.balancer.resolver;
import timely.balancer.connection.TimelyBalancedHost;
public interface MetricResolver {
public TimelyBalancedHost getHostPortKeyIngest(String metric);
public TimelyBalancedHost getHostPortKey(String metric);
}
@@ -1,92 +0,0 @@
package timely.balancer.resolver;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.healthcheck.HealthChecker;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class RegexMetricResolver implements MetricResolver {
private static final Logger LOG = LoggerFactory.getLogger(RegexMetricResolver.class);
private Map<Integer, TimelyBalancedHost> serverMap = new HashMap<>();
private Random r = new Random();
final private HealthChecker healthChecker;
public RegexMetricResolver(BalancerConfiguration config, HealthChecker healthChecker) {
int n = 0;
for (TimelyBalancedHost h : config.getTimelyHosts()) {
h.setConfig(config);
serverMap.put(n++, h);
}
this.healthChecker = healthChecker;
}
public TimelyBalancedHost getHostPortKeyInternal(String metric) {
TimelyBalancedHost tbh = null;
if (StringUtils.isNotBlank(metric)) {
for (TimelyBalancedHost h : serverMap.values()) {
if (metric.matches(h.getRegex()) && h.isUp()) {
tbh = h;
break;
}
}
}
if (tbh == null) {
tbh = getFallbackHost(metric);
}
LOG.info("routing metric: " + metric + " to " + tbh.getHost() + ":" + tbh.getTcpPort());
return tbh;
}
@Override
public TimelyBalancedHost getHostPortKeyIngest(String metric) {
return getHostPortKeyInternal(metric);
}
@Override
public TimelyBalancedHost getHostPortKey(String metric) {
return getHostPortKeyInternal(metric);
}
private TimelyBalancedHost getFallbackHost(String metric) {
TimelyBalancedHost tbh;
if (StringUtils.isBlank(metric)) {
do {
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
} while (!tbh.isUp());
} else {
// reverse metric so that similar metrics go to different hosts in a
// reproducible way
String reverseMetric = StringUtils.reverse(metric);
int x = 0;
do {
// subtract a charter off of the end until we find a server that
// is up
int hash = reverseMetric.substring(x++).hashCode();
tbh = serverMap.get(Math.abs(hash & Integer.MAX_VALUE) % serverMap.size());
} while (!tbh.isUp());
}
// if all else fails
if (!tbh.isUp()) {
for (TimelyBalancedHost h : serverMap.values()) {
if (h.isUp()) {
tbh = h;
break;
}
}
}
return tbh;
}
}
@@ -1,2 +0,0 @@
cd src/main/generated-java
flatc --java ../resources/fb/metric.fbs
-21
View File
@@ -1,21 +0,0 @@
namespace timely.api.flatbuffer;
file_identifier "TMFB";
table Tag {
key:string;
value:string;
}
table Metric {
name:string;
timestamp:long;
value:double;
tags:[Tag];
}
table Metrics {
metrics:[Metric];
}
root_type Metrics;
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Class name="timely.balancer.Balancer" />
<Method name="shutdown" />
<Bug pattern="UC_USELESS_OBJECT" />
</Match>
<Match>
<Class name="timely.balancer.netty.http.HttpRelayHandler$MyByteArrayOutputStream" />
<Method name="getBuf" />
<Bug pattern="EI_EXPOSE_REP" />
</Match>
</FindBugsFilter>
+34 -41
View File
@@ -5,11 +5,11 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
</parent>
<properties>
<!-- PEP 440 compliant version gets supstituted into src/main/python/template/setup.py -->
<python.client.version>1.0.1.dev0</python.client.version>
<python.client.version>0.0.6.dev0</python.client.version>
</properties>
<dependencies>
<dependency>
@@ -127,47 +127,40 @@
</execution>
</executions>
</plugin>
<!--
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>python-dist</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>python</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>setup.py sdist - -dist-dir ${project.build.directory}</commandlineArgs>
</configuration>
</execution>
<execution>
<id>python-clean</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>rm</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>-rf timely.egg-info setup.py</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
<profiles>
<profile>
<id>python</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>python-dist</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>python</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>setup.py sdist --dist-dir ${project.build.directory}</commandlineArgs>
</configuration>
</execution>
<execution>
<id>python-clean</id>
<goals>
<goal>exec</goal>
</goals>
<phase>package</phase>
<configuration>
<executable>rm</executable>
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
<commandlineArgs>-rf timely.egg-info setup.py</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
<plugins>
<plugin>
@@ -63,7 +63,7 @@ public class TcpClient implements AutoCloseable {
*/
@Override
public void close() throws IOException {
LOG.trace("Shutting down connection to Timely at {}:{}", host, port);
LOG.info("Shutting down connection to Timely at {}:{}", host, port);
if (null != sock) {
try {
if (null != out) {
@@ -86,13 +86,13 @@ public class TcpClient implements AutoCloseable {
sock = new Socket(host, port);
out = new PrintWriter(sock.getOutputStream(), false);
backoff = 2000;
LOG.trace("Connected to Timely at {}:{}", host, port);
LOG.info("Connected to Timely at {}:{}", host, port);
} catch (IOException e) {
LOG.error("Error connecting to Timely at {}:" + host + ":" + port + ". Error: " + e.getMessage());
backoff = backoff * 2;
sock = null;
out = null;
LOG.info("Will retry connection in {} ms.", backoff);
LOG.warn("Will retry connection in {} ms.", backoff);
return -1;
}
} else {
@@ -1,8 +1,5 @@
package timely.client.udp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
@@ -12,19 +9,13 @@ import java.net.InetSocketAddress;
public class UdpClient implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(UdpClient.class);
private final String host;
private final int port;
private final InetSocketAddress address;
private final DatagramPacket packet;
private DatagramSocket sock;
public UdpClient(String host, int port) {
this.address = new InetSocketAddress(host, port);
public UdpClient(String hostname, int port) {
this.address = new InetSocketAddress(hostname, port);
this.packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), port);
this.host = host;
this.port = port;
}
public void open() throws IOException {
@@ -38,7 +29,6 @@ public class UdpClient implements AutoCloseable {
throw new IllegalStateException("Must call open first");
}
this.packet.setData(metric.getBytes(UTF_8));
LOG.info("writing '" + metric + "' to " + this.host + ":" + this.port);
this.sock.send(packet);
}
@@ -1,8 +1,6 @@
package timely.model.parse;
import com.google.common.base.Splitter;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import timely.model.Tag;
import java.util.ArrayList;
@@ -17,22 +15,13 @@ import java.util.Map;
*/
public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag>> {
private static final Splitter commaSplitter = Splitter.on(",").omitEmptyStrings();
private static final TagParser tagParser = new TagParser();
@Override
public List<Tag> parse(String t) {
ArrayList<Tag> tags = new ArrayList<>();
// only split on commas that are not preceded with a backslash
String[] parts = t.split("(?<!\\\\),");
for (String p : parts) {
if (!p.isEmpty()) {
Tag tag = tagParser.parse(p);
// unescape commas in the key and value of the tag
tag.setKey(tag.getKey().replaceAll("\\\\,", ","));
tag.setValue(tag.getValue().replaceAll("\\\\,", ","));
tags.add(tag);
}
}
commaSplitter.splitToList(t).forEach(p -> tags.add(tagParser.parse(p)));
return tags;
}
@@ -12,7 +12,7 @@ import java.util.List;
*/
public class TagParser implements Parser<Tag>, Combiner<Tag> {
private static final Splitter equalSplitter = Splitter.on("=").limit(2).trimResults();
private static final Splitter equalSplitter = Splitter.on("=").trimResults();
private static final Joiner equalsJoiner = Joiner.on("=");
@Override
@@ -41,7 +41,7 @@ class TimeDateRange:
def getBeginMs(self):
if self.beginDt == None:
return None
return TimeDateRange.unix_time_millis(self.beginDt)
return int(time.mktime(self.beginDt.timetuple()) * 1000)
def getBeginStr(self):
if self.beginDt == None:
@@ -51,7 +51,7 @@ class TimeDateRange:
def getEndMs(self):
if self.endDt == None:
return None
return TimeDateRange.unix_time_millis(self.endDt)
return int(time.mktime(self.endDt.timetuple()) * 1000)
def getEndStr(self):
if self.endDt == None:
@@ -123,6 +123,6 @@ class TimeDateRange:
@classmethod
def unix_time_millis(cls, dt):
return int((dt - TimeDateRange.epoch).total_seconds() * 1000.0)
return (dt - TimeDateRange.epoch).total_seconds() * 1000.0
+22 -34
View File
@@ -23,7 +23,7 @@ class TimelyWebSocketClient(WebSocketClient):
self.startTime = startTime
self.endTime = endTime
self.subscriptionId = random.randint(1000000000, 9999999999)
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout)
WebSocketClient.__init__(self, hostport, connect_timeout, request_timeout)
def _on_message(self, msg):
# implement in subclass
@@ -37,6 +37,7 @@ class TimelyWebSocketClient(WebSocketClient):
"subscriptionId": self.subscriptionId,
}
self.send(create)
self.send(create)
m1 = {
"operation": "add",
@@ -104,41 +105,30 @@ class TimelyMetric(TimelyWebSocketClient):
global endtime
responsesObj = json.loads(msg)
responses = responsesObj.get("responses")
if responses is not None:
for obj in responses:
complete = bool(obj.get("complete"))
if complete:
self._on_connection_close()
return
else:
date = int(obj.get("timestamp")/1000)
dt = pandas.datetime.utcfromtimestamp(date)
metricName = str(obj.get("metric"))
metricValue = obj.get("value")
newData = {}
newData["date"] = dt
newData[metricName] = metricValue
for obj in responses:
complete = bool(obj.get("complete"))
if complete:
self._on_connection_close()
return
else:
date = int(obj.get("timestamp")/1000)
dt = pandas.datetime.utcfromtimestamp(date)
metricName = str(obj.get("metric"))
metricValue = obj.get("value")
tags = obj.get("tags")
for d in tags:
for k,v in d.items():
newData[k] = v
newData = {}
newData["date"] = dt
newData[metricName] = metricValue
tags = obj.get("tags")
for d in tags:
for k,v in d.items():
newData[k] = v
self.data.append(newData)
self.data.append(newData)
else:
print("Session ended abnormally")
exception = responsesObj.get("TimelyException")
if exception is not None:
code = exception.get("code")
message = exception.get("message")
print("Exception")
print("code: " + str(code))
print("message: " + message)
self._on_connection_close()
return
def _on_connection_close(self):
global client
@@ -147,8 +137,6 @@ class TimelyMetric(TimelyWebSocketClient):
if len(self.dataFrame) > 0:
self.dataFrame['date'] = pandas.DatetimeIndex(self.dataFrame['date'])
self.dataFrame = self.dataFrame.set_index('date')
j = pandas.to_timedelta(self.dataFrame.groupby(self.dataFrame.index).cumcount(), unit='us').values
self.dataFrame.index = self.dataFrame.index + j
self.close()
ioloop.IOLoop.current(instance=False).stop()
@@ -14,26 +14,12 @@ class WebSocketClient():
DEFAULT_REQUEST_TIMEOUT = 60
APPLICATION_JSON = 'application/json'
def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
def __init__(self, hostport, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
request_timeout=DEFAULT_REQUEST_TIMEOUT):
self.connect_timeout = connect_timeout
self.request_timeout = request_timeout
self.url = 'wss://' + hostport
if (path is not None and len(path) > 0):
if (path.startswith('/')):
self.url = self.url + path
else:
self.url = self.url + '/' + path
if (any(params.values())):
first = True
for key, value in params.iteritems():
if (first == True):
self.url = self.url + '?'
first = False
else:
self.url = self.url + '&'
self.url = self.url + key + '=' + value
self.url = 'wss://' + hostport + '/websocket'
def connect(self, ioloop=ioloop.IOLoop.current(instance=False)):
"""Connect to the server.
+14 -17
View File
@@ -1,38 +1,35 @@
import timely.TimelyMetric
import timely.TimelyAnalytic
import timely.DataOperations
import TimelyMetric
import TimelyAnalytic
import DataOperations
import os
from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
from TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
hostport = "127.0.0.1:5244"
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None).fetch()
print(timelyMetric.getDataFrame())
exit()
hostport = "127.0.0.1:4244"
timelyMetric = TimelyMetric.TimelyMetric(hostport, '', None, None, None, 48, None).fetch()
analyticConfig = TimelyAnalyticConfiguration({
'system_name' : 'TestSystem',
'counter' : False,
'includeColRegex' : None,
'excludeColRegex' : None,
'groupByColumn' : 'host',
'sample' : '1 minute',
'groupByColumn' : 'instance',
'sample' : '10 minutes',
'how' : 'mean',
'rolling_average_period' : '12 hours',
'min_threshold' : None,
'average_min_threshold' : None,
'max_threshold' : None,
'average_max_threshold' : None,
'min_threshold_percentage' : None,
'max_threshold_percentage' : None,
'min_threshold_percentage' : -50,
'max_threshold_percentage' : 50,
'boolean' : 'and',
'min_alert_period' : None,
'last_alert' : None,
'min_alert_period' : '5 minutes',
'last_alert' : '1 hour',
'display' : 'all',
'output_dir' : '/path/to/output'
})
alert = timely.TimelyAnalytic.find_alerts(timelyMetric, analyticConfig)
alert = TimelyAnalytic.find_alerts(timelyMetric, analyticConfig)
if alert is not None:
# write graph to file
@@ -40,7 +37,7 @@ if alert is not None:
file = alert.graph(type='html')
os.umask(oldmask)
text = timely.DataOperations.getTitle(timelyMetric, analyticConfig)
text = DataOperations.getTitle(timelyMetric, analyticConfig)
# send email with graph attached
alert.email("", "", text, text, [file])
@@ -1,8 +1,7 @@
package timely.model.parse;
package timely.model;
import org.junit.Assert;
import org.junit.Test;
import timely.model.Tag;
import timely.model.parse.TagListParser;
import java.util.ArrayList;
@@ -38,15 +37,4 @@ public class TagListParserTest {
String combined = new TagListParser().combine(map);
Assert.assertEquals("tag1=value1,tag2=value2", combined);
}
@Test
public void testParseTagsWithCommas() {
try {
String s = "tag1=value1,tag2=3.4.3_(default\\,_Date\\,_Time)_";
new TagListParser().parse(s);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
}
@@ -1,40 +0,0 @@
package timely.model.parse;
import org.junit.Assert;
import org.junit.Test;
import timely.model.Metric;
import timely.model.Tag;
import java.util.ArrayList;
import java.util.List;
public class MetricParserTest {
@Test
public void testParseWithEscapedCharacters() {
MetricParser parser = new MetricParser();
Metric m = parser.parse("put mymetric 12341234 5.0 tag1=value1,value1 tag2=value2=value2");
Assert.assertEquals("mymetric", m.getName());
Assert.assertEquals(12341234, (long) m.getValue().getTimestamp());
Assert.assertEquals(5.0, (double) m.getValue().getMeasure(), 0);
List<Tag> expected = new ArrayList<>();
expected.add(new Tag("tag1", "value1,value1"));
expected.add(new Tag("tag2", "value2=value2"));
Assert.assertEquals(expected, m.getTags());
}
@Test
public void testParseMalformatted() {
MetricParser parser = new MetricParser();
try {
// parser should throw an exception
parser.parse("put mymetric 12341234 5.0 tag1 tag2=value2");
Assert.fail();
} catch (IllegalArgumentException e) {
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-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.0.1-SNAPSHOT</version>
<version>0.0.6-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.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
</parent>
<artifactId>collectd-timely-plugin</artifactId>
<dependencies>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-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.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
</parent>
<build>
<plugins>
+21 -35
View File
@@ -227,47 +227,33 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
key: 'performTimeSeriesQuery',
value: function performTimeSeriesQuery(queries, start, end) {
var msResolution = true;
var responsePromises = [];
// execute backend datasourceRequests separately
// for better performance and load balancing
_.each(queries, function (query) {
var reqBody = {
start: start,
queries: queries,
msResolution: msResolution,
globalAnnotations: true,
showQuery: true
};
var reqBody = {
start: start,
queries: [query],
msResolution: msResolution,
globalAnnotations: true,
showQuery: true
};
// Relative queries (e.g. last hour) don't include an end time
if (end) {
reqBody.end = end;
}
// Relative queries (e.g. last hour) don't include an end time
if (end) {
reqBody.end = end;
}
var options = {
method: 'POST',
url: this.url + '/api/query',
data: reqBody
};
var options = {
method: 'POST',
url: this.url + '/api/query?metric=' + query.metric,
data: reqBody
};
this._addCredentialOptions(options);
this._addCredentialOptions(options);
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
// go as POST rather than OPTIONS+POST
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
responsePromises.push(this.backendSrv.datasourceRequest(options));
}.bind(this));
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
// go as POST rather than OPTIONS+POST
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// wait until all promised datasourceRequests complete
// and then return combined responses
return Promise.all(responsePromises).then(function (queryResponses) {
var combinedResponse = queryResponses[0];
for (var x = 1; x < queryResponses.length; x++) {
combinedResponse.data = combinedResponse.data.concat(queryResponses[x].data);
}
return combinedResponse;
}.bind(this));
return this.backendSrv.datasourceRequest(options);
}
}, {
key: '_saveTagKeys',
+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","reqBody","showQuery","_addCredentialOptions","headers","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,GAAxB,EAA6B;AAC3B,uBAAO,EAAEA,QAAQ,SAAV,EAAqBC,SAAS,wBAA9B,EAAwDC,OAAO,SAA/D,EAAP;AACLine truncated
+1 -2
View File
@@ -28,8 +28,7 @@
"grunt-execute": "~0.2.2",
"grunt-mocha-test": "~0.12.7",
"grunt-systemjs-builder": "^0.2.5",
"load-grunt-tasks": "~3.2.0",
"mocha": "^5.2.0"
"load-grunt-tasks": "~3.2.0"
},
"dependencies": {
"ag-grid": "^5.0.2",
+21 -35
View File
@@ -183,47 +183,33 @@ export class TimelyDatasource {
performTimeSeriesQuery(queries, start, end){
var msResolution = true;
var responsePromises = [];
// execute backend datasourceRequests separately
// for better performance and load balancing
_.each(queries, function(query) {
var reqBody = {
start: start,
queries: queries,
msResolution: msResolution,
globalAnnotations: true,
showQuery: true
};
var reqBody = {
start: start,
queries: [query],
msResolution: msResolution,
globalAnnotations: true,
showQuery: true
};
// Relative queries (e.g. last hour) don't include an end time
if (end) {
reqBody.end = end;
}
// Relative queries (e.g. last hour) don't include an end time
if (end) {
reqBody.end = end;
}
var options = {
method: 'POST',
url: this.url + '/api/query',
data: reqBody
};
var options = {
method: 'POST',
url: this.url + '/api/query?metric=' + query.metric,
data: reqBody
};
this._addCredentialOptions(options);
this._addCredentialOptions(options);
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
// go as POST rather than OPTIONS+POST
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
responsePromises.push(this.backendSrv.datasourceRequest(options));
}.bind(this));
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
// go as POST rather than OPTIONS+POST
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// wait until all promised datasourceRequests complete
// and then return combined responses
return Promise.all(responsePromises).then(function(queryResponses) {
var combinedResponse = queryResponses[0];
for (var x=1; x < queryResponses.length; x++) {
combinedResponse.data = combinedResponse.data.concat(queryResponses[x].data);
}
return combinedResponse;
}.bind(this));
return this.backendSrv.datasourceRequest(options);
}
_saveTagKeys(metricData) {
+3 -22
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Timely</name>
<description>A secure and scalable time series database based on Apache Accumulo</description>
@@ -28,7 +28,6 @@
<module>server</module>
<module>grafana</module>
<module>analytics</module>
<module>balancer</module>
</modules>
<properties>
<!-- Configure the os-maven-plugin extension to expand the classifier on -->
@@ -44,8 +43,6 @@
<version.caffeine>2.3.3</version.caffeine>
<version.collectd>5.5.1</version.collectd>
<version.commons-collections4>4.1</version.commons-collections4>
<version.commons-configuration>1.10</version.commons-configuration>
<version.commons-io>2.6</version.commons-io>
<version.commons-lang3>3.5</version.commons-lang3>
<version.commons-pool2>2.4.2</version.commons-pool2>
<version.disruptor>3.3.6</version.disruptor>
@@ -60,12 +57,11 @@
<version.jsoup>1.9.2</version.jsoup>
<version.junit>4.12</version.junit>
<version.log4j>2.6.2</version.log4j>
<version.netty>4.0.56.Final</version.netty>
<version.netty-tcnative>2.0.7.Final</version.netty-tcnative>
<version.netty>4.0.46.Final</version.netty>
<version.netty-tcnative>2.0.1.Final</version.netty-tcnative>
<version.powermock>1.6.4</version.powermock>
<version.slf4j>1.7.21</version.slf4j>
<version.spring-boot>1.4.1.RELEASE</version.spring-boot>
<version.spring-websocket>5.0.8.RELEASE</version.spring-websocket>
<!-- plugin versions -->
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
<version.findbugs-maven>3.0.3</version.findbugs-maven>
@@ -138,11 +134,6 @@
<artifactId>commons-beanutils</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>fi.iki.yak</groupId>
<artifactId>compression-gorilla</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
@@ -170,16 +161,6 @@
<artifactId>commons-collections4</artifactId>
<version>${version.commons-collections4}</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>${version.commons-configuration}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${version.commons-io}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
File diff suppressed because it is too large. Load diff
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
export TRUSTSTORE_FILE=
export TRUSTSTORE_TYPE="jks";
export TRUSTSTORE_PASS=
export KEYSTORE_FILE=
export KEYSTORE_TYPE="pkcs12";
export KEYSTORE_PASS=
export NUM_QUERY_THREADS=4
export NUM_INGEST_THREADS=4
-21
View File
@@ -1,21 +0,0 @@
#!/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}/../..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
. ${THIS_DIR}/load-test-env.sh
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1G -Xms1G"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*
-21
View File
@@ -1,21 +0,0 @@
#!/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}/../..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
. ${THIS_DIR}/load-test-env.sh
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1G -Xms1G"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*
-11
View File
@@ -1,11 +0,0 @@
#!/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%/*}"
sed -i -- "s/#.*#/#$1#/g" $THIS_DIR/load-test-data.txt
-4
View File
@@ -3,10 +3,6 @@ timely:
meta-table: timely.meta
metric-age-off-days:
default: 7
cache:
enabled:
age-off-hours:
default: 30
metrics-report-ignored-tags:
accumulo:
instance-name:
+1 -13
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>0.0.6-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
@@ -54,10 +54,6 @@
<artifactId>disruptor</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>fi.iki.yak</groupId>
<artifactId>compression-gorilla</artifactId>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
@@ -78,10 +74,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
@@ -182,10 +174,6 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.Valid;
@@ -16,7 +15,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import timely.store.cache.DataStoreCache;
import timely.validator.NotEmptyIfFieldSet;
import com.google.common.collect.Lists;
@@ -29,7 +27,6 @@ public class Configuration {
private String metaTable = "timely.meta";
private HashMap<String, Integer> metricAgeOffDays = new HashMap<>();
private List<String> metricsReportIgnoredTags = new ArrayList<>();
private String instance = null;
@Valid
@NestedConfigurationProperty
@@ -48,9 +45,6 @@ public class Configuration {
private MetaCache metaCache = new MetaCache();
@Valid
@NestedConfigurationProperty
private Cache cache = new Cache();
@Valid
@NestedConfigurationProperty
private VisibilityCache visibilityCache = new VisibilityCache();
@Valid
@NestedConfigurationProperty
@@ -74,14 +68,6 @@ public class Configuration {
return this;
}
public void setInstance(String instance) {
this.instance = instance;
}
public String getInstance() {
return instance;
}
public HashMap<String, Integer> getMetricAgeOffDays() {
return metricAgeOffDays;
}
@@ -123,10 +109,6 @@ public class Configuration {
return metaCache;
}
public Cache getCache() {
return cache;
}
public VisibilityCache getVisibilityCache() {
return visibilityCache;
}
@@ -670,61 +652,6 @@ public class Configuration {
}
}
public class Cache {
private boolean enabled = false;
private HashMap<String, Integer> metricAgeOffHours = new HashMap<>();
private List<String> nonCachedMetrics = new ArrayList<>();
private long maxUniqueTagSets = 50000;
private long flushInterval = 5000;
public HashMap<String, Integer> getMetricAgeOffHours() {
return metricAgeOffHours;
}
public Configuration setMetricAgeOffHours(HashMap<String, Integer> metricAgeOffHours) {
this.metricAgeOffHours = metricAgeOffHours;
return Configuration.this;
}
public void setDefaultAgeOffHours(int defaultAgeOffHours) {
this.metricAgeOffHours.put(DataStoreCache.DEFAULT_AGEOFF_KEY, defaultAgeOffHours);
}
public boolean isEnabled() {
return enabled;
}
public Configuration setEnabled(boolean enabled) {
this.enabled = enabled;
return Configuration.this;
}
public List<String> getNonCachedMetrics() {
return nonCachedMetrics;
}
public void setNonCachedMetrics(List<String> nonCachedMetrics) {
this.nonCachedMetrics = nonCachedMetrics;
}
public long getMaxUniqueTagSets() {
return maxUniqueTagSets;
}
public void setMaxUniqueTagSets(long maxUniqueTagSets) {
this.maxUniqueTagSets = maxUniqueTagSets;
}
public long getFlushInterval() {
return flushInterval;
}
public void setFlushInterval(long flushInterval) {
this.flushInterval = flushInterval;
}
}
public class VisibilityCache {
private long expirationMinutes = 60;
+13 -36
View File
@@ -9,7 +9,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
@@ -41,7 +40,6 @@ import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import java.io.File;
@@ -62,7 +60,6 @@ import org.springframework.context.ConfigurableApplicationContext;
import timely.api.response.TimelyException;
import timely.auth.AuthCache;
import timely.auth.VisibilityCache;
import timely.netty.http.HttpCacheRequestHandler;
import timely.netty.http.HttpMetricPutHandler;
import timely.netty.http.HttpStaticFileServerHandler;
import timely.netty.http.HttpVersionRequestHandler;
@@ -99,7 +96,6 @@ import timely.netty.websocket.timeseries.WSSuggestRequestHandler;
import timely.store.DataStore;
import timely.store.DataStoreFactory;
import timely.store.MetaCacheFactory;
import timely.store.cache.DataStoreCache;
public class Server {
@@ -126,11 +122,9 @@ public class Server {
protected Channel tcpChannelHandle = null;
protected Channel httpChannelHandle = null;
protected Channel wsChannelHandle = null;
protected List<Channel> udpChannelHandleList = new ArrayList<>();
protected Channel udpChannelHandle = null;
protected DataStore dataStore = null;
protected DataStoreCache dataStoreCache = null;
protected volatile boolean shutdown = false;
private final int DEFAULT_EVENT_LOOP_THREADS;
private static boolean useEpoll() {
@@ -221,9 +215,9 @@ public class Server {
channelFutures.add(wsChannelHandle.close());
}
for (Channel c : udpChannelHandleList) {
if (udpChannelHandle != null) {
LOG.info("Closing udpChannelHandle");
channelFutures.add(c.close());
channelFutures.add(udpChannelHandle.close());
}
// wait for the channels to shutdown
@@ -318,16 +312,8 @@ public class Server {
LOG.info("Server shut down.");
}
public Server(Configuration conf, int eventLoopThreads) throws Exception {
DEFAULT_EVENT_LOOP_THREADS = eventLoopThreads;
this.config = conf;
}
public Server(Configuration conf) throws Exception {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
this.config = conf;
}
@@ -336,10 +322,6 @@ public class Server {
int nettyThreads = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
dataStore = DataStoreFactory.create(config, nettyThreads);
if (config.getCache().isEnabled()) {
dataStoreCache = new DataStoreCache(config);
dataStore.setCache(dataStoreCache);
}
// Initialize the MetaCache
MetaCacheFactory.getCache(config);
// initialize the auth cache
@@ -429,21 +411,18 @@ public class Server {
final int udpPort = config.getServer().getUdpPort();
final String udpIp = config.getServer().getIp();
final Bootstrap udpServer = new Bootstrap();
udpServer.group(udpBossGroup);
udpServer.channel(datagramChannelClass);
udpServer.handler(setupUdpChannel());
udpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
udpChannelHandle = udpServer.bind(udpIp, udpPort).sync().channel();
final String udpAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
for (int n = 0; n < DEFAULT_EVENT_LOOP_THREADS; n++) {
final Bootstrap udpServer = new Bootstrap();
udpServer.group(udpBossGroup);
udpServer.channel(datagramChannelClass);
udpServer.handler(setupUdpChannel());
udpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
udpServer.option(EpollChannelOption.SO_REUSEADDR, true);
udpServer.option(EpollChannelOption.SO_REUSEPORT, true);
udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel());
}
shutdownHook();
LOG.info(
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, udpAddress, udpPort);
}
protected SslContext createSSLContext(Configuration config) throws Exception {
@@ -530,7 +509,6 @@ public class Server {
ch.pipeline().addLast("search", new HttpSearchLookupRequestHandler(dataStore));
ch.pipeline().addLast("suggest", new HttpSuggestRequestHandler(dataStore));
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());
}
@@ -547,7 +525,7 @@ public class Server {
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
ch.pipeline().addLast("putDecoder", new UdpDecoder());
ch.pipeline().addLast("putHandler", new TcpPutHandler(dataStore));
ch.pipeline().addLast(udpWorkerGroup, "putHandler", new TcpPutHandler(dataStore));
}
};
}
@@ -576,8 +554,7 @@ public class Server {
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
ch.pipeline().addLast("sessionExtractor", new WebSocketHttpCookieHandler(config));
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true));
ch.pipeline().addLast("ws-protocol", new WebSocketServerProtocolHandler(WS_PATH, null, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config));
ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler());
ch.pipeline().addLast("metrics", new WSMetricsRequestHandler(config));
@@ -2,6 +2,6 @@ package timely;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
@SpringBootApplication(scanBasePackageClasses = { Configuration.class })
public class SpringBootstrap {
}
@@ -1,6 +1,5 @@
package timely.adapter.accumulo;
import com.google.common.base.Joiner;
import org.apache.accumulo.core.client.lexicoder.LongLexicoder;
import org.apache.accumulo.core.client.lexicoder.PairLexicoder;
import org.apache.accumulo.core.client.lexicoder.StringLexicoder;
@@ -10,15 +9,15 @@ import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.security.ColumnVisibility;
import org.apache.accumulo.core.util.ComparablePair;
import org.apache.accumulo.core.util.Pair;
import org.apache.hadoop.io.Text;
import timely.model.Metric;
import timely.model.Tag;
import timely.model.parse.TagListParser;
import timely.model.parse.TagParser;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -29,61 +28,16 @@ public class MetricAdapter {
private static final PairLexicoder<String, Long> rowCoder = new PairLexicoder<>(new StringLexicoder(),
new LongLexicoder());
private static final PairLexicoder<Long, String> colQualCoder = new PairLexicoder<>(new LongLexicoder(),
new StringLexicoder());
private static final TagParser tagParser = new TagParser();
private static final TagListParser tagListParser = new TagListParser();
public static final ColumnVisibility EMPTY_VISIBILITY = new ColumnVisibility();
public static final String VISIBILITY_TAG = "viz";
public static final Joiner equalsJoiner = Joiner.on("=");
public static long roundTimestampToLastHour(long timestamp) {
return timestamp - (timestamp % 3600000);
}
public static long roundTimestampToNextHour(long timestamp) {
return timestamp - (timestamp % 3600000) + 3600000;
}
private static List<Tag> escapeDelimiters(List<Tag> tags) {
List<Tag> newTags = new ArrayList<>();
for (Tag t : tags) {
// escape all commas and equals in the tag key and value since they
// 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(",", "\\,");
newTags.add(new Tag(k, v));
}
return newTags;
}
private static Map<String, String> escapeDelimiters(Map<String, String> tags) {
Map<String, String> newTags = new LinkedHashMap<>();
for (Map.Entry<String, String> t : tags.entrySet()) {
// escape all commas and equals in the tag key and value since they
// 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(",", "\\,");
newTags.put(k, v);
}
return newTags;
}
public static Mutation toMutation(Metric metric) {
final Mutation mutation = new Mutation(encodeRowKey(metric));
List<Tag> tags = metric.getTags();
tags = escapeDelimiters(tags);
Collections.sort(tags);
for (final Tag entry : tags) {
@@ -97,37 +51,12 @@ public class MetricAdapter {
.map(Tag::join)
.collect(Collectors.joining(","));
// @formatter:on
byte[] cqBytes = encodeColQual(metric.getValue().getTimestamp(), cq);
mutation.put(new Text(cf.getBytes(Charset.forName("UTF-8"))), new Text(cqBytes), extractVisibility(tags),
metric.getValue().getTimestamp(), extractValue(metric));
mutation.put(cf, cq, extractVisibility(tags), metric.getValue().getTimestamp(), extractValue(metric));
}
return mutation;
}
public static Key toKey(String metric, Map<String, String> tags, long timestamp) {
byte[] row = encodeRowKey(metric, timestamp);
tags = escapeDelimiters(tags);
StringBuilder colQualSb = new StringBuilder();
String cf = null;
for (Map.Entry<String, String> entry : tags.entrySet()) {
if (entry.getKey().equals(VISIBILITY_TAG))
continue;
if (cf == null) {
cf = equalsJoiner.join(entry.getKey(), entry.getValue());
continue;
}
if (colQualSb.length() > 0) {
colQualSb.append(",");
}
colQualSb.append(equalsJoiner.join(entry.getKey(), entry.getValue()));
}
byte[] cqBytes = encodeColQual(timestamp, colQualSb.toString());
ColumnVisibility colVis = extractVisibility(tags);
return new Key(new Text(row), new Text(cf), new Text(cqBytes), colVis, timestamp);
}
private static Value extractValue(Metric metric) {
return new Value(encodeValue(metric.getValue().getMeasure()));
}
@@ -144,7 +73,7 @@ public class MetricAdapter {
return bb.getDouble();
}
public static ColumnVisibility extractVisibility(List<Tag> tags) {
private static ColumnVisibility extractVisibility(List<Tag> tags) {
// @formatter:off
Optional<Tag> visTag = tags.stream()
.filter(t -> t.getKey().equals(VISIBILITY_TAG))
@@ -153,24 +82,15 @@ public class MetricAdapter {
// @formatter:on
}
public static ColumnVisibility extractVisibility(Map<String, String> tags) {
if (tags.containsKey(VISIBILITY_TAG)) {
return new ColumnVisibility(tags.get(VISIBILITY_TAG));
} else {
return EMPTY_VISIBILITY;
}
}
public static Metric parse(Key k, Value v, boolean includeVizTag) {
ComparablePair<String, Long> row = rowCoder.decode(k.getRow().getBytes());
// @formatter:off
Metric.Builder builder = Metric.newBuilder()
.name(row.getFirst())
.value(k.getTimestamp(), ByteBuffer.wrap(v.get()).getDouble())
.value(row.getSecond(), ByteBuffer.wrap(v.get()).getDouble())
.tag(tagParser.parse(k.getColumnFamily().toString()));
// @formatter:on
ComparablePair<Long, String> cq = colQualCoder.decode(k.getColumnQualifier().getBytes());
tagListParser.parse(cq.getSecond()).forEach(builder::tag);
tagListParser.parse(k.getColumnQualifier().toString()).forEach(builder::tag);
if (includeVizTag && k.getColumnVisibility().getLength() > 0) {
tagListParser.parse("viz=" + k.getColumnVisibility().toString()).forEach(builder::tag);
}
@@ -186,16 +106,7 @@ public class MetricAdapter {
}
public static byte[] encodeRowKey(Metric metric) {
// round timestamp to hour for scan efficiency and compression
return encodeRowKey(metric.getName(), roundTimestampToLastHour(metric.getValue().getTimestamp()));
}
public static byte[] encodeColQual(Long timestamp, String colQual) {
return colQualCoder.encode(new ComparablePair<>(timestamp, colQual));
}
public static Pair<Long, String> decodeColQual(byte[] colQual) {
return colQualCoder.decode(colQual);
return encodeRowKey(metric.getName(), metric.getValue().getTimestamp());
}
public static Pair<String, Long> decodeRowKey(Key k) {
@@ -1,16 +1,5 @@
package timely.api.request;
import io.netty.handler.codec.http.FullHttpRequest;
public class AuthenticatedWebSocketRequest extends AuthenticatedRequest implements WebSocketRequest {
private FullHttpRequest httpRequest = null;
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,31 +0,0 @@
package timely.api.request;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.annotation.Http;
import timely.api.annotation.WebSocket;
@Http(path = "/api/cache")
@WebSocket(operation = "cache")
public class CacheRequest extends AuthenticatedRequest implements HttpGetRequest, HttpPostRequest, WebSocketRequest {
private FullHttpRequest httpRequest = null;
@Override
public HttpGetRequest parseQueryParameters(QueryStringDecoder decoder) throws Exception {
return new CacheRequest();
}
@Override
public HttpPostRequest parseBody(String content) throws Exception {
return new CacheRequest();
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,15 +1,9 @@
package timely.api.request;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.request.timeseries.HttpRequest;
public interface HttpGetRequest extends HttpRequest {
public interface HttpGetRequest extends Request {
public HttpGetRequest parseQueryParameters(QueryStringDecoder decoder) throws Exception;
public void setHttpRequest(FullHttpRequest httpRequest);
public FullHttpRequest getHttpRequest();
}
@@ -1,8 +1,6 @@
package timely.api.request;
import timely.api.request.timeseries.HttpRequest;
public interface HttpPostRequest extends HttpRequest {
public interface HttpPostRequest extends Request {
public HttpPostRequest parseBody(String content) throws Exception;
@@ -1,7 +1,6 @@
package timely.api.request;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import io.netty.handler.codec.http.FullHttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.annotation.Http;
@@ -24,14 +23,11 @@ import java.nio.charset.StandardCharsets;
public class MetricRequest implements TcpRequest, HttpPostRequest, WebSocketRequest, UdpRequest {
private static final Logger LOG = LoggerFactory.getLogger(MetricRequest.class);
private FullHttpRequest httpRequest = null;
@XmlElement
@JsonUnwrapped
private Metric metric;
private String line;
private static final MetricParser metricParser = new MetricParser();
public MetricRequest() {
@@ -58,13 +54,7 @@ public class MetricRequest implements TcpRequest, HttpPostRequest, WebSocketRequ
@Override
public void parse(String line) {
LOG.trace("Parsing Line: {}", line);
try {
this.metric = metricParser.parse(line);
this.line = line;
} catch (Exception e) {
LOG.error("Error parsing metric: {}", line);
throw e;
}
metric = metricParser.parse(line);
}
@Override
@@ -72,10 +62,6 @@ public class MetricRequest implements TcpRequest, HttpPostRequest, WebSocketRequ
return metric.toString();
}
public String getLine() {
return line;
}
@Override
public boolean equals(Object o) {
if (this == o)
@@ -93,12 +79,4 @@ public class MetricRequest implements TcpRequest, HttpPostRequest, WebSocketRequ
public int hashCode() {
return metric != null ? metric.hashCode() : 0;
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.annotation.Http;
import timely.api.annotation.Tcp;
@@ -11,7 +10,6 @@ import timely.api.annotation.WebSocket;
@WebSocket(operation = "version")
public class VersionRequest implements TcpRequest, HttpGetRequest, HttpPostRequest, WebSocketRequest {
private FullHttpRequest httpRequest = null;
public static final String VERSION;
static {
String ver = VersionRequest.class.getPackage().getImplementationVersion();
@@ -42,11 +40,4 @@ public class VersionRequest implements TcpRequest, HttpGetRequest, HttpPostReque
return new VersionRequest();
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request.auth;
import io.netty.handler.codec.http.FullHttpRequest;
import timely.api.BasicAuthLogin;
import timely.api.annotation.Http;
import timely.api.request.HttpPostRequest;
@@ -9,8 +8,6 @@ import timely.util.JsonUtil;
@Http(path = "/login")
public class BasicAuthLoginRequest extends BasicAuthLogin implements HttpPostRequest {
private FullHttpRequest httpRequest = null;
@Override
public void validate() {
HttpPostRequest.super.validate();
@@ -29,21 +26,4 @@ public class BasicAuthLoginRequest extends BasicAuthLogin implements HttpPostReq
return JsonUtil.getObjectMapper().readValue(content, BasicAuthLoginRequest.class);
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
@@ -1,6 +1,5 @@
package timely.api.request.auth;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.annotation.Http;
import timely.api.request.HttpGetRequest;
@@ -8,18 +7,9 @@ import timely.api.request.HttpGetRequest;
@Http(path = "/login")
public class X509LoginRequest implements HttpGetRequest {
private FullHttpRequest httpRequest = null;
@Override
public HttpGetRequest parseQueryParameters(QueryStringDecoder decoder) throws Exception {
return new X509LoginRequest();
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.annotation.Http;
import timely.api.annotation.WebSocket;
@@ -14,8 +13,6 @@ import timely.api.request.WebSocketRequest;
public class AggregatorsRequest extends AuthenticatedRequest implements HttpGetRequest, HttpPostRequest,
WebSocketRequest {
private FullHttpRequest httpRequest = null;
@Override
public String toString() {
return "";
@@ -31,11 +28,4 @@ public class AggregatorsRequest extends AuthenticatedRequest implements HttpGetR
return new AggregatorsRequest();
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,11 +0,0 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import timely.api.request.Request;
public interface HttpRequest extends Request {
public void setHttpRequest(FullHttpRequest httpRequest);
public FullHttpRequest getHttpRequest();
}
@@ -1,6 +1,5 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import timely.api.annotation.Http;
import timely.api.annotation.WebSocket;
@@ -13,8 +12,6 @@ import timely.api.request.WebSocketRequest;
@WebSocket(operation = "metrics")
public class MetricsRequest extends AuthenticatedRequest implements HttpGetRequest, HttpPostRequest, WebSocketRequest {
private FullHttpRequest httpRequest = null;
@Override
public HttpGetRequest parseQueryParameters(QueryStringDecoder decoder) throws Exception {
return new MetricsRequest();
@@ -25,11 +22,4 @@ public class MetricsRequest extends AuthenticatedRequest implements HttpGetReque
return new MetricsRequest();
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.ArrayList;
@@ -367,8 +366,6 @@ public class QueryRequest extends AuthenticatedRequest implements HttpGetRequest
private boolean showSummary = false;
private boolean showQuery = false;
private boolean delete = false;
@JsonIgnore
private FullHttpRequest httpRequest = null;
public boolean isGlobalAnnotations() {
return globalAnnotations;
@@ -616,12 +613,4 @@ public class QueryRequest extends AuthenticatedRequest implements HttpGetRequest
}
return query;
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.ArrayList;
@@ -34,8 +33,6 @@ public class SearchLookupRequest extends AuthenticatedRequest implements HttpGet
@JsonProperty("tags")
private Collection<Tag> tags = new ArrayList<>();
private FullHttpRequest httpRequest = null;
public String getQuery() {
return query;
}
@@ -145,11 +142,4 @@ public class SearchLookupRequest extends AuthenticatedRequest implements HttpGet
return search;
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,6 +1,5 @@
package timely.api.request.timeseries;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.ArrayList;
@@ -25,7 +24,6 @@ import com.fasterxml.jackson.databind.JsonNode;
public class SuggestRequest extends AuthenticatedRequest implements HttpGetRequest, HttpPostRequest, WebSocketRequest {
private static List<String> validTypes = new ArrayList<>();
private FullHttpRequest httpRequest = null;
static {
validTypes.add("metrics");
validTypes.add("tagk");
@@ -106,11 +104,4 @@ public class SuggestRequest extends AuthenticatedRequest implements HttpGetReque
return suggest;
}
public void setHttpRequest(FullHttpRequest httpRequest) {
this.httpRequest = httpRequest;
}
public FullHttpRequest getHttpRequest() {
return httpRequest;
}
}
@@ -1,46 +0,0 @@
package timely.api.response;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.util.List;
public class CacheResponse {
private Long oldestTimestamp;
private Long newestTimestamp;
private List<String> metrics;
public Long getOldestTimestamp() {
return oldestTimestamp;
}
public void setOldestTimestamp(Long oldestTimestamp) {
this.oldestTimestamp = oldestTimestamp;
}
public Long getNewestTimestamp() {
return newestTimestamp;
}
public void setNewestTimestamp(Long newestTimestamp) {
this.newestTimestamp = newestTimestamp;
}
public List<String> getMetrics() {
return metrics;
}
public void setMetrics(List<String> metrics) {
this.metrics = metrics;
}
@Override
public String toString() {
ToStringBuilder tsb = new ToStringBuilder(this);
tsb.append("oldestTimestamp", this.oldestTimestamp);
tsb.append("newestTimestamp", this.newestTimestamp);
tsb.append("metrics", this.metrics);
return tsb.toString();
}
}
@@ -2,9 +2,9 @@ package timely.api.response.timeseries;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -13,7 +13,7 @@ public class QueryResponse {
private String metric;
private Map<String, String> tags = new HashMap<>();
private List<String> aggregatedTags = new ArrayList<>();
private Map<String, Object> dps = new LinkedHashMap<>();
private Map<String, Object> dps = new TreeMap<>();
public String getMetric() {
return metric;
@@ -69,4 +69,4 @@ public class QueryResponse {
return tsb.toString();
}
}
}
@@ -1,43 +0,0 @@
package timely.netty.http;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import timely.api.request.CacheRequest;
import timely.api.response.TimelyException;
import timely.netty.Constants;
import timely.store.cache.DataStoreCache;
import timely.util.JsonUtil;
public class HttpCacheRequestHandler extends SimpleChannelInboundHandler<CacheRequest> implements TimelyHttpHandler {
private DataStoreCache cache = null;
public HttpCacheRequestHandler(DataStoreCache cache) {
this.cache = cache;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, CacheRequest v) throws Exception {
byte[] buf;
try {
buf = JsonUtil.getObjectMapper().writeValueAsBytes(cache.getCacheStatus());
} catch (Exception e) {
LOG.error(e.getMessage(), e);
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
"Error getting cache status", "Error getting cache status", e));
return;
}
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response);
}
}
@@ -22,7 +22,6 @@ import timely.api.request.AuthenticatedRequest;
import timely.api.request.HttpGetRequest;
import timely.api.request.HttpPostRequest;
import timely.api.request.Request;
import timely.api.request.timeseries.HttpRequest;
import timely.api.response.StrictTransportResponse;
import timely.api.response.TimelyException;
import timely.auth.AuthCache;
@@ -80,7 +79,7 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
final String sessionId = getSessionId(msg, this.anonymousAccessAllowed);
LOG.trace("SessionID: " + sessionId);
HttpRequest request;
Request request;
try {
if (msg.getMethod().equals(HttpMethod.GET)) {
HttpGetRequest get = AnnotationResolver.getClassForHttpGet(decoder.path());
@@ -104,7 +103,6 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
((AuthenticatedRequest) request).setSessionId(sessionId);
((AuthenticatedRequest) request).addHeaders(msg.headers().entries());
}
request.setHttpRequest(msg.copy());
LOG.trace(LOG_PARSED_REQUEST, request);
request.validate();
out.add(request);
@@ -1,21 +0,0 @@
package timely.netty.http.auth;
import io.netty.handler.codec.http.HttpHeaders;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import java.security.cert.X509Certificate;
public class TimelyPreAuthenticatedAuthenticationToken extends PreAuthenticatedAuthenticationToken {
private HttpHeaders httpHeaders = null;
public TimelyPreAuthenticatedAuthenticationToken(String subjectDN, X509Certificate clientCert,
HttpHeaders httpHeaders) {
super(subjectDN, clientCert);
this.httpHeaders = httpHeaders;
}
public HttpHeaders getHttpHeaders() {
return httpHeaders;
}
}
@@ -1,7 +1,6 @@
package timely.netty.http.auth;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.ssl.SslHandler;
import java.security.cert.X509Certificate;
@@ -27,9 +26,7 @@ public class X509LoginRequestHandler extends TimelyLoginRequestHandler<X509Login
if (null != sslHandler) {
X509Certificate clientCert = (X509Certificate) sslHandler.engine().getSession().getPeerCertificates()[0];
String subjectDN = AuthenticationService.extractDN(clientCert);
HttpHeaders httpHeaders = loginRequest.getHttpRequest().headers();
TimelyPreAuthenticatedAuthenticationToken token = new TimelyPreAuthenticatedAuthenticationToken(subjectDN,
clientCert, httpHeaders);
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(subjectDN, clientCert);
return AuthenticationService.getAuthenticationManager().authenticate(token);
} else {
throw new IllegalStateException("The expected SSL handler is not in the pipeline.");
@@ -141,10 +141,6 @@ public class Aggregation implements Iterable<Sample>, Serializable {
return result;
}
public void clear() {
buckets.clear();
}
public int getNumBuckets() {
return buckets.size();
}
@@ -104,8 +104,6 @@ public class AggregationIterator extends WrappingIterator {
return new Value(bos.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
aggregation.clear();
}
}
@@ -5,11 +5,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.data.Key;
@@ -18,41 +14,124 @@ import org.apache.accumulo.core.iterators.IteratorEnvironment;
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
import org.apache.accumulo.core.iterators.WrappingIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.log4j.Logger;
import timely.adapter.accumulo.MetricAdapter;
import timely.api.request.timeseries.QueryRequest;
import timely.model.Metric;
import timely.model.ObjectSizeOf;
import timely.model.Tag;
import timely.api.response.TimelyException;
import timely.sample.Aggregator;
import timely.sample.Downsample;
import timely.sample.DownsampleFactory;
import timely.sample.aggregators.Avg;
import static org.apache.accumulo.core.conf.AccumuloConfiguration.getTimeInMillis;
public class DownsampleIterator extends WrappingIterator {
private static final Logger LOG = LoggerFactory.getLogger(DownsampleIterator.class);
private static final String START = "downsample.start";
private static final String END = "downsample.end";
private static final String PERIOD = "downsample.period";
private static final String MAX_DOWNSAMPLE_MEMORY = "downsample.maxDownsampleMemory";
private static final String AGGCLASS = "downsample.aggclass";
private static final long DEFAULT_DOWNSAMPLE_MS = 1;
private static final String DEFAULT_DOWNSAMPLE_AGGREGATOR = Avg.class.getSimpleName().toLowerCase();
private DownsampleFactory factory;
private final Map<Set<Tag>, Downsample> value = new HashMap<>();
private long start;
private long end;
private long period;
private Key last;
private MemoryEstimator memoryEstimator = null;
private DownsampleMemoryEstimator memoryEstimator = null;
static public class MemoryEstimator {
private boolean newBucket = false;
private long start;
private long period;
private long startOfCurrentBucket;
private long bucketsCompleted = 0;
private long bytesPerBucket = 0;
boolean highVolumeBuckets = false;
long maxDownsampleMemory = 0; // max aggregation memory (bytes) before
// current batch is returned (after
// bucket is complete)
LinkedList<Long> percentageChecks = new LinkedList<>();
public void reset() {
newBucket = false;
bucketsCompleted = 0;
bytesPerBucket = 0;
percentageChecks.add(5l);
percentageChecks.add(25l);
percentageChecks.add(50l);
percentageChecks.add(75l);
}
public MemoryEstimator(long maxDownsampleMemory, long start, long period) {
this.maxDownsampleMemory = maxDownsampleMemory;
this.start = start;
this.period = period;
this.startOfCurrentBucket = this.start;
reset();
}
public double getMemoryUsedPercentage() {
return (bucketsCompleted * bytesPerBucket) / (double) maxDownsampleMemory * 100;
}
public long getBucketsCompleted() {
return bucketsCompleted;
}
public long getBytesPerBucket() {
return bytesPerBucket;
}
public boolean isHighVolumeBuckets() {
return highVolumeBuckets;
}
public boolean shouldReturnBasedOnMemoryUsage(long timestamp, Object value) {
boolean shouldReturn = false;
if (maxDownsampleMemory >= 0) {
sample(timestamp);
if (isNewBucket()) {
bucketsCompleted++;
double memoryUsedPercentage = getMemoryUsedPercentage();
if (memoryUsedPercentage >= 100) {
shouldReturn = true;
} else {
boolean checkMemoryNow = false;
Long check = percentageChecks.peek();
if (check != null && memoryUsedPercentage >= check) {
checkMemoryNow = true;
percentageChecks.removeFirst();
}
// recalculate bytesPerBucket
if (bytesPerBucket == 0 || highVolumeBuckets || checkMemoryNow) {
long memoryUsed = ObjectSizeOf.Sizer.getObjectSize(value);
bytesPerBucket = memoryUsed / bucketsCompleted;
}
// bucket average greater than 10% of max
highVolumeBuckets = (bytesPerBucket / (double) maxDownsampleMemory) >= 0.1;
shouldReturn = false;
}
}
}
return shouldReturn;
}
public boolean isNewBucket() {
return newBucket;
}
private void sample(long timestamp) {
if (timestamp >= (startOfCurrentBucket + period)) {
newBucket = true;
startOfCurrentBucket = timestamp - ((timestamp - start) % period);
} else {
newBucket = false;
}
}
}
@SuppressWarnings("unchecked")
@Override
@@ -67,7 +146,7 @@ public class DownsampleIterator extends WrappingIterator {
if (options.containsKey(MAX_DOWNSAMPLE_MEMORY)) {
maxDownsampleMemory = Long.parseLong(options.get(MAX_DOWNSAMPLE_MEMORY));
}
memoryEstimator = new DownsampleMemoryEstimator(maxDownsampleMemory, start, period);
memoryEstimator = new MemoryEstimator(maxDownsampleMemory, start, period);
String aggClassname = options.get(AGGCLASS);
Class<?> aggClass;
@@ -86,29 +165,23 @@ public class DownsampleIterator extends WrappingIterator {
while (super.hasTop()) {
Key topKey = super.getTopKey();
Value topValue = super.getTopValue();
try {
Metric metric = MetricAdapter.parse(topKey, topValue);
long timestamp = metric.getValue().getTimestamp();
if (memoryEstimator.shouldReturnBasedOnMemoryUsage(timestamp, value)) {
LOG.trace("returning current values - memory usage > " + memoryEstimator.maxDownsampleMemory
+ " for metric=" + metric.toString());
break;
}
last = topKey;
Set<Tag> tags = new HashSet<Tag>(metric.getTags());
Downsample sample = value.get(tags);
if (sample == null) {
try {
value.put(tags, sample = factory.create());
} catch (TimelyException e) {
throw new RuntimeException(e);
}
}
sample.add(metric.getValue().getTimestamp(), metric.getValue().getMeasure());
} catch (Exception e) {
LOG.error("Error: {} parsing metric at key: {}", e.getMessage(), topKey.toString());
Metric metric = MetricAdapter.parse(topKey, topValue);
long timestamp = metric.getValue().getTimestamp();
if (memoryEstimator.shouldReturnBasedOnMemoryUsage(timestamp, value)) {
break;
}
last = topKey;
Set<Tag> tags = new HashSet<Tag>(metric.getTags());
Downsample sample = value.get(tags);
if (sample == null) {
try {
value.put(tags, sample = factory.create());
} catch (TimelyException e) {
throw new RuntimeException(e);
}
}
sample.add(metric.getValue().getTimestamp(), metric.getValue().getMeasure());
try {
super.next();
} catch (IOException e) {
@@ -163,30 +236,4 @@ public class DownsampleIterator extends WrappingIterator {
ObjectInputStream ois = new ObjectInputStream(bis);
return (Map<Set<Tag>, Downsample>) ois.readObject();
}
public static long getDownsamplePeriod(QueryRequest.SubQuery query) {
// disabling the downsampling OR setting the aggregation to none are
// both considered to be disabling
if (!query.getDownsample().isPresent() || query.getDownsample().get().endsWith("-none")) {
return DEFAULT_DOWNSAMPLE_MS;
}
String parts[] = query.getDownsample().get().split("-");
return getTimeInMillis(parts[0]);
}
public static Class<? extends Aggregator> getDownsampleAggregator(QueryRequest.SubQuery query) {
String aggregatorName = Aggregator.NONE;
if (query.getDownsample().isPresent()) {
String parts[] = query.getDownsample().get().split("-");
aggregatorName = parts[1];
}
// disabling the downsampling OR setting the aggregation to none are
// both considered to be disabling
if (aggregatorName.equals(Aggregator.NONE)) {
// we need a downsampling iterator, so default to max to ensure we
// return something
aggregatorName = DEFAULT_DOWNSAMPLE_AGGREGATOR;
}
return Aggregator.getAggregator(aggregatorName);
}
}
@@ -1,101 +0,0 @@
package timely.sample.iterators;
import timely.model.ObjectSizeOf;
import java.util.LinkedList;
public class DownsampleMemoryEstimator {
private boolean newBucket = false;
private long start;
private long period;
private long startOfCurrentBucket;
private long bucketsCompleted = 0;
private long bytesPerBucket = 0;
boolean highVolumeBuckets = false;
long maxDownsampleMemory = 0; // max aggregation memory (bytes) before
// current batch is returned (after bucket is
// complete)
LinkedList<Long> percentageChecks = new LinkedList<>();
public void reset() {
newBucket = false;
bucketsCompleted = 0;
bytesPerBucket = 0;
percentageChecks.clear();
percentageChecks.add(1l);
percentageChecks.add(5l);
percentageChecks.add(25l);
percentageChecks.add(50l);
percentageChecks.add(75l);
percentageChecks.add(95l);
}
public DownsampleMemoryEstimator(long maxDownsampleMemory, long start, long period) {
this.maxDownsampleMemory = maxDownsampleMemory;
this.start = start;
this.period = period;
this.startOfCurrentBucket = this.start;
reset();
}
public double getMemoryUsedPercentage() {
return (bucketsCompleted * bytesPerBucket) / (double) maxDownsampleMemory * 100;
}
public long getBucketsCompleted() {
return bucketsCompleted;
}
public long getBytesPerBucket() {
return bytesPerBucket;
}
public boolean isHighVolumeBuckets() {
return highVolumeBuckets;
}
public boolean shouldReturnBasedOnMemoryUsage(long timestamp, Object value) {
boolean shouldReturn = false;
if (maxDownsampleMemory >= 0) {
sample(timestamp);
if (isNewBucket()) {
bucketsCompleted++;
double memoryUsedPercentage = getMemoryUsedPercentage();
if (memoryUsedPercentage >= 100) {
shouldReturn = true;
} else {
boolean checkMemoryNow = false;
Long check = percentageChecks.peek();
if (check != null && memoryUsedPercentage >= check) {
checkMemoryNow = true;
percentageChecks.removeFirst();
}
// recalculate bytesPerBucket
if (bytesPerBucket < 100 || bucketsCompleted == 10 || highVolumeBuckets || checkMemoryNow) {
long memoryUsed = ObjectSizeOf.Sizer.getObjectSize(value);
bytesPerBucket = memoryUsed / bucketsCompleted;
}
// bucket average greater than 10% of max
highVolumeBuckets = (bytesPerBucket / (double) maxDownsampleMemory) >= 0.1;
shouldReturn = false;
}
}
}
return shouldReturn;
}
public boolean isNewBucket() {
return newBucket;
}
private void sample(long timestamp) {
if (timestamp >= (startOfCurrentBucket + period)) {
newBucket = true;
startOfCurrentBucket = timestamp - ((timestamp - start) % period);
} else {
newBucket = false;
}
}
}
@@ -2,12 +2,9 @@ package timely.store;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.data.Range;
import timely.model.Metric;
import timely.api.request.timeseries.QueryRequest;
import timely.api.request.timeseries.SearchLookupRequest;
@@ -17,10 +14,6 @@ import timely.api.response.timeseries.QueryResponse;
import timely.api.response.timeseries.SearchLookupResponse;
import timely.api.response.timeseries.SuggestResponse;
import timely.model.Tag;
import timely.store.cache.DataStoreCache;
public interface DataStore {
void store(Metric metric) throws TimelyException;
@@ -33,16 +26,7 @@ public interface DataStore {
void flush() throws TimelyException;
long getAgeOffForMetric(String metricName);
List<Range> getQueryRanges(String metric, long start, long end, Set<Tag> colFamValues);
Set<Tag> getColumnFamilies(String metric, Map<String, String> tags) throws TableNotFoundException;
Scanner createScannerForMetric(String sessionId, String metric, Map<String, String> tags, long startTime,
long endTime, int lag, int scannerBatchSize, int scannerReadAhead) throws TimelyException;
void setCache(DataStoreCache cache);
DataStoreCache getCache();
}
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more