All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zipkin2.storage.mysql.v1.DependencyLinkV2SpanIterator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.storage.mysql.v1;

import java.util.Iterator;
import java.util.NoSuchElementException;
import org.jooq.Record;
import org.jooq.TableField;
import zipkin2.Endpoint;
import zipkin2.Span;
import zipkin2.internal.Nullable;
import zipkin2.storage.mysql.v1.internal.generated.tables.ZipkinSpans;
import zipkin2.v1.V1BinaryAnnotation;

import static zipkin2.storage.mysql.v1.Schema.maybeGet;
import static zipkin2.storage.mysql.v1.internal.generated.tables.ZipkinAnnotations.ZIPKIN_ANNOTATIONS;

/**
 * Lazy converts rows into {@linkplain Span} objects suitable for dependency links. This takes
 * short-cuts to require less data. For example, it folds shared RPC spans into one, and doesn't
 * include tags, non-core annotations or time units.
 *
 * 

Out-of-date schemas may be missing the trace_id_high field. When present, the {@link * Span#traceId()} could be 32 characters in logging statements. */ final class DependencyLinkV2SpanIterator implements Iterator { /** Assumes the input records are sorted by trace id, span id */ static final class ByTraceId implements Iterator> { final PeekingIterator delegate; final boolean hasTraceIdHigh; long currentTraceIdHi, currentTraceIdLo; ByTraceId(Iterator delegate, boolean hasTraceIdHigh) { this.delegate = new PeekingIterator<>(delegate); this.hasTraceIdHigh = hasTraceIdHigh; } @Override public boolean hasNext() { return delegate.hasNext(); } @Override public Iterator next() { if (!hasNext()) throw new NoSuchElementException(); currentTraceIdHi = hasTraceIdHigh ? traceIdHigh(delegate) : 0L; currentTraceIdLo = delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID); return new DependencyLinkV2SpanIterator(delegate, currentTraceIdHi, currentTraceIdLo); } @Override public void remove() { throw new UnsupportedOperationException(); } } final PeekingIterator delegate; final long traceIdHi, traceIdLo; DependencyLinkV2SpanIterator(PeekingIterator delegate, long traceIdHi, long traceIdLo) { this.delegate = delegate; this.traceIdHi = traceIdHi; this.traceIdLo = traceIdLo; } @Override public boolean hasNext() { return delegate.hasNext() // We don't have a query parameter for strictTraceId when fetching dependency links, so we // ignore traceIdHigh. Otherwise, a single trace can appear as two, doubling callCount. && delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID) == traceIdLo; } @Override public Span next() { if (!hasNext()) throw new NoSuchElementException(); Record row = delegate.peek(); long spanId = row.getValue(ZipkinSpans.ZIPKIN_SPANS.ID); boolean error = false; String lcService = null, srService = null, csService = null, caService = null, saService = null, maService = null, mrService = null, msService = null; while (hasNext()) { // there are more values for this trace if (spanId != delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.ID)) { break; // if we are in a new span } Record next = delegate.next(); // row for the same span String key = emptyToNull(next, ZIPKIN_ANNOTATIONS.A_KEY); String value = emptyToNull(next, ZIPKIN_ANNOTATIONS.ENDPOINT_SERVICE_NAME); if (key == null || value == null) continue; // neither client nor server switch (key) { case "lc": lcService = value; break; case "ca": caService = value; break; case "cs": csService = value; break; case "ma": maService = value; break; case "mr": mrService = value; break; case "ms": msService = value; break; case "sa": saService = value; break; case "sr": srService = value; break; case "error": // a span is in error if it has a tag, not an annotation, of name "error" error = V1BinaryAnnotation.TYPE_STRING == next.get(ZIPKIN_ANNOTATIONS.A_TYPE); } } // The client address is more authoritative than the client send owner. if (caService == null) caService = csService; // Finagle labels two sides of the same socket ("ca", "sa") with the same name. // Skip the client side, so it isn't mistaken for a loopback request if (saService != null && saService.equals(caService)) caService = null; long parentId = maybeGet(row, ZipkinSpans.ZIPKIN_SPANS.PARENT_ID, 0L); Span.Builder result = Span.newBuilder().traceId(traceIdHi, traceIdLo).parentId(parentId).id(spanId); if (error) { result.putTag("error", "" /* actual value doesn't matter */); } if (srService != null) { return result .kind(Span.Kind.SERVER) .localEndpoint(ep(srService)) .remoteEndpoint(ep(caService)) .build(); } else if (saService != null) { Endpoint localEndpoint = ep(caService); // When span.kind is missing, the local endpoint is "lc" and the remote endpoint is "sa" if (localEndpoint == null) localEndpoint = ep(lcService); return result .kind(csService != null ? Span.Kind.CLIENT : null) .localEndpoint(localEndpoint) .remoteEndpoint(ep(saService)) .build(); } else if (csService != null) { return result.kind(Span.Kind.SERVER).localEndpoint(ep(caService)).build(); } else if (mrService != null) { return result .kind(Span.Kind.CONSUMER) .localEndpoint(ep(mrService)) .remoteEndpoint(ep(maService)) .build(); } else if (msService != null) { return result .kind(Span.Kind.PRODUCER) .localEndpoint(ep(msService)) .remoteEndpoint(ep(maService)) .build(); } return result.build(); } @Override public void remove() { throw new UnsupportedOperationException(); } static long traceIdHigh(PeekingIterator delegate) { return delegate.peek().getValue(ZipkinSpans.ZIPKIN_SPANS.TRACE_ID_HIGH); } static @Nullable String emptyToNull(Record next, TableField field) { String result = next.getValue(field); return result != null && !result.isEmpty() ? result : null; } static Endpoint ep(@Nullable String serviceName) { return serviceName != null ? Endpoint.newBuilder().serviceName(serviceName).build() : null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy