/**
* 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.cassandra3;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import zipkin.Annotation;
import zipkin.BinaryAnnotation;
import zipkin.Endpoint;
import zipkin.Span;
import zipkin.TestObjects;
import zipkin.internal.ApplyTimestampAndDuration;
import zipkin.internal.Util;
import zipkin.storage.QueryRequest;
import zipkin.storage.SpanStoreTest;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
public class CassandraSpanStoreTest extends SpanStoreTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private final Cassandra3Storage storage;
public CassandraSpanStoreTest() {
this.storage = Cassandra3TestGraph.INSTANCE.storage.get();
}
@Override protected Cassandra3Storage storage() {
return storage;
}
@Override public void clear() {
storage.clear();
}
/** Cassandra indexing is performed separately, allowing the raw span to be stored unaltered. */
@Test
public void rawTraceStoredWithoutAdjustments() {
Span rawSpan = TestObjects.TRACE.get(0).toBuilder().timestamp(null).duration(null).build();
accept(rawSpan);
// At query time, timestamp and duration are added.
assertThat(store().getTrace(rawSpan.traceIdHigh, rawSpan.traceId))
.containsExactly(ApplyTimestampAndDuration.apply(rawSpan));
// Unlike other stores, Cassandra can show that timestamp and duration weren't reported
assertThat(store().getRawTrace(rawSpan.traceIdHigh, rawSpan.traceId))
.containsExactly(rawSpan);
}
@Test
public void overFetchesToCompensateForDuplicateIndexData() {
int traceCount = 100;
List<Span> spans = new ArrayList<>();
for (int i = 0; i < traceCount; i++) {
final long delta = i * 1000; // all timestamps happen a millisecond later
for (Span s : TestObjects.TRACE) {
spans.add(TestObjects.TRACE.get(0).toBuilder()
.traceId(s.traceId + i * 10)
.id(s.id + i * 10)
.timestamp(s.timestamp + delta)
.annotations(s.annotations.stream()
.map(a -> Annotation.create(a.timestamp + delta, a.value, a.endpoint))
.collect(toList()))
.build());
}
}
accept(spans.toArray(new Span[spans.size()]));
// Index ends up containing more rows than services * trace count, and cannot be de-duped
// in a server-side query.
assertThat(rowCount(Schema.TABLE_TRACE_BY_SERVICE_SPAN))
.isGreaterThan(traceCount * store().getServiceNames().size());
// Implementation over-fetches on the index to allow the user to receive unsurprising results.
assertThat(
store().getTraces(QueryRequest.builder().lookback(86400000L).limit(traceCount).build()))
.hasSize(traceCount);
}
@Test
public void searchingByAnnotationShouldFilterBeforeLimiting() {
long now = System.currentTimeMillis();
int queryLimit = 2;
Endpoint endpoint = TestObjects.LOTS_OF_SPANS[0].annotations.get(0).endpoint;
BinaryAnnotation ba = BinaryAnnotation.create("host.name", "host1", endpoint);
int nbTraceFetched = queryLimit * storage.indexFetchMultiplier;
IntStream.range(0, nbTraceFetched).forEach(i ->
accept(TestObjects.LOTS_OF_SPANS[i++].toBuilder().timestamp(now - (i * 1000)).build())
);
// Add two traces with the binary annotation we're looking for
IntStream.range(nbTraceFetched, nbTraceFetched + 2).forEach(i ->
accept(TestObjects.LOTS_OF_SPANS[i++].toBuilder().timestamp(now - (i * 1000))
.addBinaryAnnotation(ba)
.build())
);
QueryRequest queryRequest =
QueryRequest.builder()
.addBinaryAnnotation(ba.key, new String(ba.value, Util.UTF_8))
.serviceName(endpoint.serviceName)
.limit(queryLimit)
.build();
assertThat(store().getTraces(queryRequest)).hasSize(queryLimit);
}
long rowCount(String table) {
return storage.session().execute("SELECT COUNT(*) from " + table).one().getLong(0);
}
}