/** * 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.elasticsearch.http; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import okhttp3.HttpUrl; import okhttp3.Request; import okhttp3.RequestBody; import okio.Buffer; import zipkin.internal.Nullable; import zipkin.storage.Callback; import zipkin.storage.elasticsearch.http.internal.client.HttpCall; import static zipkin.storage.elasticsearch.http.ElasticsearchHttpStorage.APPLICATION_JSON; // See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html // exposed to re-use for testing writes of dependency links final class HttpBulkIndexer { final String tag; final HttpCall.Factory http; final String pipeline; final boolean flushOnWrites; // Mutated for each call to add final Buffer body = new Buffer(); final Set<String> indices = new LinkedHashSet<>(); HttpBulkIndexer(String tag, ElasticsearchHttpStorage es) { this.tag = tag; http = es.http(); pipeline = es.pipeline(); flushOnWrites = es.flushOnWrites(); } void add(String index, String typeName, byte[] document, @Nullable String id) { writeIndexMetadata(index, typeName, id); writeDocument(document); } void writeIndexMetadata(String index, String typeName, @Nullable String id) { if (flushOnWrites) indices.add(index); body.writeUtf8("{\"index\":{\"_index\":\"").writeUtf8(index).writeByte('"'); body.writeUtf8(",\"_type\":\"").writeUtf8(typeName).writeByte('"'); if (id != null) { body.writeUtf8(",\"_id\":\"").writeUtf8(id).writeByte('"'); } body.writeUtf8("}}\n"); } void writeDocument(byte[] document) { body.write(document); body.writeByte('\n'); } /** Creates a bulk request when there is more than one object to store */ void execute(Callback<Void> callback) { HttpUrl url = pipeline != null ? http.baseUrl.newBuilder("_bulk").addQueryParameter("pipeline", pipeline).build() : http.baseUrl.resolve("_bulk"); Request request = new Request.Builder().url(url).tag(tag) .post(RequestBody.create(APPLICATION_JSON, body.readByteString())).build(); http.<Void>newCall(request, b -> { if (indices.isEmpty()) return null; ElasticsearchHttpStorage.flush(http, join(indices)); return null; }).submit(callback); } static String join(Collection<String> parts) { Iterator<String> iterator = parts.iterator(); StringBuilder result = new StringBuilder(iterator.next()); while (iterator.hasNext()) { result.append(',').append(iterator.next()); } return result.toString(); } }