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

com.netflix.ndbench.plugin.es.EsWriter Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2021 Netflix, Inc.
 *
 *  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 com.netflix.ndbench.plugin.es;

import com.netflix.ndbench.api.plugin.DataGenerator;
import org.apache.http.StatusLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.UUID;


/**
 * Writes instances of JSON documents returned by {@link EsUtils#createDefaultDocumentAsJson}
 * to Elasticsearch either via single REST calls, or via the bulk write API.
 * 

* The responsibilities of this class are to: *

* - Determine if a particular write operation is done via Elasticsearch's bulk API or not (using the value of * the isBulkWrite boolean passed to this class's constructor) *

* - Immediately issue a PUT of the document to Elasticsearch if a given write is determined not * to be handled by bulk write. *

* - Maintain a thread local buffer of pending documents to be bulk written. *

* - Flush the pending buffer once its size equals {@link EsConfig#getBulkWriteBatchSize()}. *

* Note that if a document is written via bulk write its isBulkWrite attribute will be "true". * Note that any exception thrown will be bubbled up to the driver and * will result in a failed write being recorded. *

*/ class EsWriter { private static final Logger logger = LoggerFactory.getLogger(EsWriter.class); private final String indexName; private final String docType; private final int bulkWriteBatchSize; private final boolean isBulkWrite; private final int indexRollsPerDay; private final DataGenerator dataGenerator; /** * Returns a writer whose {@link EsWriter#writeDocument } method will issue writes to 'indexName' and 'docType'. * * @param indexName - index name to which writes will be targeted (with possibly appended date pattern, * as determined by {@link EsConfig#getIndexRollsPerDay()} * @param docType - document type (of index named esIndexName) to which writes will be targeted. * @param isBulkWrite - whether to perform 1 write or a batch of writes in the context of a writeSingle call. * @param indexRollsPerDay - a value determined by the configuration setting {@link EsConfig#getIndexRollsPerDay()} * @param bulkWriteBatchSize - the size the bulk write queue must reach before a bulk write operation * is performed and the queue is flushed (ignored if isBulkWrite = false, * but nevertheless cannot be less than or equal to zero.) * @param dataGenerator - data generator used to inject random values into documents written to Elasticsearch. */ EsWriter(String indexName, String docType, boolean isBulkWrite, int indexRollsPerDay, int bulkWriteBatchSize, DataGenerator dataGenerator) { if (bulkWriteBatchSize < 0) { throw new IllegalArgumentException("bulkWriteBatchSize cannot be less than to zero"); } if (!isBulkWrite && indexRollsPerDay > 0) { throw new IllegalArgumentException( "getIndexRollsPerDay fast property only makes sense to be set when isBulkWrite is set"); } this.docType = docType; this.indexName = indexName; this.bulkWriteBatchSize = bulkWriteBatchSize; this.isBulkWrite = isBulkWrite; this.indexRollsPerDay = indexRollsPerDay; this.dataGenerator = dataGenerator; } /** * Issues writes to esIndexName' and esDocType given 'restClient' * (which determines the host/port of the Elasticsearch cluster to write to) */ WriteResult writeDocument(EsRestClient restClient, String key, Boolean randomizeKeys) throws Exception { if (isBulkWrite) { writeBulk(restClient, key, randomizeKeys); } else { writeSingle(restClient, key, randomizeKeys); } return WriteResult.PROVISIONAL_RESULT_THAT_ASSUMES_ALL_WENT_WELL; } private void writeSingle(EsRestClient restClient, String key, Boolean randomizeKeys) throws IOException { String randomizedKey = key + (randomizeKeys ? UUID.randomUUID().toString() : ""); String doc = EsUtils.createDefaultDocumentAsJson(dataGenerator, false); StatusLine response = restClient.writeSingleDocument(this.indexName, this.docType, randomizedKey, doc); logger.debug("Writing document id=[{}] to index [{}], response=[{}]", randomizedKey, indexName, response); int responseCode = response.getStatusCode(); if (responseCode != 200 && responseCode != 201) { throw new RuntimeException("Write operation failed for " + this.indexName + ". Response: " + response); } } private void writeBulk(EsRestClient restClient, String key, Boolean randomizeKeys) throws IOException { StringBuilder stringBuilder = new StringBuilder(); String indexName = constructIndexName(this.indexName, this.indexRollsPerDay, new Date()); for (int i = 0; i < bulkWriteBatchSize; i++) { String doc = EsUtils.createDefaultDocumentAsJson(dataGenerator, true); String randomizedKey = key + (randomizeKeys ? UUID.randomUUID().toString() : ""); stringBuilder.append(getBulkWriteEntry(randomizedKey, doc, indexName, this.docType)); } String bulkPayload = stringBuilder.toString(); StatusLine response = restClient.writeDocumentsBulk(bulkPayload); if (logger.isTraceEnabled()) { logger.trace("Received [{}] after sending bulk write payload of [{}]", response, bulkPayload); } else { logger.debug("Received [{}] after sending bulk write payload", response); } } private String getBulkWriteEntry(String key, String doc, String indexName, String docType) { String bulkWriteEntry = String.format( "{\"index\":{\"_index\":\"%s\",\"_type\":\"%s\",\"_id\":\"%s\"}}\n%s\n", indexName, docType, key, doc); logger.trace("Bulk write entry for one doc: {}", bulkWriteEntry); return bulkWriteEntry; } /** * Methods below are package scoped to facilitate unit testing */ static String constructIndexName(String indexName, int indexRollsPerDay, Date date) { if (indexRollsPerDay > 0) { ZonedDateTime zdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC")); int minutesPerRoll = 1440 / indexRollsPerDay; int minutesElapsedSinceStartOfDay = zdt.getHour() * 60 + zdt.getMinute(); int nthRoll = minutesElapsedSinceStartOfDay / minutesPerRoll; String timestampedIndexName = String.format("%s-%s.%04d", indexName, zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), nthRoll); logger.debug("constructIndexName from rolls per day = {} gives: {}", indexRollsPerDay, timestampedIndexName); return timestampedIndexName; } else { return indexName; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy