/** * Copyright 2015-2016 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.util.Iterator; import org.jooq.Record; import zipkin.internal.DependencyLinkSpan; import zipkin.internal.Nullable; import zipkin.internal.PeekingIterator; import zipkin.storage.mysql.internal.generated.tables.ZipkinSpans; import static zipkin.Constants.CLIENT_ADDR; import static zipkin.Constants.CLIENT_SEND; import static zipkin.Constants.SERVER_ADDR; import static zipkin.Constants.SERVER_RECV; import static zipkin.storage.mysql.internal.generated.tables.ZipkinAnnotations.ZIPKIN_ANNOTATIONS; /** * Convenience that lazy converts rows into {@linkplain DependencyLinkSpan} objects. * * <p>Out-of-date schemas may be missing the trace_id_high field. When present, this becomes {@link * DependencyLinkSpan.TraceId#hi} used as the left-most 16 characters of the traceId in logging * statements. */ final class DependencyLinkSpanIterator implements Iterator<DependencyLinkSpan> { /** Assumes the input records are sorted by trace id, span id */ static final class ByTraceId implements Iterator<Iterator<DependencyLinkSpan>> { final PeekingIterator<Record> delegate; final boolean hasTraceIdHigh; @Nullable Long currentTraceIdHi; long currentTraceIdLo; ByTraceId(Iterator<Record> delegate, boolean hasTraceIdHigh) { this.delegate = new PeekingIterator<>(delegate); this.hasTraceIdHigh = hasTraceIdHigh; } @Override public boolean hasNext() { return delegate.hasNext(); } @Override public Iterator<DependencyLinkSpan> next() { currentTraceIdHi = hasTraceIdHigh ? traceIdHigh(delegate) : null; currentTraceIdLo = delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID); return new DependencyLinkSpanIterator(delegate, currentTraceIdHi, currentTraceIdLo); } @Override public void remove() { throw new UnsupportedOperationException(); } } final PeekingIterator<Record> delegate; @Nullable final Long traceIdHi; final long traceIdLo; DependencyLinkSpanIterator(PeekingIterator<Record> delegate, Long traceIdHi, long traceIdLo) { this.delegate = delegate; this.traceIdHi = traceIdHi; this.traceIdLo = traceIdLo; } @Override public boolean hasNext() { return delegate.hasNext() && (traceIdHi == null || traceIdHi.equals(traceIdHigh(delegate))) && delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID) == traceIdLo; } @Override public DependencyLinkSpan next() { Record row = delegate.next(); DependencyLinkSpan.Builder result = DependencyLinkSpan.builder( traceIdHi != null ? traceIdHi : 0L, traceIdLo, row.getValue(ZipkinSpans.ZIPKIN_SPANS.PARENT_ID), row.getValue(ZipkinSpans.ZIPKIN_SPANS.ID) ); parseClientAndServerNames( result, row.getValue(ZIPKIN_ANNOTATIONS.A_KEY), row.getValue(ZIPKIN_ANNOTATIONS.ENDPOINT_SERVICE_NAME)); while (hasNext()) { Record next = delegate.peek(); if (next == null) { continue; } if (row.getValue(ZipkinSpans.ZIPKIN_SPANS.ID).equals(next.getValue(ZipkinSpans.ZIPKIN_SPANS.ID))) { delegate.next(); // advance the iterator since we are in the same span id parseClientAndServerNames( result, next.getValue(ZIPKIN_ANNOTATIONS.A_KEY), next.getValue(ZIPKIN_ANNOTATIONS.ENDPOINT_SERVICE_NAME)); } else { break; } } return result.build(); } void parseClientAndServerNames(DependencyLinkSpan.Builder span, String key, String value) { if (key == null) return; // neither client nor server switch (key) { case CLIENT_ADDR: span.caService(value); break; case CLIENT_SEND: span.csService(value); break; case SERVER_ADDR: span.saService(value); break; case SERVER_RECV: span.srService(value); } } @Override public void remove() { throw new UnsupportedOperationException(); } static long traceIdHigh(PeekingIterator<Record> delegate) { return delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID_HIGH); } }