Merge branch 'master' into index-lifecycle
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestCon verters.java /Users/colings86/dev/work/git/elasticsearch/.git/worktrees/elasticsearch -ilm/MERGE_HEAD client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseCli ent.java client/rest-high-level/src/main/java/org/elasticsearch/client/RequestCon verters.java client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.j ava client/rest-high-level/src/test/java/org/elasticsearch/client/documentat ion/LicensingDocumentationIT.java docs/java-rest/high-level/licensing/delete-license.asciidoc server/src/main/java/org/elasticsearch/action/bulk/BulkPrimaryExecutionC ontext.java server/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.j ava server/src/main/java/org/elasticsearch/common/Rounding.java server/src/main/java/org/elasticsearch/common/rounding/Rounding.java server/src/main/java/org/elasticsearch/search/aggregations/bucket/signif icant/ParsedSignificantTerms.java server/src/test/java/org/elasticsearch/action/IndicesRequestIT.java server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIn gestTests.java server/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkAct ionTests.java server/src/test/java/org/elasticsearch/common/RoundingTests.java server/src/test/java/org/elasticsearch/common/rounding/DateTimeUnitTests .java server/src/test/java/org/elasticsearch/common/rounding/RoundingDuelTests .java server/src/test/java/org/elasticsearch/search/aggregations/bucket/signif icant/SignificantLongTermsTests.java server/src/test/java/org/elasticsearch/search/aggregations/bucket/signif icant/SignificantStringTermsTests.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/DeleteLicense Action.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/DeleteLicense Request.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/DeleteLicense RequestBuilder.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/DeleteLicense Response.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseServic e.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClie nt.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestDeleteLic enseAction.java x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDele teLicenseAction.java x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManag erServiceTests.java x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesTrans portTests.java x-pack/protocol/src/main/java/org/elasticsearch/protocol/xpack/license/D eleteLicenseRequest.java x-pack/protocol/src/main/java/org/elasticsearch/protocol/xpack/license/D eleteLicenseResponse.java x-pack/protocol/src/test/java/org/elasticsearch/protocol/xpack/license/D eleteLicenseResponseTests.java
This commit is contained in:
commit
a84b3239c3
|
@ -29,6 +29,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
|
@ -98,6 +100,27 @@ public final class LicenseClient {
|
|||
response -> new GetLicenseResponse(convertResponseToJson(response)), listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes license from the cluster.
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response
|
||||
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||
*/
|
||||
public DeleteLicenseResponse deleteLicense(DeleteLicenseRequest request, RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::deleteLicense, options,
|
||||
DeleteLicenseResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously deletes license from the cluster.
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @param listener the listener to be notified upon request completion
|
||||
*/
|
||||
public void deleteLicenseAsync(DeleteLicenseRequest request, RequestOptions options, ActionListener<DeleteLicenseResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::deleteLicense, options,
|
||||
DeleteLicenseResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an entire response into a json string
|
||||
*
|
||||
|
|
|
@ -108,6 +108,7 @@ import org.elasticsearch.index.VersionType;
|
|||
import org.elasticsearch.index.rankeval.RankEvalRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackUsageRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.indexlifecycle.ExplainLifecycleRequest;
|
||||
import org.elasticsearch.protocol.xpack.indexlifecycle.SetIndexLifecyclePolicyRequest;
|
||||
import org.elasticsearch.protocol.xpack.indexlifecycle.StartILMRequest;
|
||||
|
@ -1234,7 +1235,6 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
|
||||
static Request getLicense(GetLicenseRequest getLicenseRequest) {
|
||||
String endpoint = new EndpointBuilder()
|
||||
.addPathPartAsIs("_xpack")
|
||||
|
@ -1246,6 +1246,14 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request deleteLicense(DeleteLicenseRequest deleteLicenseRequest) {
|
||||
Request request = new Request(HttpDelete.METHOD_NAME, "/_xpack/license");
|
||||
Params parameters = new Params(request);
|
||||
parameters.withTimeout(deleteLicenseRequest.timeout());
|
||||
parameters.withMasterTimeout(deleteLicenseRequest.masterNodeTimeout());
|
||||
return request;
|
||||
}
|
||||
|
||||
static Request putMachineLearningJob(PutJobRequest putJobRequest) throws IOException {
|
||||
String endpoint = new EndpointBuilder()
|
||||
.addPathPartAsIs("_xpack")
|
||||
|
|
|
@ -59,6 +59,9 @@ import org.elasticsearch.search.SearchHit;
|
|||
import org.elasticsearch.search.aggregations.BucketOrder;
|
||||
import org.elasticsearch.search.aggregations.bucket.range.Range;
|
||||
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.bucket.significant.SignificantTerms;
|
||||
import org.elasticsearch.search.aggregations.bucket.significant.SignificantTermsAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.bucket.significant.heuristics.PercentageScore;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.matrix.stats.MatrixStats;
|
||||
|
@ -267,6 +270,33 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||
assertEquals(2, type2.getDocCount());
|
||||
assertEquals(0, type2.getAggregations().asList().size());
|
||||
}
|
||||
|
||||
public void testSearchWithSignificantTermsAgg() throws IOException {
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
searchSourceBuilder.query(new MatchQueryBuilder("num","50"));
|
||||
searchSourceBuilder.aggregation(new SignificantTermsAggregationBuilder("agg1", ValueType.STRING)
|
||||
.field("type.keyword")
|
||||
.minDocCount(1)
|
||||
.significanceHeuristic(new PercentageScore()));
|
||||
searchSourceBuilder.size(0);
|
||||
searchRequest.source(searchSourceBuilder);
|
||||
SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync);
|
||||
assertSearchHeader(searchResponse);
|
||||
assertNull(searchResponse.getSuggest());
|
||||
assertEquals(Collections.emptyMap(), searchResponse.getProfileResults());
|
||||
assertEquals(0, searchResponse.getHits().getHits().length);
|
||||
assertEquals(0f, searchResponse.getHits().getMaxScore(), 0f);
|
||||
SignificantTerms significantTermsAgg = searchResponse.getAggregations().get("agg1");
|
||||
assertEquals("agg1", significantTermsAgg.getName());
|
||||
assertEquals(1, significantTermsAgg.getBuckets().size());
|
||||
SignificantTerms.Bucket type1 = significantTermsAgg.getBucketByKey("type1");
|
||||
assertEquals(1, type1.getDocCount());
|
||||
assertEquals(1, type1.getSubsetDf());
|
||||
assertEquals(1, type1.getSubsetSize());
|
||||
assertEquals(3, type1.getSupersetDf());
|
||||
assertEquals(1d/3d, type1.getSignificanceScore(), 0d);
|
||||
}
|
||||
|
||||
public void testSearchWithRangeAgg() throws IOException {
|
||||
{
|
||||
|
|
|
@ -25,6 +25,9 @@ import org.elasticsearch.action.LatchedActionListener;
|
|||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
|
@ -47,7 +50,7 @@ import static org.hamcrest.Matchers.startsWith;
|
|||
*/
|
||||
public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
public void testPutLicense() throws Exception {
|
||||
public void testLicense() throws Exception {
|
||||
assumeTrue("License is only valid when tested against snapshot/test builds", Build.CURRENT.isSnapshot());
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
String license = "{\"license\": {\"uid\":\"893361dc-9749-4997-93cb-802e3d7fa4a8\",\"type\":\"gold\"," +
|
||||
|
@ -86,7 +89,7 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
// tag::put-license-execute-listener
|
||||
ActionListener<PutLicenseResponse> listener = new ActionListener<PutLicenseResponse>() {
|
||||
@Override
|
||||
public void onResponse(PutLicenseResponse indexResponse) {
|
||||
public void onResponse(PutLicenseResponse putLicenseResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
|
@ -108,6 +111,51 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
// we cannot actually delete the license, otherwise the remaining tests won't work
|
||||
if (Booleans.isTrue("true")) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
//tag::delete-license-execute
|
||||
DeleteLicenseRequest request = new DeleteLicenseRequest();
|
||||
|
||||
DeleteLicenseResponse response = client.license().deleteLicense(request, RequestOptions.DEFAULT);
|
||||
//end::delete-license-execute
|
||||
|
||||
//tag::delete-license-response
|
||||
boolean acknowledged = response.isAcknowledged(); // <1>
|
||||
//end::delete-license-response
|
||||
|
||||
assertTrue(acknowledged);
|
||||
}
|
||||
{
|
||||
DeleteLicenseRequest request = new DeleteLicenseRequest();
|
||||
// tag::delete-license-execute-listener
|
||||
ActionListener<DeleteLicenseResponse> listener = new ActionListener<DeleteLicenseResponse>() {
|
||||
@Override
|
||||
public void onResponse(DeleteLicenseResponse deleteLicenseResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::delete-license-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::delete-license-execute-async
|
||||
client.license().deleteLicenseAsync(
|
||||
request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::delete-license-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetLicense() throws Exception {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
[[java-rest-high-delete-license]]
|
||||
=== Delete License
|
||||
|
||||
[[java-rest-high-delete-license-execution]]
|
||||
==== Execution
|
||||
|
||||
The license can be deleted using the `deleteLicense()` method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[delete-license-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-delete-license-response]]
|
||||
==== Response
|
||||
|
||||
The returned `DeleteLicenseResponse` contains the `acknowledged` flag, which
|
||||
returns true if the request was processed by all nodes.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[delete-license-response]
|
||||
--------------------------------------------------
|
||||
<1> Check the acknowledge flag. It should be true if license deletion is acknowledged.
|
||||
|
||||
[[java-rest-high-delete-license-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
This request can be executed asynchronously:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[delete-license-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `DeleteLicenseRequest` to execute and the `ActionListener` to use when
|
||||
the execution completes
|
||||
|
||||
The asynchronous method does not block and returns immediately. Once it is
|
||||
completed the `ActionListener` is called back using the `onResponse` method
|
||||
if the execution successfully completed or using the `onFailure` method if
|
||||
it failed.
|
||||
|
||||
A typical listener for `DeleteLicenseResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[delete-license-execute-listener]
|
||||
--------------------------------------------------
|
||||
<1> Called when the execution is successfully completed. The response is
|
||||
provided as an argument
|
||||
<2> Called in case of failure. The raised exception is provided as an argument
|
|
@ -194,9 +194,11 @@ The Java High Level REST Client supports the following Licensing APIs:
|
|||
|
||||
* <<java-rest-high-put-license>>
|
||||
* <<java-rest-high-get-license>>
|
||||
* <<java-rest-high-delete-license>>
|
||||
|
||||
include::licensing/put-license.asciidoc[]
|
||||
include::licensing/get-license.asciidoc[]
|
||||
include::licensing/delete-license.asciidoc[]
|
||||
|
||||
== Migration APIs
|
||||
|
||||
|
|
|
@ -290,10 +290,10 @@ class BulkPrimaryExecutionContext {
|
|||
/** finishes the execution of the current request, with the response that should be returned to the user */
|
||||
public void markAsCompleted(BulkItemResponse translatedResponse) {
|
||||
assertInvariants(ItemProcessingState.EXECUTED);
|
||||
assert executionResult == null || translatedResponse.getItemId() == executionResult.getItemId();
|
||||
assert executionResult != null && translatedResponse.getItemId() == executionResult.getItemId();
|
||||
assert translatedResponse.getItemId() == getCurrentItem().id();
|
||||
|
||||
if (translatedResponse.isFailed() == false && requestToExecute != getCurrent()) {
|
||||
if (translatedResponse.isFailed() == false && requestToExecute != null && requestToExecute != getCurrent()) {
|
||||
request.items()[currentIndex] = new BulkItemRequest(request.items()[currentIndex].id(), requestToExecute);
|
||||
}
|
||||
getCurrentItem().setPrimaryResponse(translatedResponse);
|
||||
|
|
|
@ -127,37 +127,6 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
|
|||
|
||||
@Override
|
||||
protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener<BulkResponse> listener) {
|
||||
boolean hasIndexRequestsWithPipelines = false;
|
||||
ImmutableOpenMap<String, IndexMetaData> indicesMetaData = clusterService.state().getMetaData().indices();
|
||||
for (DocWriteRequest<?> actionRequest : bulkRequest.requests) {
|
||||
if (actionRequest instanceof IndexRequest) {
|
||||
IndexRequest indexRequest = (IndexRequest) actionRequest;
|
||||
String pipeline = indexRequest.getPipeline();
|
||||
if (pipeline == null) {
|
||||
IndexMetaData indexMetaData = indicesMetaData.get(indexRequest.index());
|
||||
if (indexMetaData == null) {
|
||||
indexRequest.setPipeline(IngestService.NOOP_PIPELINE_NAME);
|
||||
} else {
|
||||
String defaultPipeline = IndexSettings.DEFAULT_PIPELINE.get(indexMetaData.getSettings());
|
||||
indexRequest.setPipeline(defaultPipeline);
|
||||
if (IngestService.NOOP_PIPELINE_NAME.equals(defaultPipeline) == false) {
|
||||
hasIndexRequestsWithPipelines = true;
|
||||
}
|
||||
}
|
||||
} else if (IngestService.NOOP_PIPELINE_NAME.equals(pipeline) == false) {
|
||||
hasIndexRequestsWithPipelines = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasIndexRequestsWithPipelines) {
|
||||
if (clusterService.localNode().isIngestNode()) {
|
||||
processBulkIndexIngestRequest(task, bulkRequest, listener);
|
||||
} else {
|
||||
ingestForwarder.forwardIngestRequest(BulkAction.INSTANCE, bulkRequest, listener);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final long startTime = relativeTime();
|
||||
final AtomicArray<BulkItemResponse> responses = new AtomicArray<>(bulkRequest.requests.size());
|
||||
|
||||
|
@ -191,7 +160,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
|
|||
}
|
||||
// Step 3: create all the indices that are missing, if there are any missing. start the bulk after all the creates come back.
|
||||
if (autoCreateIndices.isEmpty()) {
|
||||
executeBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated);
|
||||
executeIngestAndBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated);
|
||||
} else {
|
||||
final AtomicInteger counter = new AtomicInteger(autoCreateIndices.size());
|
||||
for (String index : autoCreateIndices) {
|
||||
|
@ -199,7 +168,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
|
|||
@Override
|
||||
public void onResponse(CreateIndexResponse result) {
|
||||
if (counter.decrementAndGet() == 0) {
|
||||
executeBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated);
|
||||
executeIngestAndBulk(task, bulkRequest, startTime, listener, responses, indicesThatCannotBeCreated);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +184,7 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
|
|||
}
|
||||
}
|
||||
if (counter.decrementAndGet() == 0) {
|
||||
executeBulk(task, bulkRequest, startTime, ActionListener.wrap(listener::onResponse, inner -> {
|
||||
executeIngestAndBulk(task, bulkRequest, startTime, ActionListener.wrap(listener::onResponse, inner -> {
|
||||
inner.addSuppressed(e);
|
||||
listener.onFailure(inner);
|
||||
}), responses, indicesThatCannotBeCreated);
|
||||
|
@ -225,7 +194,47 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
|
|||
}
|
||||
}
|
||||
} else {
|
||||
executeBulk(task, bulkRequest, startTime, listener, responses, emptyMap());
|
||||
executeIngestAndBulk(task, bulkRequest, startTime, listener, responses, emptyMap());
|
||||
}
|
||||
}
|
||||
|
||||
private void executeIngestAndBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos,
|
||||
final ActionListener<BulkResponse> listener, final AtomicArray<BulkItemResponse> responses,
|
||||
Map<String, IndexNotFoundException> indicesThatCannotBeCreated) {
|
||||
boolean hasIndexRequestsWithPipelines = false;
|
||||
ImmutableOpenMap<String, IndexMetaData> indicesMetaData = clusterService.state().getMetaData().indices();
|
||||
for (DocWriteRequest<?> actionRequest : bulkRequest.requests) {
|
||||
if (actionRequest instanceof IndexRequest) {
|
||||
IndexRequest indexRequest = (IndexRequest) actionRequest;
|
||||
String pipeline = indexRequest.getPipeline();
|
||||
if (pipeline == null) {
|
||||
IndexMetaData indexMetaData = indicesMetaData.get(indexRequest.index());
|
||||
if (indexMetaData == null) {
|
||||
indexRequest.setPipeline(IngestService.NOOP_PIPELINE_NAME);
|
||||
} else {
|
||||
String defaultPipeline = IndexSettings.DEFAULT_PIPELINE.get(indexMetaData.getSettings());
|
||||
indexRequest.setPipeline(defaultPipeline);
|
||||
if (IngestService.NOOP_PIPELINE_NAME.equals(defaultPipeline) == false) {
|
||||
hasIndexRequestsWithPipelines = true;
|
||||
}
|
||||
}
|
||||
} else if (IngestService.NOOP_PIPELINE_NAME.equals(pipeline) == false) {
|
||||
hasIndexRequestsWithPipelines = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasIndexRequestsWithPipelines) {
|
||||
try {
|
||||
if (clusterService.localNode().isIngestNode()) {
|
||||
processBulkIndexIngestRequest(task, bulkRequest, listener);
|
||||
} else {
|
||||
ingestForwarder.forwardIngestRequest(BulkAction.INSTANCE, bulkRequest, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
} else {
|
||||
executeBulk(task, bulkRequest, startTimeNanos, listener, responses, indicesThatCannotBeCreated);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.IsoFields;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.zone.ZoneOffsetTransition;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A strategy for rounding date/time based values.
|
||||
*
|
||||
* There are two implementations for rounding.
|
||||
* The first one requires a date time unit and rounds to the supplied date time unit (i.e. quarter of year, day of month)
|
||||
* The second one allows you to specify an interval to round to
|
||||
*/
|
||||
public abstract class Rounding implements Writeable {
|
||||
|
||||
public static String format(long epochMillis) {
|
||||
return Instant.ofEpochMilli(epochMillis) + "/" + epochMillis;
|
||||
}
|
||||
|
||||
public enum DateTimeUnit {
|
||||
WEEK_OF_WEEKYEAR( (byte) 1, IsoFields.WEEK_OF_WEEK_BASED_YEAR),
|
||||
YEAR_OF_CENTURY( (byte) 2, ChronoField.YEAR_OF_ERA),
|
||||
QUARTER_OF_YEAR( (byte) 3, IsoFields.QUARTER_OF_YEAR),
|
||||
MONTH_OF_YEAR( (byte) 4, ChronoField.MONTH_OF_YEAR),
|
||||
DAY_OF_MONTH( (byte) 5, ChronoField.DAY_OF_MONTH),
|
||||
HOUR_OF_DAY( (byte) 6, ChronoField.HOUR_OF_DAY),
|
||||
MINUTES_OF_HOUR( (byte) 7, ChronoField.MINUTE_OF_HOUR),
|
||||
SECOND_OF_MINUTE( (byte) 8, ChronoField.SECOND_OF_MINUTE);
|
||||
|
||||
private final byte id;
|
||||
private final TemporalField field;
|
||||
|
||||
DateTimeUnit(byte id, TemporalField field) {
|
||||
this.id = id;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public byte getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public TemporalField getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public static DateTimeUnit resolve(byte id) {
|
||||
switch (id) {
|
||||
case 1: return WEEK_OF_WEEKYEAR;
|
||||
case 2: return YEAR_OF_CENTURY;
|
||||
case 3: return QUARTER_OF_YEAR;
|
||||
case 4: return MONTH_OF_YEAR;
|
||||
case 5: return DAY_OF_MONTH;
|
||||
case 6: return HOUR_OF_DAY;
|
||||
case 7: return MINUTES_OF_HOUR;
|
||||
case 8: return SECOND_OF_MINUTE;
|
||||
default: throw new ElasticsearchException("Unknown date time unit id [" + id + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void innerWriteTo(StreamOutput out) throws IOException;
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeByte(id());
|
||||
innerWriteTo(out);
|
||||
}
|
||||
|
||||
public abstract byte id();
|
||||
|
||||
/**
|
||||
* Rounds the given value.
|
||||
*/
|
||||
public abstract long round(long value);
|
||||
|
||||
/**
|
||||
* Given the rounded value (which was potentially generated by {@link #round(long)}, returns the next rounding value. For example, with
|
||||
* interval based rounding, if the interval is 3, {@code nextRoundValue(6) = 9 }.
|
||||
*
|
||||
* @param value The current rounding value
|
||||
* @return The next rounding value
|
||||
*/
|
||||
public abstract long nextRoundingValue(long value);
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object obj);
|
||||
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
public static Builder builder(DateTimeUnit unit) {
|
||||
return new Builder(unit);
|
||||
}
|
||||
|
||||
public static Builder builder(TimeValue interval) {
|
||||
return new Builder(interval);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final DateTimeUnit unit;
|
||||
private final long interval;
|
||||
|
||||
private ZoneId timeZone = ZoneOffset.UTC;
|
||||
|
||||
public Builder(DateTimeUnit unit) {
|
||||
this.unit = unit;
|
||||
this.interval = -1;
|
||||
}
|
||||
|
||||
public Builder(TimeValue interval) {
|
||||
this.unit = null;
|
||||
if (interval.millis() < 1)
|
||||
throw new IllegalArgumentException("Zero or negative time interval not supported");
|
||||
this.interval = interval.millis();
|
||||
}
|
||||
|
||||
public Builder timeZone(ZoneId timeZone) {
|
||||
if (timeZone == null) {
|
||||
throw new IllegalArgumentException("Setting null as timezone is not supported");
|
||||
}
|
||||
this.timeZone = timeZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rounding build() {
|
||||
Rounding timeZoneRounding;
|
||||
if (unit != null) {
|
||||
timeZoneRounding = new TimeUnitRounding(unit, timeZone);
|
||||
} else {
|
||||
timeZoneRounding = new TimeIntervalRounding(interval, timeZone);
|
||||
}
|
||||
return timeZoneRounding;
|
||||
}
|
||||
}
|
||||
|
||||
static class TimeUnitRounding extends Rounding {
|
||||
|
||||
static final byte ID = 1;
|
||||
|
||||
private final DateTimeUnit unit;
|
||||
private final ZoneId timeZone;
|
||||
private final boolean unitRoundsToMidnight;
|
||||
|
||||
|
||||
TimeUnitRounding(DateTimeUnit unit, ZoneId timeZone) {
|
||||
this.unit = unit;
|
||||
this.timeZone = timeZone;
|
||||
this.unitRoundsToMidnight = this.unit.field.getBaseUnit().getDuration().toMillis() > 60L * 60L * 1000L;
|
||||
}
|
||||
|
||||
TimeUnitRounding(StreamInput in) throws IOException {
|
||||
unit = DateTimeUnit.resolve(in.readByte());
|
||||
timeZone = ZoneId.of(in.readString());
|
||||
unitRoundsToMidnight = unit.getField().getBaseUnit().getDuration().toMillis() > 60L * 60L * 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte id() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
private LocalDateTime truncateLocalDateTime(LocalDateTime localDateTime) {
|
||||
localDateTime = localDateTime.withNano(0);
|
||||
assert localDateTime.getNano() == 0;
|
||||
if (unit.equals(DateTimeUnit.SECOND_OF_MINUTE)) {
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
localDateTime = localDateTime.withSecond(0);
|
||||
assert localDateTime.getSecond() == 0;
|
||||
if (unit.equals(DateTimeUnit.MINUTES_OF_HOUR)) {
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
localDateTime = localDateTime.withMinute(0);
|
||||
assert localDateTime.getMinute() == 0;
|
||||
if (unit.equals(DateTimeUnit.HOUR_OF_DAY)) {
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
localDateTime = localDateTime.withHour(0);
|
||||
assert localDateTime.getHour() == 0;
|
||||
if (unit.equals(DateTimeUnit.DAY_OF_MONTH)) {
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
if (unit.equals(DateTimeUnit.WEEK_OF_WEEKYEAR)) {
|
||||
localDateTime = localDateTime.with(ChronoField.DAY_OF_WEEK, 1);
|
||||
assert localDateTime.getDayOfWeek() == DayOfWeek.MONDAY;
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
localDateTime = localDateTime.withDayOfMonth(1);
|
||||
assert localDateTime.getDayOfMonth() == 1;
|
||||
if (unit.equals(DateTimeUnit.MONTH_OF_YEAR)) {
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
if (unit.equals(DateTimeUnit.QUARTER_OF_YEAR)) {
|
||||
int quarter = (int) IsoFields.QUARTER_OF_YEAR.getFrom(localDateTime);
|
||||
int month = ((quarter - 1) * 3) + 1;
|
||||
localDateTime = localDateTime.withMonth(month);
|
||||
assert localDateTime.getMonthValue() % 3 == 1;
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
if (unit.equals(DateTimeUnit.YEAR_OF_CENTURY)) {
|
||||
localDateTime = localDateTime.withMonth(1);
|
||||
assert localDateTime.getMonthValue() == 1;
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("NOT YET IMPLEMENTED for unit " + unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long round(long utcMillis) {
|
||||
if (unitRoundsToMidnight) {
|
||||
final ZonedDateTime zonedDateTime = Instant.ofEpochMilli(utcMillis).atZone(timeZone);
|
||||
final LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
|
||||
final LocalDateTime localMidnight = truncateLocalDateTime(localDateTime);
|
||||
return firstTimeOnDay(localMidnight);
|
||||
} else {
|
||||
while (true) {
|
||||
final Instant truncatedTime = truncateAsLocalTime(utcMillis);
|
||||
final ZoneOffsetTransition previousTransition = timeZone.getRules().previousTransition(Instant.ofEpochMilli(utcMillis));
|
||||
|
||||
if (previousTransition == null) {
|
||||
// truncateAsLocalTime cannot have failed if there were no previous transitions
|
||||
return truncatedTime.toEpochMilli();
|
||||
}
|
||||
|
||||
final long previousTransitionMillis = previousTransition.getInstant().toEpochMilli();
|
||||
|
||||
if (truncatedTime != null && previousTransitionMillis <= truncatedTime.toEpochMilli()) {
|
||||
return truncatedTime.toEpochMilli();
|
||||
}
|
||||
|
||||
// There was a transition in between the input time and the truncated time. Return to the transition time and
|
||||
// round that down instead.
|
||||
utcMillis = previousTransitionMillis - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long firstTimeOnDay(LocalDateTime localMidnight) {
|
||||
assert localMidnight.toLocalTime().equals(LocalTime.of(0, 0, 0)) : "firstTimeOnDay should only be called at midnight";
|
||||
assert unitRoundsToMidnight : "firstTimeOnDay should only be called if unitRoundsToMidnight";
|
||||
|
||||
// Now work out what localMidnight actually means
|
||||
final List<ZoneOffset> currentOffsets = timeZone.getRules().getValidOffsets(localMidnight);
|
||||
if (currentOffsets.size() >= 1) {
|
||||
// There is at least one midnight on this day, so choose the first
|
||||
final ZoneOffset firstOffset = currentOffsets.get(0);
|
||||
final OffsetDateTime offsetMidnight = localMidnight.atOffset(firstOffset);
|
||||
return offsetMidnight.toInstant().toEpochMilli();
|
||||
} else {
|
||||
// There were no midnights on this day, so we must have entered the day via an offset transition.
|
||||
// Use the time of the transition as it is the earliest time on the right day.
|
||||
ZoneOffsetTransition zoneOffsetTransition = timeZone.getRules().getTransition(localMidnight);
|
||||
return zoneOffsetTransition.getInstant().toEpochMilli();
|
||||
}
|
||||
}
|
||||
|
||||
private Instant truncateAsLocalTime(long utcMillis) {
|
||||
assert unitRoundsToMidnight == false : "truncateAsLocalTime should not be called if unitRoundsToMidnight";
|
||||
|
||||
final LocalDateTime truncatedLocalDateTime
|
||||
= truncateLocalDateTime(Instant.ofEpochMilli(utcMillis).atZone(timeZone).toLocalDateTime());
|
||||
final List<ZoneOffset> currentOffsets = timeZone.getRules().getValidOffsets(truncatedLocalDateTime);
|
||||
|
||||
if (currentOffsets.size() >= 1) {
|
||||
// at least one possibilities - choose the latest one that's still no later than the input time
|
||||
for (int offsetIndex = currentOffsets.size() - 1; offsetIndex >= 0; offsetIndex--) {
|
||||
final Instant result = truncatedLocalDateTime.atOffset(currentOffsets.get(offsetIndex)).toInstant();
|
||||
if (result.toEpochMilli() <= utcMillis) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
assert false : "rounded time not found for " + utcMillis + " with " + this;
|
||||
return null;
|
||||
} else {
|
||||
// The chosen local time didn't happen. This means we were given a time in an hour (or a minute) whose start
|
||||
// is missing due to an offset transition, so the time cannot be truncated.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime nextRelevantMidnight(LocalDateTime localMidnight) {
|
||||
assert localMidnight.toLocalTime().equals(LocalTime.of(0, 0, 0)) : "nextRelevantMidnight should only be called at midnight";
|
||||
assert unitRoundsToMidnight : "firstTimeOnDay should only be called if unitRoundsToMidnight";
|
||||
|
||||
switch (unit) {
|
||||
case DAY_OF_MONTH:
|
||||
return localMidnight.plus(1, ChronoUnit.DAYS);
|
||||
case WEEK_OF_WEEKYEAR:
|
||||
return localMidnight.plus(7, ChronoUnit.DAYS);
|
||||
case MONTH_OF_YEAR:
|
||||
return localMidnight.plus(1, ChronoUnit.MONTHS);
|
||||
case QUARTER_OF_YEAR:
|
||||
return localMidnight.plus(3, ChronoUnit.MONTHS);
|
||||
case YEAR_OF_CENTURY:
|
||||
return localMidnight.plus(1, ChronoUnit.YEARS);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown round-to-midnight unit: " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextRoundingValue(long utcMillis) {
|
||||
if (unitRoundsToMidnight) {
|
||||
final ZonedDateTime zonedDateTime = Instant.ofEpochMilli(utcMillis).atZone(timeZone);
|
||||
final LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
|
||||
final LocalDateTime earlierLocalMidnight = truncateLocalDateTime(localDateTime);
|
||||
final LocalDateTime localMidnight = nextRelevantMidnight(earlierLocalMidnight);
|
||||
return firstTimeOnDay(localMidnight);
|
||||
} else {
|
||||
final long unitSize = unit.field.getBaseUnit().getDuration().toMillis();
|
||||
final long roundedAfterOneIncrement = round(utcMillis + unitSize);
|
||||
if (utcMillis < roundedAfterOneIncrement) {
|
||||
return roundedAfterOneIncrement;
|
||||
} else {
|
||||
return round(utcMillis + 2 * unitSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void innerWriteTo(StreamOutput out) throws IOException {
|
||||
out.writeByte(unit.getId());
|
||||
String tz = ZoneOffset.UTC.equals(timeZone) ? "UTC" : timeZone.getId(); // stay joda compatible
|
||||
out.writeString(tz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(unit, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimeUnitRounding other = (TimeUnitRounding) obj;
|
||||
return Objects.equals(unit, other.unit) && Objects.equals(timeZone, other.timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + timeZone + "][" + unit + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static class TimeIntervalRounding extends Rounding {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeIntervalRounding{" +
|
||||
"interval=" + interval +
|
||||
", timeZone=" + timeZone +
|
||||
'}';
|
||||
}
|
||||
|
||||
static final byte ID = 2;
|
||||
|
||||
private final long interval;
|
||||
private final ZoneId timeZone;
|
||||
|
||||
TimeIntervalRounding(long interval, ZoneId timeZone) {
|
||||
if (interval < 1)
|
||||
throw new IllegalArgumentException("Zero or negative time interval not supported");
|
||||
this.interval = interval;
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
TimeIntervalRounding(StreamInput in) throws IOException {
|
||||
interval = in.readVLong();
|
||||
timeZone = ZoneId.of(in.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte id() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long round(final long utcMillis) {
|
||||
final Instant utcInstant = Instant.ofEpochMilli(utcMillis);
|
||||
final LocalDateTime rawLocalDateTime = Instant.ofEpochMilli(utcMillis).atZone(timeZone).toLocalDateTime();
|
||||
|
||||
// a millisecond value with the same local time, in UTC, as `utcMillis` has in `timeZone`
|
||||
final long localMillis = utcMillis + timeZone.getRules().getOffset(utcInstant).getTotalSeconds() * 1000;
|
||||
assert localMillis == rawLocalDateTime.toInstant(ZoneOffset.UTC).toEpochMilli();
|
||||
|
||||
final long roundedMillis = roundKey(localMillis, interval) * interval;
|
||||
final LocalDateTime roundedLocalDateTime = Instant.ofEpochMilli(roundedMillis).atZone(ZoneOffset.UTC).toLocalDateTime();
|
||||
|
||||
// Now work out what roundedLocalDateTime actually means
|
||||
final List<ZoneOffset> currentOffsets = timeZone.getRules().getValidOffsets(roundedLocalDateTime);
|
||||
if (currentOffsets.isEmpty() == false) {
|
||||
// There is at least one instant with the desired local time. In general the desired result is
|
||||
// the latest rounded time that's no later than the input time, but this could involve rounding across
|
||||
// a timezone transition, which may yield the wrong result
|
||||
final ZoneOffsetTransition previousTransition = timeZone.getRules().previousTransition(utcInstant.plusMillis(1));
|
||||
for (int offsetIndex = currentOffsets.size() - 1; 0 <= offsetIndex; offsetIndex--) {
|
||||
final OffsetDateTime offsetTime = roundedLocalDateTime.atOffset(currentOffsets.get(offsetIndex));
|
||||
final Instant offsetInstant = offsetTime.toInstant();
|
||||
if (previousTransition != null && offsetInstant.isBefore(previousTransition.getInstant())) {
|
||||
// Rounding down across the transition can yield the wrong result. It's best to return to the transition time
|
||||
// and round that down.
|
||||
return round(previousTransition.getInstant().toEpochMilli() - 1);
|
||||
}
|
||||
|
||||
if (utcInstant.isBefore(offsetTime.toInstant()) == false) {
|
||||
return offsetInstant.toEpochMilli();
|
||||
}
|
||||
}
|
||||
|
||||
final OffsetDateTime offsetTime = roundedLocalDateTime.atOffset(currentOffsets.get(0));
|
||||
final Instant offsetInstant = offsetTime.toInstant();
|
||||
assert false : this + " failed to round " + utcMillis + " down: " + offsetInstant + " is the earliest possible";
|
||||
return offsetInstant.toEpochMilli(); // TODO or throw something?
|
||||
} else {
|
||||
// The desired time isn't valid because within a gap, so just return the gap time.
|
||||
ZoneOffsetTransition zoneOffsetTransition = timeZone.getRules().getTransition(roundedLocalDateTime);
|
||||
return zoneOffsetTransition.getInstant().toEpochMilli();
|
||||
}
|
||||
}
|
||||
|
||||
private static long roundKey(long value, long interval) {
|
||||
if (value < 0) {
|
||||
return (value - interval + 1) / interval;
|
||||
} else {
|
||||
return value / interval;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextRoundingValue(long time) {
|
||||
int offsetSeconds = timeZone.getRules().getOffset(Instant.ofEpochMilli(time)).getTotalSeconds();
|
||||
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneOffset.UTC)
|
||||
.plusSeconds(offsetSeconds)
|
||||
.plusNanos(interval * 1_000_000)
|
||||
.withZoneSameLocal(timeZone)
|
||||
.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void innerWriteTo(StreamOutput out) throws IOException {
|
||||
out.writeVLong(interval);
|
||||
String tz = ZoneOffset.UTC.equals(timeZone) ? "UTC" : timeZone.getId(); // stay joda compatible
|
||||
out.writeString(tz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(interval, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimeIntervalRounding other = (TimeIntervalRounding) obj;
|
||||
return Objects.equals(interval, other.interval) && Objects.equals(timeZone, other.timeZone);
|
||||
}
|
||||
}
|
||||
|
||||
public static Rounding read(StreamInput in) throws IOException {
|
||||
Rounding rounding;
|
||||
byte id = in.readByte();
|
||||
switch (id) {
|
||||
case TimeUnitRounding.ID:
|
||||
rounding = new TimeUnitRounding(in);
|
||||
break;
|
||||
case TimeIntervalRounding.ID:
|
||||
rounding = new TimeIntervalRounding(in);
|
||||
break;
|
||||
default:
|
||||
throw new ElasticsearchException("unknown rounding id [" + id + "]");
|
||||
}
|
||||
return rounding;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,10 @@ import java.util.Objects;
|
|||
|
||||
/**
|
||||
* A strategy for rounding long values.
|
||||
*
|
||||
* Use the java based Rounding class where applicable
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class Rounding implements Writeable {
|
||||
|
||||
public abstract byte id();
|
||||
|
@ -404,7 +407,7 @@ public abstract class Rounding implements Writeable {
|
|||
}
|
||||
|
||||
public static Rounding read(StreamInput in) throws IOException {
|
||||
Rounding rounding = null;
|
||||
Rounding rounding;
|
||||
byte id = in.readByte();
|
||||
switch (id) {
|
||||
case TimeUnitRounding.ID: rounding = new TimeUnitRounding(in); break;
|
||||
|
|
|
@ -175,7 +175,7 @@ public abstract class ParsedSignificantTerms extends ParsedMultiBucketAggregatio
|
|||
bucket.subsetDf = value;
|
||||
bucket.setDocCount(value);
|
||||
} else if (InternalSignificantTerms.SCORE.equals(currentFieldName)) {
|
||||
bucket.score = parser.longValue();
|
||||
bucket.score = parser.doubleValue();
|
||||
} else if (InternalSignificantTerms.BG_COUNT.equals(currentFieldName)) {
|
||||
bucket.supersetDf = parser.longValue();
|
||||
}
|
||||
|
|
|
@ -284,7 +284,6 @@ public class IndicesRequestIT extends ESIntegTestCase {
|
|||
assertSameIndices(updateRequest, updateShardActions);
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/32808")
|
||||
public void testBulk() {
|
||||
String[] bulkShardActions = new String[]{BulkAction.NAME + "[s][p]", BulkAction.NAME + "[s][r]"};
|
||||
interceptTransportActions(bulkShardActions);
|
||||
|
|
|
@ -22,20 +22,25 @@ package org.elasticsearch.action.bulk;
|
|||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.DocWriteRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||
import org.elasticsearch.action.index.IndexAction;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.AutoCreateIndex;
|
||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.ClusterStateApplier;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.settings.ClusterSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.AtomicArray;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
|
@ -77,6 +82,9 @@ public class TransportBulkActionIngestTests extends ESTestCase {
|
|||
*/
|
||||
private static final String WITH_DEFAULT_PIPELINE = "index_with_default_pipeline";
|
||||
|
||||
private static final Settings SETTINGS =
|
||||
Settings.builder().put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), true).build();
|
||||
|
||||
/** Services needed by bulk action */
|
||||
TransportService transportService;
|
||||
ClusterService clusterService;
|
||||
|
@ -112,25 +120,42 @@ public class TransportBulkActionIngestTests extends ESTestCase {
|
|||
/** A subclass of the real bulk action to allow skipping real bulk indexing, and marking when it would have happened. */
|
||||
class TestTransportBulkAction extends TransportBulkAction {
|
||||
boolean isExecuted = false; // set when the "real" bulk execution happens
|
||||
|
||||
boolean needToCheck; // pluggable return value for `needToCheck`
|
||||
|
||||
boolean indexCreated = true; // If set to false, will be set to true by call to createIndex
|
||||
|
||||
TestTransportBulkAction() {
|
||||
super(Settings.EMPTY, null, transportService, clusterService, ingestService,
|
||||
null, null, new ActionFilters(Collections.emptySet()), null, null);
|
||||
super(SETTINGS, null, transportService, clusterService, ingestService,
|
||||
null, null, new ActionFilters(Collections.emptySet()), null,
|
||||
new AutoCreateIndex(
|
||||
SETTINGS, new ClusterSettings(SETTINGS, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS),
|
||||
new IndexNameExpressionResolver(SETTINGS)
|
||||
)
|
||||
);
|
||||
}
|
||||
@Override
|
||||
protected boolean needToCheck() {
|
||||
return false;
|
||||
return needToCheck;
|
||||
}
|
||||
@Override
|
||||
void executeBulk(Task task, final BulkRequest bulkRequest, final long startTimeNanos, final ActionListener<BulkResponse> listener,
|
||||
final AtomicArray<BulkItemResponse> responses, Map<String, IndexNotFoundException> indicesThatCannotBeCreated) {
|
||||
assertTrue(indexCreated);
|
||||
isExecuted = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void createIndex(String index, TimeValue timeout, ActionListener<CreateIndexResponse> listener) {
|
||||
indexCreated = true;
|
||||
listener.onResponse(null);
|
||||
}
|
||||
}
|
||||
|
||||
class TestSingleItemBulkWriteAction extends TransportSingleItemBulkWriteAction<IndexRequest, IndexResponse> {
|
||||
|
||||
TestSingleItemBulkWriteAction(TestTransportBulkAction bulkAction) {
|
||||
super(Settings.EMPTY, IndexAction.NAME, TransportBulkActionIngestTests.this.transportService,
|
||||
super(SETTINGS, IndexAction.NAME, TransportBulkActionIngestTests.this.transportService,
|
||||
TransportBulkActionIngestTests.this.clusterService,
|
||||
null, null, null, new ActionFilters(Collections.emptySet()), null,
|
||||
IndexRequest::new, IndexRequest::new, ThreadPool.Names.WRITE, bulkAction, null);
|
||||
|
@ -162,7 +187,7 @@ public class TransportBulkActionIngestTests extends ESTestCase {
|
|||
when(nodes.getIngestNodes()).thenReturn(ingestNodes);
|
||||
ClusterState state = mock(ClusterState.class);
|
||||
when(state.getNodes()).thenReturn(nodes);
|
||||
when(state.getMetaData()).thenReturn(MetaData.builder().indices(ImmutableOpenMap.<String, IndexMetaData>builder()
|
||||
MetaData metaData = MetaData.builder().indices(ImmutableOpenMap.<String, IndexMetaData>builder()
|
||||
.putAll(
|
||||
Collections.singletonMap(
|
||||
WITH_DEFAULT_PIPELINE,
|
||||
|
@ -170,7 +195,9 @@ public class TransportBulkActionIngestTests extends ESTestCase {
|
|||
settings(Version.CURRENT).put(IndexSettings.DEFAULT_PIPELINE.getKey(), "default_pipeline")
|
||||
.build()
|
||||
).numberOfShards(1).numberOfReplicas(1).build()))
|
||||
.build()).build());
|
||||
.build()).build();
|
||||
when(state.getMetaData()).thenReturn(metaData);
|
||||
when(state.metaData()).thenReturn(metaData);
|
||||
when(clusterService.state()).thenReturn(state);
|
||||
doAnswer(invocation -> {
|
||||
ClusterChangedEvent event = mock(ClusterChangedEvent.class);
|
||||
|
@ -408,4 +435,36 @@ public class TransportBulkActionIngestTests extends ESTestCase {
|
|||
verifyZeroInteractions(transportService);
|
||||
}
|
||||
|
||||
public void testCreateIndexBeforeRunPipeline() throws Exception {
|
||||
Exception exception = new Exception("fake exception");
|
||||
IndexRequest indexRequest = new IndexRequest("missing_index", "type", "id");
|
||||
indexRequest.setPipeline("testpipeline");
|
||||
indexRequest.source(Collections.emptyMap());
|
||||
AtomicBoolean responseCalled = new AtomicBoolean(false);
|
||||
AtomicBoolean failureCalled = new AtomicBoolean(false);
|
||||
action.needToCheck = true;
|
||||
action.indexCreated = false;
|
||||
singleItemBulkWriteAction.execute(null, indexRequest, ActionListener.wrap(
|
||||
response -> responseCalled.set(true),
|
||||
e -> {
|
||||
assertThat(e, sameInstance(exception));
|
||||
failureCalled.set(true);
|
||||
}));
|
||||
|
||||
// check failure works, and passes through to the listener
|
||||
assertFalse(action.isExecuted); // haven't executed yet
|
||||
assertFalse(responseCalled.get());
|
||||
assertFalse(failureCalled.get());
|
||||
verify(executionService).executeBulkRequest(bulkDocsItr.capture(), failureHandler.capture(), completionHandler.capture());
|
||||
completionHandler.getValue().accept(exception);
|
||||
assertTrue(failureCalled.get());
|
||||
|
||||
// now check success
|
||||
indexRequest.setPipeline(IngestService.NOOP_PIPELINE_NAME); // this is done by the real pipeline execution service when processing
|
||||
completionHandler.getValue().accept(null);
|
||||
assertTrue(action.isExecuted);
|
||||
assertFalse(responseCalled.get()); // listener would only be called by real index action, not our mocked one
|
||||
verifyZeroInteractions(transportService);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -472,6 +472,8 @@ public class TransportShardBulkActionTests extends IndexShardTestCase {
|
|||
assertThat(primaryResponse.getResponse(), equalTo(noopUpdateResponse));
|
||||
assertThat(primaryResponse.getResponse().getResult(),
|
||||
equalTo(DocWriteResponse.Result.NOOP));
|
||||
assertThat(bulkShardRequest.items().length, equalTo(1));
|
||||
assertEquals(primaryRequest, bulkShardRequest.items()[0]); // check that bulk item was not mutated
|
||||
assertThat(primaryResponse.getResponse().getSeqNo(), equalTo(SequenceNumbers.UNASSIGNED_SEQ_NO));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common;
|
||||
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.rounding.DateTimeUnit;
|
||||
import org.elasticsearch.common.time.DateFormatters;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.zone.ZoneOffsetTransition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
|
||||
public class RoundingTests extends ESTestCase {
|
||||
|
||||
public void testUTCTimeUnitRounding() {
|
||||
Rounding tzRounding = Rounding.builder(Rounding.DateTimeUnit.MONTH_OF_YEAR).build();
|
||||
ZoneId tz = ZoneOffset.UTC;
|
||||
assertThat(tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-01T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-01T00:00:00.000Z")), isDate(time("2009-03-01T00:00:00.000Z"), tz));
|
||||
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.WEEK_OF_WEEKYEAR).build();
|
||||
assertThat(tzRounding.round(time("2012-01-10T01:01:01")), isDate(time("2012-01-09T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2012-01-09T00:00:00.000Z")), isDate(time("2012-01-16T00:00:00.000Z"), tz));
|
||||
}
|
||||
|
||||
public void testUTCIntervalRounding() {
|
||||
Rounding tzRounding = Rounding.builder(TimeValue.timeValueHours(12)).build();
|
||||
ZoneId tz = ZoneOffset.UTC;
|
||||
assertThat(tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-03T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T00:00:00.000Z")), isDate(time("2009-02-03T12:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.round(time("2009-02-03T13:01:01")), isDate(time("2009-02-03T12:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T12:00:00.000Z")), isDate(time("2009-02-04T00:00:00.000Z"), tz));
|
||||
|
||||
tzRounding = Rounding.builder(TimeValue.timeValueHours(48)).build();
|
||||
assertThat(tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-03T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T00:00:00.000Z")), isDate(time("2009-02-05T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.round(time("2009-02-05T13:01:01")), isDate(time("2009-02-05T00:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-05T00:00:00.000Z")), isDate(time("2009-02-07T00:00:00.000Z"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* test TimeIntervalRounding, (interval < 12h) with time zone shift
|
||||
*/
|
||||
public void testTimeIntervalRounding() {
|
||||
ZoneId tz = ZoneOffset.ofHours(-1);
|
||||
Rounding tzRounding = Rounding.builder(TimeValue.timeValueHours(6)).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2009-02-03T00:01:01")), isDate(time("2009-02-02T19:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-02T19:00:00.000Z")), isDate(time("2009-02-03T01:00:00.000Z"), tz));
|
||||
|
||||
assertThat(tzRounding.round(time("2009-02-03T13:01:01")), isDate(time("2009-02-03T13:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T13:00:00.000Z")), isDate(time("2009-02-03T19:00:00.000Z"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* test DayIntervalRounding, (interval >= 12h) with time zone shift
|
||||
*/
|
||||
public void testDayIntervalRounding() {
|
||||
ZoneId tz = ZoneOffset.ofHours(-8);
|
||||
Rounding tzRounding = Rounding.builder(TimeValue.timeValueHours(12)).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2009-02-03T00:01:01")), isDate(time("2009-02-02T20:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-02T20:00:00.000Z")), isDate(time("2009-02-03T08:00:00.000Z"), tz));
|
||||
|
||||
assertThat(tzRounding.round(time("2009-02-03T13:01:01")), isDate(time("2009-02-03T08:00:00.000Z"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T08:00:00.000Z")), isDate(time("2009-02-03T20:00:00.000Z"), tz));
|
||||
}
|
||||
|
||||
public void testDayRounding() {
|
||||
int timezoneOffset = -2;
|
||||
Rounding tzRounding = Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH)
|
||||
.timeZone(ZoneOffset.ofHours(timezoneOffset)).build();
|
||||
assertThat(tzRounding.round(0), equalTo(0L - TimeValue.timeValueHours(24 + timezoneOffset).millis()));
|
||||
assertThat(tzRounding.nextRoundingValue(0L - TimeValue.timeValueHours(24 + timezoneOffset).millis()), equalTo(0L - TimeValue
|
||||
.timeValueHours(timezoneOffset).millis()));
|
||||
|
||||
ZoneId tz = ZoneId.of("-08:00");
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2012-04-01T04:15:30Z")), isDate(time("2012-03-31T08:00:00Z"), tz));
|
||||
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.MONTH_OF_YEAR).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2012-04-01T04:15:30Z")), equalTo(time("2012-03-01T08:00:00Z")));
|
||||
|
||||
// date in Feb-3rd, but still in Feb-2nd in -02:00 timezone
|
||||
tz = ZoneId.of("-02:00");
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-02T02:00:00"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-02T02:00:00")), isDate(time("2009-02-03T02:00:00"), tz));
|
||||
|
||||
// date in Feb-3rd, also in -02:00 timezone
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2009-02-03T02:01:01")), isDate(time("2009-02-03T02:00:00"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T02:00:00")), isDate(time("2009-02-04T02:00:00"), tz));
|
||||
}
|
||||
|
||||
public void testTimeRounding() {
|
||||
// hour unit
|
||||
ZoneId tz = ZoneOffset.ofHours(-2);
|
||||
Rounding tzRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(0), equalTo(0L));
|
||||
assertThat(tzRounding.nextRoundingValue(0L), equalTo(TimeValue.timeValueHours(1L).getMillis()));
|
||||
|
||||
assertThat(tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-03T01:00:00"), tz));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2009-02-03T01:00:00")), isDate(time("2009-02-03T02:00:00"), tz));
|
||||
}
|
||||
|
||||
public void testTimeUnitRoundingDST() {
|
||||
Rounding tzRounding;
|
||||
// testing savings to non savings switch
|
||||
ZoneId cet = ZoneId.of("CET");
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(cet).build();
|
||||
assertThat(tzRounding.round(time("2014-10-26T01:01:01", cet)), isDate(time("2014-10-26T01:00:00+02:00"), cet));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2014-10-26T01:00:00", cet)),isDate(time("2014-10-26T02:00:00+02:00"), cet));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2014-10-26T02:00:00", cet)), isDate(time("2014-10-26T02:00:00+01:00"), cet));
|
||||
|
||||
// testing non savings to savings switch
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(cet).build();
|
||||
assertThat(tzRounding.round(time("2014-03-30T01:01:01", cet)), isDate(time("2014-03-30T01:00:00+01:00"), cet));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2014-03-30T01:00:00", cet)), isDate(time("2014-03-30T03:00:00", cet), cet));
|
||||
assertThat(tzRounding.nextRoundingValue(time("2014-03-30T03:00:00", cet)), isDate(time("2014-03-30T04:00:00", cet), cet));
|
||||
|
||||
// testing non savings to savings switch (America/Chicago)
|
||||
ZoneId chg = ZoneId.of("America/Chicago");
|
||||
Rounding tzRounding_utc = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY)
|
||||
.timeZone(ZoneOffset.UTC).build();
|
||||
assertThat(tzRounding.round(time("2014-03-09T03:01:01", chg)), isDate(time("2014-03-09T03:00:00", chg), chg));
|
||||
|
||||
Rounding tzRounding_chg = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(chg).build();
|
||||
assertThat(tzRounding_chg.round(time("2014-03-09T03:01:01", chg)), isDate(time("2014-03-09T03:00:00", chg), chg));
|
||||
|
||||
// testing savings to non savings switch 2013 (America/Chicago)
|
||||
assertThat(tzRounding_utc.round(time("2013-11-03T06:01:01", chg)), isDate(time("2013-11-03T06:00:00", chg), chg));
|
||||
assertThat(tzRounding_chg.round(time("2013-11-03T06:01:01", chg)), isDate(time("2013-11-03T06:00:00", chg), chg));
|
||||
|
||||
// testing savings to non savings switch 2014 (America/Chicago)
|
||||
assertThat(tzRounding_utc.round(time("2014-11-02T06:01:01", chg)), isDate(time("2014-11-02T06:00:00", chg), chg));
|
||||
assertThat(tzRounding_chg.round(time("2014-11-02T06:01:01", chg)), isDate(time("2014-11-02T06:00:00", chg), chg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomized test on TimeUnitRounding. Test uses random
|
||||
* {@link DateTimeUnit} and {@link ZoneId} and often (50% of the time)
|
||||
* chooses test dates that are exactly on or close to offset changes (e.g.
|
||||
* DST) in the chosen time zone.
|
||||
*
|
||||
* It rounds the test date down and up and performs various checks on the
|
||||
* rounding unit interval that is defined by this. Assumptions tested are
|
||||
* described in
|
||||
* {@link #assertInterval(long, long, long, Rounding, ZoneId)}
|
||||
*/
|
||||
public void testRoundingRandom() {
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
Rounding.DateTimeUnit unit = randomFrom(Rounding.DateTimeUnit.values());
|
||||
ZoneId tz = randomZone();
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(unit, tz);
|
||||
long date = Math.abs(randomLong() % (2 * (long) 10e11)); // 1970-01-01T00:00:00Z - 2033-05-18T05:33:20.000+02:00
|
||||
long unitMillis = unit.getField().getBaseUnit().getDuration().toMillis();
|
||||
// FIXME this was copy pasted from the other impl and not used. breaks the nasty date actually gets assigned
|
||||
if (randomBoolean()) {
|
||||
nastyDate(date, tz, unitMillis);
|
||||
}
|
||||
final long roundedDate = rounding.round(date);
|
||||
final long nextRoundingValue = rounding.nextRoundingValue(roundedDate);
|
||||
|
||||
assertInterval(roundedDate, date, nextRoundingValue, rounding, tz);
|
||||
|
||||
// check correct unit interval width for units smaller than a day, they should be fixed size except for transitions
|
||||
if (unitMillis <= 86400 * 1000) {
|
||||
// if the interval defined didn't cross timezone offset transition, it should cover unitMillis width
|
||||
int offsetRounded = tz.getRules().getOffset(Instant.ofEpochMilli(roundedDate - 1)).getTotalSeconds();
|
||||
int offsetNextValue = tz.getRules().getOffset(Instant.ofEpochMilli(nextRoundingValue + 1)).getTotalSeconds();
|
||||
if (offsetRounded == offsetNextValue) {
|
||||
assertThat("unit interval width not as expected for [" + unit + "], [" + tz + "] at "
|
||||
+ Instant.ofEpochMilli(roundedDate), nextRoundingValue - roundedDate, equalTo(unitMillis));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be even more nasty, go to a transition in the selected time zone.
|
||||
* In one third of the cases stay there, otherwise go half a unit back or forth
|
||||
*/
|
||||
private static long nastyDate(long initialDate, ZoneId timezone, long unitMillis) {
|
||||
ZoneOffsetTransition transition = timezone.getRules().nextTransition(Instant.ofEpochMilli(initialDate));
|
||||
long date = initialDate;
|
||||
if (transition != null) {
|
||||
date = transition.getInstant().toEpochMilli();
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
return date + (randomLong() % unitMillis); // positive and negative offset possible
|
||||
} else {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test DST end with interval rounding
|
||||
* CET: 25 October 2015, 03:00:00 clocks were turned backward 1 hour to 25 October 2015, 02:00:00 local standard time
|
||||
*/
|
||||
public void testTimeIntervalCET_DST_End() {
|
||||
long interval = TimeUnit.MINUTES.toMillis(20);
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
|
||||
assertThat(rounding.round(time("2015-10-25T01:55:00+02:00")), isDate(time("2015-10-25T01:40:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T02:15:00+02:00")), isDate(time("2015-10-25T02:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T02:35:00+02:00")), isDate(time("2015-10-25T02:20:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T02:55:00+02:00")), isDate(time("2015-10-25T02:40:00+02:00"), tz));
|
||||
// after DST shift
|
||||
assertThat(rounding.round(time("2015-10-25T02:15:00+01:00")), isDate(time("2015-10-25T02:00:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T02:35:00+01:00")), isDate(time("2015-10-25T02:20:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T02:55:00+01:00")), isDate(time("2015-10-25T02:40:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2015-10-25T03:15:00+01:00")), isDate(time("2015-10-25T03:00:00+01:00"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* test DST start with interval rounding
|
||||
* CET: 27 March 2016, 02:00:00 clocks were turned forward 1 hour to 27 March 2016, 03:00:00 local daylight time
|
||||
*/
|
||||
public void testTimeIntervalCET_DST_Start() {
|
||||
long interval = TimeUnit.MINUTES.toMillis(20);
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
// test DST start
|
||||
assertThat(rounding.round(time("2016-03-27T01:55:00+01:00")), isDate(time("2016-03-27T01:40:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T02:00:00+01:00")), isDate(time("2016-03-27T03:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:15:00+02:00")), isDate(time("2016-03-27T03:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:35:00+02:00")), isDate(time("2016-03-27T03:20:00+02:00"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* test DST start with offset not fitting interval, e.g. Asia/Kathmandu
|
||||
* adding 15min on 1986-01-01T00:00:00 the interval from
|
||||
* 1986-01-01T00:15:00+05:45 to 1986-01-01T00:20:00+05:45 to only be 5min
|
||||
* long
|
||||
*/
|
||||
public void testTimeInterval_Kathmandu_DST_Start() {
|
||||
long interval = TimeUnit.MINUTES.toMillis(20);
|
||||
ZoneId tz = ZoneId.of("Asia/Kathmandu");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
assertThat(rounding.round(time("1985-12-31T23:55:00+05:30")), isDate(time("1985-12-31T23:40:00+05:30"), tz));
|
||||
assertThat(rounding.round(time("1986-01-01T00:16:00+05:45")), isDate(time("1986-01-01T00:15:00+05:45"), tz));
|
||||
assertThat(time("1986-01-01T00:15:00+05:45") - time("1985-12-31T23:40:00+05:30"), equalTo(TimeUnit.MINUTES.toMillis(20)));
|
||||
assertThat(rounding.round(time("1986-01-01T00:26:00+05:45")), isDate(time("1986-01-01T00:20:00+05:45"), tz));
|
||||
assertThat(time("1986-01-01T00:20:00+05:45") - time("1986-01-01T00:15:00+05:45"), equalTo(TimeUnit.MINUTES.toMillis(5)));
|
||||
assertThat(rounding.round(time("1986-01-01T00:46:00+05:45")), isDate(time("1986-01-01T00:40:00+05:45"), tz));
|
||||
assertThat(time("1986-01-01T00:40:00+05:45") - time("1986-01-01T00:20:00+05:45"), equalTo(TimeUnit.MINUTES.toMillis(20)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Special test for intervals that don't fit evenly into rounding interval.
|
||||
* In this case, when interval crosses DST transition point, rounding in local
|
||||
* time can land in a DST gap which results in wrong UTC rounding values.
|
||||
*/
|
||||
public void testIntervalRounding_NotDivisibleInteval() {
|
||||
long interval = TimeUnit.MINUTES.toMillis(14);
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
|
||||
assertThat(rounding.round(time("2016-03-27T01:41:00+01:00")), isDate(time("2016-03-27T01:30:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T01:51:00+01:00")), isDate(time("2016-03-27T01:44:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T01:59:00+01:00")), isDate(time("2016-03-27T01:58:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:05:00+02:00")), isDate(time("2016-03-27T03:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:12:00+02:00")), isDate(time("2016-03-27T03:08:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:25:00+02:00")), isDate(time("2016-03-27T03:22:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T03:39:00+02:00")), isDate(time("2016-03-27T03:36:00+02:00"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for half day rounding intervals scrossing DST.
|
||||
*/
|
||||
public void testIntervalRounding_HalfDay_DST() {
|
||||
long interval = TimeUnit.HOURS.toMillis(12);
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
|
||||
assertThat(rounding.round(time("2016-03-26T01:00:00+01:00")), isDate(time("2016-03-26T00:00:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-26T13:00:00+01:00")), isDate(time("2016-03-26T12:00:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T01:00:00+01:00")), isDate(time("2016-03-27T00:00:00+01:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-27T13:00:00+02:00")), isDate(time("2016-03-27T12:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-28T01:00:00+02:00")), isDate(time("2016-03-28T00:00:00+02:00"), tz));
|
||||
assertThat(rounding.round(time("2016-03-28T13:00:00+02:00")), isDate(time("2016-03-28T12:00:00+02:00"), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* randomized test on {@link org.elasticsearch.common.rounding.Rounding.TimeIntervalRounding} with random interval and time zone offsets
|
||||
*/
|
||||
public void testIntervalRoundingRandom() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
TimeUnit unit = randomFrom(TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS);
|
||||
long interval = unit.toMillis(randomIntBetween(1, 365));
|
||||
ZoneId tz = randomZone();
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
long mainDate = Math.abs(randomLong() % (2 * (long) 10e11)); // 1970-01-01T00:00:00Z - 2033-05-18T05:33:20.000+02:00
|
||||
if (randomBoolean()) {
|
||||
mainDate = nastyDate(mainDate, tz, interval);
|
||||
}
|
||||
// check two intervals around date
|
||||
long previousRoundedValue = Long.MIN_VALUE;
|
||||
for (long date = mainDate - 2 * interval; date < mainDate + 2 * interval; date += interval / 2) {
|
||||
try {
|
||||
final long roundedDate = rounding.round(date);
|
||||
final long nextRoundingValue = rounding.nextRoundingValue(roundedDate);
|
||||
assertThat("Rounding should be idempotent", roundedDate, equalTo(rounding.round(roundedDate)));
|
||||
assertThat("Rounded value smaller or equal than unrounded", roundedDate, lessThanOrEqualTo(date));
|
||||
assertThat("Values smaller than rounded value should round further down", rounding.round(roundedDate - 1),
|
||||
lessThan(roundedDate));
|
||||
assertThat("Rounding should be >= previous rounding value", roundedDate, greaterThanOrEqualTo(previousRoundedValue));
|
||||
|
||||
if (tz.getRules().isFixedOffset()) {
|
||||
assertThat("NextRounding value should be greater than date", nextRoundingValue, greaterThan(roundedDate));
|
||||
assertThat("NextRounding value should be interval from rounded value", nextRoundingValue - roundedDate,
|
||||
equalTo(interval));
|
||||
assertThat("NextRounding value should be a rounded date", nextRoundingValue,
|
||||
equalTo(rounding.round(nextRoundingValue)));
|
||||
}
|
||||
previousRoundedValue = roundedDate;
|
||||
} catch (AssertionError e) {
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(date), tz);
|
||||
ZonedDateTime previousRoundedValueDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(previousRoundedValue), tz);
|
||||
logger.error("Rounding error at {}/{}, timezone {}, interval: {} previousRoundedValue {}/{}", dateTime, date,
|
||||
tz, interval, previousRoundedValueDate, previousRoundedValue);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that rounded values are always greater or equal to last rounded value if date is increasing.
|
||||
* The example covers an interval around 2011-10-30T02:10:00+01:00, time zone CET, interval: 2700000ms
|
||||
*/
|
||||
public void testIntervalRoundingMonotonic_CET() {
|
||||
long interval = TimeUnit.MINUTES.toMillis(45);
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
|
||||
List<Tuple<String, String>> expectedDates = new ArrayList<>();
|
||||
// first date is the date to be rounded, second the expected result
|
||||
expectedDates.add(new Tuple<>("2011-10-30T01:40:00.000+02:00", "2011-10-30T01:30:00.000+02:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:02:30.000+02:00", "2011-10-30T01:30:00.000+02:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:25:00.000+02:00", "2011-10-30T02:15:00.000+02:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:47:30.000+02:00", "2011-10-30T02:15:00.000+02:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:10:00.000+01:00", "2011-10-30T02:15:00.000+02:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:32:30.000+01:00", "2011-10-30T02:15:00.000+01:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T02:55:00.000+01:00", "2011-10-30T02:15:00.000+01:00"));
|
||||
expectedDates.add(new Tuple<>("2011-10-30T03:17:30.000+01:00", "2011-10-30T03:00:00.000+01:00"));
|
||||
|
||||
long previousDate = Long.MIN_VALUE;
|
||||
for (Tuple<String, String> dates : expectedDates) {
|
||||
final long roundedDate = rounding.round(time(dates.v1()));
|
||||
assertThat(dates.toString(), roundedDate, isDate(time(dates.v2()), tz));
|
||||
assertThat(dates.toString(), roundedDate, greaterThanOrEqualTo(previousDate));
|
||||
previousDate = roundedDate;
|
||||
}
|
||||
// here's what this means for interval widths
|
||||
assertEquals(TimeUnit.MINUTES.toMillis(45), time("2011-10-30T02:15:00.000+02:00") - time("2011-10-30T01:30:00.000+02:00"));
|
||||
assertEquals(TimeUnit.MINUTES.toMillis(60), time("2011-10-30T02:15:00.000+01:00") - time("2011-10-30T02:15:00.000+02:00"));
|
||||
assertEquals(TimeUnit.MINUTES.toMillis(45), time("2011-10-30T03:00:00.000+01:00") - time("2011-10-30T02:15:00.000+01:00"));
|
||||
}
|
||||
|
||||
/**
|
||||
* special test for DST switch from #9491
|
||||
*/
|
||||
public void testAmbiguousHoursAfterDSTSwitch() {
|
||||
Rounding tzRounding;
|
||||
final ZoneId tz = ZoneId.of("Asia/Jerusalem");
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2014-10-26T00:30:00+03:00")), isDate(time("2014-10-26T00:00:00+03:00"), tz));
|
||||
assertThat(tzRounding.round(time("2014-10-26T01:30:00+03:00")), isDate(time("2014-10-26T01:00:00+03:00"), tz));
|
||||
// the utc date for "2014-10-25T03:00:00+03:00" and "2014-10-25T03:00:00+02:00" is the same, local time turns back 1h here
|
||||
assertThat(time("2014-10-26T03:00:00+03:00"), isDate(time("2014-10-26T02:00:00+02:00"), tz));
|
||||
assertThat(tzRounding.round(time("2014-10-26T01:30:00+02:00")), isDate(time("2014-10-26T01:00:00+02:00"), tz));
|
||||
assertThat(tzRounding.round(time("2014-10-26T02:30:00+02:00")), isDate(time("2014-10-26T02:00:00+02:00"), tz));
|
||||
|
||||
// Day interval
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2014-11-11T17:00:00", tz)), isDate(time("2014-11-11T00:00:00", tz), tz));
|
||||
// DST on
|
||||
assertThat(tzRounding.round(time("2014-08-11T17:00:00", tz)), isDate(time("2014-08-11T00:00:00", tz), tz));
|
||||
// Day of switching DST on -> off
|
||||
assertThat(tzRounding.round(time("2014-10-26T17:00:00", tz)), isDate(time("2014-10-26T00:00:00", tz), tz));
|
||||
// Day of switching DST off -> on
|
||||
assertThat(tzRounding.round(time("2015-03-27T17:00:00", tz)), isDate(time("2015-03-27T00:00:00", tz), tz));
|
||||
|
||||
// Month interval
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.MONTH_OF_YEAR).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2014-11-11T17:00:00", tz)), isDate(time("2014-11-01T00:00:00", tz), tz));
|
||||
// DST on
|
||||
assertThat(tzRounding.round(time("2014-10-10T17:00:00", tz)), isDate(time("2014-10-01T00:00:00", tz), tz));
|
||||
|
||||
// Year interval
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.YEAR_OF_CENTURY).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2014-11-11T17:00:00", tz)), isDate(time("2014-01-01T00:00:00", tz), tz));
|
||||
|
||||
// Two timestamps in same year and different timezone offset ("Double buckets" issue - #9491)
|
||||
tzRounding = Rounding.builder(Rounding.DateTimeUnit.YEAR_OF_CENTURY).timeZone(tz).build();
|
||||
assertThat(tzRounding.round(time("2014-11-11T17:00:00", tz)),
|
||||
isDate(tzRounding.round(time("2014-08-11T17:00:00", tz)), tz));
|
||||
}
|
||||
|
||||
/**
|
||||
* test for #10025, strict local to UTC conversion can cause joda exceptions
|
||||
* on DST start
|
||||
*/
|
||||
public void testLenientConversionDST() {
|
||||
ZoneId tz = ZoneId.of("America/Sao_Paulo");
|
||||
|
||||
long start = time("2014-10-18T20:50:00.000", tz);
|
||||
long end = time("2014-10-19T01:00:00.000", tz);
|
||||
Rounding tzRounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.MINUTES_OF_HOUR, tz);
|
||||
Rounding dayTzRounding = new Rounding.TimeIntervalRounding(60000, tz);
|
||||
for (long time = start; time < end; time = time + 60000) {
|
||||
assertThat(tzRounding.nextRoundingValue(time), greaterThan(time));
|
||||
assertThat(dayTzRounding.nextRoundingValue(time), greaterThan(time));
|
||||
}
|
||||
}
|
||||
|
||||
public void testEdgeCasesTransition() {
|
||||
{
|
||||
// standard +/-1 hour DST transition, CET
|
||||
ZoneId tz = ZoneId.of("CET");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.HOUR_OF_DAY, tz);
|
||||
|
||||
// 29 Mar 2015 - Daylight Saving Time Started
|
||||
// at 02:00:00 clocks were turned forward 1 hour to 03:00:00
|
||||
assertInterval(time("2015-03-29T00:00:00.000+01:00"), time("2015-03-29T01:00:00.000+01:00"), rounding, 60, tz);
|
||||
assertInterval(time("2015-03-29T01:00:00.000+01:00"), time("2015-03-29T03:00:00.000+02:00"), rounding, 60, tz);
|
||||
assertInterval(time("2015-03-29T03:00:00.000+02:00"), time("2015-03-29T04:00:00.000+02:00"), rounding, 60, tz);
|
||||
|
||||
// 25 Oct 2015 - Daylight Saving Time Ended
|
||||
// at 03:00:00 clocks were turned backward 1 hour to 02:00:00
|
||||
assertInterval(time("2015-10-25T01:00:00.000+02:00"), time("2015-10-25T02:00:00.000+02:00"), rounding, 60, tz);
|
||||
assertInterval(time("2015-10-25T02:00:00.000+02:00"), time("2015-10-25T02:00:00.000+01:00"), rounding, 60, tz);
|
||||
assertInterval(time("2015-10-25T02:00:00.000+01:00"), time("2015-10-25T03:00:00.000+01:00"), rounding, 60, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// time zone "Asia/Kathmandu"
|
||||
// 1 Jan 1986 - Time Zone Change (IST → NPT), at 00:00:00 clocks were turned forward 00:15 minutes
|
||||
//
|
||||
// hour rounding is stable before 1985-12-31T23:00:00.000 and after 1986-01-01T01:00:00.000+05:45
|
||||
// the interval between is 105 minutes long because the hour after transition starts at 00:15
|
||||
// which is not a round value for hourly rounding
|
||||
ZoneId tz = ZoneId.of("Asia/Kathmandu");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.HOUR_OF_DAY, tz);
|
||||
|
||||
assertInterval(time("1985-12-31T22:00:00.000+05:30"), time("1985-12-31T23:00:00.000+05:30"), rounding, 60, tz);
|
||||
assertInterval(time("1985-12-31T23:00:00.000+05:30"), time("1986-01-01T01:00:00.000+05:45"), rounding, 105, tz);
|
||||
assertInterval(time("1986-01-01T01:00:00.000+05:45"), time("1986-01-01T02:00:00.000+05:45"), rounding, 60, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// time zone "Australia/Lord_Howe"
|
||||
// 3 Mar 1991 - Daylight Saving Time Ended
|
||||
// at 02:00:00 clocks were turned backward 0:30 hours to Sunday, 3 March 1991, 01:30:00
|
||||
ZoneId tz = ZoneId.of("Australia/Lord_Howe");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.HOUR_OF_DAY, tz);
|
||||
|
||||
assertInterval(time("1991-03-03T00:00:00.000+11:00"), time("1991-03-03T01:00:00.000+11:00"), rounding, 60, tz);
|
||||
assertInterval(time("1991-03-03T01:00:00.000+11:00"), time("1991-03-03T02:00:00.000+10:30"), rounding, 90, tz);
|
||||
assertInterval(time("1991-03-03T02:00:00.000+10:30"), time("1991-03-03T03:00:00.000+10:30"), rounding, 60, tz);
|
||||
|
||||
// 27 Oct 1991 - Daylight Saving Time Started
|
||||
// at 02:00:00 clocks were turned forward 0:30 hours to 02:30:00
|
||||
assertInterval(time("1991-10-27T00:00:00.000+10:30"), time("1991-10-27T01:00:00.000+10:30"), rounding, 60, tz);
|
||||
// the interval containing the switch time is 90 minutes long
|
||||
assertInterval(time("1991-10-27T01:00:00.000+10:30"), time("1991-10-27T03:00:00.000+11:00"), rounding, 90, tz);
|
||||
assertInterval(time("1991-10-27T03:00:00.000+11:00"), time("1991-10-27T04:00:00.000+11:00"), rounding, 60, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// time zone "Pacific/Chatham"
|
||||
// 5 Apr 2015 - Daylight Saving Time Ended
|
||||
// at 03:45:00 clocks were turned backward 1 hour to 02:45:00
|
||||
ZoneId tz = ZoneId.of("Pacific/Chatham");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.HOUR_OF_DAY, tz);
|
||||
|
||||
assertInterval(time("2015-04-05T02:00:00.000+13:45"), time("2015-04-05T03:00:00.000+13:45"), rounding, 60, tz);
|
||||
assertInterval(time("2015-04-05T03:00:00.000+13:45"), time("2015-04-05T03:00:00.000+12:45"), rounding, 60, tz);
|
||||
assertInterval(time("2015-04-05T03:00:00.000+12:45"), time("2015-04-05T04:00:00.000+12:45"), rounding, 60, tz);
|
||||
|
||||
// 27 Sep 2015 - Daylight Saving Time Started
|
||||
// at 02:45:00 clocks were turned forward 1 hour to 03:45:00
|
||||
|
||||
assertInterval(time("2015-09-27T01:00:00.000+12:45"), time("2015-09-27T02:00:00.000+12:45"), rounding, 60, tz);
|
||||
assertInterval(time("2015-09-27T02:00:00.000+12:45"), time("2015-09-27T04:00:00.000+13:45"), rounding, 60, tz);
|
||||
assertInterval(time("2015-09-27T04:00:00.000+13:45"), time("2015-09-27T05:00:00.000+13:45"), rounding, 60, tz);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDST_Europe_Rome() {
|
||||
// time zone "Europe/Rome", rounding to days. Rome had two midnights on the day the clocks went back in 1978, and
|
||||
// timeZone.convertLocalToUTC() gives the later of the two because Rome is east of UTC, whereas we want the earlier.
|
||||
|
||||
ZoneId tz = ZoneId.of("Europe/Rome");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.DAY_OF_MONTH, tz);
|
||||
|
||||
{
|
||||
long timeBeforeFirstMidnight = time("1978-09-30T23:59:00+02:00");
|
||||
long floor = rounding.round(timeBeforeFirstMidnight);
|
||||
assertThat(floor, isDate(time("1978-09-30T00:00:00+02:00"), tz));
|
||||
}
|
||||
|
||||
{
|
||||
long timeBetweenMidnights = time("1978-10-01T00:30:00+02:00");
|
||||
long floor = rounding.round(timeBetweenMidnights);
|
||||
assertThat(floor, isDate(time("1978-10-01T00:00:00+02:00"), tz));
|
||||
}
|
||||
|
||||
{
|
||||
long timeAfterSecondMidnight = time("1978-10-01T00:30:00+01:00");
|
||||
long floor = rounding.round(timeAfterSecondMidnight);
|
||||
assertThat(floor, isDate(time("1978-10-01T00:00:00+02:00"), tz));
|
||||
|
||||
long prevFloor = rounding.round(floor - 1);
|
||||
assertThat(prevFloor, lessThan(floor));
|
||||
assertThat(prevFloor, isDate(time("1978-09-30T00:00:00+02:00"), tz));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a time zone whose days overlap because the clocks are set back across midnight at the end of DST.
|
||||
*/
|
||||
public void testDST_America_St_Johns() {
|
||||
// time zone "America/St_Johns", rounding to days.
|
||||
ZoneId tz = ZoneId.of("America/St_Johns");
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(Rounding.DateTimeUnit.DAY_OF_MONTH, tz);
|
||||
|
||||
// 29 October 2006 - Daylight Saving Time ended, changing the UTC offset from -02:30 to -03:30.
|
||||
// This happened at 02:31 UTC, 00:01 local time, so the clocks were set back 1 hour to 23:01 on the 28th.
|
||||
// This means that 2006-10-29 has _two_ midnights, one in the -02:30 offset and one in the -03:30 offset.
|
||||
// Only the first of these is considered "rounded". Moreover, the extra time between 23:01 and 23:59
|
||||
// should be considered as part of the 28th even though it comes after midnight on the 29th.
|
||||
|
||||
{
|
||||
// Times before the first midnight should be rounded up to the first midnight.
|
||||
long timeBeforeFirstMidnight = time("2006-10-28T23:30:00.000-02:30");
|
||||
long floor = rounding.round(timeBeforeFirstMidnight);
|
||||
assertThat(floor, isDate(time("2006-10-28T00:00:00.000-02:30"), tz));
|
||||
long ceiling = rounding.nextRoundingValue(timeBeforeFirstMidnight);
|
||||
assertThat(ceiling, isDate(time("2006-10-29T00:00:00.000-02:30"), tz));
|
||||
assertInterval(floor, timeBeforeFirstMidnight, ceiling, rounding, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// Times between the two midnights which are on the later day should be rounded down to the later day's midnight.
|
||||
long timeBetweenMidnights = time("2006-10-29T00:00:30.000-02:30");
|
||||
// (this is halfway through the last minute before the clocks changed, in which local time was ambiguous)
|
||||
|
||||
long floor = rounding.round(timeBetweenMidnights);
|
||||
assertThat(floor, isDate(time("2006-10-29T00:00:00.000-02:30"), tz));
|
||||
|
||||
long ceiling = rounding.nextRoundingValue(timeBetweenMidnights);
|
||||
assertThat(ceiling, isDate(time("2006-10-30T00:00:00.000-03:30"), tz));
|
||||
|
||||
assertInterval(floor, timeBetweenMidnights, ceiling, rounding, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// Times between the two midnights which are on the earlier day should be rounded down to the earlier day's midnight.
|
||||
long timeBetweenMidnights = time("2006-10-28T23:30:00.000-03:30");
|
||||
// (this is halfway through the hour after the clocks changed, in which local time was ambiguous)
|
||||
|
||||
long floor = rounding.round(timeBetweenMidnights);
|
||||
assertThat(floor, isDate(time("2006-10-28T00:00:00.000-02:30"), tz));
|
||||
|
||||
long ceiling = rounding.nextRoundingValue(timeBetweenMidnights);
|
||||
assertThat(ceiling, isDate(time("2006-10-29T00:00:00.000-02:30"), tz));
|
||||
|
||||
assertInterval(floor, timeBetweenMidnights, ceiling, rounding, tz);
|
||||
}
|
||||
|
||||
{
|
||||
// Times after the second midnight should be rounded down to the first midnight.
|
||||
long timeAfterSecondMidnight = time("2006-10-29T06:00:00.000-03:30");
|
||||
long floor = rounding.round(timeAfterSecondMidnight);
|
||||
assertThat(floor, isDate(time("2006-10-29T00:00:00.000-02:30"), tz));
|
||||
long ceiling = rounding.nextRoundingValue(timeAfterSecondMidnight);
|
||||
assertThat(ceiling, isDate(time("2006-10-30T00:00:00.000-03:30"), tz));
|
||||
assertInterval(floor, timeAfterSecondMidnight, ceiling, rounding, tz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tests for dst transition with overlaps and day roundings.
|
||||
*/
|
||||
public void testDST_END_Edgecases() {
|
||||
// First case, dst happens at 1am local time, switching back one hour.
|
||||
// We want the overlapping hour to count for the next day, making it a 25h interval
|
||||
|
||||
ZoneId tz = ZoneId.of("Atlantic/Azores");
|
||||
Rounding.DateTimeUnit timeUnit = Rounding.DateTimeUnit.DAY_OF_MONTH;
|
||||
Rounding rounding = new Rounding.TimeUnitRounding(timeUnit, tz);
|
||||
|
||||
// Sunday, 29 October 2000, 01:00:00 clocks were turned backward 1 hour
|
||||
// to Sunday, 29 October 2000, 00:00:00 local standard time instead
|
||||
// which means there were two midnights that day.
|
||||
|
||||
long midnightBeforeTransition = time("2000-10-29T00:00:00", tz);
|
||||
long midnightOfTransition = time("2000-10-29T00:00:00-01:00");
|
||||
assertEquals(60L * 60L * 1000L, midnightOfTransition - midnightBeforeTransition);
|
||||
long nextMidnight = time("2000-10-30T00:00:00", tz);
|
||||
|
||||
assertInterval(midnightBeforeTransition, nextMidnight, rounding, 25 * 60, tz);
|
||||
|
||||
assertThat(rounding.round(time("2000-10-29T06:00:00-01:00")), isDate(time("2000-10-29T00:00:00Z"), tz));
|
||||
|
||||
// Second case, dst happens at 0am local time, switching back one hour to 23pm local time.
|
||||
// We want the overlapping hour to count for the previous day here
|
||||
|
||||
tz = ZoneId.of("America/Lima");
|
||||
rounding = new Rounding.TimeUnitRounding(timeUnit, tz);
|
||||
|
||||
// Sunday, 1 April 1990, 00:00:00 clocks were turned backward 1 hour to
|
||||
// Saturday, 31 March 1990, 23:00:00 local standard time instead
|
||||
|
||||
midnightBeforeTransition = time("1990-03-31T00:00:00.000-04:00");
|
||||
nextMidnight = time("1990-04-01T00:00:00.000-05:00");
|
||||
assertInterval(midnightBeforeTransition, nextMidnight, rounding, 25 * 60, tz);
|
||||
|
||||
// make sure the next interval is 24h long again
|
||||
long midnightAfterTransition = time("1990-04-01T00:00:00.000-05:00");
|
||||
nextMidnight = time("1990-04-02T00:00:00.000-05:00");
|
||||
assertInterval(midnightAfterTransition, nextMidnight, rounding, 24 * 60, tz);
|
||||
}
|
||||
|
||||
private void assertInterval(long rounded, long nextRoundingValue, Rounding rounding, int minutes,
|
||||
ZoneId tz) {
|
||||
assertInterval(rounded, dateBetween(rounded, nextRoundingValue), nextRoundingValue, rounding, tz);
|
||||
long millisPerMinute = 60_000;
|
||||
assertEquals(millisPerMinute * minutes, nextRoundingValue - rounded);
|
||||
}
|
||||
|
||||
/**
|
||||
* perform a number on assertions and checks on {@link org.elasticsearch.common.rounding.Rounding.TimeUnitRounding} intervals
|
||||
* @param rounded the expected low end of the rounding interval
|
||||
* @param unrounded a date in the interval to be checked for rounding
|
||||
* @param nextRoundingValue the expected upper end of the rounding interval
|
||||
* @param rounding the rounding instance
|
||||
*/
|
||||
private void assertInterval(long rounded, long unrounded, long nextRoundingValue, Rounding rounding, ZoneId tz) {
|
||||
assertThat("rounding should be idempotent ", rounding.round(rounded), isDate(rounded, tz));
|
||||
assertThat("rounded value smaller or equal than unrounded" + rounding, rounded, lessThanOrEqualTo(unrounded));
|
||||
assertThat("values less than rounded should round further down" + rounding, rounding.round(rounded - 1), lessThan(rounded));
|
||||
assertThat("nextRounding value should be a rounded date", rounding.round(nextRoundingValue), isDate(nextRoundingValue, tz));
|
||||
assertThat("values above nextRounding should round down there", rounding.round(nextRoundingValue + 1),
|
||||
isDate(nextRoundingValue, tz));
|
||||
|
||||
if (isTimeWithWellDefinedRounding(tz, unrounded)) {
|
||||
assertThat("nextRounding value should be greater than date" + rounding, nextRoundingValue, greaterThan(unrounded));
|
||||
|
||||
long dateBetween = dateBetween(rounded, nextRoundingValue);
|
||||
long roundingDateBetween = rounding.round(dateBetween);
|
||||
ZonedDateTime zonedDateBetween = ZonedDateTime.ofInstant(Instant.ofEpochMilli(dateBetween), tz);
|
||||
assertThat("dateBetween [" + zonedDateBetween + "/" + dateBetween + "] should round down to roundedDate [" +
|
||||
Instant.ofEpochMilli(roundingDateBetween) + "]", roundingDateBetween, isDate(rounded, tz));
|
||||
assertThat("dateBetween [" + zonedDateBetween + "] should round up to nextRoundingValue",
|
||||
rounding.nextRoundingValue(dateBetween), isDate(nextRoundingValue, tz));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isTimeWithWellDefinedRounding(ZoneId tz, long t) {
|
||||
if (tz.getId().equals("America/St_Johns")
|
||||
|| tz.getId().equals("America/Goose_Bay")
|
||||
|| tz.getId().equals("America/Moncton")
|
||||
|| tz.getId().equals("Canada/Newfoundland")) {
|
||||
|
||||
// Clocks went back at 00:01 between 1987 and 2010, causing overlapping days.
|
||||
// These timezones are otherwise uninteresting, so just skip this period.
|
||||
|
||||
return t <= time("1987-10-01T00:00:00Z")
|
||||
|| t >= time("2010-12-01T00:00:00Z");
|
||||
}
|
||||
|
||||
if (tz.getId().equals("Antarctica/Casey")) {
|
||||
|
||||
// Clocks went back 3 hours at 02:00 on 2010-03-05, causing overlapping days.
|
||||
|
||||
return t <= time("2010-03-03T00:00:00Z")
|
||||
|| t >= time("2010-03-07T00:00:00Z");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long dateBetween(long lower, long upper) {
|
||||
long dateBetween = randomLongBetween(lower, upper - 1);
|
||||
assert lower <= dateBetween && dateBetween < upper;
|
||||
return dateBetween;
|
||||
}
|
||||
|
||||
private static long time(String time) {
|
||||
return time(time, ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private static long time(String time, ZoneId zone) {
|
||||
TemporalAccessor accessor = DateFormatters.forPattern("date_optional_time").withZone(zone).parse(time);
|
||||
return DateFormatters.toZonedDateTime(accessor).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
private static Matcher<Long> isDate(final long expected, ZoneId tz) {
|
||||
return new TypeSafeMatcher<Long>() {
|
||||
@Override
|
||||
public boolean matchesSafely(final Long item) {
|
||||
return expected == item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(expected), tz);
|
||||
description.appendText(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime) + " [" + expected + "] ");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void describeMismatchSafely(final Long actual, final Description mismatchDescription) {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(actual), tz);
|
||||
mismatchDescription.appendText(" was ")
|
||||
.appendValue(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime) + " [" + actual + "]");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,11 @@
|
|||
package org.elasticsearch.common.rounding;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import static org.elasticsearch.common.rounding.DateTimeUnit.DAY_OF_MONTH;
|
||||
import static org.elasticsearch.common.rounding.DateTimeUnit.HOUR_OF_DAY;
|
||||
|
@ -28,6 +33,7 @@ import static org.elasticsearch.common.rounding.DateTimeUnit.QUARTER;
|
|||
import static org.elasticsearch.common.rounding.DateTimeUnit.SECOND_OF_MINUTE;
|
||||
import static org.elasticsearch.common.rounding.DateTimeUnit.WEEK_OF_WEEKYEAR;
|
||||
import static org.elasticsearch.common.rounding.DateTimeUnit.YEAR_OF_CENTURY;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class DateTimeUnitTests extends ESTestCase {
|
||||
|
||||
|
@ -59,4 +65,17 @@ public class DateTimeUnitTests extends ESTestCase {
|
|||
assertEquals(8, SECOND_OF_MINUTE.id());
|
||||
assertEquals(SECOND_OF_MINUTE, DateTimeUnit.resolve((byte) 8));
|
||||
}
|
||||
|
||||
public void testConversion() {
|
||||
long millis = randomLongBetween(0, Instant.now().toEpochMilli());
|
||||
DateTimeZone zone = randomDateTimeZone();
|
||||
ZoneId zoneId = ZoneId.of(zone.getID());
|
||||
|
||||
int offsetSeconds = zoneId.getRules().getOffset(Instant.ofEpochMilli(millis)).getTotalSeconds();
|
||||
long parsedMillisJavaTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId)
|
||||
.minusSeconds(offsetSeconds).toInstant().toEpochMilli();
|
||||
|
||||
long parsedMillisJodaTime = zone.convertLocalToUTC(millis, true);
|
||||
assertThat(parsedMillisJavaTime, is(parsedMillisJodaTime));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common.rounding;
|
||||
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class RoundingDuelTests extends ESTestCase {
|
||||
|
||||
// dont include nano/micro seconds as rounding would become zero then and throw an exception
|
||||
private static final String[] ALLOWED_TIME_SUFFIXES = new String[]{"d", "h", "ms", "s", "m"};
|
||||
|
||||
public void testSerialization() throws Exception {
|
||||
org.elasticsearch.common.Rounding.DateTimeUnit randomDateTimeUnit =
|
||||
randomFrom(org.elasticsearch.common.Rounding.DateTimeUnit.values());
|
||||
org.elasticsearch.common.Rounding rounding;
|
||||
if (randomBoolean()) {
|
||||
rounding = org.elasticsearch.common.Rounding.builder(randomDateTimeUnit).timeZone(ZoneOffset.UTC).build();
|
||||
} else {
|
||||
rounding = org.elasticsearch.common.Rounding.builder(timeValue()).timeZone(ZoneOffset.UTC).build();
|
||||
}
|
||||
BytesStreamOutput output = new BytesStreamOutput();
|
||||
rounding.writeTo(output);
|
||||
|
||||
Rounding roundingJoda = Rounding.Streams.read(output.bytes().streamInput());
|
||||
org.elasticsearch.common.Rounding roundingJavaTime =
|
||||
org.elasticsearch.common.Rounding.read(output.bytes().streamInput());
|
||||
|
||||
int randomInt = randomIntBetween(1, 1_000_000_000);
|
||||
assertThat(roundingJoda.round(randomInt), is(roundingJavaTime.round(randomInt)));
|
||||
assertThat(roundingJoda.nextRoundingValue(randomInt), is(roundingJavaTime.nextRoundingValue(randomInt)));
|
||||
}
|
||||
|
||||
static TimeValue timeValue() {
|
||||
return TimeValue.parseTimeValue(randomIntBetween(1, 1000) + randomFrom(ALLOWED_TIME_SUFFIXES), "settingName");
|
||||
}
|
||||
}
|
|
@ -57,7 +57,10 @@ public class SignificantLongTermsTests extends InternalSignificantTermsTestCase
|
|||
Set<Long> terms = new HashSet<>();
|
||||
for (int i = 0; i < numBuckets; ++i) {
|
||||
long term = randomValueOtherThanMany(l -> terms.add(l) == false, random()::nextLong);
|
||||
buckets.add(new SignificantLongTerms.Bucket(subsetDfs[i], subsetSize, supersetDfs[i], supersetSize, term, aggs, format));
|
||||
SignificantLongTerms.Bucket bucket = new SignificantLongTerms.Bucket(subsetDfs[i], subsetSize,
|
||||
supersetDfs[i], supersetSize, term, aggs, format);
|
||||
bucket.updateScore(significanceHeuristic);
|
||||
buckets.add(bucket);
|
||||
}
|
||||
return new SignificantLongTerms(name, requiredSize, 1L, pipelineAggregators, metaData, format, subsetSize,
|
||||
supersetSize, significanceHeuristic, buckets);
|
||||
|
|
|
@ -50,7 +50,10 @@ public class SignificantStringTermsTests extends InternalSignificantTermsTestCas
|
|||
Set<BytesRef> terms = new HashSet<>();
|
||||
for (int i = 0; i < numBuckets; ++i) {
|
||||
BytesRef term = randomValueOtherThanMany(b -> terms.add(b) == false, () -> new BytesRef(randomAlphaOfLength(10)));
|
||||
buckets.add(new SignificantStringTerms.Bucket(term, subsetDfs[i], subsetSize, supersetDfs[i], supersetSize, aggs, format));
|
||||
SignificantStringTerms.Bucket bucket = new SignificantStringTerms.Bucket(term, subsetDfs[i], subsetSize,
|
||||
supersetDfs[i], supersetSize, aggs, format);
|
||||
bucket.updateScore(significanceHeuristic);
|
||||
buckets.add(bucket);
|
||||
}
|
||||
return new SignificantStringTerms(name, requiredSize, 1L, pipelineAggregators, metaData, format, subsetSize,
|
||||
supersetSize, significanceHeuristic, buckets);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
|
||||
public class DeleteLicenseAction extends Action<DeleteLicenseResponse> {
|
||||
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class DeleteLicenseRequest extends AcknowledgedRequest<DeleteLicenseRequest> {
|
||||
|
||||
public DeleteLicenseRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ package org.elasticsearch.license;
|
|||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
|
||||
public class DeleteLicenseRequestBuilder extends AcknowledgedRequestBuilder<DeleteLicenseRequest, DeleteLicenseResponse,
|
||||
DeleteLicenseRequestBuilder> {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
|
||||
public class DeleteLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
DeleteLicenseResponse() {
|
||||
}
|
||||
|
||||
DeleteLicenseResponse(boolean acknowledged) {
|
||||
super(acknowledged);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.elasticsearch.discovery.DiscoveryModule;
|
|||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
|
|
|
@ -7,6 +7,8 @@ package org.elasticsearch.license;
|
|||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.RestToXContentListener;
|
||||
|
|
|
@ -17,6 +17,8 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
|||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.cluster.service.ClusterService;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.protocol.xpack.license;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
|
||||
|
||||
public class DeleteLicenseRequest extends AcknowledgedRequest<DeleteLicenseRequest> {
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.protocol.xpack.license;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
public class DeleteLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
public DeleteLicenseResponse() {
|
||||
}
|
||||
|
||||
public DeleteLicenseResponse(boolean acknowledged) {
|
||||
super(acknowledged);
|
||||
}
|
||||
|
||||
public static DeleteLicenseResponse fromXContent(XContentParser parser) {
|
||||
return new DeleteLicenseResponse(parseAcknowledged(parser));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.protocol.xpack.license;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
|
||||
public class DeleteLicenseResponseTests extends AbstractStreamableXContentTestCase<DeleteLicenseResponse> {
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeleteLicenseResponse createTestInstance() {
|
||||
return new DeleteLicenseResponse(randomBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeleteLicenseResponse doParseInstance(XContentParser parser) {
|
||||
return DeleteLicenseResponse.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeleteLicenseResponse createBlankInstance() {
|
||||
return new DeleteLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DeleteLicenseResponse mutateInstance(DeleteLicenseResponse response) {
|
||||
return new DeleteLicenseResponse(!response.isAcknowledged());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue