Skip to content

Commit f730da4

Browse files
adriancoleabesto
authored andcommitted
Adds Elasticsearch 6.x support using Span2 model (openzipkin#1674)
This adds Elasticsearch 6.x support via single-type indexes: * zipkin:span-2017-08-05 - span2 (single endpoint) format * zipkin:dependency-2017-08-05 - dependency links in existing format This indexing model will be available in the next minor release of Zipkin, particularly for Elasticsearch 2.4+. If you aren't running Elasticsearch 2.4+, yet. Please upgrade. Those wishing to experiment with this format before the next minor release can set `ES_EXPERIMENTAL_SPAN2=true` to use this style now. When set, writes will use the above scheme, but both the former and new indexes will be read.
1 parent 8c71ecc commit f730da4

File tree

43 files changed

+3021
-913
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3021
-913
lines changed

zipkin-autoconfigure/storage-elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ZipkinElasticsearchHttpStorageAutoConfigurationTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import static org.assertj.core.api.Assertions.assertThat;
3636
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
37+
import static zipkin.storage.elasticsearch.http.ElasticsearchHttpSpanStore.SPAN;
3738

3839
public class ZipkinElasticsearchHttpStorageAutoConfigurationTest {
3940

@@ -246,8 +247,8 @@ public void dailyIndexFormat() {
246247
ZipkinElasticsearchHttpStorageAutoConfiguration.class);
247248
context.refresh();
248249

249-
assertThat(es().indexNameFormatter().indexNameForTimestamp(0))
250-
.isEqualTo("zipkin-1970-01-01");
250+
assertThat(es().indexNameFormatter().formatTypeAndTimestamp(SPAN, 0))
251+
.isEqualTo("zipkin:span-1970-01-01");
251252
}
252253

253254
@Test
@@ -262,8 +263,8 @@ public void dailyIndexFormat_overridingPrefix() {
262263
ZipkinElasticsearchHttpStorageAutoConfiguration.class);
263264
context.refresh();
264265

265-
assertThat(es().indexNameFormatter().indexNameForTimestamp(0))
266-
.isEqualTo("zipkin_prod-1970-01-01");
266+
assertThat(es().indexNameFormatter().formatTypeAndTimestamp(SPAN, 0))
267+
.isEqualTo("zipkin_prod:span-1970-01-01");
267268
}
268269

269270
@Test
@@ -278,8 +279,8 @@ public void dailyIndexFormat_overridingDateSeparator() {
278279
ZipkinElasticsearchHttpStorageAutoConfiguration.class);
279280
context.refresh();
280281

281-
assertThat(es().indexNameFormatter().indexNameForTimestamp(0))
282-
.isEqualTo("zipkin-1970.01.01");
282+
assertThat(es().indexNameFormatter().formatTypeAndTimestamp(SPAN, 0))
283+
.isEqualTo("zipkin:span-1970.01.01");
283284
}
284285

285286
@Test

zipkin-storage/cassandra3/src/test/java/zipkin/storage/cassandra3/integration/CassandraTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class CassandraTest {
3030

3131
@ClassRule
3232
public static LazyCassandra3Storage storage =
33-
new LazyCassandra3Storage("cassandra:3.10", "test_zipkin3");
33+
new LazyCassandra3Storage("openzipkin/zipkin-cassandra:1.29.1", "test_zipkin3");
3434

3535
public static class DependenciesTest extends CassandraDependenciesTest {
3636
@Override protected Cassandra3Storage storage() {

zipkin-storage/elasticsearch-http/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ That said, all integration tests run on pull request via Travis.
171171

172172
### Debugging tests
173173
To see each http message sent to elasticsearch during testing, export the
174-
environment variable `ES_DEBUG=true`.
174+
environment variable `ES_DEBUG=true`. This will also show output from the
175+
docker container.
175176

176177
Note: this will produce a lot of output!
177178

zipkin-storage/elasticsearch-http/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,12 @@
9292
<artifactId>testcontainers</artifactId>
9393
<scope>test</scope>
9494
</dependency>
95+
96+
<dependency>
97+
<groupId>org.mockito</groupId>
98+
<artifactId>mockito-core</artifactId>
99+
<version>${mockito.version}</version>
100+
<scope>test</scope>
101+
</dependency>
95102
</dependencies>
96103
</project>

zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumer.java

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,21 @@
1313
*/
1414
package zipkin.storage.elasticsearch.http;
1515

16-
import com.squareup.moshi.JsonWriter;
1716
import java.io.IOException;
18-
import java.util.LinkedHashMap;
19-
import java.util.LinkedHashSet;
2017
import java.util.List;
21-
import java.util.Map;
22-
import java.util.Set;
2318
import java.util.concurrent.TimeUnit;
24-
import okio.Buffer;
25-
import zipkin.Codec;
2619
import zipkin.Span;
27-
import zipkin.internal.Pair;
20+
import zipkin.internal.Nullable;
21+
import zipkin.internal.Span2;
22+
import zipkin.internal.Span2Codec;
23+
import zipkin.internal.Span2Converter;
2824
import zipkin.storage.AsyncSpanConsumer;
2925
import zipkin.storage.Callback;
3026

3127
import static zipkin.internal.ApplyTimestampAndDuration.guessTimestamp;
3228
import static zipkin.internal.Util.UTF_8;
3329
import static zipkin.internal.Util.propagateIfFatal;
34-
import static zipkin.storage.elasticsearch.http.ElasticsearchHttpSpanStore.SERVICE_SPAN;
30+
import static zipkin.storage.elasticsearch.http.ElasticsearchHttpSpanStore.SPAN;
3531

3632
class ElasticsearchHttpSpanConsumer implements AsyncSpanConsumer { // not final for testing
3733

@@ -49,76 +45,64 @@ class ElasticsearchHttpSpanConsumer implements AsyncSpanConsumer { // not final
4945
return;
5046
}
5147
try {
52-
HttpBulkIndexer indexer = new HttpBulkIndexer("index-span", es);
53-
Map<String, Set<Pair<String>>> indexToServiceSpans = indexSpans(indexer, spans);
54-
if (!indexToServiceSpans.isEmpty()) {
55-
indexNames(indexer, indexToServiceSpans);
56-
}
48+
BulkSpanIndexer indexer = newBulkSpanIndexer(es);
49+
indexSpans(indexer, spans);
5750
indexer.execute(callback);
5851
} catch (Throwable t) {
5952
propagateIfFatal(t);
6053
callback.onError(t);
6154
}
6255
}
6356

64-
/** Indexes spans and returns a mapping of indexes that may need a names update */
65-
Map<String, Set<Pair<String>>> indexSpans(HttpBulkIndexer indexer, List<Span> spans) {
66-
Map<String, Set<Pair<String>>> indexToServiceSpans = new LinkedHashMap<>();
57+
void indexSpans(BulkSpanIndexer indexer, List<Span> spans) throws IOException {
6758
for (Span span : spans) {
6859
Long timestamp = guessTimestamp(span);
69-
Long timestampMillis;
70-
String index; // which index to store this span into
60+
long indexTimestamp = 0L; // which index to store this span into
61+
Long spanTimestamp;
7162
if (timestamp != null) {
72-
timestampMillis = TimeUnit.MICROSECONDS.toMillis(timestamp);
73-
index = indexNameFormatter.indexNameForTimestamp(timestampMillis);
63+
indexTimestamp = spanTimestamp = TimeUnit.MICROSECONDS.toMillis(timestamp);
7464
} else {
75-
timestampMillis = null;
65+
spanTimestamp = null;
7666
// guessTimestamp is made for determining the span's authoritative timestamp. When choosing
7767
// the index bucket, any annotation is better than using current time.
78-
Long indexTimestamp = null;
7968
for (int i = 0, length = span.annotations.size(); i < length; i++) {
8069
indexTimestamp = span.annotations.get(i).timestamp / 1000;
8170
break;
8271
}
83-
if (indexTimestamp == null) indexTimestamp = System.currentTimeMillis();
84-
index = indexNameFormatter.indexNameForTimestamp(indexTimestamp);
72+
if (indexTimestamp == 0L) indexTimestamp = System.currentTimeMillis();
8573
}
86-
if (!span.name.isEmpty()) putServiceSpans(indexToServiceSpans, index, span);
87-
byte[] document = Codec.JSON.writeSpan(span);
88-
if (timestampMillis != null) document = prefixWithTimestampMillis(document, timestampMillis);
89-
indexer.add(index, ElasticsearchHttpSpanStore.SPAN, document, null /* Allow ES to choose an ID */);
74+
indexer.add(indexTimestamp, span, spanTimestamp);
9075
}
91-
return indexToServiceSpans;
9276
}
9377

94-
void putServiceSpans(Map<String, Set<Pair<String>>> indexToServiceSpans, String index, Span s) {
95-
Set<Pair<String>> serviceSpans = indexToServiceSpans.get(index);
96-
if (serviceSpans == null) indexToServiceSpans.put(index, serviceSpans = new LinkedHashSet<>());
97-
for (String serviceName : s.serviceNames()) {
98-
serviceSpans.add(Pair.create(serviceName, s.name));
99-
}
78+
79+
BulkSpanIndexer newBulkSpanIndexer(ElasticsearchHttpStorage es) {
80+
return new BulkSpanIndexer(es);
10081
}
10182

102-
/**
103-
* Adds service and span names to the pending batch. The id is "serviceName|spanName" to prevent
104-
* a large order of duplicates ending up in the daily index. This also means queries do not need
105-
* to deduplicate.
106-
*/
107-
void indexNames(HttpBulkIndexer indexer, Map<String, Set<Pair<String>>> indexToServiceSpans)
108-
throws IOException {
109-
Buffer buffer = new Buffer();
110-
for (Map.Entry<String, Set<Pair<String>>> entry : indexToServiceSpans.entrySet()) {
111-
String index = entry.getKey();
112-
for (Pair<String> serviceSpan : entry.getValue()) {
113-
JsonWriter writer = JsonWriter.of(buffer);
114-
writer.beginObject();
115-
writer.name("serviceName").value(serviceSpan._1);
116-
writer.name("spanName").value(serviceSpan._2);
117-
writer.endObject();
118-
byte[] document = buffer.readByteArray();
119-
indexer.add(index, SERVICE_SPAN, document, serviceSpan._1 + "|" + serviceSpan._2);
83+
static class BulkSpanIndexer {
84+
final HttpBulkIndexer indexer;
85+
final IndexNameFormatter indexNameFormatter;
86+
87+
BulkSpanIndexer(ElasticsearchHttpStorage es) {
88+
this.indexer = new HttpBulkIndexer("index-span", es);
89+
this.indexNameFormatter = es.indexNameFormatter();
90+
}
91+
92+
void add(long indexTimestamp, Span span, @Nullable Long timestampMillis) {
93+
String index = indexNameFormatter.formatTypeAndTimestamp(SPAN, indexTimestamp);
94+
for (Span2 span2 : Span2Converter.fromSpan(span)) {
95+
byte[] document = Span2Codec.JSON.writeSpan(span2);
96+
if (timestampMillis != null) {
97+
document = prefixWithTimestampMillis(document, timestampMillis);
98+
}
99+
indexer.add(index, SPAN, document, null /* Allow ES to choose an ID */);
120100
}
121101
}
102+
103+
void execute(Callback<Void> callback) throws IOException {
104+
indexer.execute(callback);
105+
}
122106
}
123107

124108
private static final byte[] TIMESTAMP_MILLIS_PREFIX = "{\"timestamp_millis\":".getBytes(UTF_8);

0 commit comments

Comments
 (0)