/** * Copyright 2015-2017 The OpenZipkin Authors * * 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 zipkin.storage.mysql; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.jooq.DSLContext; import org.jooq.InsertSetMoreStep; import org.jooq.Query; import org.jooq.Record; import org.jooq.TableField; import zipkin.Annotation; import zipkin.BinaryAnnotation; import zipkin.Span; import zipkin.storage.AsyncSpanConsumer; import zipkin.storage.StorageAdapters; import static zipkin.internal.ApplyTimestampAndDuration.authoritativeTimestamp; import static zipkin.internal.ApplyTimestampAndDuration.guessTimestamp; import static zipkin.storage.mysql.internal.generated.tables.ZipkinAnnotations.ZIPKIN_ANNOTATIONS; import static zipkin.storage.mysql.internal.generated.tables.ZipkinSpans.ZIPKIN_SPANS; final class MySQLSpanConsumer implements StorageAdapters.SpanConsumer { private final DataSource datasource; private final DSLContexts context; private final Schema schema; MySQLSpanConsumer(DataSource datasource, DSLContexts context, Schema schema) { this.datasource = datasource; this.context = context; this.schema = schema; } /** Blocking version of {@link AsyncSpanConsumer#accept} */ @Override public void accept(List<Span> spans) { if (spans.isEmpty()) return; try (Connection conn = datasource.getConnection()) { DSLContext create = context.get(conn); List<Query> inserts = new ArrayList<>(); for (Span span : spans) { Long overridingTimestamp = authoritativeTimestamp(span); Long timestamp = overridingTimestamp != null ? overridingTimestamp : guessTimestamp(span); Map<TableField<Record, ?>, Object> updateFields = new LinkedHashMap<>(); if (!span.name.equals("") && !span.name.equals("unknown")) { updateFields.put(ZIPKIN_SPANS.NAME, span.name); } // replace any tentative timestamp with the authoritative one. if (overridingTimestamp != null) { updateFields.put(ZIPKIN_SPANS.START_TS, overridingTimestamp); } if (span.duration != null) { updateFields.put(ZIPKIN_SPANS.DURATION, span.duration); } InsertSetMoreStep<Record> insertSpan = create.insertInto(ZIPKIN_SPANS) .set(ZIPKIN_SPANS.TRACE_ID, span.traceId) .set(ZIPKIN_SPANS.ID, span.id) .set(ZIPKIN_SPANS.PARENT_ID, span.parentId) .set(ZIPKIN_SPANS.NAME, span.name) .set(ZIPKIN_SPANS.DEBUG, span.debug) .set(ZIPKIN_SPANS.START_TS, timestamp) .set(ZIPKIN_SPANS.DURATION, span.duration); if (span.traceIdHigh != 0 && schema.hasTraceIdHigh) { insertSpan.set(ZIPKIN_SPANS.TRACE_ID_HIGH, span.traceIdHigh); } inserts.add(updateFields.isEmpty() ? insertSpan.onDuplicateKeyIgnore() : insertSpan.onDuplicateKeyUpdate().set(updateFields)); for (Annotation annotation : span.annotations) { InsertSetMoreStep<Record> insert = create.insertInto(ZIPKIN_ANNOTATIONS) .set(ZIPKIN_ANNOTATIONS.TRACE_ID, span.traceId) .set(ZIPKIN_ANNOTATIONS.SPAN_ID, span.id) .set(ZIPKIN_ANNOTATIONS.A_KEY, annotation.value) .set(ZIPKIN_ANNOTATIONS.A_TYPE, -1) .set(ZIPKIN_ANNOTATIONS.A_TIMESTAMP, annotation.timestamp); if (span.traceIdHigh != 0 && schema.hasTraceIdHigh) { insert.set(ZIPKIN_ANNOTATIONS.TRACE_ID_HIGH, span.traceIdHigh); } if (annotation.endpoint != null) { insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_SERVICE_NAME, annotation.endpoint.serviceName); insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_IPV4, annotation.endpoint.ipv4); if (annotation.endpoint.ipv6 != null && schema.hasIpv6) { insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_IPV6, annotation.endpoint.ipv6); } insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_PORT, annotation.endpoint.port); } inserts.add(insert.onDuplicateKeyIgnore()); } for (BinaryAnnotation annotation : span.binaryAnnotations) { InsertSetMoreStep<Record> insert = create.insertInto(ZIPKIN_ANNOTATIONS) .set(ZIPKIN_ANNOTATIONS.TRACE_ID, span.traceId) .set(ZIPKIN_ANNOTATIONS.SPAN_ID, span.id) .set(ZIPKIN_ANNOTATIONS.A_KEY, annotation.key) .set(ZIPKIN_ANNOTATIONS.A_VALUE, annotation.value) .set(ZIPKIN_ANNOTATIONS.A_TYPE, annotation.type.value) .set(ZIPKIN_ANNOTATIONS.A_TIMESTAMP, timestamp); if (span.traceIdHigh != 0 && schema.hasTraceIdHigh) { insert.set(ZIPKIN_ANNOTATIONS.TRACE_ID_HIGH, span.traceIdHigh); } if (annotation.endpoint != null) { insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_SERVICE_NAME, annotation.endpoint.serviceName); insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_IPV4, annotation.endpoint.ipv4); if (annotation.endpoint.ipv6 != null && schema.hasIpv6) { insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_IPV6, annotation.endpoint.ipv6); } insert.set(ZIPKIN_ANNOTATIONS.ENDPOINT_PORT, annotation.endpoint.port); } inserts.add(insert.onDuplicateKeyIgnore()); } } create.batch(inserts).execute(); } catch (SQLException e) { throw new RuntimeException(e); // TODO } } }