From 76f4cf18bd68b71740554d61182e6e7b46758243 Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Fri, 4 Jan 2019 13:40:02 +0530 Subject: [PATCH 1/7] first code for mysql trace backend --- backends/mysql/Makefile | 24 +++ backends/mysql/README.md | 14 ++ backends/mysql/build/docker/Dockerfile | 25 +++ .../mysql/build/docker/jmxtrans-agent.xml | 30 ++++ backends/mysql/build/docker/start-app.sh | 21 +++ .../build/integration-tests/docker-app.conf | 39 +++++ .../integration-tests/docker-compose.yml | 9 + backends/mysql/pom.xml | 157 ++++++++++++++++++ .../mysql/src/main/resources/config/base.conf | 61 +++++++ backends/mysql/src/main/resources/logback.xml | 27 +++ .../storage/backends/mysql/Service.scala | 88 ++++++++++ .../mysql/config/ProjectConfiguration.scala | 125 ++++++++++++++ .../AwsNodeDiscoveryConfiguration.scala | 25 +++ .../config/entities/ClientConfiguration.scala | 61 +++++++ .../entities/CredentialsConfiguration.scala | 21 +++ .../entities/ServiceConfiguration.scala | 23 +++ .../config/entities/SocketConfiguration.scala | 23 +++ .../mysql/metrics/AppMetricNames.scala | 26 +++ .../backends/mysql/services/GrpcHandler.scala | 63 +++++++ .../mysql/services/GrpcHealthService.scala | 31 ++++ .../services/SpansPersistenceService.scala | 58 +++++++ .../mysql/store/MysqlTraceRecordReader.scala | 48 ++++++ .../mysql/store/MysqlTraceRecordWriter.scala | 86 ++++++++++ .../mysql/src/test/resources/config/base.conf | 39 +++++ .../mysql/src/test/resources/logback-test.xml | 1 + .../integration/BaseIntegrationTestSpec.scala | 66 ++++++++ ...ageBackendServiceIntegrationTestSpec.scala | 47 ++++++ .../mysql/unit/BaseUnitTestSpec.scala | 22 +++ .../unit/config/ConfigurationLoaderSpec.scala | 56 +++++++ backends/pom.xml | 1 + 30 files changed, 1317 insertions(+) create mode 100644 backends/mysql/Makefile create mode 100644 backends/mysql/README.md create mode 100644 backends/mysql/build/docker/Dockerfile create mode 100644 backends/mysql/build/docker/jmxtrans-agent.xml create mode 100755 backends/mysql/build/docker/start-app.sh create mode 100644 backends/mysql/build/integration-tests/docker-app.conf create mode 100644 backends/mysql/build/integration-tests/docker-compose.yml create mode 100644 backends/mysql/pom.xml create mode 100644 backends/mysql/src/main/resources/config/base.conf create mode 100644 backends/mysql/src/main/resources/logback.xml create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala create mode 100644 backends/mysql/src/test/resources/config/base.conf create mode 100644 backends/mysql/src/test/resources/logback-test.xml create mode 100644 backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala create mode 100644 backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala create mode 100644 backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala create mode 100644 backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala diff --git a/backends/mysql/Makefile b/backends/mysql/Makefile new file mode 100644 index 00000000..ed0f5c30 --- /dev/null +++ b/backends/mysql/Makefile @@ -0,0 +1,24 @@ +.PHONY: docker_build prepare_integration_test_env integration_test release + +export DOCKER_ORG := expediadotcom +export DOCKER_IMAGE_NAME := haystack-trace-backend-mysql +PWD := $(shell pwd) +SERVICE_DEBUG_ON ?= false + +docker_build: + # build docker image using existing app jar + docker build -t $(DOCKER_IMAGE_NAME) -f build/docker/Dockerfile . + +prepare_integration_test_env: docker_build + # prepare environment to run integration tests against + docker-compose -f build/integration-tests/docker-compose.yml -p sandbox up -d + sleep 30 + +integration_test: prepare_integration_test_env + cd ../../ &&./mvnw integration-test -pl backends/mysql -am + docker-compose -f build/integration-tests/docker-compose.yml -p sandbox stop + docker rm $(shell docker ps -a -q) + docker volume rm $(shell docker volume ls -q) + +release: + ../../deployment/scripts/publish-to-docker-hub.sh diff --git a/backends/mysql/README.md b/backends/mysql/README.md new file mode 100644 index 00000000..85931af6 --- /dev/null +++ b/backends/mysql/README.md @@ -0,0 +1,14 @@ +# Storage Backend - Mysql + + +Grpc service which can read a write spans to a mysql cluster + +##Technical Details + +In order to understand this service, we recommend to read the details of [haystack](https://github.com/ExpediaDotCom/haystack) project. +This service reads from [Mysql](https://www.mysql.com/). API endpoints are exposed as [GRPC](https://grpc.io/) endpoints. + +Will fill in more details as we go.. + +## Building +Check the details on [Build Section](../README.md) \ No newline at end of file diff --git a/backends/mysql/build/docker/Dockerfile b/backends/mysql/build/docker/Dockerfile new file mode 100644 index 00000000..f3292561 --- /dev/null +++ b/backends/mysql/build/docker/Dockerfile @@ -0,0 +1,25 @@ +FROM openjdk:8-jre +MAINTAINER Haystack + +ENV APP_NAME haystack-trace-backend-mysql +ENV APP_HOME /app/bin +ENV JMXTRANS_AGENT jmxtrans-agent-1.2.6 + +RUN mkdir -p ${APP_HOME} + +COPY target/${APP_NAME}.jar ${APP_HOME}/ +COPY build/docker/start-app.sh ${APP_HOME}/ +RUN chmod +x ${APP_HOME}/start-app.sh + +RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe + +COPY build/docker/jmxtrans-agent.xml ${APP_HOME}/ +ADD https://github.com/jmxtrans/jmxtrans-agent/releases/download/${JMXTRANS_AGENT}/${JMXTRANS_AGENT}.jar ${APP_HOME}/ + +WORKDIR ${APP_HOME} + +EXPOSE 8090 + +ENTRYPOINT ["./start-app.sh"] diff --git a/backends/mysql/build/docker/jmxtrans-agent.xml b/backends/mysql/build/docker/jmxtrans-agent.xml new file mode 100644 index 00000000..fb89d65a --- /dev/null +++ b/backends/mysql/build/docker/jmxtrans-agent.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + ${HAYSTACK_GRAPHITE_HOST:monitoring-influxdb-graphite.kube-system.svc} + ${HAYSTACK_GRAPHITE_PORT:2003} + ${HAYSTACK_GRAPHITE_ENABLED:true} + haystack.traces.backend-mysql.#hostname#. + + 30 + diff --git a/backends/mysql/build/docker/start-app.sh b/backends/mysql/build/docker/start-app.sh new file mode 100755 index 00000000..ba2c6556 --- /dev/null +++ b/backends/mysql/build/docker/start-app.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +[ -z "$JAVA_XMS" ] && JAVA_XMS=1024m +[ -z "$JAVA_XMX" ] && JAVA_XMX=1024m +[ -z "$JAVA_GC_OPTS" ] && JAVA_GC_OPTS="-XX:+UseG1GC" + +set -e +JAVA_OPTS="${JAVA_OPTS} \ +-javaagent:${APP_HOME}/${JMXTRANS_AGENT}.jar=${APP_HOME}/jmxtrans-agent.xml \ +${JAVA_GC_OPTS} \ +-Xmx${JAVA_XMX} \ +-Xms${JAVA_XMS} \ +-XX:+ExitOnOutOfMemoryError \ +-Dapplication.name=${APP_NAME} \ +-Dapplication.home=${APP_HOME}" + +if [[ -n "$SERVICE_DEBUG_ON" ]] && [[ "$SERVICE_DEBUG_ON" == true ]]; then + JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y" +fi + +exec java ${JAVA_OPTS} -jar "${APP_HOME}/${APP_NAME}.jar" diff --git a/backends/mysql/build/integration-tests/docker-app.conf b/backends/mysql/build/integration-tests/docker-app.conf new file mode 100644 index 00000000..9235503f --- /dev/null +++ b/backends/mysql/build/integration-tests/docker-app.conf @@ -0,0 +1,39 @@ +haystack.graphite.host = "monitoring-influxdb-graphite.kube-system.svc" + +service { + port = 8090 + ssl { + enabled = false + cert.path = "" + private.key.path = "" + } +} + +mysql { + # multiple endpoints can be provided as comma separated list + endpoints = "mysql" + + # if auto.discovery.enabled is true, we ignore the manually supplied endpoints(above) + auto.discovery { + enabled: false + ## optional AWS discovery + # aws: { + # region: "us-west-2" + # tags: { + # name: "cassandra" + # } + # } + } + + connections { + max.per.host = 10 + read.timeout.ms = 5000 + conn.timeout.ms = 10000 + keep.alive = true + } + + keyspace { + name = "haystack" + table.name = "traces" + } +} diff --git a/backends/mysql/build/integration-tests/docker-compose.yml b/backends/mysql/build/integration-tests/docker-compose.yml new file mode 100644 index 00000000..b812179e --- /dev/null +++ b/backends/mysql/build/integration-tests/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' +services: + mysql: + image: mysql:8.0.13 + environment: + MAX_HEAP_SIZE: 256m + HEAP_NEWSIZE: 256m + ports: + - "9042:9042" diff --git a/backends/mysql/pom.xml b/backends/mysql/pom.xml new file mode 100644 index 00000000..7dfc3536 --- /dev/null +++ b/backends/mysql/pom.xml @@ -0,0 +1,157 @@ + + + + + haystack-trace-backends + com.expedia.www + 1.0.0-SNAPSHOT + ../pom.xml + + + 4.0.0 + haystack-trace-backend-mysql + jar + + + com.expedia.www.haystack.trace.storage.backends.mysql.Service + ${project.artifactId}-${project.version} + 3.3.0.1 + + + + + com.google.protobuf + protobuf-java + + + + io.grpc + grpc-protobuf + + + + io.grpc + grpc-stub + + + + io.grpc + grpc-services + + + + io.grpc + grpc-netty + + + + io.netty + netty-handler + + + + org.apache.commons + commons-lang3 + + + + org.apache.httpcomponents + httpclient + + + + com.amazonaws + aws-java-sdk-ec2 + + + + + + ${finalName} + + + org.scalatest + scalatest-maven-plugin + + + test + + test + + + com.expedia.www.haystack.trace.reader.unit + + + + integration-test + integration-test + + test + + + + /src/reader/build/integration-tests/docker-app.conf + + + com.expedia.www.haystack.trace.reader.integration + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + reference.conf + + + ${mainClass} + + + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + org.scalastyle + scalastyle-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + diff --git a/backends/mysql/src/main/resources/config/base.conf b/backends/mysql/src/main/resources/config/base.conf new file mode 100644 index 00000000..511879e4 --- /dev/null +++ b/backends/mysql/src/main/resources/config/base.conf @@ -0,0 +1,61 @@ +health.status.path = "/app/isHealthy" + +service { + port = 8090 + ssl { + enabled = false + cert.path = "" + private.key.path = "" + } +} + +cassandra { + # multiple endpoints can be provided as comma separated list + endpoints = "cassandra" + + # enable the auto.discovery mode, if true then we ignore the endpoints(above) and use auto discovery + # mechanism to find cassandra nodes. For today we only support aws node discovery provider + auto.discovery { + enabled: false + // aws: { + // region: "us-west-2" + // tags: { + // Role: haystack-cassandra + // Environment: ewetest + // } + // } + } + + connections { + max.per.host = 50 + read.timeout.ms = 30000 + conn.timeout.ms = 10000 + keep.alive = true + } + + retries { + max = 10 + backoff { + initial.ms = 100 + factor = 2 + } + } + + consistency.level = "one" + + on.error.consistency.level = [ + "com.datastax.driver.core.exceptions.UnavailableException", + "any" + ] + + ttl.sec = 259200 + + keyspace: { + # auto creates the keyspace and table name in cassandra(if absent) + # if schema field is empty or not present, then no operation is performed + auto.create.schema = "CREATE KEYSPACE IF NOT EXISTS haystack WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes = false; CREATE TABLE IF NOT EXISTS haystack.traces (id varchar, ts timestamp, spans blob, PRIMARY KEY ((id), ts)) WITH CLUSTERING ORDER BY (ts ASC) AND compaction = { 'class' : 'DateTieredCompactionStrategy', 'max_sstable_age_days': '3' } AND gc_grace_seconds = 86400;" + + name: "haystack" + table.name: "traces" + } +} diff --git a/backends/mysql/src/main/resources/logback.xml b/backends/mysql/src/main/resources/logback.xml new file mode 100644 index 00000000..dfaf958d --- /dev/null +++ b/backends/mysql/src/main/resources/logback.xml @@ -0,0 +1,27 @@ + + + + + + + true + + + + + + %d{yyyy-MM-dd HH:mm:ss:SSS} %thread, %level, %logger{70}, "%msg" %replace(%ex){'[\n]+', '\\n'}%nopex%n + + + + + + ${HAYSTACK_LOG_QUEUE_SIZE:-500} + ${HAYSTACK_LOG_DISCARD_THRESHOLD:-0} + + + + + + + \ No newline at end of file diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala new file mode 100644 index 00000000..bf38be06 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.expedia.www.haystack.trace.storage.backends.mysql + +import java.io.File + +import com.codahale.metrics.JmxReporter +import com.expedia.www.haystack.commons.logger.LoggerUtils +import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.trace.storage.backends.mysql.config.ProjectConfiguration +import com.expedia.www.haystack.trace.storage.backends.mysql.services.{GrpcHealthService, SpansPersistenceService} +import com.expedia.www.haystack.trace.storage.backends.mysql.store.{MysqlTraceRecordReader, MysqlTraceRecordWriter} +import io.grpc.netty.NettyServerBuilder +import org.slf4j.{Logger, LoggerFactory} + +object Service extends MetricsSupport { + private val LOGGER: Logger = LoggerFactory.getLogger("MysqlBackend") + + // primary executor for service's async tasks + implicit private val executor = scala.concurrent.ExecutionContext.global + + def main(args: Array[String]): Unit = { + startJmxReporter() + startService() + } + + private def startJmxReporter(): Unit = { + JmxReporter + .forRegistry(metricRegistry) + .build() + .start() + } + + private def startService(): Unit = { + try { + val config = new ProjectConfiguration + val serviceConfig = config.serviceConfig + + val tracerRecordWriter = new MysqlTraceRecordWriter(config.mysqlConfig) + val tracerRecordReader = new MysqlTraceRecordReader(config.mysqlConfig.clientConfig) + + val serverBuilder = NettyServerBuilder + .forPort(serviceConfig.port) + .directExecutor() + .addService(new GrpcHealthService()) + .addService(new SpansPersistenceService(reader = tracerRecordReader, writer = tracerRecordWriter)(executor)) + + // enable ssl if enabled + if (serviceConfig.ssl.enabled) { + serverBuilder.useTransportSecurity(new File(serviceConfig.ssl.certChainFilePath), new File(serviceConfig.ssl.privateKeyPath)) + } + + val server = serverBuilder.build().start() + + LOGGER.info(s"server started, listening on ${serviceConfig.port}") + + Runtime.getRuntime.addShutdownHook(new Thread() { + override def run(): Unit = { + LOGGER.info("shutting down gRPC server since JVM is shutting down") + server.shutdown() + LOGGER.info("server has been shutdown now") + } + }) + + server.awaitTermination() + } catch { + case ex: Throwable => + ex.printStackTrace() + LOGGER.error("Fatal error observed while running the app", ex) + LoggerUtils.shutdownLogger() + System.exit(1) + } + } +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala new file mode 100644 index 00000000..44ea988e --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala @@ -0,0 +1,125 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config + +import com.datastax.driver.core.ConsistencyLevel +import com.expedia.www.haystack.commons.config.ConfigurationLoader +import com.expedia.www.haystack.commons.retries.RetryOperation +import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities._ +import com.typesafe.config.Config +import org.apache.commons.lang3.StringUtils + +import scala.collection.JavaConverters._ + +class ProjectConfiguration { + private val config = ConfigurationLoader.loadConfigFileWithEnvOverrides() + + val healthStatusFilePath: String = config.getString("health.status.path") + + val serviceConfig: ServiceConfiguration = { + val serviceConfig = config.getConfig("service") + + val ssl = serviceConfig.getConfig("ssl") + val sslConfig = SslConfiguration(ssl.getBoolean("enabled"), ssl.getString("cert.path"), ssl.getString("private.key.path")) + + ServiceConfiguration(serviceConfig.getInt("port"), sslConfig) + } + /** + * + * cassandra configuration object + */ + val mysqlConfig: MysqlConfiguration = { + + def toConsistencyLevel(level: String) = ConsistencyLevel.values().find(_.toString.equalsIgnoreCase(level)).get + + def consistencyLevelOnErrors(cs: Config) = { + val consistencyLevelOnErrors = cs.getStringList("on.error.consistency.level") + val consistencyLevelOnErrorList = scala.collection.mutable.ListBuffer[(Class[_], ConsistencyLevel)]() + + var idx = 0 + while (idx < consistencyLevelOnErrors.size()) { + val errorClass = consistencyLevelOnErrors.get(idx) + val level = consistencyLevelOnErrors.get(idx + 1) + consistencyLevelOnErrorList.+=((Class.forName(errorClass), toConsistencyLevel(level))) + idx = idx + 2 + } + + consistencyLevelOnErrorList.toList + } + + def keyspaceConfig(kConfig: Config, ttl: Int): KeyspaceConfiguration = { + val autoCreateSchemaField = "auto.create.schema" + val autoCreateSchema = if (kConfig.hasPath(autoCreateSchemaField) + && StringUtils.isNotEmpty(kConfig.getString(autoCreateSchemaField))) { + Some(kConfig.getString(autoCreateSchemaField)) + } else { + None + } + + KeyspaceConfiguration(kConfig.getString("name"), kConfig.getString("table.name"), ttl, autoCreateSchema) + } + + val cs = config.getConfig("cassandra") + + val awsConfig: Option[AwsNodeDiscoveryConfiguration] = + if (cs.hasPath("auto.discovery.aws")) { + val aws = cs.getConfig("auto.discovery.aws") + val tags = aws.getConfig("tags") + .entrySet() + .asScala + .map(elem => elem.getKey -> elem.getValue.unwrapped().toString) + .toMap + Some(AwsNodeDiscoveryConfiguration(aws.getString("region"), tags)) + } else { + None + } + + val credentialsConfig: Option[CredentialsConfiguration] = + if (cs.hasPath("credentials")) { + Some(CredentialsConfiguration(cs.getString("credentials.username"), cs.getString("credentials.password"))) + } else { + None + } + + val socketConfig = cs.getConfig("connections") + + val socket = SocketConfiguration( + socketConfig.getInt("max.per.host"), + socketConfig.getBoolean("keep.alive"), + socketConfig.getInt("conn.timeout.ms"), + socketConfig.getInt("read.timeout.ms")) + + val consistencyLevel = cs.getString("consistency.level") + + MysqlConfiguration( + clientConfig = ClientConfiguration( + if (cs.hasPath("endpoints")) cs.getString("endpoints").split(",").toList else Nil, + cs.getBoolean("auto.discovery.enabled"), + awsConfig, + credentialsConfig, + keyspaceConfig(cs.getConfig("keyspace"), cs.getInt("ttl.sec")), + socket), + consistencyLevel = consistencyLevel, + retryConfig = RetryOperation.Config( + cs.getInt("retries.max"), + cs.getLong("retries.backoff.initial.ms"), + cs.getDouble("retries.backoff.factor")), + consistencyLevelOnErrors(cs)) + } + +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala new file mode 100644 index 00000000..e076026a --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala @@ -0,0 +1,25 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities + +/** + * defines the parameters required for aws discovery + * @param region aws region e.g. us-east-1, us-west-2 + * @param tags: ec2 tags + */ +case class AwsNodeDiscoveryConfiguration(region: String, tags: Map[String, String]) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala new file mode 100644 index 00000000..5b487922 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities + +import com.expedia.www.haystack.commons.retries.RetryOperation +import org.apache.commons.lang3.StringUtils + + +/** define the keyspace and table information in cassandra + * + * @param name : name of cassandra keyspace + * @param table : name of cassandra table + * @param recordTTLInSec : ttl of record in sec + * @param autoCreateSchema : apply cql and create keyspace and tables if not exist, optional + */ +case class KeyspaceConfiguration(name: String, + table: String, + recordTTLInSec: Int = -1, + autoCreateSchema: Option[String] = None) { + require(StringUtils.isNotEmpty(name)) + require(StringUtils.isNotEmpty(table)) +} + +/** + * defines the configuration parameters for cassandra client + * + * @param endpoints : list of cassandra endpoints + * @param autoDiscoverEnabled : if autodiscovery is enabled, then 'endpoints' config parameter will be ignored + * @param awsNodeDiscovery : discovery configuration for aws, optional. This is applied only if autoDiscoverEnabled is true + * @param tracesKeyspace : cassandra keyspace for traces + * @param socket : socket configuration like maxConnections, timeouts and keepAlive + */ +case class ClientConfiguration(endpoints: List[String], + autoDiscoverEnabled: Boolean, + awsNodeDiscovery: Option[AwsNodeDiscoveryConfiguration], + plaintextCredentials: Option[CredentialsConfiguration], + tracesKeyspace: KeyspaceConfiguration, + socket: SocketConfiguration) + +/** + * @param retryConfig retry configuration if writes fail + */ +case class MysqlConfiguration(clientConfig: ClientConfiguration, + retryConfig: RetryOperation.Config) { + +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala new file mode 100644 index 00000000..fd4648db --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities + +case class CredentialsConfiguration(username: String, + password: String) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala new file mode 100644 index 00000000..f00ef2f6 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala @@ -0,0 +1,23 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities + +/** + * @param port port to start grpc servicer on + */ +case class ServiceConfiguration(port: Int, ssl: SslConfiguration) +case class SslConfiguration(enabled: Boolean, certChainFilePath: String, privateKeyPath: String) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala new file mode 100644 index 00000000..bd80af7a --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala @@ -0,0 +1,23 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities + +case class SocketConfiguration(maxConnectionPerHost: Int, + keepAlive: Boolean, + connectionTimeoutMillis: Int, + readTimeoutMills: Int) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala new file mode 100644 index 00000000..ed004a5c --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.metrics + +object AppMetricNames { + val MYSQL_READ_TIME = "mysql.read.time" + val MYSQL_READ_FAILURES = "mysql.read.failures" + val MYSQL_WRITE_TIME = "mysql.write.time" + val MYSQL_WRITE_FAILURE = "mysql.write.failure" + val MYSQL_WRITE_WARNINGS = "mysql.write.warnings" +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala new file mode 100644 index 00000000..cc1c5674 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala @@ -0,0 +1,63 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.services + +import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.trace.storage.backends.mysql.services.GrpcHandler._ +import com.google.protobuf.GeneratedMessageV3 +import io.grpc.Status +import io.grpc.stub.StreamObserver +import org.slf4j.{Logger, LoggerFactory} + +import scala.concurrent.{ExecutionContextExecutor, Future} +import scala.util.{Failure, Success} + +object GrpcHandler { + protected val LOGGER: Logger = LoggerFactory.getLogger(classOf[GrpcHandler]) +} + +/** + * Handler for Grpc response + * populates responseObserver with response object or error accordingly + * takes care of corresponding logging and updating counters + * + * @param operationName : name of operation + * @param executor : executor service on which handler is invoked + */ + +class GrpcHandler(operationName: String)(implicit val executor: ExecutionContextExecutor) extends MetricsSupport { + private val metricFriendlyOperationName = operationName.replace('/', '.') + private val timer = metricRegistry.timer(metricFriendlyOperationName) + private val failureMeter = metricRegistry.meter(s"$metricFriendlyOperationName.failures") + + def handle[Rs](request: GeneratedMessageV3, responseObserver: StreamObserver[Rs])(op: => Future[Rs]): Unit = { + val time = timer.time() + op onComplete { + case Success(response) => + responseObserver.onNext(response) + responseObserver.onCompleted() + time.stop() + LOGGER.debug(s"service invocation for operation=$operationName and request=${request.toString} completed successfully") + + case Failure(ex) => + responseObserver.onError(Status.fromThrowable(ex).asRuntimeException()) + failureMeter.mark() + time.stop() + LOGGER.debug(s"service invocation for operation=$operationName and request=${request.toString} failed with error", ex) + } + } +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala new file mode 100644 index 00000000..400adc13 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2018 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.services + +import io.grpc.health.v1.{HealthCheckRequest, HealthCheckResponse, HealthGrpc} +import io.grpc.stub.StreamObserver + +class GrpcHealthService extends HealthGrpc.HealthImplBase { + + override def check(request: HealthCheckRequest, responseObserver: StreamObserver[HealthCheckResponse]): Unit = { + responseObserver.onNext(HealthCheckResponse + .newBuilder() + .setStatus(HealthCheckResponse.ServingStatus.SERVING) + .build()) + responseObserver.onCompleted() + } +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala new file mode 100644 index 00000000..34e22562 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala @@ -0,0 +1,58 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.services + +import com.expedia.open.tracing.backend.WriteSpansResponse.ResultCode +import com.expedia.open.tracing.backend._ +import com.expedia.www.haystack.trace.storage.backends.mysql.store.{MysqlTraceRecordReader, MysqlTraceRecordWriter} +import io.grpc.stub.StreamObserver + +import scala.collection.JavaConverters._ +import scala.concurrent.ExecutionContextExecutor + +class SpansPersistenceService(reader: MysqlTraceRecordReader, + writer: MysqlTraceRecordWriter) + (implicit val executor: ExecutionContextExecutor) extends StorageBackendGrpc.StorageBackendImplBase { + + private val handleReadSpansResponse = new GrpcHandler(StorageBackendGrpc.METHOD_READ_SPANS.getFullMethodName) + private val handleWriteSpansResponse = new GrpcHandler(StorageBackendGrpc.METHOD_WRITE_SPANS.getFullMethodName) + + override def writeSpans(request: WriteSpansRequest, responseObserver: StreamObserver[WriteSpansResponse]): Unit = { + handleWriteSpansResponse.handle(request, responseObserver) { + writer.writeTraceRecords(request.getRecordsList.asScala.toList) map (_ => + WriteSpansResponse.newBuilder().setCode(ResultCode.SUCCESS).build()) + } + } + + /** + *
+    * read buffered spans from backend
+    * 
+ */ + override def readSpans(request: ReadSpansRequest, responseObserver: StreamObserver[ReadSpansResponse]): Unit = { + + handleReadSpansResponse.handle(request, responseObserver) { + reader.readTraceRecords(request.getTraceIdsList.iterator().asScala.toList).map { + records => { + ReadSpansResponse.newBuilder() + .addAllRecords(records.asJava) + .build() + } + } + } + } +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala new file mode 100644 index 00000000..62405599 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala @@ -0,0 +1,48 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.store + +import com.expedia.open.tracing.backend.TraceRecord +import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.ClientConfiguration +import com.expedia.www.haystack.trace.storage.backends.mysql.metrics.AppMetricNames +import org.slf4j.LoggerFactory + +import scala.concurrent.{ExecutionContextExecutor, Future, Promise} + +class MysqlTraceRecordReader(config: ClientConfiguration) + (implicit val dispatcher: ExecutionContextExecutor) extends MetricsSupport { + private val LOGGER = LoggerFactory.getLogger(classOf[MysqlTraceRecordReader]) + + private lazy val readTimer = metricRegistry.timer(AppMetricNames.MYSQL_READ_TIME) + private lazy val readFailures = metricRegistry.meter(AppMetricNames.MYSQL_READ_FAILURES) + + def readTraceRecords(traceIds: List[String]): Future[Seq[TraceRecord]] = { + val timer = readTimer.time() + val promise = Promise[Seq[TraceRecord]] + + try { + promise.future + } catch { + case ex: Exception => + readFailures.mark() + timer.stop() + LOGGER.error("Failed to read raw traces with exception", ex) + Future.failed(ex) + } + } +} \ No newline at end of file diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala new file mode 100644 index 00000000..38671f35 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala @@ -0,0 +1,86 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.store + +import java.util.concurrent.atomic.AtomicInteger + +import com.expedia.open.tracing.backend.TraceRecord +import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.commons.retries.RetryOperation._ +import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.MysqlConfiguration +import com.expedia.www.haystack.trace.storage.backends.mysql.metrics.AppMetricNames +import org.slf4j.LoggerFactory + +import scala.concurrent.{ExecutionContextExecutor, Future, Promise} +import scala.util.{Failure, Success} + +class MysqlTraceRecordWriter(config: MysqlConfiguration)(implicit val dispatcher: ExecutionContextExecutor) + extends MetricsSupport { + + private val LOGGER = LoggerFactory.getLogger(classOf[MysqlTraceRecordWriter]) + private lazy val writeTimer = metricRegistry.timer(AppMetricNames.MYSQL_WRITE_TIME) + private lazy val writeFailures = metricRegistry.meter(AppMetricNames.MYSQL_WRITE_FAILURE) + + private def execute(record: TraceRecord): Future[Unit] = { + + val promise = Promise[Unit] + // execute the request async with retry + withRetryBackoff(retryCallback => { + val timer = writeTimer.time() + + // prepare the statement + + }, + config.retryConfig, + onSuccess = (_: Any) => promise.success(), + onFailure = ex => { + writeFailures.mark() + LOGGER.error(s"Fail to write to mysql after ${config.retryConfig.maxRetries} retry attempts for ${record.getTraceId}", ex) + promise.failure(ex) + }) + promise.future + } + + /** + * writes the traceId and its spans to mysql. Use the current timestamp as the sort key for the writes to same + * TraceId. Also if the parallel writes exceed the max inflight requests, then we block and this puts backpressure on + * upstream + * + * @param traceRecords : trace records which need to be written + * @return + */ + def writeTraceRecords(traceRecords: List[TraceRecord]): Future[Unit] = { + val promise = Promise[Unit] + val writableRecordsLatch = new AtomicInteger(traceRecords.size) + traceRecords.foreach(record => { + /* write spanBuffer for a given traceId */ + execute(record).onComplete { + case Success(_) => if (writableRecordsLatch.decrementAndGet() == 0) { + promise.success() + } + case Failure(ex) => + //TODO: We fail the response only if the last mysql write fails, ideally we should be failing if any of the mysql writes fail + if (writableRecordsLatch.decrementAndGet() == 0) { + promise.failure(ex) + } + } + }) + promise.future + + } +} diff --git a/backends/mysql/src/test/resources/config/base.conf b/backends/mysql/src/test/resources/config/base.conf new file mode 100644 index 00000000..907a05ed --- /dev/null +++ b/backends/mysql/src/test/resources/config/base.conf @@ -0,0 +1,39 @@ +haystack.graphite.host = "monitoring-influxdb-graphite.kube-system.svc" + +service { + port = 8090 + ssl { + enabled = false + cert.path = "" + private.key.path = "" + } +} + +cassandra { + # multiple endpoints can be provided as comma separated list + endpoints = "cassandra" + + # if auto.discovery.enabled is true, we ignore the manually supplied endpoints(above) + auto.discovery { + enabled: false + ## optional AWS discovery + # aws: { + # region: "us-west-2" + # tags: { + # name: "cassandra" + # } + # } + } + + connections { + max.per.host = 10 + read.timeout.ms = 5000 + conn.timeout.ms = 10000 + keep.alive = true + } + + keyspace { + name = "haystack" + table.name = "traces" + } +} diff --git a/backends/mysql/src/test/resources/logback-test.xml b/backends/mysql/src/test/resources/logback-test.xml new file mode 100644 index 00000000..298193e0 --- /dev/null +++ b/backends/mysql/src/test/resources/logback-test.xml @@ -0,0 +1 @@ + diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala new file mode 100644 index 00000000..31ed5824 --- /dev/null +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.integration + +import java.util.UUID +import java.util.concurrent.Executors + +import com.expedia.open.tracing.backend.{StorageBackendGrpc, TraceRecord} +import com.expedia.www.haystack.trace.storage.backends.mysql.Service +import com.google.protobuf.ByteString +import io.grpc.ManagedChannelBuilder +import io.grpc.health.v1.HealthGrpc +import org.scalatest._ + +trait BaseIntegrationTestSpec extends FunSpec with GivenWhenThen with Matchers with BeforeAndAfterAll with BeforeAndAfterEach { + protected var client: StorageBackendGrpc.StorageBackendBlockingStub = _ + + protected var healthCheckClient: HealthGrpc.HealthBlockingStub = _ + + private val executors = Executors.newSingleThreadExecutor() + + + override def beforeAll() { + + + + executors.submit(new Runnable { + override def run(): Unit = Service.main(null) + }) + + Thread.sleep(5000) + + client = StorageBackendGrpc.newBlockingStub(ManagedChannelBuilder.forAddress("localhost", 8090) + .usePlaintext(true) + .build()) + + healthCheckClient = HealthGrpc.newBlockingStub(ManagedChannelBuilder.forAddress("localhost", 8090) + .usePlaintext(true) + .build()) + } + + + protected def createTraceRecord(traceId: String = UUID.randomUUID().toString, + ): TraceRecord = { + val spans = "random span".getBytes + TraceRecord + .newBuilder() + .setTraceId(traceId) + .setTimestamp(System.currentTimeMillis()) + .setSpans(ByteString.copyFrom(spans)).build() + } +} diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala new file mode 100644 index 00000000..e9ff0262 --- /dev/null +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.integration + +import java.util.UUID + +import com.expedia.open.tracing.backend.{ReadSpansRequest, WriteSpansRequest} + +class StorageBackendServiceIntegrationTestSpec extends BaseIntegrationTestSpec { + + + describe("Mysql Persistence Service read trace records") { + + it("should read and write trace records for given traceID") { + Given("trace") + val traceId = UUID.randomUUID().toString + val record = createTraceRecord(traceId) + val writeSpansRequest = WriteSpansRequest.newBuilder().addRecords(record).build() + + When("writespans is invoked") + val traceRecords = client.writeSpans(writeSpansRequest) + + Then("should write the trace") + val readSpansRequest = ReadSpansRequest.newBuilder().addTraceIds(traceId).build() + val retrievedRecord = client.readSpans(readSpansRequest) + + retrievedRecord.getRecordsList should not be empty + retrievedRecord.getRecordsCount shouldEqual 1 + retrievedRecord.getRecordsList.get(0).getTraceId shouldEqual traceId + } + + } +} diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala new file mode 100644 index 00000000..4afe61f4 --- /dev/null +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala @@ -0,0 +1,22 @@ +/* + * Copyright 2018 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.unit + +import org.scalatest.{FunSpec, GivenWhenThen, Matchers} +import org.scalatest.easymock.EasyMockSugar + +trait BaseUnitTestSpec extends FunSpec with GivenWhenThen with Matchers with EasyMockSugar diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala new file mode 100644 index 00000000..1611ceee --- /dev/null +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala @@ -0,0 +1,56 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.expedia.www.haystack.trace.storage.backends.mysql.unit.config + +import com.datastax.driver.core.ConsistencyLevel +import com.datastax.driver.core.exceptions.UnavailableException +import com.expedia.www.haystack.trace.storage.backends.mysql.config.ProjectConfiguration +import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.ServiceConfiguration +import com.expedia.www.haystack.trace.storage.backends.mysql.unit.BaseUnitTestSpec + +class ConfigurationLoaderSpec extends BaseUnitTestSpec { + describe("ConfigurationLoader") { + val project = new ProjectConfiguration() + it("should load the service config from base.conf") { + val serviceConfig: ServiceConfiguration = project.serviceConfig + serviceConfig.port shouldBe 8090 + serviceConfig.ssl.enabled shouldBe true + serviceConfig.ssl.certChainFilePath shouldBe "/ssl/cert" + serviceConfig.ssl.privateKeyPath shouldBe "/ssl/private-key" + } + it("should load the cassandra config from base.conf and few properties overridden from env variable") { + val cassandraWriteConfig = project.mysqlConfig + val clientConfig = cassandraWriteConfig.clientConfig + + clientConfig.autoDiscoverEnabled shouldBe false + // this will fail if run inside an editor, we override this config using env variable inside pom.xml + clientConfig.endpoints should contain allOf("cass1", "cass2") + clientConfig.tracesKeyspace.autoCreateSchema shouldBe Some("cassandra_cql_schema_1") + clientConfig.tracesKeyspace.name shouldBe "haystack" + clientConfig.tracesKeyspace.table shouldBe "traces" + clientConfig.tracesKeyspace.recordTTLInSec shouldBe 86400 + + clientConfig.awsNodeDiscovery shouldBe empty + clientConfig.socket.keepAlive shouldBe true + clientConfig.socket.maxConnectionPerHost shouldBe 100 + clientConfig.socket.readTimeoutMills shouldBe 5000 + clientConfig.socket.connectionTimeoutMillis shouldBe 10000 + cassandraWriteConfig.retryConfig.maxRetries shouldBe 10 + cassandraWriteConfig.retryConfig.backOffInMillis shouldBe 250 + cassandraWriteConfig.retryConfig.backoffFactor shouldBe 2 } + + } +} diff --git a/backends/pom.xml b/backends/pom.xml index cdc66a93..0264e8bd 100644 --- a/backends/pom.xml +++ b/backends/pom.xml @@ -18,6 +18,7 @@ cassandra memory + mysql From e423f59eaba4f09a8f8e0b7d4b3fb8c2524e905a Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Tue, 8 Jan 2019 17:36:57 +0530 Subject: [PATCH 2/7] first code for mysql trace backend --- .../build/integration-tests/docker-app.conf | 21 ++---- backends/mysql/pom.xml | 12 ++++ .../mysql/src/main/resources/config/base.conf | 38 +++------- .../mysql/client/SqlConnectionManager.scala | 35 ++++++++++ .../mysql/config/ProjectConfiguration.scala | 70 +++++-------------- .../AwsNodeDiscoveryConfiguration.scala | 25 ------- .../config/entities/ClientConfiguration.scala | 34 ++++----- .../mysql/src/test/resources/config/base.conf | 38 ++++------ .../unit/config/ConfigurationLoaderSpec.scala | 25 +++---- 9 files changed, 119 insertions(+), 179 deletions(-) create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala delete mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala diff --git a/backends/mysql/build/integration-tests/docker-app.conf b/backends/mysql/build/integration-tests/docker-app.conf index 9235503f..bfd62a56 100644 --- a/backends/mysql/build/integration-tests/docker-app.conf +++ b/backends/mysql/build/integration-tests/docker-app.conf @@ -8,22 +8,10 @@ service { private.key.path = "" } } - mysql { # multiple endpoints can be provided as comma separated list endpoints = "mysql" - # if auto.discovery.enabled is true, we ignore the manually supplied endpoints(above) - auto.discovery { - enabled: false - ## optional AWS discovery - # aws: { - # region: "us-west-2" - # tags: { - # name: "cassandra" - # } - # } - } connections { max.per.host = 10 @@ -32,8 +20,11 @@ mysql { keep.alive = true } - keyspace { - name = "haystack" - table.name = "traces" + table: { + # auto creates the table in mysql(if absent) + # if schema field is empty or not present, then no operation is performed + auto.create.schema = "mysql_table_schema" + name: "spans" + ttl.sec = 86400 } } diff --git a/backends/mysql/pom.xml b/backends/mysql/pom.xml index 7dfc3536..daa5bb4a 100644 --- a/backends/mysql/pom.xml +++ b/backends/mysql/pom.xml @@ -17,6 +17,8 @@ com.expedia.www.haystack.trace.storage.backends.mysql.Service ${project.artifactId}-${project.version} 3.3.0.1 + 8.0.13 + 1.4 @@ -24,6 +26,16 @@ com.google.protobuf protobuf-java + + mysql + mysql-connector-java + ${mysql.connector.version} + + + commons-dbcp + commons-dbcp + ${apache.commons.dhcp.version} + io.grpc diff --git a/backends/mysql/src/main/resources/config/base.conf b/backends/mysql/src/main/resources/config/base.conf index 511879e4..1b221a06 100644 --- a/backends/mysql/src/main/resources/config/base.conf +++ b/backends/mysql/src/main/resources/config/base.conf @@ -9,22 +9,12 @@ service { } } -cassandra { +mysql { # multiple endpoints can be provided as comma separated list - endpoints = "cassandra" - - # enable the auto.discovery mode, if true then we ignore the endpoints(above) and use auto discovery - # mechanism to find cassandra nodes. For today we only support aws node discovery provider - auto.discovery { - enabled: false - // aws: { - // region: "us-west-2" - // tags: { - // Role: haystack-cassandra - // Environment: ewetest - // } - // } - } + url = "mysql" + driver = "" + + connections { max.per.host = 50 @@ -41,21 +31,13 @@ cassandra { } } - consistency.level = "one" - - on.error.consistency.level = [ - "com.datastax.driver.core.exceptions.UnavailableException", - "any" - ] - - ttl.sec = 259200 - - keyspace: { - # auto creates the keyspace and table name in cassandra(if absent) + table: { + # auto creates the table in mysql(if absent) # if schema field is empty or not present, then no operation is performed auto.create.schema = "CREATE KEYSPACE IF NOT EXISTS haystack WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes = false; CREATE TABLE IF NOT EXISTS haystack.traces (id varchar, ts timestamp, spans blob, PRIMARY KEY ((id), ts)) WITH CLUSTERING ORDER BY (ts ASC) AND compaction = { 'class' : 'DateTieredCompactionStrategy', 'max_sstable_age_days': '3' } AND gc_grace_seconds = 86400;" - name: "haystack" - table.name: "traces" + name: "spans" + + ttl.sec = 259200 } } diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala new file mode 100644 index 00000000..a6c9f046 --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala @@ -0,0 +1,35 @@ +package com.expedia.www.haystack.trace.storage.backends.mysql.client + +import java.sql.Connection + +import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.ClientConfiguration +import org.apache.commons.dbcp.BasicDataSource +import org.slf4j.{Logger, LoggerFactory} + +class SqlConnectionManager(config: ClientConfiguration) extends AutoCloseable { + + private val LOGGER: Logger = LoggerFactory.getLogger(this.getClass) + + private lazy val connectionPool: BasicDataSource = { + + val basicDataSource = new BasicDataSource + basicDataSource.setDriverClassName(config.driver) + basicDataSource.setUrl(config.url) + basicDataSource.setMaxActive(config.socket.maxConnectionPerHost) + config.plaintextCredentials.foreach { credentials => + basicDataSource.setPassword(credentials.password) + basicDataSource.setUsername(credentials.username) + } + basicDataSource + } + + def getConnection: Connection = { + connectionPool.getConnection + } + + override def close(): Unit = { + connectionPool.close() + } + + +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala index 44ea988e..48f4d842 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala @@ -17,15 +17,12 @@ package com.expedia.www.haystack.trace.storage.backends.mysql.config -import com.datastax.driver.core.ConsistencyLevel import com.expedia.www.haystack.commons.config.ConfigurationLoader import com.expedia.www.haystack.commons.retries.RetryOperation import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities._ import com.typesafe.config.Config import org.apache.commons.lang3.StringUtils -import scala.collection.JavaConverters._ - class ProjectConfiguration { private val config = ConfigurationLoader.loadConfigFileWithEnvOverrides() @@ -41,62 +38,34 @@ class ProjectConfiguration { } /** * - * cassandra configuration object + * mysql configuration object */ val mysqlConfig: MysqlConfiguration = { - def toConsistencyLevel(level: String) = ConsistencyLevel.values().find(_.toString.equalsIgnoreCase(level)).get - - def consistencyLevelOnErrors(cs: Config) = { - val consistencyLevelOnErrors = cs.getStringList("on.error.consistency.level") - val consistencyLevelOnErrorList = scala.collection.mutable.ListBuffer[(Class[_], ConsistencyLevel)]() - - var idx = 0 - while (idx < consistencyLevelOnErrors.size()) { - val errorClass = consistencyLevelOnErrors.get(idx) - val level = consistencyLevelOnErrors.get(idx + 1) - consistencyLevelOnErrorList.+=((Class.forName(errorClass), toConsistencyLevel(level))) - idx = idx + 2 - } - - consistencyLevelOnErrorList.toList - } - def keyspaceConfig(kConfig: Config, ttl: Int): KeyspaceConfiguration = { + def tableConfiguration(tableConfig: Config): TableConfiguration = { val autoCreateSchemaField = "auto.create.schema" - val autoCreateSchema = if (kConfig.hasPath(autoCreateSchemaField) - && StringUtils.isNotEmpty(kConfig.getString(autoCreateSchemaField))) { - Some(kConfig.getString(autoCreateSchemaField)) + val autoCreateSchema: Option[String] = if (tableConfig.hasPath(autoCreateSchemaField) + && StringUtils.isNotEmpty(tableConfig.getString(autoCreateSchemaField))) { + Some(tableConfig.getString(autoCreateSchemaField)) } else { None } - KeyspaceConfiguration(kConfig.getString("name"), kConfig.getString("table.name"), ttl, autoCreateSchema) + TableConfiguration(tableConfig.getString("name"), tableConfig.getInt("ttl.sec"), autoCreateSchema) } - val cs = config.getConfig("cassandra") - - val awsConfig: Option[AwsNodeDiscoveryConfiguration] = - if (cs.hasPath("auto.discovery.aws")) { - val aws = cs.getConfig("auto.discovery.aws") - val tags = aws.getConfig("tags") - .entrySet() - .asScala - .map(elem => elem.getKey -> elem.getValue.unwrapped().toString) - .toMap - Some(AwsNodeDiscoveryConfiguration(aws.getString("region"), tags)) - } else { - None - } + val mysqlConfig = config.getConfig("mysql") + val credentialsConfig: Option[CredentialsConfiguration] = - if (cs.hasPath("credentials")) { - Some(CredentialsConfiguration(cs.getString("credentials.username"), cs.getString("credentials.password"))) + if (mysqlConfig.hasPath("credentials")) { + Some(CredentialsConfiguration(mysqlConfig.getString("credentials.username"), mysqlConfig.getString("credentials.password"))) } else { None } - val socketConfig = cs.getConfig("connections") + val socketConfig = mysqlConfig.getConfig("connections") val socket = SocketConfiguration( socketConfig.getInt("max.per.host"), @@ -104,22 +73,17 @@ class ProjectConfiguration { socketConfig.getInt("conn.timeout.ms"), socketConfig.getInt("read.timeout.ms")) - val consistencyLevel = cs.getString("consistency.level") - MysqlConfiguration( clientConfig = ClientConfiguration( - if (cs.hasPath("endpoints")) cs.getString("endpoints").split(",").toList else Nil, - cs.getBoolean("auto.discovery.enabled"), - awsConfig, + mysqlConfig.getString("url"), + mysqlConfig.getString("driver"), credentialsConfig, - keyspaceConfig(cs.getConfig("keyspace"), cs.getInt("ttl.sec")), + tableConfiguration(mysqlConfig.getConfig("table")), socket), - consistencyLevel = consistencyLevel, retryConfig = RetryOperation.Config( - cs.getInt("retries.max"), - cs.getLong("retries.backoff.initial.ms"), - cs.getDouble("retries.backoff.factor")), - consistencyLevelOnErrors(cs)) + mysqlConfig.getInt("retries.max"), + mysqlConfig.getLong("retries.backoff.initial.ms"), + mysqlConfig.getDouble("retries.backoff.factor"))) } } diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala deleted file mode 100644 index e076026a..00000000 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/AwsNodeDiscoveryConfiguration.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Expedia, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.expedia.www.haystack.trace.storage.backends.mysql.config.entities - -/** - * defines the parameters required for aws discovery - * @param region aws region e.g. us-east-1, us-west-2 - * @param tags: ec2 tags - */ -case class AwsNodeDiscoveryConfiguration(region: String, tags: Map[String, String]) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala index 5b487922..359611a6 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala @@ -21,35 +21,29 @@ import com.expedia.www.haystack.commons.retries.RetryOperation import org.apache.commons.lang3.StringUtils -/** define the keyspace and table information in cassandra +/** define the table information in mysql * - * @param name : name of cassandra keyspace - * @param table : name of cassandra table - * @param recordTTLInSec : ttl of record in sec - * @param autoCreateSchema : apply cql and create keyspace and tables if not exist, optional + * @param name : name of mysql table + * @param recordTTLInSec : ttl of record in sec + * @param autoCreateSchema : apply sql and create table if not exist, optional */ -case class KeyspaceConfiguration(name: String, - table: String, - recordTTLInSec: Int = -1, - autoCreateSchema: Option[String] = None) { +case class TableConfiguration(name: String, + recordTTLInSec: Int = -1, + autoCreateSchema: Option[String] = None) { require(StringUtils.isNotEmpty(name)) - require(StringUtils.isNotEmpty(table)) } /** - * defines the configuration parameters for cassandra client + * defines the configuration parameters for mysql client * - * @param endpoints : list of cassandra endpoints - * @param autoDiscoverEnabled : if autodiscovery is enabled, then 'endpoints' config parameter will be ignored - * @param awsNodeDiscovery : discovery configuration for aws, optional. This is applied only if autoDiscoverEnabled is true - * @param tracesKeyspace : cassandra keyspace for traces - * @param socket : socket configuration like maxConnections, timeouts and keepAlive + * @param url : list of mysql endpoints + * @param driver : Sql Driver Implementation Class + * @param socket : socket configuration like maxConnections, timeouts and keepAlive */ -case class ClientConfiguration(endpoints: List[String], - autoDiscoverEnabled: Boolean, - awsNodeDiscovery: Option[AwsNodeDiscoveryConfiguration], +case class ClientConfiguration(url: String, + driver: String, plaintextCredentials: Option[CredentialsConfiguration], - tracesKeyspace: KeyspaceConfiguration, + spansTable: TableConfiguration, socket: SocketConfiguration) /** diff --git a/backends/mysql/src/test/resources/config/base.conf b/backends/mysql/src/test/resources/config/base.conf index 907a05ed..5862185f 100644 --- a/backends/mysql/src/test/resources/config/base.conf +++ b/backends/mysql/src/test/resources/config/base.conf @@ -1,39 +1,31 @@ haystack.graphite.host = "monitoring-influxdb-graphite.kube-system.svc" service { - port = 8090 - ssl { - enabled = false - cert.path = "" - private.key.path = "" - } + port = 8090 + ssl { + enabled = true + cert.path = "/ssl/cert" + private.key.path = "/ssl/private-key" + } } -cassandra { +mysql { # multiple endpoints can be provided as comma separated list - endpoints = "cassandra" + endpoints = "mysql" - # if auto.discovery.enabled is true, we ignore the manually supplied endpoints(above) - auto.discovery { - enabled: false - ## optional AWS discovery - # aws: { - # region: "us-west-2" - # tags: { - # name: "cassandra" - # } - # } - } connections { - max.per.host = 10 + max.per.host = 100 read.timeout.ms = 5000 conn.timeout.ms = 10000 keep.alive = true } - keyspace { - name = "haystack" - table.name = "traces" + table: { + # auto creates the table in mysql(if absent) + # if schema field is empty or not present, then no operation is performed + auto.create.schema = "mysql_table_schema" + name: "spans" + ttl.sec = 86400 } } diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala index 1611ceee..7c066682 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala @@ -15,8 +15,6 @@ */ package com.expedia.www.haystack.trace.storage.backends.mysql.unit.config -import com.datastax.driver.core.ConsistencyLevel -import com.datastax.driver.core.exceptions.UnavailableException import com.expedia.www.haystack.trace.storage.backends.mysql.config.ProjectConfiguration import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.ServiceConfiguration import com.expedia.www.haystack.trace.storage.backends.mysql.unit.BaseUnitTestSpec @@ -31,26 +29,23 @@ class ConfigurationLoaderSpec extends BaseUnitTestSpec { serviceConfig.ssl.certChainFilePath shouldBe "/ssl/cert" serviceConfig.ssl.privateKeyPath shouldBe "/ssl/private-key" } - it("should load the cassandra config from base.conf and few properties overridden from env variable") { - val cassandraWriteConfig = project.mysqlConfig - val clientConfig = cassandraWriteConfig.clientConfig + it("should load the mysql config from base.conf and few properties overridden from env variable") { + val mysqlConfig = project.mysqlConfig + val clientConfig = mysqlConfig.clientConfig - clientConfig.autoDiscoverEnabled shouldBe false // this will fail if run inside an editor, we override this config using env variable inside pom.xml - clientConfig.endpoints should contain allOf("cass1", "cass2") - clientConfig.tracesKeyspace.autoCreateSchema shouldBe Some("cassandra_cql_schema_1") - clientConfig.tracesKeyspace.name shouldBe "haystack" - clientConfig.tracesKeyspace.table shouldBe "traces" - clientConfig.tracesKeyspace.recordTTLInSec shouldBe 86400 + clientConfig.url shouldBe "mysql" + clientConfig.spansTable.autoCreateSchema shouldBe Some("mysql_table_schema") + clientConfig.spansTable.name shouldBe "spans" + clientConfig.spansTable.recordTTLInSec shouldBe 86400 - clientConfig.awsNodeDiscovery shouldBe empty clientConfig.socket.keepAlive shouldBe true clientConfig.socket.maxConnectionPerHost shouldBe 100 clientConfig.socket.readTimeoutMills shouldBe 5000 clientConfig.socket.connectionTimeoutMillis shouldBe 10000 - cassandraWriteConfig.retryConfig.maxRetries shouldBe 10 - cassandraWriteConfig.retryConfig.backOffInMillis shouldBe 250 - cassandraWriteConfig.retryConfig.backoffFactor shouldBe 2 } + mysqlConfig.retryConfig.maxRetries shouldBe 10 + mysqlConfig.retryConfig.backOffInMillis shouldBe 100 + mysqlConfig.retryConfig.backoffFactor shouldBe 2 } } } From f28642dd1934bc93334fbc577d375562c3667eeb Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Fri, 8 Feb 2019 15:43:17 +0530 Subject: [PATCH 3/7] added integration test for mysql backend --- backends/Makefile | 9 ++- .../client/CassandraTableSchema.scala | 2 - .../build/integration-tests/docker-app.conf | 30 --------- .../integration-tests/docker-compose.yml | 5 +- backends/mysql/pom.xml | 8 +-- .../mysql/src/main/resources/config/base.conf | 13 ++-- .../storage/backends/mysql/Service.scala | 8 ++- .../mysql/client/MysqlTableSchema.scala | 65 +++++++++++++++++++ .../mysql/client/SqlConnectionManager.scala | 2 +- .../mysql/config/ProjectConfiguration.scala | 17 ++--- .../config/entities/ClientConfiguration.scala | 18 ++--- .../mysql/store/MysqlTraceRecordReader.scala | 29 ++++++++- .../mysql/store/MysqlTraceRecordWriter.scala | 24 +++++-- .../mysql/src/test/resources/config/base.conf | 23 +++++-- .../unit/config/ConfigurationLoaderSpec.scala | 14 ++-- 15 files changed, 185 insertions(+), 82 deletions(-) delete mode 100644 backends/mysql/build/integration-tests/docker-app.conf create mode 100644 backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala diff --git a/backends/Makefile b/backends/Makefile index 04d475ee..5e2caf0f 100644 --- a/backends/Makefile +++ b/backends/Makefile @@ -1,4 +1,4 @@ -.PHONY: all cassandra memory release +.PHONY: all cassandra mysql memory release PWD := $(shell pwd) @@ -11,6 +11,13 @@ build_cassandra: cd ../ && ./mvnw package -DfinalName=haystack-trace-backend-cassandra -pl backends/cassandra -am +mysql: build_mysql + cd mysql && $(MAKE) integration_test + +build_mysql: + cd ../ && ./mvnw package -DfinalName=haystack-trace-backend-mysql -pl backends/mysql -am + + memory: build_memory cd memory && $(MAKE) integration_test diff --git a/backends/cassandra/src/main/scala/com/expedia/www/haystack/trace/storage/backends/cassandra/client/CassandraTableSchema.scala b/backends/cassandra/src/main/scala/com/expedia/www/haystack/trace/storage/backends/cassandra/client/CassandraTableSchema.scala index 8e8490ae..07961ae7 100644 --- a/backends/cassandra/src/main/scala/com/expedia/www/haystack/trace/storage/backends/cassandra/client/CassandraTableSchema.scala +++ b/backends/cassandra/src/main/scala/com/expedia/www/haystack/trace/storage/backends/cassandra/client/CassandraTableSchema.scala @@ -26,8 +26,6 @@ object CassandraTableSchema { val ID_COLUMN_NAME = "id" val TIMESTAMP_COLUMN_NAME = "ts" val SPANS_COLUMN_NAME = "spans" - val SERVICE_COLUMN_NAME = "service_name" - val OPERATION_COLUMN_NAME = "operation_name" /** diff --git a/backends/mysql/build/integration-tests/docker-app.conf b/backends/mysql/build/integration-tests/docker-app.conf deleted file mode 100644 index bfd62a56..00000000 --- a/backends/mysql/build/integration-tests/docker-app.conf +++ /dev/null @@ -1,30 +0,0 @@ -haystack.graphite.host = "monitoring-influxdb-graphite.kube-system.svc" - -service { - port = 8090 - ssl { - enabled = false - cert.path = "" - private.key.path = "" - } -} -mysql { - # multiple endpoints can be provided as comma separated list - endpoints = "mysql" - - - connections { - max.per.host = 10 - read.timeout.ms = 5000 - conn.timeout.ms = 10000 - keep.alive = true - } - - table: { - # auto creates the table in mysql(if absent) - # if schema field is empty or not present, then no operation is performed - auto.create.schema = "mysql_table_schema" - name: "spans" - ttl.sec = 86400 - } -} diff --git a/backends/mysql/build/integration-tests/docker-compose.yml b/backends/mysql/build/integration-tests/docker-compose.yml index b812179e..62dc8fcf 100644 --- a/backends/mysql/build/integration-tests/docker-compose.yml +++ b/backends/mysql/build/integration-tests/docker-compose.yml @@ -5,5 +5,8 @@ services: environment: MAX_HEAP_SIZE: 256m HEAP_NEWSIZE: 256m + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: haystack-spans ports: - - "9042:9042" + - "3306:3306" + command: --default-authentication-plugin=mysql_native_password diff --git a/backends/mysql/pom.xml b/backends/mysql/pom.xml index daa5bb4a..a3cd57ed 100644 --- a/backends/mysql/pom.xml +++ b/backends/mysql/pom.xml @@ -92,7 +92,7 @@ test - com.expedia.www.haystack.trace.reader.unit + com.expedia.www.haystack.trace.storage.backends.mysql.unit @@ -102,11 +102,7 @@ test - - /src/reader/build/integration-tests/docker-app.conf - - - com.expedia.www.haystack.trace.reader.integration + com.expedia.www.haystack.trace.storage.backends.mysql.integration diff --git a/backends/mysql/src/main/resources/config/base.conf b/backends/mysql/src/main/resources/config/base.conf index 1b221a06..cffe9371 100644 --- a/backends/mysql/src/main/resources/config/base.conf +++ b/backends/mysql/src/main/resources/config/base.conf @@ -11,8 +11,8 @@ service { mysql { # multiple endpoints can be provided as comma separated list - url = "mysql" - driver = "" + endpoints = "jdbc:mysql://mysql/haystack" + driver = "com.mysql.cj.jdbc.Driver" @@ -23,6 +23,11 @@ mysql { keep.alive = true } + credentials { + username: "root" + password : "root" + } + retries { max = 10 backoff { @@ -31,10 +36,10 @@ mysql { } } - table: { + database: { # auto creates the table in mysql(if absent) # if schema field is empty or not present, then no operation is performed - auto.create.schema = "CREATE KEYSPACE IF NOT EXISTS haystack WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes = false; CREATE TABLE IF NOT EXISTS haystack.traces (id varchar, ts timestamp, spans blob, PRIMARY KEY ((id), ts)) WITH CLUSTERING ORDER BY (ts ASC) AND compaction = { 'class' : 'DateTieredCompactionStrategy', 'max_sstable_age_days': '3' } AND gc_grace_seconds = 86400;" + auto.create.schema = "CREATE DATABASE IF NOT EXISTS haystack; USE haystack; create table IF NOT EXISTS spans (id varchar(255) not null, spans LONGBLOB not null, ts timestamp default CURRENT_TIMESTAMP, PRIMARY KEY (id, ts))" name: "spans" diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala index bf38be06..a914a513 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala @@ -21,6 +21,7 @@ import java.io.File import com.codahale.metrics.JmxReporter import com.expedia.www.haystack.commons.logger.LoggerUtils import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.trace.storage.backends.mysql.client.{MysqlTableSchema, SqlConnectionManager} import com.expedia.www.haystack.trace.storage.backends.mysql.config.ProjectConfiguration import com.expedia.www.haystack.trace.storage.backends.mysql.services.{GrpcHealthService, SpansPersistenceService} import com.expedia.www.haystack.trace.storage.backends.mysql.store.{MysqlTraceRecordReader, MysqlTraceRecordWriter} @@ -49,9 +50,12 @@ object Service extends MetricsSupport { try { val config = new ProjectConfiguration val serviceConfig = config.serviceConfig + val sqlConnectionManager = new SqlConnectionManager(config.mysqlConfig.clientConfig) - val tracerRecordWriter = new MysqlTraceRecordWriter(config.mysqlConfig) - val tracerRecordReader = new MysqlTraceRecordReader(config.mysqlConfig.clientConfig) + MysqlTableSchema.ensureExists(config.mysqlConfig.databaseConfig.autoCreateSchema, sqlConnectionManager.getConnection) + + val tracerRecordWriter = new MysqlTraceRecordWriter(config.mysqlConfig,sqlConnectionManager) + val tracerRecordReader = new MysqlTraceRecordReader(config.mysqlConfig.clientConfig,sqlConnectionManager) val serverBuilder = NettyServerBuilder .forPort(serviceConfig.port) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala new file mode 100644 index 00000000..2ac3bbaf --- /dev/null +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala @@ -0,0 +1,65 @@ +/* + * Copyright 2017 Expedia, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.expedia.www.haystack.trace.storage.backends.mysql.client + +import java.sql.Connection + +import org.slf4j.LoggerFactory + +object MysqlTableSchema { + private val LOGGER = LoggerFactory.getLogger(MysqlTableSchema.getClass) + + val ID_COLUMN_NAME = "id" + val TIMESTAMP_COLUMN_NAME = "ts" + val SPANS_COLUMN_NAME = "spans" + val SERVICE_COLUMN_NAME = "service_name" + val OPERATION_COLUMN_NAME = "operation_name" + + /** + * ensures the keyspace and table name exists in com.expedia.www.haystack.trace.storage.backends.mysql + * + * @param connection com.expedia.www.haystack.trace.storage.backends.mysql client connection + * @param autoCreateSchema if present, then apply the sql schema that should create the keyspace and com.expedia.www.haystack.trace.storage.backends.mysql table + * + */ + def ensureExists(autoCreateSchema: Option[String], connection: Connection): Unit = { + autoCreateSchema match { + case Some(schema) => applySqlSchema(connection, schema) + case _ => + } + } + + /** + * apply the sql schema + * + * @param connection session object to interact with com.expedia.www.haystack.trace.storage.backends.mysql + * @param schema schema data + */ + private def applySqlSchema(connection: Connection, schema: String): Unit = { + val statement = connection.createStatement() + try { + for (cmd <- schema.split(";")) { + if (cmd.nonEmpty) statement.execute(cmd) + } + } catch { + case ex: Exception => + LOGGER.error(s"Failed to apply sql $schema with following reason:", ex) + throw new RuntimeException(ex) + } + } +} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala index a6c9f046..8cc0de8f 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/SqlConnectionManager.scala @@ -14,7 +14,7 @@ class SqlConnectionManager(config: ClientConfiguration) extends AutoCloseable { val basicDataSource = new BasicDataSource basicDataSource.setDriverClassName(config.driver) - basicDataSource.setUrl(config.url) + basicDataSource.setUrl(config.endpoints) basicDataSource.setMaxActive(config.socket.maxConnectionPerHost) config.plaintextCredentials.foreach { credentials => basicDataSource.setPassword(credentials.password) diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala index 48f4d842..2a465a6e 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala @@ -43,16 +43,16 @@ class ProjectConfiguration { val mysqlConfig: MysqlConfiguration = { - def tableConfiguration(tableConfig: Config): TableConfiguration = { + def databaseConfig(databaseConfig: Config): DatabaseConfiguration = { val autoCreateSchemaField = "auto.create.schema" - val autoCreateSchema: Option[String] = if (tableConfig.hasPath(autoCreateSchemaField) - && StringUtils.isNotEmpty(tableConfig.getString(autoCreateSchemaField))) { - Some(tableConfig.getString(autoCreateSchemaField)) + val autoCreateSchema: Option[String] = if (databaseConfig.hasPath(autoCreateSchemaField) + && StringUtils.isNotEmpty(databaseConfig.getString(autoCreateSchemaField))) { + Some(databaseConfig.getString(autoCreateSchemaField)) } else { None } - TableConfiguration(tableConfig.getString("name"), tableConfig.getInt("ttl.sec"), autoCreateSchema) + DatabaseConfiguration(databaseConfig.getString("name"), databaseConfig.getInt("ttl.sec"), autoCreateSchema) } val mysqlConfig = config.getConfig("mysql") @@ -75,15 +75,16 @@ class ProjectConfiguration { MysqlConfiguration( clientConfig = ClientConfiguration( - mysqlConfig.getString("url"), + mysqlConfig.getString("endpoints"), mysqlConfig.getString("driver"), credentialsConfig, - tableConfiguration(mysqlConfig.getConfig("table")), socket), retryConfig = RetryOperation.Config( mysqlConfig.getInt("retries.max"), mysqlConfig.getLong("retries.backoff.initial.ms"), - mysqlConfig.getDouble("retries.backoff.factor"))) + mysqlConfig.getDouble("retries.backoff.factor")), + databaseConfig = databaseConfig(mysqlConfig.getConfig("database")) + ) } } diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala index 359611a6..a1eba915 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala @@ -23,33 +23,33 @@ import org.apache.commons.lang3.StringUtils /** define the table information in mysql * - * @param name : name of mysql table + * @param name : name of mysql table * @param recordTTLInSec : ttl of record in sec * @param autoCreateSchema : apply sql and create table if not exist, optional */ -case class TableConfiguration(name: String, - recordTTLInSec: Int = -1, - autoCreateSchema: Option[String] = None) { +case class DatabaseConfiguration(name: String, + recordTTLInSec: Int = -1, + autoCreateSchema: Option[String] = None) { require(StringUtils.isNotEmpty(name)) } /** * defines the configuration parameters for mysql client * - * @param url : list of mysql endpoints + * @param endpoints : list of mysql endpoints * @param driver : Sql Driver Implementation Class * @param socket : socket configuration like maxConnections, timeouts and keepAlive */ -case class ClientConfiguration(url: String, +case class ClientConfiguration(endpoints: String, driver: String, plaintextCredentials: Option[CredentialsConfiguration], - spansTable: TableConfiguration, socket: SocketConfiguration) /** * @param retryConfig retry configuration if writes fail */ case class MysqlConfiguration(clientConfig: ClientConfiguration, - retryConfig: RetryOperation.Config) { + retryConfig: RetryOperation.Config, + databaseConfig: DatabaseConfiguration + ) -} diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala index 62405599..11a901ad 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala @@ -18,16 +18,19 @@ package com.expedia.www.haystack.trace.storage.backends.mysql.store import com.expedia.open.tracing.backend.TraceRecord import com.expedia.www.haystack.commons.metrics.MetricsSupport +import com.expedia.www.haystack.trace.storage.backends.mysql.client.MysqlTableSchema._ +import com.expedia.www.haystack.trace.storage.backends.mysql.client.SqlConnectionManager import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.ClientConfiguration import com.expedia.www.haystack.trace.storage.backends.mysql.metrics.AppMetricNames +import com.google.protobuf.ByteString import org.slf4j.LoggerFactory import scala.concurrent.{ExecutionContextExecutor, Future, Promise} -class MysqlTraceRecordReader(config: ClientConfiguration) +class MysqlTraceRecordReader(config: ClientConfiguration, sqlConnectionManager: SqlConnectionManager) (implicit val dispatcher: ExecutionContextExecutor) extends MetricsSupport { private val LOGGER = LoggerFactory.getLogger(classOf[MysqlTraceRecordReader]) - + val readRecordSql = "SELECT * FROM spans WHERE id = ?" private lazy val readTimer = metricRegistry.timer(AppMetricNames.MYSQL_READ_TIME) private lazy val readFailures = metricRegistry.meter(AppMetricNames.MYSQL_READ_FAILURES) @@ -36,8 +39,27 @@ class MysqlTraceRecordReader(config: ClientConfiguration) val promise = Promise[Seq[TraceRecord]] try { + val connection = sqlConnectionManager.getConnection + val statement = connection.prepareStatement(readRecordSql) + statement.setString(1, traceIds.head) + statement.execute() + val results = statement.getResultSet + var records: List[TraceRecord] = List() + + while (results.next()) { + val spans = results.getBlob(SPANS_COLUMN_NAME) + val record = TraceRecord.newBuilder() + .setTraceId(results.getString(ID_COLUMN_NAME)) + .setSpans(ByteString.copyFrom(spans.getBytes(1, spans.length().toInt))) + .setTimestamp(results.getTimestamp(TIMESTAMP_COLUMN_NAME).getTime) + .build() + records = record :: records + + } + promise.success(records) promise.future - } catch { + } + catch { case ex: Exception => readFailures.mark() timer.stop() @@ -45,4 +67,5 @@ class MysqlTraceRecordReader(config: ClientConfiguration) Future.failed(ex) } } + } \ No newline at end of file diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala index 38671f35..5698eb9a 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala @@ -17,33 +17,49 @@ package com.expedia.www.haystack.trace.storage.backends.mysql.store +import java.io.ByteArrayInputStream +import java.sql.{Time, Timestamp} import java.util.concurrent.atomic.AtomicInteger import com.expedia.open.tracing.backend.TraceRecord import com.expedia.www.haystack.commons.metrics.MetricsSupport import com.expedia.www.haystack.commons.retries.RetryOperation._ +import com.expedia.www.haystack.trace.storage.backends.mysql.client.SqlConnectionManager import com.expedia.www.haystack.trace.storage.backends.mysql.config.entities.MysqlConfiguration import com.expedia.www.haystack.trace.storage.backends.mysql.metrics.AppMetricNames import org.slf4j.LoggerFactory import scala.concurrent.{ExecutionContextExecutor, Future, Promise} -import scala.util.{Failure, Success} +import scala.util.{Failure, Success, Try} -class MysqlTraceRecordWriter(config: MysqlConfiguration)(implicit val dispatcher: ExecutionContextExecutor) +class MysqlTraceRecordWriter(config: MysqlConfiguration, sqlConnectionManager: SqlConnectionManager)(implicit val dispatcher: ExecutionContextExecutor) extends MetricsSupport { private val LOGGER = LoggerFactory.getLogger(classOf[MysqlTraceRecordWriter]) private lazy val writeTimer = metricRegistry.timer(AppMetricNames.MYSQL_WRITE_TIME) private lazy val writeFailures = metricRegistry.meter(AppMetricNames.MYSQL_WRITE_FAILURE) + val writeRecordSql = "insert into spans values(?,?,?)" + private def execute(record: TraceRecord): Future[Unit] = { val promise = Promise[Unit] // execute the request async with retry withRetryBackoff(retryCallback => { - val timer = writeTimer.time() + Try { + val timer = writeTimer.time() - // prepare the statement + val connection = sqlConnectionManager.getConnection + val statement = connection.prepareStatement(writeRecordSql) + statement.setString(1, record.getTraceId) + statement.setBlob(2, new ByteArrayInputStream(record.getSpans.toByteArray)) + statement.setTimestamp(3, new Timestamp(record.getTimestamp)) + statement.execute() + statement.getResultSet + } match { + case Success(resultSet) => retryCallback.onResult(result = resultSet) + case Failure(ex) => retryCallback.onError(ex, true) + } }, config.retryConfig, diff --git a/backends/mysql/src/test/resources/config/base.conf b/backends/mysql/src/test/resources/config/base.conf index 5862185f..5a7d842a 100644 --- a/backends/mysql/src/test/resources/config/base.conf +++ b/backends/mysql/src/test/resources/config/base.conf @@ -3,7 +3,7 @@ haystack.graphite.host = "monitoring-influxdb-graphite.kube-system.svc" service { port = 8090 ssl { - enabled = true + enabled = false cert.path = "/ssl/cert" private.key.path = "/ssl/private-key" } @@ -11,8 +11,8 @@ service { mysql { # multiple endpoints can be provided as comma separated list - endpoints = "mysql" - + endpoints = "jdbc:mysql://mysql/haystack" + driver = "com.mysql.cj.jdbc.Driver" connections { max.per.host = 100 @@ -21,10 +21,23 @@ mysql { keep.alive = true } - table: { + retries { + max = 2 + backoff { + initial.ms = 100 + factor = 2 + } + } + + credentials { + username: "root" + password : "root" + } + + database: { # auto creates the table in mysql(if absent) # if schema field is empty or not present, then no operation is performed - auto.create.schema = "mysql_table_schema" + auto.create.schema = "CREATE DATABASE IF NOT EXISTS haystack; USE haystack; create table IF NOT EXISTS spans (id varchar(255) not null, spans LONGBLOB not null, ts timestamp default CURRENT_TIMESTAMP, PRIMARY KEY (id, ts))" name: "spans" ttl.sec = 86400 } diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala index 7c066682..dcc327c5 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala @@ -25,7 +25,7 @@ class ConfigurationLoaderSpec extends BaseUnitTestSpec { it("should load the service config from base.conf") { val serviceConfig: ServiceConfiguration = project.serviceConfig serviceConfig.port shouldBe 8090 - serviceConfig.ssl.enabled shouldBe true + serviceConfig.ssl.enabled shouldBe false serviceConfig.ssl.certChainFilePath shouldBe "/ssl/cert" serviceConfig.ssl.privateKeyPath shouldBe "/ssl/private-key" } @@ -34,10 +34,7 @@ class ConfigurationLoaderSpec extends BaseUnitTestSpec { val clientConfig = mysqlConfig.clientConfig // this will fail if run inside an editor, we override this config using env variable inside pom.xml - clientConfig.url shouldBe "mysql" - clientConfig.spansTable.autoCreateSchema shouldBe Some("mysql_table_schema") - clientConfig.spansTable.name shouldBe "spans" - clientConfig.spansTable.recordTTLInSec shouldBe 86400 + clientConfig.endpoints shouldBe "mysql" clientConfig.socket.keepAlive shouldBe true clientConfig.socket.maxConnectionPerHost shouldBe 100 @@ -45,7 +42,12 @@ class ConfigurationLoaderSpec extends BaseUnitTestSpec { clientConfig.socket.connectionTimeoutMillis shouldBe 10000 mysqlConfig.retryConfig.maxRetries shouldBe 10 mysqlConfig.retryConfig.backOffInMillis shouldBe 100 - mysqlConfig.retryConfig.backoffFactor shouldBe 2 } + mysqlConfig.retryConfig.backoffFactor shouldBe 2 + + mysqlConfig.databaseConfig.autoCreateSchema should not be None + mysqlConfig.databaseConfig.name shouldBe "spans" + mysqlConfig.databaseConfig.recordTTLInSec shouldBe 86400 + } } } From 44943cf4f3b81548aed956d0b2cd18a162df9899 Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Tue, 19 Feb 2019 09:25:52 +0530 Subject: [PATCH 4/7] incorporating review comments --- backends/Makefile | 1 + .../mysql/store/MysqlTraceRecordReader.scala | 9 ++++++-- .../mysql/store/MysqlTraceRecordWriter.scala | 22 ++++++++++--------- .../unit/config/ConfigurationLoaderSpec.scala | 4 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/backends/Makefile b/backends/Makefile index 5e2caf0f..20b65196 100644 --- a/backends/Makefile +++ b/backends/Makefile @@ -28,3 +28,4 @@ build_memory: release: cd cassandra && $(MAKE) docker_build && $(MAKE) release cd memory && $(MAKE) docker_build && $(MAKE) release + cd mysql && $(MAKE) docker_build && $(MAKE) release diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala index 11a901ad..b08517c6 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala @@ -16,6 +16,8 @@ package com.expedia.www.haystack.trace.storage.backends.mysql.store +import java.sql.Connection + import com.expedia.open.tracing.backend.TraceRecord import com.expedia.www.haystack.commons.metrics.MetricsSupport import com.expedia.www.haystack.trace.storage.backends.mysql.client.MysqlTableSchema._ @@ -37,9 +39,9 @@ class MysqlTraceRecordReader(config: ClientConfiguration, sqlConnectionManager: def readTraceRecords(traceIds: List[String]): Future[Seq[TraceRecord]] = { val timer = readTimer.time() val promise = Promise[Seq[TraceRecord]] - + var connection: Connection = null try { - val connection = sqlConnectionManager.getConnection + connection = sqlConnectionManager.getConnection val statement = connection.prepareStatement(readRecordSql) statement.setString(1, traceIds.head) statement.execute() @@ -66,6 +68,9 @@ class MysqlTraceRecordReader(config: ClientConfiguration, sqlConnectionManager: LOGGER.error("Failed to read raw traces with exception", ex) Future.failed(ex) } + finally { + if (connection != null) connection.close() + } } } \ No newline at end of file diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala index 5698eb9a..3d9682e2 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala @@ -18,7 +18,7 @@ package com.expedia.www.haystack.trace.storage.backends.mysql.store import java.io.ByteArrayInputStream -import java.sql.{Time, Timestamp} +import java.sql.{Connection, Timestamp} import java.util.concurrent.atomic.AtomicInteger import com.expedia.open.tracing.backend.TraceRecord @@ -30,7 +30,7 @@ import com.expedia.www.haystack.trace.storage.backends.mysql.metrics.AppMetricNa import org.slf4j.LoggerFactory import scala.concurrent.{ExecutionContextExecutor, Future, Promise} -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Success} class MysqlTraceRecordWriter(config: MysqlConfiguration, sqlConnectionManager: SqlConnectionManager)(implicit val dispatcher: ExecutionContextExecutor) extends MetricsSupport { @@ -44,23 +44,25 @@ class MysqlTraceRecordWriter(config: MysqlConfiguration, sqlConnectionManager: S private def execute(record: TraceRecord): Future[Unit] = { val promise = Promise[Unit] + // execute the request async with retry withRetryBackoff(retryCallback => { - Try { + var connection:Connection = null + try { val timer = writeTimer.time() - - val connection = sqlConnectionManager.getConnection + connection = sqlConnectionManager.getConnection val statement = connection.prepareStatement(writeRecordSql) statement.setString(1, record.getTraceId) statement.setBlob(2, new ByteArrayInputStream(record.getSpans.toByteArray)) statement.setTimestamp(3, new Timestamp(record.getTimestamp)) statement.execute() - statement.getResultSet - } match { - case Success(resultSet) => retryCallback.onResult(result = resultSet) - case Failure(ex) => retryCallback.onError(ex, true) + retryCallback.onResult(statement.getResultSet) + } catch { + case ex: Exception => retryCallback.onError(ex, retry = true) + } + finally { + if (connection != null) connection.close() } - }, config.retryConfig, onSuccess = (_: Any) => promise.success(), diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala index dcc327c5..3613e43b 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala @@ -34,13 +34,13 @@ class ConfigurationLoaderSpec extends BaseUnitTestSpec { val clientConfig = mysqlConfig.clientConfig // this will fail if run inside an editor, we override this config using env variable inside pom.xml - clientConfig.endpoints shouldBe "mysql" + clientConfig.endpoints shouldBe "jdbc:mysql://mysql/haystack" clientConfig.socket.keepAlive shouldBe true clientConfig.socket.maxConnectionPerHost shouldBe 100 clientConfig.socket.readTimeoutMills shouldBe 5000 clientConfig.socket.connectionTimeoutMillis shouldBe 10000 - mysqlConfig.retryConfig.maxRetries shouldBe 10 + mysqlConfig.retryConfig.maxRetries shouldBe 2 mysqlConfig.retryConfig.backOffInMillis shouldBe 100 mysqlConfig.retryConfig.backoffFactor shouldBe 2 From 518ec8d25c9295687465ed0c3ef2847b378bc3ad Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Tue, 26 Feb 2019 21:52:13 +0530 Subject: [PATCH 5/7] updated the documentation --- CONTRIBUTING.md | 83 ++++++++++++-- README.md | 72 +++++++----- Release.md | 19 ---- backends/cassandra/README.md | 14 ++- backends/memory/README.md | 8 +- backends/mysql/Makefile | 4 +- backends/mysql/README.md | 15 ++- backends/mysql/build/docker/Dockerfile | 4 - backends/mysql/docker-compose.yml | 107 ++++++++++++++++++ .../mysql/store/MysqlTraceRecordReader.scala | 1 + .../mysql/store/MysqlTraceRecordWriter.scala | 3 +- indexer/README.md | 5 +- reader/README.md | 4 +- 13 files changed, 254 insertions(+), 85 deletions(-) delete mode 100644 Release.md create mode 100644 backends/mysql/docker-compose.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31775712..1c9d4039 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,77 @@ -##Bugs -We use Github Issues for our bug reporting. Please make sure the bug isn't already listed before opening a new issue. +# Contributing -##Development -All work on Haystack happens directly on Github. Core Haystack team members will review opened pull requests. +Code contributions are always welcome! -##Requests -If you see a feature that you would like to be added, please open an issue in the respective repository or in the general Haystack repo. +* Open an issue in the repo with defect/enhancements +* We can also be reached @ https://gitter.im/expedia-haystack/Lobby +* Fork, make the changes, build and test it locally +* Issue a PR - watch the PR build in [travis-ci](https://travis-ci.org/ExpediaDotCom/haystack-traces) +* Once merged to master, travis-ci will build and release the artifacts to [docker hub] -##Contributing to Documentation -To contribute to documentation, you can directly modify the corresponding .md files in the docs directory under the base haystack repository, and submit a pull request. Once your PR is merged, the documentation is automatically built and deployed to https://expediadotcom.github.io/haystack. -##License -By contributing to Haystack, you agree that your contributions will be licensed under its Apache License. \ No newline at end of file +## Building + +####Prerequisite: + +* Make sure you have Java 1.8 +* Make sure you have maven 3.3.9 or higher +* Make sure you have docker 1.13 or higher + + +Note : For mac users you can download docker for mac to set you up for the last two steps. + +####Build + +For a full build, including unit tests and integration tests, docker image build, you can run - +``` +make all +``` + +####Integration Test + +####Prerequisite: +1. Install docker using Docker Tools or native docker if on mac +2. Verify if docker-compose is installed by running following command else install it. +``` +docker-compose + +``` + +Run the build and integration tests for individual components with +``` +make indexer + +``` + +&& + +``` +make reader + +``` + + +``` +make backends + +``` + + +## Releasing the artifacts + +Currently we publish the repo to docker hub and nexus central repository. + +* Git tagging: + +``` +git tag -a -m "Release description..." +git push origin +``` + +`` must follow semantic versioning scheme. + +Or one can also tag using UI: https://github.com/ExpediaDotCom/haystack-traces/releases + +It is preferred to create an annotated tag using `git tag -a` and then use the release UI to add release notes for the tag. + +* After the release is completed, please update the `pom.xml` files to next `-SNAPSHOT` version to match the next release \ No newline at end of file diff --git a/README.md b/README.md index 7a5e7308..574b75b0 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,61 @@ [![Build Status](https://travis-ci.org/ExpediaDotCom/haystack-traces.svg?branch=master)](https://travis-ci.org/ExpediaDotCom/haystack-traces) [![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/ExpediaDotCom/haystack/blob/master/LICENSE) -# haystack-traces -This repo contains the haystack components that build the traces, store them in Cassandra and ElasticSearch(for indexing) and provide a grpc endpoint for accessing them +# Haystack Traces +Traces is a subsystem included in Haystack that provides a distributed tracing system to troubleshoot problems in microservice architectures. Its design is based on the [Google Dapper](http://research.google.com/pubs/pub36356.html) paper. -## Building -#### -Since this repo contains haystack-idl as the submodule, so use the following to clone the repo -* git clone --recursive git@github.com:ExpediaDotCom/haystack-traces.git . +This repo contains the haystack components that build the traces. It uses ElasticSearch for indexing and a storage backend for persistence -####Prerequisite: +## Architecture +Please see the [architecture document](https://expediadotcom.github.io/haystack/docs/subsystems/subsystems_traces.html) for the high level architecture of the traces subsystem -* Make sure you have Java 1.8 -* Make sure you have maven 3.3.9 or higher -* Make sure you have docker 1.13 or higher +## Components -Note : For mac users you can download docker for mac to set you up for the last two steps. +### haystack-trace-indexer -####Build +Trace Indexer is the component which reads spans from a kafka topic and writes to elasticsearch(for indexing) +and the storage backend for persistence. Please see the [indexer app](indexer/) for more details -For a full build, including unit tests and integration tests, docker image build, you can run - -``` -make all -``` +### haystack-trace-reader -####Integration Test +Trace Reader is the component which retrieves the trace-ids from elastic search based on the given queries and then fetches the spans from +the storage backend. Please see the [reader app](reader/) for more details -####Prerequisite: -1. Install docker using Docker Tools or native docker if on mac -2. Verify if docker-compose is installed by running following command else install it. -``` -docker-compose +### Storage Backend -``` +Haystack Traces multiple storage backend apps, used to store and query spans. The Storage backend apps are +grpc apps which are expected to implement this [grpc contract](https://github.com/ExpediaDotCom/haystack-idl/blob/master/proto/backend/storageBackend.proto) +The [reader](reader/src/main/scala/com/expedia/www/haystack/trace/reader/stores/readers/grpc/GrpcTraceReader.scala) and [indexer](indexer/src/main/scala/com/expedia/www/haystack/trace/indexer/writers/grpc/GrpcTraceWriter.scala) components read and write to the underlying datastore using this service and the default configuration expects the storage backend app to run on the same host(localhost) as the indexer and reader app. -Run the build and integration tests for individual components with -``` -make indexer +By default the traces subsystem comes bundled with the following backends. You can always run your custom backends as long as it implements the [grpc contract](https://github.com/ExpediaDotCom/haystack-idl/blob/master/proto/backend/storageBackend.proto). -``` +#### In-Memory +The in-memory storage backend app keeps the spans in memory. It +is neither persistent, nor viable for realistic work loads. Please see the [memory backend app](backends/memory) for more details -&& -``` -make reader +#### Cassandra +The Cassandra storage-backend app is tested against [Cassandra 3.11.3+](http://cassandra.apache.org/). It is designed for production scale. Please see the [cassandra backend app](backends/cassandra) for more details -``` +#### Mysql +The Mysql storage-backend app is tested against [Mysql 5.6++](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-13.html) and [amazon aurora mysql](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMySQL.Overview.html). +It is designed for production scale. Please see the [mysql backend app](backends/mysql) for more details + + +## Contributing to this codebase +Please see [CONTRIBUTING.md](CONTRIBUTING.md) + + +## Bugs, Feature Requests, Documentation Updates +Please see the [contributing page](https://expediadotcom.github.io/haystack/docs/contributing.html) on our website + +## Contact Info + +Interested in haystack? Want to talk? Have questions, concerns or great ideas? +Please join us on [gitter](https://gitter.im/expedia-haystack/Lobby) + +##License +By contributing to Haystack, you agree that your contributions will be licensed under its Apache License. \ No newline at end of file diff --git a/Release.md b/Release.md deleted file mode 100644 index b26e745c..00000000 --- a/Release.md +++ /dev/null @@ -1,19 +0,0 @@ -#Releasing -Currently we publish the repo to docker hub and nexus central repository. - -#How to release and publish - -* Git tagging: - -``` -git tag -a -m "Release description..." -git push origin -``` - -`` must follow semantic versioning scheme. - -Or one can also tag using UI: https://github.com/ExpediaDotCom/haystack-traces/releases - -It is preferred to create an annotated tag using `git tag -a` and then use the release UI to add release notes for the tag. - -* After the release is completed, please update the `pom.xml` files to next `-SNAPSHOT` version to match the next release \ No newline at end of file diff --git a/backends/cassandra/README.md b/backends/cassandra/README.md index ff9588c3..9f5b8196 100644 --- a/backends/cassandra/README.md +++ b/backends/cassandra/README.md @@ -6,9 +6,17 @@ Grpc service which can read a write spans to a cassandra cluster ##Technical Details In order to understand this service, we recommend to read the details of [haystack](https://github.com/ExpediaDotCom/haystack) project. -This service reads from [Cassandra](http://cassandra.apache.org/). API endpoints are exposed as [GRPC](https://grpc.io/) endpoints. +This service reads from [Cassandra](http://cassandra.apache.org/). API endpoints are exposed as [GRPC](https://grpc.io/) endpoints based on [this]((https://github.com/ExpediaDotCom/haystack-idl/blob/master/proto/backend/storageBackend.proto)) contract. + +The Schema for the cassandra table is created by the code when it starts up if it doesn't exist using the following command + +` +CREATE KEYSPACE IF NOT EXISTS haystack WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes = false; CREATE TABLE IF NOT EXISTS haystack.traces (id varchar, ts timestamp, spans blob, PRIMARY KEY ((id), ts)) WITH CLUSTERING ORDER BY (ts ASC) AND compaction = { 'class' : 'DateTieredCompactionStrategy', 'max_sstable_age_days': '3' } AND gc_grace_seconds = 86400; +` + +## Deployments +The reader and the indexer app expects the storage-backend app as a sidecar container and sample deployment topology using docker compose is shared [here](https://github.com/ExpediaDotCom/haystack-docker) -Will fill in more details as we go.. ## Building -Check the details on [Build Section](../README.md) \ No newline at end of file +Check the details on [Build Section](../../CONTRIBUTING.md) \ No newline at end of file diff --git a/backends/memory/README.md b/backends/memory/README.md index a53728b9..5f82a53b 100644 --- a/backends/memory/README.md +++ b/backends/memory/README.md @@ -5,9 +5,9 @@ Grpc service which can read a write spans to a an in memory map ##Technical Details In order to understand this service, we recommend to read the details of [haystack](https://github.com/ExpediaDotCom/haystack) project. -This service reads from an in memory map. API endpoints are exposed as [GRPC](https://grpc.io/) endpoints. +This service reads from an in memory map. API endpoints are exposed as [GRPC](https://grpc.io/) endpoints based on [this]((https://github.com/ExpediaDotCom/haystack-idl/blob/master/proto/backend/storageBackend.proto)) contract. -Will fill in more details as we go.. +* Note : Its purpose is for testing, for example starting a server on your laptop without any database needed. This only works if the reader and indexer apps are running locally and talk to the same in-memory backend server. -## Building -Check the details on [Build Section](../README.md) \ No newline at end of file +# Building +Check the details on [Build Section](../../CONTRIBUTING.md) \ No newline at end of file diff --git a/backends/mysql/Makefile b/backends/mysql/Makefile index ed0f5c30..227d9282 100644 --- a/backends/mysql/Makefile +++ b/backends/mysql/Makefile @@ -1,7 +1,7 @@ .PHONY: docker_build prepare_integration_test_env integration_test release -export DOCKER_ORG := expediadotcom -export DOCKER_IMAGE_NAME := haystack-trace-backend-mysql +export DOCKER_ORG := ayansen89 +export DOCKER_IMAGE_NAME := ayansen89/haystack-trace-backend-mysql PWD := $(shell pwd) SERVICE_DEBUG_ON ?= false diff --git a/backends/mysql/README.md b/backends/mysql/README.md index 85931af6..b35fc782 100644 --- a/backends/mysql/README.md +++ b/backends/mysql/README.md @@ -1,14 +1,21 @@ # Storage Backend - Mysql - Grpc service which can read a write spans to a mysql cluster ##Technical Details In order to understand this service, we recommend to read the details of [haystack](https://github.com/ExpediaDotCom/haystack) project. -This service reads from [Mysql](https://www.mysql.com/). API endpoints are exposed as [GRPC](https://grpc.io/) endpoints. +This service reads from [Mysql](https://www.mysql.com/). API endpoints are exposed as [GRPC](https://grpc.io/) endpoints based on [this]((https://github.com/ExpediaDotCom/haystack-idl/blob/master/proto/backend/storageBackend.proto)) contract. + +The Schema for the sql table is created by the code when it starts up if it doesn't exist using the following command + +` +CREATE DATABASE IF NOT EXISTS haystack; USE haystack; create table IF NOT EXISTS spans (id varchar(255) not null, spans LONGBLOB not null, ts timestamp default CURRENT_TIMESTAMP, PRIMARY KEY (id, ts)) +` + +## Deployments +The reader and the indexer app expects the storage-backend app as a sidecar container and sample deployment topology using docker compose is shared [here](docker-compose.yml) -Will fill in more details as we go.. ## Building -Check the details on [Build Section](../README.md) \ No newline at end of file +Check the details on [Build Section](../../CONTRIBUTING.md) \ No newline at end of file diff --git a/backends/mysql/build/docker/Dockerfile b/backends/mysql/build/docker/Dockerfile index f3292561..1cd73feb 100644 --- a/backends/mysql/build/docker/Dockerfile +++ b/backends/mysql/build/docker/Dockerfile @@ -11,10 +11,6 @@ COPY target/${APP_NAME}.jar ${APP_HOME}/ COPY build/docker/start-app.sh ${APP_HOME}/ RUN chmod +x ${APP_HOME}/start-app.sh -RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ - wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ - chmod +x /bin/grpc_health_probe - COPY build/docker/jmxtrans-agent.xml ${APP_HOME}/ ADD https://github.com/jmxtrans/jmxtrans-agent/releases/download/${JMXTRANS_AGENT}/${JMXTRANS_AGENT}.jar ${APP_HOME}/ diff --git a/backends/mysql/docker-compose.yml b/backends/mysql/docker-compose.yml new file mode 100644 index 00000000..04670ba4 --- /dev/null +++ b/backends/mysql/docker-compose.yml @@ -0,0 +1,107 @@ +# +# Copyright 2018 Expedia, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +version: "3" +services: + mysql: + image: mysql:8.0.13 + environment: + MAX_HEAP_SIZE: 256m + HEAP_NEWSIZE: 256m + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: haystack-spans + ports: + - "3306:3306" + command: --default-authentication-plugin=mysql_native_password + + storage-backend: + image: expediadotcom/haystack-trace-backend-mysql:1.0.8 + environment: + HAYSTACK_GRAPHITE_ENABLED: "false" + HAYSTACK_LOG_LEVEL: "DEBUG" + JAVA_XMS: 128m + depends_on: + - "mysql" + restart: always + # uncomment below port mapping to expose and connect to this application out of local docker container network +# ports: +# - "8090:8090" + + trace-reader: + image: expediadotcom/haystack-trace-reader:1.0.8 + environment: + HAYSTACK_GRAPHITE_ENABLED: "false" + HAYSTACK_PROP_BACKEND_CLIENT_HOST: "storage-backend" + JAVA_XMS: 128m + restart: always + + trace-indexer: + image: expediadotcom/haystack-trace-indexer:1.0.8 + environment: + HAYSTACK_GRAPHITE_ENABLED: "false" + HAYSTACK_PROP_BACKEND_CLIENT_HOST: "storage-backend" + HAYSTACK_PROP_SERVICE_METADATA_ENABLED: "true" + HAYSTACK_PROP_KAFKA_MAX_WAKEUPS: "100" + HAYSTACK_PROP_SERVICE_METADATA_FLUSH_INTERVAL_SEC: "0" + JAVA_XMS: 128m + depends_on: + - "elasticsearch" + restart: always + + elasticsearch: + image: elastic/elasticsearch:6.0.1 + environment: + ES_JAVA_OPTS: "-Xms512m -Xmx512m" + xpack.security.enabled: "false" + ports: + - "9200:9200" + restart: always + + zookeeper: + image: wurstmeister/zookeeper + ports: + - "2181:2181" + + kafkasvc: + image: wurstmeister/kafka:2.11-1.1.1 + depends_on: + - zookeeper + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ADVERTISED_LISTENERS: INSIDE://kafkasvc:9092,OUTSIDE://localhost:19092 + KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:19092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_CREATE_TOPICS: "proto-spans:1:1,metricpoints:1:1,metric-data-points:1:1,mdm:1:1,metrics:1:1,graph-nodes:1:1,service-graph:1:1,mapped-metrics:1:1,anomalies:1:1,alerts:1:1" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + ports: + - "9092:9092" + - "19092:19092" + + + ui: + image: expediadotcom/haystack-ui:1.1.4 + volumes: + - ./:/data + ports: + - "8080:8080" + environment: + HAYSTACK_OVERRIDES_CONFIG_PATH: /data/connectors.json + HAYSTACK_PROP_CONNECTORS_TRACES_CONNECTOR__NAME: "haystack" + HAYSTACK_PROP_CONNECTORS_TRACES_SERVICE__REFRESH__INTERVAL__IN__SECS: "0" + HAYSTACK_PROP_CONNECTORS_TRACES_HAYSTACK__HOST: "trace-reader" + HAYSTACK_PROP_CONNECTORS_TRACES_HAYSTACK__PORT: "8088" \ No newline at end of file diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala index b08517c6..347ebf15 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala @@ -36,6 +36,7 @@ class MysqlTraceRecordReader(config: ClientConfiguration, sqlConnectionManager: private lazy val readTimer = metricRegistry.timer(AppMetricNames.MYSQL_READ_TIME) private lazy val readFailures = metricRegistry.meter(AppMetricNames.MYSQL_READ_FAILURES) + //We currently don't have a way to make an async jdbc call, we should investigate this further to see if its possible to make this call async. def readTraceRecords(traceIds: List[String]): Future[Seq[TraceRecord]] = { val timer = readTimer.time() val promise = Promise[Seq[TraceRecord]] diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala index 3d9682e2..ec05f715 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala @@ -41,6 +41,7 @@ class MysqlTraceRecordWriter(config: MysqlConfiguration, sqlConnectionManager: S val writeRecordSql = "insert into spans values(?,?,?)" + //We currently don't have a way to make an async jdbc call, we should investigate this further to see if its possible to make this call async. private def execute(record: TraceRecord): Future[Unit] = { val promise = Promise[Unit] @@ -54,7 +55,7 @@ class MysqlTraceRecordWriter(config: MysqlConfiguration, sqlConnectionManager: S val statement = connection.prepareStatement(writeRecordSql) statement.setString(1, record.getTraceId) statement.setBlob(2, new ByteArrayInputStream(record.getSpans.toByteArray)) - statement.setTimestamp(3, new Timestamp(record.getTimestamp)) + statement.setTimestamp(3, new Timestamp(System.currentTimeMillis())) statement.execute() retryCallback.onResult(statement.getResultSet) } catch { diff --git a/indexer/README.md b/indexer/README.md index a46d310e..aceb5344 100644 --- a/indexer/README.md +++ b/indexer/README.md @@ -23,8 +23,5 @@ In order to understand the haystack, we recommend to read the details of [haysta Its written in kafka-streams(http://docs.confluent.io/current/streams/index.html) and hence some prior knowledge of kafka-streams would be useful. -##Technical Details -Fill this as we go along.. - ## Building -Check the details on [Build Section](../README.md) \ No newline at end of file +Check the details on [Build Section](../CONTRIBUTING.md) \ No newline at end of file diff --git a/reader/README.md b/reader/README.md index 3ab07130..d6e32640 100644 --- a/reader/README.md +++ b/reader/README.md @@ -7,7 +7,5 @@ Service for fetching traces and fields from persistent storages. In order to understand this service, we recommend to read the details of [haystack](https://github.com/ExpediaDotCom/haystack) project. This service reads from [TraceBackend]() and [ElasticSearch](https://www.elastic.co/) stores. API endpoints are exposed as [GRPC](https://grpc.io/) endpoints. -Will fill in more details as we go.. - ## Building -Check the details on [Build Section](../README.md) \ No newline at end of file +Check the details on [Build Section](../CONTRIBUTING.md) \ No newline at end of file From 1b21c64bf80dfaa04909f44033512e9774a2a979 Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Sun, 10 Mar 2019 10:27:47 +0530 Subject: [PATCH 6/7] fixed review comments --- backends/mysql/build/docker/jmxtrans-agent.xml | 2 +- backends/mysql/build/docker/start-app.sh | 4 ++-- backends/mysql/pom.xml | 1 - .../www/haystack/trace/storage/backends/mysql/Service.scala | 2 +- .../storage/backends/mysql/client/MysqlTableSchema.scala | 2 +- .../storage/backends/mysql/config/ProjectConfiguration.scala | 2 +- .../backends/mysql/config/entities/ClientConfiguration.scala | 2 +- .../mysql/config/entities/CredentialsConfiguration.scala | 2 +- .../backends/mysql/config/entities/ServiceConfiguration.scala | 2 +- .../backends/mysql/config/entities/SocketConfiguration.scala | 2 +- .../trace/storage/backends/mysql/metrics/AppMetricNames.scala | 2 +- .../trace/storage/backends/mysql/services/GrpcHandler.scala | 2 +- .../backends/mysql/services/SpansPersistenceService.scala | 2 +- .../storage/backends/mysql/store/MysqlTraceRecordReader.scala | 2 +- .../storage/backends/mysql/store/MysqlTraceRecordWriter.scala | 2 +- .../backends/mysql/integration/BaseIntegrationTestSpec.scala | 2 +- .../StorageBackendServiceIntegrationTestSpec.scala | 2 +- .../backends/mysql/unit/config/ConfigurationLoaderSpec.scala | 2 +- 18 files changed, 18 insertions(+), 19 deletions(-) diff --git a/backends/mysql/build/docker/jmxtrans-agent.xml b/backends/mysql/build/docker/jmxtrans-agent.xml index fb89d65a..0f9ecbc5 100644 --- a/backends/mysql/build/docker/jmxtrans-agent.xml +++ b/backends/mysql/build/docker/jmxtrans-agent.xml @@ -23,7 +23,7 @@ ${HAYSTACK_GRAPHITE_HOST:monitoring-influxdb-graphite.kube-system.svc} ${HAYSTACK_GRAPHITE_PORT:2003} - ${HAYSTACK_GRAPHITE_ENABLED:true} + ${HAYSTACK_GRAPHITE_ENABLED:false} haystack.traces.backend-mysql.#hostname#. 30 diff --git a/backends/mysql/build/docker/start-app.sh b/backends/mysql/build/docker/start-app.sh index ba2c6556..a1d956bf 100755 --- a/backends/mysql/build/docker/start-app.sh +++ b/backends/mysql/build/docker/start-app.sh @@ -1,7 +1,7 @@ #!/bin/bash -[ -z "$JAVA_XMS" ] && JAVA_XMS=1024m -[ -z "$JAVA_XMX" ] && JAVA_XMX=1024m +[ -z "$JAVA_XMS" ] && JAVA_XMS=256m +[ -z "$JAVA_XMX" ] && JAVA_XMX=256m [ -z "$JAVA_GC_OPTS" ] && JAVA_GC_OPTS="-XX:+UseG1GC" set -e diff --git a/backends/mysql/pom.xml b/backends/mysql/pom.xml index a3cd57ed..4dfa40ce 100644 --- a/backends/mysql/pom.xml +++ b/backends/mysql/pom.xml @@ -16,7 +16,6 @@ com.expedia.www.haystack.trace.storage.backends.mysql.Service ${project.artifactId}-${project.version} - 3.3.0.1 8.0.13 1.4 diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala index a914a513..4ed5cf66 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/Service.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala index 2ac3bbaf..492f56d2 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/client/MysqlTableSchema.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala index 2a465a6e..d5d91edf 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/ProjectConfiguration.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala index a1eba915..b482b7b6 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ClientConfiguration.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala index fd4648db..b93364b1 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/CredentialsConfiguration.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala index f00ef2f6..b17afe89 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/ServiceConfiguration.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala index bd80af7a..61eb083d 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/config/entities/SocketConfiguration.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala index ed004a5c..d10e8c26 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/metrics/AppMetricNames.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala index cc1c5674..c78fe26a 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHandler.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala index 34e22562..4a4db7c4 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/SpansPersistenceService.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala index 347ebf15..dc6cb735 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordReader.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala index ec05f715..71a29325 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/store/MysqlTraceRecordWriter.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala index 31ed5824..c45423b7 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/BaseIntegrationTestSpec.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala index e9ff0262..92a8c9eb 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/integration/StorageBackendServiceIntegrationTestSpec.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala index 3613e43b..9bbbd22d 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/config/ConfigurationLoaderSpec.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 1ad1b92310caa8a09df1040521d9dbebc3de5bae Mon Sep 17 00:00:00 2001 From: Ayan Sen Date: Sun, 10 Mar 2019 10:31:03 +0530 Subject: [PATCH 7/7] updated license --- backends/mysql/docker-compose.yml | 2 +- .../storage/backends/mysql/services/GrpcHealthService.scala | 2 +- .../trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/mysql/docker-compose.yml b/backends/mysql/docker-compose.yml index 04670ba4..c06de6f5 100644 --- a/backends/mysql/docker-compose.yml +++ b/backends/mysql/docker-compose.yml @@ -1,5 +1,5 @@ # -# Copyright 2018 Expedia, Inc. +# Copyright 2019 Expedia, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala index 400adc13..426bdf1b 100644 --- a/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala +++ b/backends/mysql/src/main/scala/com/expedia/www/haystack/trace/storage/backends/mysql/services/GrpcHealthService.scala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala index 4afe61f4..b81cf527 100644 --- a/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala +++ b/backends/mysql/src/test/scala/com/expedia/www/haystack/trace/storage/backends/mysql/unit/BaseUnitTestSpec.scala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Expedia, Inc. + * Copyright 2019 Expedia, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.