From 1cb1febd9842e65de9198b12cf741f2a0c338d9d Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 8 Jan 2019 08:47:35 +0200 Subject: [PATCH 001/121] SQL: fix COUNT DISTINCT filtering (#37176) * Use `_count` aggregation value only for not-DISTINCT COUNT function calls * COUNT DISTINCT will use the _exact_ version of a field (the `keyword` sub-field for example), if there is one (cherry picked from commit 3fad9d25f622be5857854c2cb677426393172871) --- .../sql/qa/src/main/resources/agg.sql-spec | 14 ++++++++ .../expression/function/aggregate/Count.java | 5 ++- .../xpack/sql/planner/QueryTranslator.java | 8 ++++- .../sql/planner/QueryTranslatorTests.java | 35 ++++++++++++++++++- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec b/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec index 149e23f771349..39775fc13aed3 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec @@ -110,6 +110,8 @@ aggCountWithAlias SELECT gender g, COUNT(*) c FROM "test_emp" GROUP BY g ORDER BY gender; countDistinct SELECT COUNT(DISTINCT hire_date) AS count FROM test_emp; +countDistinctAndCountSimpleWithAlias +SELECT COUNT(*) cnt, COUNT(DISTINCT first_name) as names, gender FROM test_emp GROUP BY gender ORDER BY gender; aggCountAliasAndWhereClauseMultiGroupBy SELECT gender g, languages l, COUNT(*) c FROM "test_emp" WHERE emp_no < 10020 GROUP BY gender, languages ORDER BY gender, languages; @@ -121,6 +123,8 @@ aggCountWithAliasMultiGroupBy SELECT gender g, languages l, COUNT(*) c FROM "test_emp" GROUP BY g, l ORDER BY gender, languages; aggCountWithAliasMultiGroupByDifferentOrder SELECT gender g, languages l, COUNT(*) c FROM "test_emp" GROUP BY g, l ORDER BY languages ASC, gender DESC; +aggCountDistinctWithAliasAndGroupBy +SELECT COUNT(*) cnt, COUNT(DISTINCT first_name) as names, gender FROM test_emp GROUP BY gender ORDER BY gender; @@ -161,12 +165,20 @@ aggCountStarAndHavingBetween SELECT gender g, COUNT(*) c FROM "test_emp" GROUP BY g HAVING c BETWEEN 10 AND 70 ORDER BY gender ASC; aggCountStarAndHavingBetweenWithLimit SELECT gender g, COUNT(*) c FROM "test_emp" GROUP BY g HAVING c BETWEEN 10 AND 70 ORDER BY gender LIMIT 1; +aggCountDistinctAndHavingBetweenWithLimit +SELECT gender g, COUNT(DISTINCT first_name) c FROM "test_emp" GROUP BY g HAVING c BETWEEN 40 AND 50 ORDER BY gender LIMIT 1; aggCountOnColumnAndHavingOnAliasAndFunction SELECT gender g, COUNT(gender) c FROM "test_emp" GROUP BY g HAVING c > 10 AND COUNT(gender) < 70 ORDER BY gender; aggCountOnColumnAndHavingOnAliasAndFunctionWildcard -> COUNT(*/1) vs COUNT(gender) SELECT gender g, COUNT(gender) c FROM "test_emp" GROUP BY g HAVING c > 10 AND COUNT(*) < 70 ORDER BY gender; aggCountOnColumnAndHavingOnAliasAndFunctionConstant SELECT gender g, COUNT(gender) c FROM "test_emp" GROUP BY g HAVING c > 10 AND COUNT(1) < 70 ORDER BY gender; +aggDistinctCountWithAliasAndHaving +SELECT COUNT(*) c, COUNT(DISTINCT first_name) AS names, gender FROM test_emp GROUP BY gender HAVING names > 40 ORDER BY gender; +aggDistinctCountWithFunctionWildcardAndHaving +SELECT COUNT(*) c, COUNT(DISTINCT first_name) AS names, gender FROM test_emp GROUP BY gender HAVING names < 50 AND c < 50 ORDER BY gender; +aggDistinctCountWithFunctionWildcardAndFunctionConstantAndHaving +SELECT COUNT(*) c, COUNT(DISTINCT first_name) AS names, COUNT(123) AS c123, gender FROM test_emp GROUP BY gender HAVING names < 50 AND c < 50 AND c123 < 50 ORDER BY gender; aggCountAndHavingMultiGroupBy SELECT gender g, languages l, COUNT(*) c FROM "test_emp" GROUP BY g, l HAVING COUNT(*) > 10 ORDER BY gender, l; @@ -195,6 +207,8 @@ aggCountOnColumnAndHavingOnAliasAndFunctionWildcardMultiGroupBy -> COUNT(*/1) vs SELECT gender g, languages l, COUNT(gender) c FROM "test_emp" GROUP BY g, l HAVING c > 10 AND COUNT(*) < 70 ORDER BY gender, languages; aggCountOnColumnAndHavingOnAliasAndFunctionConstantMultiGroupBy SELECT gender g, languages l, COUNT(gender) c FROM "test_emp" GROUP BY g, l HAVING c > 10 AND COUNT(1) < 70 ORDER BY gender, languages; +aggCountOnDistinctColumnAndHavingOnAliasAndFunctionConstantMultiGroupBy +SELECT gender g, languages l, COUNT(DISTINCT last_name) c FROM "test_emp" GROUP BY g, l HAVING c > 5 AND COUNT(1) < 70 ORDER BY gender, languages; // MIN diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java index 3653f84be29b6..10424118a3693 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java @@ -61,6 +61,9 @@ public String functionId() { @Override public AggregateFunctionAttribute toAttribute() { - return new AggregateFunctionAttribute(location(), name(), dataType(), id(), functionId(), "_count"); + if (!distinct()) { + return new AggregateFunctionAttribute(location(), name(), dataType(), id(), functionId(), "_count"); + } + return super.toAttribute(); } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java index 4f071ee50f4f1..f692f31787113 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java @@ -429,7 +429,13 @@ static String dateFormat(Expression e) { static String field(AggregateFunction af) { Expression arg = af.field(); if (arg instanceof FieldAttribute) { - return ((FieldAttribute) arg).name(); + FieldAttribute field = (FieldAttribute) arg; + // COUNT(DISTINCT) uses cardinality aggregation which works on exact values (not changed by analyzers or normalizers) + if (af instanceof Count && ((Count) af).distinct()) { + // use the `keyword` version of the field, if there is one + return field.isInexact() ? field.exactAttribute().name() : field.name(); + } + return field.name(); } if (arg instanceof Literal) { return String.valueOf(((Literal) arg).value()); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java index 559d676f1b95f..d18ea72f2b7f8 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java @@ -5,6 +5,8 @@ */ package org.elasticsearch.xpack.sql.planner; +import org.elasticsearch.search.aggregations.AggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.TestUtils; @@ -19,11 +21,14 @@ import org.elasticsearch.xpack.sql.expression.function.grouping.Histogram; import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate; +import org.elasticsearch.xpack.sql.optimizer.Optimizer; import org.elasticsearch.xpack.sql.parser.SqlParser; import org.elasticsearch.xpack.sql.plan.logical.Aggregate; import org.elasticsearch.xpack.sql.plan.logical.Filter; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.plan.logical.Project; +import org.elasticsearch.xpack.sql.plan.physical.EsQueryExec; +import org.elasticsearch.xpack.sql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.sql.planner.QueryTranslator.QueryTranslation; import org.elasticsearch.xpack.sql.querydsl.agg.AggFilter; import org.elasticsearch.xpack.sql.querydsl.query.ExistsQuery; @@ -41,6 +46,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; @@ -55,6 +61,8 @@ public class QueryTranslatorTests extends ESTestCase { private static SqlParser parser; private static Analyzer analyzer; + private static Optimizer optimizer; + private static Planner planner; @BeforeClass public static void init() { @@ -64,6 +72,8 @@ public static void init() { EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); analyzer = new Analyzer(TestUtils.TEST_CFG, new FunctionRegistry(), getIndexResult, new Verifier(new Metrics())); + optimizer = new Optimizer(); + planner = new Planner(); } @AfterClass @@ -75,6 +85,10 @@ public static void destroy() { private LogicalPlan plan(String sql) { return analyzer.analyze(parser.createStatement(sql), true); } + + private PhysicalPlan optimizeAndPlan(String sql) { + return planner.plan(optimizer.optimize(plan(sql)), true); + } public void testTermEqualityAnalyzer() { LogicalPlan p = plan("SELECT some.string FROM test WHERE some.string = 'value'"); @@ -433,6 +447,7 @@ public void testTranslateNullIf_GroupBy_Painless() { scriptTemplate.toString()); assertEquals("[{v=int}, {v=10}]", scriptTemplate.params().toString()); } + public void testGroupByDateHistogram() { LogicalPlan p = plan("SELECT MAX(int) FROM test GROUP BY HISTOGRAM(int, 1000)"); assertTrue(p instanceof Aggregate); @@ -448,7 +463,6 @@ public void testGroupByDateHistogram() { assertEquals(DataType.INTEGER, field.dataType()); } - public void testGroupByHistogram() { LogicalPlan p = plan("SELECT MAX(int) FROM test GROUP BY HISTOGRAM(date, INTERVAL 2 YEARS)"); assertTrue(p instanceof Aggregate); @@ -463,4 +477,23 @@ public void testGroupByHistogram() { assertEquals(FieldAttribute.class, field.getClass()); assertEquals(DataType.DATE, field.dataType()); } + + public void testCountDistinctCardinalityFolder() { + PhysicalPlan p = optimizeAndPlan("SELECT COUNT(DISTINCT keyword) cnt FROM test GROUP BY bool HAVING cnt = 0"); + assertEquals(EsQueryExec.class, p.getClass()); + EsQueryExec ee = (EsQueryExec) p; + assertEquals(1, ee.output().size()); + assertThat(ee.output().get(0).toString(), startsWith("cnt{a->")); + + Collection subAggs = ee.queryContainer().aggs().asAggBuilder().getSubAggregations(); + assertEquals(1, subAggs.size()); + assertTrue(subAggs.toArray()[0] instanceof CardinalityAggregationBuilder); + + CardinalityAggregationBuilder cardinalityAgg = (CardinalityAggregationBuilder) subAggs.toArray()[0]; + assertEquals("keyword", cardinalityAgg.field()); + assertThat(ee.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""), + endsWith("{\"buckets_path\":{\"a0\":\"" + cardinalityAgg.getName() +"\"},\"script\":{" + + "\"source\":\"InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.eq(params.a0,params.v0))\"," + + "\"lang\":\"painless\",\"params\":{\"v0\":0}},\"gap_policy\":\"skip\"}}}}}")); + } } From c1660dc441818d6257f1affa64981ea6bf833cbf Mon Sep 17 00:00:00 2001 From: Andrew Banchich Date: Tue, 8 Jan 2019 04:22:50 -0500 Subject: [PATCH 002/121] =?UTF-8?q?[Docs]=C2=A0Fix=20wrong=20math=20in=20o?= =?UTF-8?q?verview.asciidoc=20(#37209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/reference/rollup/overview.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/rollup/overview.asciidoc b/docs/reference/rollup/overview.asciidoc index b2570f647e72b..90c5e20a850c1 100644 --- a/docs/reference/rollup/overview.asciidoc +++ b/docs/reference/rollup/overview.asciidoc @@ -6,7 +6,7 @@ experimental[] Time-based data (documents that are predominantly identified by their timestamp) often have associated retention policies -to manage data growth. For example, your system may be generating 500,000 documents every second. That will generate +to manage data growth. For example, your system may be generating 500 documents every second. That will generate 43 million documents per day, and nearly 16 billion documents a year. While your analysts and data scientists may wish you stored that data indefinitely for analysis, time is never-ending and From 9fedf67cda60a167f25b928acf770a861aa5bc9b Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 9 Jan 2019 08:51:00 +0000 Subject: [PATCH 003/121] [TEST] Ensure interrupted flag reset after test that sets it (#37230) Test fix to stop a problem in one test leaking into a different test and causing that other test to spuriously fail. --- .../xpack/ml/filestructurefinder/TimeoutCheckerTests.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/filestructurefinder/TimeoutCheckerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/filestructurefinder/TimeoutCheckerTests.java index 8518096d64478..ea581f663462f 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/filestructurefinder/TimeoutCheckerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/filestructurefinder/TimeoutCheckerTests.java @@ -72,6 +72,9 @@ public void testWatchdog() { } finally { TimeoutChecker.watchdog.unregister(); } + } finally { + // ensure the interrupted flag is cleared to stop it making subsequent tests fail + Thread.interrupted(); } } @@ -89,6 +92,9 @@ public void testGrokCaptures() throws Exception { assertEquals("Aborting grok captures test during [should timeout] as it has taken longer than the timeout of [" + timeout + "]", e.getMessage()); }); + } finally { + // ensure the interrupted flag is cleared to stop it making subsequent tests fail + Thread.interrupted(); } } } From 2c4c345d5581561e51f8497bb6274708af6dd092 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 9 Jan 2019 10:42:47 +0000 Subject: [PATCH 004/121] [ML] Stop datafeeds running when their jobs are stale (#37227) We already had logic to stop datafeeds running against jobs that were OPENING, but a job that relocates from one node to another while OPENED stays OPENED, and this could cause the datafeed to fail when it sent data to the OPENED job on its new node before it had a corresponding autodetect process. This change extends the check to stop datafeeds running when their job is OPENING _or_ stale (i.e. has not had its status reset since relocating to a different node). Relates #36810 --- .../elasticsearch/xpack/core/ml/MlTasks.java | 35 ++++++++++++++ .../core/ml/job/config/JobTaskState.java | 11 +++++ .../ml/action/TransportOpenJobAction.java | 26 ++-------- .../xpack/ml/datafeed/DatafeedManager.java | 8 ++-- .../action/TransportOpenJobActionTests.java | 10 +++- .../ml/datafeed/DatafeedManagerTests.java | 47 +++++++++++++++++-- 6 files changed, 104 insertions(+), 33 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlTasks.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlTasks.java index e78649d152296..b81a1f7d7b9c0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlTasks.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlTasks.java @@ -55,6 +55,11 @@ public static PersistentTasksCustomMetaData.PersistentTask getDatafeedTask(St return tasks == null ? null : tasks.getTask(datafeedTaskId(datafeedId)); } + /** + * Note that the return value of this method does NOT take node relocations into account. + * Use {@link #getJobStateModifiedForReassignments} to return a value adjusted to the most + * appropriate value following relocations. + */ public static JobState getJobState(String jobId, @Nullable PersistentTasksCustomMetaData tasks) { PersistentTasksCustomMetaData.PersistentTask task = getJobTask(jobId, tasks); if (task != null) { @@ -68,6 +73,36 @@ public static JobState getJobState(String jobId, @Nullable PersistentTasksCustom return JobState.CLOSED; } + public static JobState getJobStateModifiedForReassignments(String jobId, @Nullable PersistentTasksCustomMetaData tasks) { + return getJobStateModifiedForReassignments(getJobTask(jobId, tasks)); + } + + public static JobState getJobStateModifiedForReassignments(@Nullable PersistentTasksCustomMetaData.PersistentTask task) { + if (task == null) { + // A closed job has no persistent task + return JobState.CLOSED; + } + JobTaskState jobTaskState = (JobTaskState) task.getState(); + if (jobTaskState == null) { + return JobState.OPENING; + } + JobState jobState = jobTaskState.getState(); + if (jobTaskState.isStatusStale(task)) { + // the job is re-locating + if (jobState == JobState.CLOSING) { + // previous executor node failed while the job was closing - it won't + // be reopened on another node, so consider it CLOSED for most purposes + return JobState.CLOSED; + } + if (jobState != JobState.FAILED) { + // previous executor node failed and current executor node didn't + // have the chance to set job status to OPENING + return JobState.OPENING; + } + } + return jobState; + } + public static DatafeedState getDatafeedState(String datafeedId, @Nullable PersistentTasksCustomMetaData tasks) { PersistentTasksCustomMetaData.PersistentTask task = getDatafeedTask(datafeedId, tasks); if (task != null && task.getState() != null) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/JobTaskState.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/JobTaskState.java index 2e6cc4b99c4bb..d979b897ad43a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/JobTaskState.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/JobTaskState.java @@ -67,6 +67,17 @@ public JobState getState() { return state; } + /** + * The job state stores the allocation ID at the time it was last set. + * This method compares the allocation ID in the state with the allocation + * ID in the task. If the two are different then the task has been relocated + * to a different node after the last time the state was set. This in turn + * means that the state is not necessarily correct. For example, a job that + * has a state of OPENED but is stale must be considered to be OPENING, because + * it won't yet have a corresponding autodetect process. + * @param task The job task to check. + * @return Has the task been relocated to another node and not had its status set since then? + */ public boolean isStatusStale(PersistentTask task) { return allocationId != task.getAllocationId(); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java index c1386c8aee047..3b8a7d97c25dd 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java @@ -255,31 +255,13 @@ static PersistentTasksCustomMetaData.Assignment selectLeastLoadedMlNode(String j Collection> assignedTasks = persistentTasks.findTasks( MlTasks.JOB_TASK_NAME, task -> node.getId().equals(task.getExecutorNode())); for (PersistentTasksCustomMetaData.PersistentTask assignedTask : assignedTasks) { - JobTaskState jobTaskState = (JobTaskState) assignedTask.getState(); - JobState jobState; - if (jobTaskState == null) { - // executor node didn't have the chance to set job status to OPENING - ++numberOfAllocatingJobs; - jobState = JobState.OPENING; - } else { - jobState = jobTaskState.getState(); - if (jobTaskState.isStatusStale(assignedTask)) { - // the job is re-locating - if (jobState == JobState.CLOSING) { - // previous executor node failed while the job was closing - it won't - // be reopened, so consider it CLOSED for resource usage purposes - jobState = JobState.CLOSED; - } else if (jobState != JobState.FAILED) { - // previous executor node failed and current executor node didn't - // have the chance to set job status to OPENING - ++numberOfAllocatingJobs; - jobState = JobState.OPENING; - } - } - } + JobState jobState = MlTasks.getJobStateModifiedForReassignments(assignedTask); if (jobState.isAnyOf(JobState.CLOSED, JobState.FAILED) == false) { // Don't count CLOSED or FAILED jobs, as they don't consume native memory ++numberOfAssignedJobs; + if (jobState == JobState.OPENING) { + ++numberOfAllocatingJobs; + } OpenJobAction.JobParams params = (OpenJobAction.JobParams) assignedTask.getParams(); Long jobMemoryRequirement = memoryTracker.getJobMemoryRequirement(params.getJobId()); if (jobMemoryRequirement == null) { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java index 724c858584b80..7944657aaddc0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java @@ -161,7 +161,7 @@ public void onFailure(Exception e) { protected void doRun() { Long next = null; try { - next = holder.executeLoopBack(startTime, endTime); + next = holder.executeLookBack(startTime, endTime); } catch (DatafeedJob.ExtractionProblemException e) { if (endTime == null) { next = e.nextDelayInMsSinceEpoch; @@ -253,7 +253,7 @@ private String getJobId(TransportStartDatafeedAction.DatafeedTask task) { } private JobState getJobState(PersistentTasksCustomMetaData tasks, TransportStartDatafeedAction.DatafeedTask datafeedTask) { - return MlTasks.getJobState(getJobId(datafeedTask), tasks); + return MlTasks.getJobStateModifiedForReassignments(getJobId(datafeedTask), tasks); } private TimeValue computeNextDelay(long next) { @@ -272,7 +272,7 @@ public class Holder { private final TransportStartDatafeedAction.DatafeedTask task; private final long allocationId; private final String datafeedId; - // To ensure that we wait until loopback / realtime search has completed before we stop the datafeed + // To ensure that we wait until lookback / realtime search has completed before we stop the datafeed private final ReentrantLock datafeedJobLock = new ReentrantLock(true); private final DatafeedJob datafeedJob; private final boolean autoCloseJob; @@ -352,7 +352,7 @@ public void setRelocating() { isRelocating = true; } - private Long executeLoopBack(long startTime, Long endTime) throws Exception { + private Long executeLookBack(long startTime, Long endTime) throws Exception { datafeedJobLock.lock(); try { if (isRunning() && !isIsolated()) { diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java index 3ff80e7c9e882..f6392c629f2ec 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java @@ -614,10 +614,16 @@ public void testJobTaskMatcherMatch() { } public static void addJobTask(String jobId, String nodeId, JobState jobState, PersistentTasksCustomMetaData.Builder builder) { + addJobTask(jobId, nodeId, jobState, builder, false); + } + + public static void addJobTask(String jobId, String nodeId, JobState jobState, PersistentTasksCustomMetaData.Builder builder, + boolean isStale) { builder.addTask(MlTasks.jobTaskId(jobId), MlTasks.JOB_TASK_NAME, new OpenJobAction.JobParams(jobId), - new Assignment(nodeId, "test assignment")); + new Assignment(nodeId, "test assignment")); if (jobState != null) { - builder.updateTaskState(MlTasks.jobTaskId(jobId), new JobTaskState(jobState, builder.getLastAllocationId())); + builder.updateTaskState(MlTasks.jobTaskId(jobId), + new JobTaskState(jobState, builder.getLastAllocationId() - (isStale ? 1 : 0))); } } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java index 5d038a46c0df4..4b0d34fec79c8 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java @@ -222,7 +222,7 @@ public void testRealTime_GivenNonStoppingAnalysisProblem() throws Exception { assertThat(datafeedManager.isRunning(task.getAllocationId()), is(true)); } - public void testStart_GivenNewlyCreatedJobLoopBackAndRealtime() throws Exception { + public void testStart_GivenNewlyCreatedJobLookBackAndRealtime() throws Exception { when(datafeedJob.runLookBack(anyLong(), anyLong())).thenReturn(1L); when(datafeedJob.runRealtime()).thenReturn(1L); @@ -282,8 +282,45 @@ public void testDatafeedTaskWaitsUntilJobIsOpened() { verify(threadPool, times(1)).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); } + public void testDatafeedTaskWaitsUntilJobIsNotStale() { + PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); + addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder, true); + ClusterState.Builder cs = ClusterState.builder(clusterService.state()) + .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); + when(clusterService.state()).thenReturn(cs.build()); + + Consumer handler = mockConsumer(); + DatafeedTask task = createDatafeedTask("datafeed_id", 0L, 60000L); + datafeedManager.run(task, handler); + + // Verify datafeed has not started running yet as job is stale (i.e. even though opened it is part way through relocating) + verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + + tasksBuilder = PersistentTasksCustomMetaData.builder(); + addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder, true); + addJobTask("another_job", "node_id", JobState.OPENED, tasksBuilder); + ClusterState.Builder anotherJobCs = ClusterState.builder(clusterService.state()) + .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); + + capturedClusterStateListener.getValue().clusterChanged(new ClusterChangedEvent("_source", anotherJobCs.build(), cs.build())); + + // Still no run + verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + + tasksBuilder = PersistentTasksCustomMetaData.builder(); + addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder); + ClusterState.Builder jobOpenedCs = ClusterState.builder(clusterService.state()) + .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); + + capturedClusterStateListener.getValue().clusterChanged( + new ClusterChangedEvent("_source", jobOpenedCs.build(), anotherJobCs.build())); + + // Now it should run as the job state chanded to OPENED + verify(threadPool, times(1)).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + } + public void testDatafeedTaskStopsBecauseJobFailedWhileOpening() { - PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); + PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.OPENING, tasksBuilder); ClusterState.Builder cs = ClusterState.builder(clusterService.state()) .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); @@ -296,7 +333,7 @@ public void testDatafeedTaskStopsBecauseJobFailedWhileOpening() { // Verify datafeed has not started running yet as job is still opening verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); - tasksBuilder = PersistentTasksCustomMetaData.builder(); + tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.FAILED, tasksBuilder); ClusterState.Builder updatedCs = ClusterState.builder(clusterService.state()) .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); @@ -309,7 +346,7 @@ public void testDatafeedTaskStopsBecauseJobFailedWhileOpening() { } public void testDatafeedGetsStoppedWhileWaitingForJobToOpen() { - PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); + PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.OPENING, tasksBuilder); ClusterState.Builder cs = ClusterState.builder(clusterService.state()) .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); @@ -326,7 +363,7 @@ public void testDatafeedGetsStoppedWhileWaitingForJobToOpen() { datafeedManager.stopDatafeed(task, "test", StopDatafeedAction.DEFAULT_TIMEOUT); // Update job state to opened - tasksBuilder = PersistentTasksCustomMetaData.builder(); + tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder); ClusterState.Builder updatedCs = ClusterState.builder(clusterService.state()) .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); From 335eeeb28c0598aff579a0f6b64d057fc306a56e Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 9 Jan 2019 15:26:20 +0000 Subject: [PATCH 005/121] [TEST] Unmute 60_ml_config_migration rolling upgrade (#37258) This reverts commit d7efadcc47862ae2910f7c82c95c45853d49e446 The test should now work following the change made in #37227 Closes #36810 --- .../test/upgraded_cluster/60_ml_config_migration.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/60_ml_config_migration.yml b/x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/60_ml_config_migration.yml index d8616b4e8277f..be36d7358e794 100644 --- a/x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/60_ml_config_migration.yml +++ b/x-pack/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/60_ml_config_migration.yml @@ -8,9 +8,6 @@ setup: --- "Test old cluster jobs and datafeeds and delete them": - - skip: - version: "all" - reason: "@AwaitsFix: https://github.com/elastic/elasticsearch/issues/36810" - do: xpack.ml.get_jobs: From 844616b7bed84d36e87cc1e919280f1f30223890 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 9 Jan 2019 18:14:22 +0200 Subject: [PATCH 006/121] [DOCS] Rolling upgrade with old internal indices (#37184) Upgrading the Elastic Stack perfectly documents the process to upgrade ES from 5 to 6 when internal indices are present. However, the rolling upgrade docs do not mention anything about internal indices. This adds a warning in the rolling upgrade procedure, highlighting that internal indices should be upgraded before the rolling upgrade procedure can be started. --- docs/reference/upgrade/rolling_upgrade.asciidoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/upgrade/rolling_upgrade.asciidoc b/docs/reference/upgrade/rolling_upgrade.asciidoc index 86a21627a8901..dff3895ac4c1d 100644 --- a/docs/reference/upgrade/rolling_upgrade.asciidoc +++ b/docs/reference/upgrade/rolling_upgrade.asciidoc @@ -18,6 +18,12 @@ you can do a rolling upgrade you must encrypt the internode-communication with SSL/TLS, which requires a full cluster restart. For more information about this requirement and the associated bootstrap check, see <>. +WARNING: The format used for the internal indices used by Kibana and {xpack} +has changed in 6.x. When upgrading from 5.6 to 6.x, these internal indices have +to be {stack-ref}/upgrading-elastic-stack.html#upgrade-internal-indices[upgraded] +before the rolling upgrade procedure can start. Otherwise the upgraded node will +refuse to join the cluster. + To perform a rolling upgrade: . *Disable shard allocation*. From 43f034b4bdf5ae97fb94c0c5105269a438e4322b Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Tue, 8 Jan 2019 13:00:55 +0200 Subject: [PATCH 007/121] Mute failing test clusters test Tracking issue: #37218 --- .../elasticsearch/gradle/testclusters/TestClustersPluginIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildSrc/src/test/java/org/elasticsearch/gradle/testclusters/TestClustersPluginIT.java b/buildSrc/src/test/java/org/elasticsearch/gradle/testclusters/TestClustersPluginIT.java index ee366ac7b7c65..514f75eaa86e9 100644 --- a/buildSrc/src/test/java/org/elasticsearch/gradle/testclusters/TestClustersPluginIT.java +++ b/buildSrc/src/test/java/org/elasticsearch/gradle/testclusters/TestClustersPluginIT.java @@ -21,9 +21,11 @@ import org.elasticsearch.gradle.test.GradleIntegrationTestCase; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; +import org.junit.Ignore; import java.util.Arrays; +@Ignore // https://github.com/elastic/elasticsearch/issues/37218 public class TestClustersPluginIT extends GradleIntegrationTestCase { public void testListClusters() { From 36cc625d6a4ad6c79d6bf328703578511d85f362 Mon Sep 17 00:00:00 2001 From: Morris Schreibman Date: Wed, 9 Jan 2019 19:12:31 +0200 Subject: [PATCH 008/121] [DOCS] Clarify client settings (#31469) --- docs/plugins/repository-s3.asciidoc | 39 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/docs/plugins/repository-s3.asciidoc b/docs/plugins/repository-s3.asciidoc index 2d1c9f10a7f47..e821cd0cba480 100644 --- a/docs/plugins/repository-s3.asciidoc +++ b/docs/plugins/repository-s3.asciidoc @@ -34,10 +34,10 @@ PUT _snapshot/my_s3_repository [[repository-s3-client]] ==== Client Settings -The client used to connect to S3 has a number of settings available. Client setting names are of -the form `s3.client.CLIENT_NAME.SETTING_NAME`. The default client name, which is looked up by -an `s3` repository, is called `default`. It can be modified using the -<> `client`. For example: +The client that you use to connect to S3 has a number of settings available. The +settings have the form `s3.client.CLIENT_NAME.SETTING_NAME`. The default client +name that is looked up by an `s3` repository is `default`. It can be modified +using the <> `client`. For example: [source,js] ---- @@ -53,11 +53,13 @@ PUT _snapshot/my_s3_repository // CONSOLE // TEST[skip:we don't have s3 setup while testing this] -Most client settings are specified inside `elasticsearch.yml`, but some are -sensitive and must be stored in the {ref}/secure-settings.html[elasticsearch keystore]. +Most client settings can be added to the `elasticsearch.yml` configuration file +with the exception of the secure settings, which you add to the {es} keystore. +For more information about creating and updating the {es} keystore, see +{ref}/secure-settings.html[Secure settings]. -For example, before you start the node, run these commands to add AWS access -key settings to the keystore: +For example, before you start the node, run these commands to add AWS access key +settings to the keystore: [source,sh] ---- @@ -76,16 +78,17 @@ NOTE: In progress snapshot/restore tasks will not be preempted by a *reload* of the client's secure settings. The task will complete using the client as it was built when the operation started. -The following is the list of all the available client settings. -Those that must be stored in the keystore are marked as `Secure` and are *reloadable*. +The following list contains the available client settings. Those that must be +stored in the keystore are marked as "secure" and are *reloadable*; the other +settings belong in the `elasticsearch.yml` file. -`access_key`:: +`access_key` ({ref}/secure-settings.html[Secure]):: - An s3 access key. The `secret_key` setting must also be specified. (Secure) + An s3 access key. The `secret_key` setting must also be specified. -`secret_key`:: +`secret_key` ({ref}/secure-settings.html[Secure]):: - An s3 secret key. The `access_key` setting must also be specified. (Secure) + An s3 secret key. The `access_key` setting must also be specified. `session_token`:: An s3 session token. The `access_key` and `secret_key` settings must also @@ -110,13 +113,13 @@ Those that must be stored in the keystore are marked as `Secure` and are *reload The port of a proxy to connect to s3 through. -`proxy.username`:: +`proxy.username` ({ref}/secure-settings.html[Secure]):: - The username to connect to the `proxy.host` with. (Secure) + The username to connect to the `proxy.host` with. -`proxy.password`:: +`proxy.password` ({ref}/secure-settings.html[Secure]):: - The password to connect to the `proxy.host` with. (Secure) + The password to connect to the `proxy.host` with. `read_timeout`:: From 736deff39d00d6089d8d0d7d6f7b5a816236d42e Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 10 Jan 2019 09:51:51 +0200 Subject: [PATCH 009/121] SQL: Proper handling of COUNT(field_name) and COUNT(DISTINCT field_name) (#37254) * provide overriden `hashCode` and toString methods to account for `DISTINCT` * change the analyzer for scenarios where `COUNT ` and `COUNT DISTINCT` have different paths * defined a new `filter` aggregation encapsulating an `exists` query to filter out null or missing values (cherry picked from commit 4a92de214a0811bfde65311aae65979866b0db5e) --- docs/reference/sql/functions/aggs.asciidoc | 33 ++++++- .../sql/qa/src/main/resources/agg.csv-spec | 57 +++++++++++- .../sql/qa/src/main/resources/agg.sql-spec | 12 +++ .../sql/qa/src/main/resources/docs.csv-spec | 18 +++- .../xpack/sql/analysis/analyzer/Analyzer.java | 21 ++++- .../search/extractor/MetricAggExtractor.java | 4 + .../function/UnresolvedFunction.java | 11 ++- .../expression/function/aggregate/Count.java | 41 ++++++++- .../function/grouping/GroupingFunction.java | 2 +- .../xpack/sql/planner/QueryFolder.java | 11 ++- .../xpack/sql/planner/QueryTranslator.java | 14 +-- .../sql/querydsl/agg/CardinalityAgg.java | 3 +- .../sql/querydsl/agg/FilterExistsAgg.java | 26 ++++++ .../sql/planner/QueryTranslatorTests.java | 91 +++++++++++++++++-- 14 files changed, 312 insertions(+), 32 deletions(-) create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/FilterExistsAgg.java diff --git a/docs/reference/sql/functions/aggs.asciidoc b/docs/reference/sql/functions/aggs.asciidoc index d55da5a45a032..e9a84376ba0b3 100644 --- a/docs/reference/sql/functions/aggs.asciidoc +++ b/docs/reference/sql/functions/aggs.asciidoc @@ -53,11 +53,42 @@ COUNT(expression<1>) Returns the total number (count) of input values. +In case of `COUNT(*)` or `COUNT()`, _all_ values are considered (including `null` or missing ones). + +In case of `COUNT()` `null` values are not considered. + + ["source","sql",subs="attributes,macros"] -------------------------------------------------- include-tagged::{sql-specs}/docs.csv-spec[aggCountStar] -------------------------------------------------- + +[[sql-functions-aggs-count-all]] +===== `COUNT(ALL)` + +.Synopsis: +[source, sql] +-------------------------------------------------- +COUNT(ALL field_name<1>) +-------------------------------------------------- + +*Input*: + +<1> a field name + +*Output*: numeric value + +.Description: + +Returns the total number (count) of all _non-null_ input values. `COUNT()` and `COUNT(ALL )` are equivalent. + +["source","sql",subs="attributes,macros"] +-------------------------------------------------- +include-tagged::{sql-specs}/docs.csv-spec[aggCountAll] +-------------------------------------------------- + + [[sql-functions-aggs-count-distinct]] ===== `COUNT(DISTINCT)` @@ -75,7 +106,7 @@ COUNT(DISTINCT field_name<1>) .Description: -Returns the total number of _distinct_ values in input values. +Returns the total number of _distinct non-null_ values in input values. ["source","sql",subs="attributes,macros"] -------------------------------------------------- diff --git a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec index f9576c7b859a6..bdb94321b76d5 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec @@ -309,4 +309,59 @@ SELECT HISTOGRAM(emp_no % 100, 10) AS h, COUNT(*) as c FROM test_emp GROUP BY h 20 |10 10 |10 0 |10 -; \ No newline at end of file +; + +countAll +schema::all_names:l|c:l +SELECT COUNT(ALL first_name) all_names, COUNT(*) c FROM test_emp; + + all_names | c +---------------+--------------- +90 |100 +; + +countAllCountTypesWithHaving +schema::ln:l|dln:l|fn:l|dfn:l|ccc:l +SELECT COUNT(last_name) ln, COUNT(distinct last_name) dln, COUNT(first_name) fn, COUNT(distinct first_name) dfn, COUNT(*) ccc FROM test_emp GROUP BY gender HAVING dln>5 AND ln>32 AND dfn>1 AND fn>1 AND ccc>5; + + ln | dln | fn | dfn | ccc +---------------+-------------+---------------+------------+------------- +33 |32 |32 |32 |33 +57 |54 |48 |48 |57 +; + +aggCountEqualityFalse +schema::areEqual:b|ln:l|dln:l +SELECT COUNT(last_name)=COUNT(DISTINCT last_name) AS areEqual, COUNT(last_name) ln, COUNT(DISTINCT last_name) dln FROM test_emp; + + areEqual | ln | dln +---------------+---------------+--------------- +false |100 |96 +; + +aggCountEqualityTrue +schema::areEqual:b|fn:l|dfn:l +SELECT COUNT(first_name)=COUNT(DISTINCT first_name) AS areEqual, COUNT(first_name) fn, COUNT(DISTINCT first_name) dfn FROM test_emp; + + areEqual | fn | dfn +---------------+---------------+--------------- +true |90 |90 +; + +aggCountAllEquality +schema::areEqual:b|afn:l +SELECT COUNT(first_name)=COUNT(ALL first_name) AS areEqual, COUNT(ALL first_name) afn FROM test_emp; + + areEqual | afn +---------------+--------------- +true |90 +; + +aggCountAllDifferentFields +schema::areEqual:b|afn:l|aln:l +SELECT COUNT(ALL last_name)=COUNT(ALL first_name) AS areEqual, COUNT(ALL first_name) afn, COUNT(ALL last_name) aln FROM test_emp; + + areEqual | afn | aln +---------------+---------------+--------------- +false |90 |100 +; diff --git a/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec b/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec index 39775fc13aed3..21dd7bf530e3d 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/agg.sql-spec @@ -210,6 +210,18 @@ SELECT gender g, languages l, COUNT(gender) c FROM "test_emp" GROUP BY g, l HAVI aggCountOnDistinctColumnAndHavingOnAliasAndFunctionConstantMultiGroupBy SELECT gender g, languages l, COUNT(DISTINCT last_name) c FROM "test_emp" GROUP BY g, l HAVING c > 5 AND COUNT(1) < 70 ORDER BY gender, languages; +aggCount +SELECT COUNT(last_name) c FROM test_emp; +aggCountAndCountDistinct +SELECT COUNT(last_name) c, COUNT(DISTINCT last_name) distinct_names FROM test_emp; +aggCountAndCountDistinctWithHaving +SELECT COUNT(last_name) c, COUNT(DISTINCT last_name) distinct_names, gender FROM test_emp GROUP BY gender HAVING distinct_names > 10 ORDER BY gender; +aggCountMultiComparisonWithHaving +SELECT COUNT(last_name) ln, COUNT(distinct last_name) dln, COUNT(first_name) fn, COUNT(distinct first_name) dfn, COUNT(*) ccc FROM test_emp GROUP BY gender HAVING dln>5 AND ln>32 AND dfn>1 AND fn>1 AND ccc>5 ORDER BY gender DESC; +aggCountMultiComparisonWithHavingAndNullGrouping +SELECT gender, COUNT(last_name) ln, COUNT(distinct last_name) dln, COUNT(first_name) fn, COUNT(distinct first_name) dfn, COUNT(*) ccc FROM test_emp GROUP BY gender HAVING dln>1 AND ln>1 AND dfn>1 AND fn>1 AND ccc>1 ORDER BY gender DESC; +aggCountWithHavingAndWhere +SELECT COUNT(last_name) c, COUNT(DISTINCT last_name) distinct_names, gender FROM test_emp WHERE salary > 65000 GROUP BY gender HAVING distinct_names > 10 ORDER BY gender; // MIN aggMinImplicit diff --git a/x-pack/plugin/sql/qa/src/main/resources/docs.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/docs.csv-spec index f71ec6f97c5ee..d29954af2629e 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/docs.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/docs.csv-spec @@ -977,14 +977,24 @@ SELECT COUNT(*) AS count FROM emp; // end::aggCountStar ; +aggCountAll +// tag::aggCountAll +SELECT COUNT(ALL last_name) AS count_all, COUNT(DISTINCT last_name) count_distinct FROM emp; + + count_all | count_distinct +---------------+------------------ +100 |96 +// end::aggCountAll +; + aggCountDistinct // tag::aggCountDistinct -SELECT COUNT(DISTINCT hire_date) AS hires FROM emp; +SELECT COUNT(DISTINCT hire_date) unique_hires, COUNT(hire_date) AS hires FROM emp; - hires ---------------- -99 + unique_hires | hires +----------------+--------------- +99 |100 // end::aggCountDistinct ; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index c5062b405c265..6dad62f68368c 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry; import org.elasticsearch.xpack.sql.expression.function.Functions; import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; +import org.elasticsearch.xpack.sql.expression.function.aggregate.Count; import org.elasticsearch.xpack.sql.expression.function.scalar.Cast; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.ArithmeticOperation; import org.elasticsearch.xpack.sql.plan.TableIdentifier; @@ -770,7 +771,15 @@ private Expression collectResolvedAndReplace(Expression e, Map list = getList(seen, fName); for (Function seenFunction : list) { if (seenFunction != f && f.arguments().equals(seenFunction.arguments())) { - return seenFunction; + // Special check for COUNT: an already seen COUNT function will be returned only if its DISTINCT property + // matches the one from the unresolved function to be checked. + if (seenFunction instanceof Count) { + if (seenFunction.equals(f)){ + return seenFunction; + } + } else { + return seenFunction; + } } } list.add(f); @@ -808,7 +817,15 @@ protected LogicalPlan resolve(LogicalPlan plan, Map> seen if (!list.isEmpty()) { for (Function seenFunction : list) { if (uf.arguments().equals(seenFunction.arguments())) { - return seenFunction; + // Special check for COUNT: an already seen COUNT function will be returned only if its DISTINCT property + // matches the one from the unresolved function to be checked. + if (seenFunction instanceof Count) { + if (uf.sameAs((Count) seenFunction)) { + return seenFunction; + } + } else { + return seenFunction; + } } } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/MetricAggExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/MetricAggExtractor.java index b95d5ab4696bd..35b0127e3b178 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/MetricAggExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/MetricAggExtractor.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; +import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter; import org.elasticsearch.search.aggregations.matrix.stats.MatrixStats; import org.elasticsearch.search.aggregations.metrics.InternalNumericMetricsAggregation; import org.elasticsearch.search.aggregations.metrics.InternalNumericMetricsAggregation.SingleValue; @@ -83,6 +84,9 @@ public Object extract(Bucket bucket) { // throw new SqlIllegalArgumentException("Invalid innerKey {} specified for aggregation {}", innerKey, name); //} return ((InternalNumericMetricsAggregation.MultiValue) agg).value(property); + } else if (agg instanceof InternalFilter) { + // COUNT(expr) and COUNT(ALL expr) uses this type of aggregation to account for non-null values only + return ((InternalFilter) agg).getDocCount(); } Object v = agg.getProperty(property); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java index c87080a5b3717..47e77d6cd1bd6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/UnresolvedFunction.java @@ -11,10 +11,11 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.Nullability; +import org.elasticsearch.xpack.sql.expression.function.aggregate.Count; import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate; import org.elasticsearch.xpack.sql.session.Configuration; -import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; +import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -129,6 +130,14 @@ ResolutionType resolutionType() { public boolean analyzed() { return analyzed; } + + public boolean sameAs(Count count) { + if (this.resolutionType == ResolutionType.DISTINCT && count.distinct() + || this.resolutionType == ResolutionType.STANDARD && count.distinct() == false) { + return true; + } + return false; + } @Override public DataType dataType() { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java index 10424118a3693..814e873d05bc6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java @@ -5,14 +5,16 @@ */ package org.elasticsearch.xpack.sql.expression.function.aggregate; -import java.util.List; - import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; +import java.util.List; +import java.util.Objects; + /** * Count the number of documents matched ({@code COUNT}) * OR count the number of distinct values @@ -53,17 +55,48 @@ public DataType dataType() { public String functionId() { String functionId = id().toString(); // if count works against a given expression, use its id (to identify the group) - if (field() instanceof NamedExpression) { + // in case of COUNT DISTINCT don't use the expression id to avoid possible duplicate IDs when COUNT and COUNT DISTINCT is used + // in the same query + if (!distinct() && field() instanceof NamedExpression) { functionId = ((NamedExpression) field()).id().toString(); } return functionId; } + @Override + public String name() { + if (distinct()) { + StringBuilder sb = new StringBuilder(super.name()); + sb.insert(sb.indexOf("(") + 1, "DISTINCT "); + return sb.toString(); + } + return super.name(); + } + @Override public AggregateFunctionAttribute toAttribute() { - if (!distinct()) { + // COUNT(*) gets its value from the parent aggregation on which _count is called + if (field() instanceof Literal) { return new AggregateFunctionAttribute(location(), name(), dataType(), id(), functionId(), "_count"); } + // COUNT(column) gets its value from a sibling aggregation (an exists filter agg) by calling its id and then _count on it + if (!distinct()) { + return new AggregateFunctionAttribute(location(), name(), dataType(), id(), functionId(), functionId() + "._count"); + } return super.toAttribute(); } + + @Override + public boolean equals(Object obj) { + if (false == super.equals(obj)) { + return false; + } + Count other = (Count) obj; + return Objects.equals(other.distinct(), distinct()); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), distinct()); + } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java index dbfef6aeb5dc4..a86b4e38e8863 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java @@ -51,7 +51,7 @@ public List parameters() { @Override public GroupingFunctionAttribute toAttribute() { if (lazyAttribute == null) { - // this is highly correlated with QueryFolder$FoldAggregate#addFunction (regarding the function name within the querydsl) + // this is highly correlated with QueryFolder$FoldAggregate#addAggFunction (regarding the function name within the querydsl) lazyAttribute = new GroupingFunctionAttribute(location(), name(), dataType(), id(), functionId()); } return lazyAttribute; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java index 96c267b3ba6fd..97c92bfff2d69 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java @@ -13,6 +13,7 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Foldables; +import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.expression.Order; import org.elasticsearch.xpack.sql.expression.function.Function; @@ -380,7 +381,8 @@ private Tuple addAggFunction(GroupByKey groupingAg // handle count as a special case agg if (f instanceof Count) { Count c = (Count) f; - if (!c.distinct()) { + // COUNT(*) or COUNT() + if (c.field() instanceof Literal) { AggRef ref = groupingAgg == null ? GlobalCountRef.INSTANCE : new GroupByRef(groupingAgg.id(), Property.COUNT, null); @@ -388,7 +390,14 @@ private Tuple addAggFunction(GroupByKey groupingAg Map pseudoFunctions = new LinkedHashMap<>(queryC.pseudoFunctions()); pseudoFunctions.put(functionId, groupingAgg); return new Tuple<>(queryC.withPseudoFunctions(pseudoFunctions), new AggPathInput(f, ref)); + // COUNT() + } else if (!c.distinct()) { + LeafAgg leafAgg = toAgg(functionId, f); + AggPathInput a = new AggPathInput(f, new MetricAggRef(leafAgg.id(), "doc_count", "_count")); + queryC = queryC.with(queryC.aggs().addAgg(leafAgg)); + return new Tuple<>(queryC, a); } + // the only variant left - COUNT(DISTINCT) - will be covered by the else branch below } AggPathInput aggInput = null; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java index f692f31787113..a5046aaa6bb56 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java @@ -62,6 +62,7 @@ import org.elasticsearch.xpack.sql.querydsl.agg.AvgAgg; import org.elasticsearch.xpack.sql.querydsl.agg.CardinalityAgg; import org.elasticsearch.xpack.sql.querydsl.agg.ExtendedStatsAgg; +import org.elasticsearch.xpack.sql.querydsl.agg.FilterExistsAgg; import org.elasticsearch.xpack.sql.querydsl.agg.GroupByDateHistogram; import org.elasticsearch.xpack.sql.querydsl.agg.GroupByKey; import org.elasticsearch.xpack.sql.querydsl.agg.GroupByNumericHistogram; @@ -135,7 +136,7 @@ private QueryTranslator(){} new MatrixStatsAggs(), new PercentilesAggs(), new PercentileRanksAggs(), - new DistinctCounts(), + new CountAggs(), new DateTimes() ); @@ -778,15 +779,16 @@ protected QueryTranslation asQuery(ScalarFunction f, boolean onAggs) { // // Agg translators // - - static class DistinctCounts extends SingleValueAggTranslator { + + static class CountAggs extends SingleValueAggTranslator { @Override protected LeafAgg toAgg(String id, Count c) { - if (!c.distinct()) { - return null; + if (c.distinct()) { + return new CardinalityAgg(id, field(c)); + } else { + return new FilterExistsAgg(id, field(c)); } - return new CardinalityAgg(id, field(c)); } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/CardinalityAgg.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/CardinalityAgg.java index f4fb20428c59f..847509125ba49 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/CardinalityAgg.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/CardinalityAgg.java @@ -15,7 +15,8 @@ public CardinalityAgg(String id, String fieldName) { super(id, fieldName); } - @Override AggregationBuilder toBuilder() { + @Override + AggregationBuilder toBuilder() { return cardinality(id()).field(fieldName()); } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/FilterExistsAgg.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/FilterExistsAgg.java new file mode 100644 index 0000000000000..4c14fa6dad82a --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/FilterExistsAgg.java @@ -0,0 +1,26 @@ +/* + * 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.xpack.sql.querydsl.agg; + +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.aggregations.AggregationBuilder; + +import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; + +/** + * Aggregation builder for a "filter" aggregation encapsulating an "exists" query. + */ +public class FilterExistsAgg extends LeafAgg { + + public FilterExistsAgg(String id, String fieldName) { + super(id, fieldName); + } + + @Override + AggregationBuilder toBuilder() { + return filter(id(), QueryBuilders.existsQuery(fieldName())); + } +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java index d18ea72f2b7f8..4b3c74f57fadb 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java @@ -5,8 +5,11 @@ */ package org.elasticsearch.xpack.sql.planner; +import org.elasticsearch.index.query.ExistsQueryBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.TestUtils; @@ -478,22 +481,90 @@ public void testGroupByHistogram() { assertEquals(DataType.DATE, field.dataType()); } - public void testCountDistinctCardinalityFolder() { - PhysicalPlan p = optimizeAndPlan("SELECT COUNT(DISTINCT keyword) cnt FROM test GROUP BY bool HAVING cnt = 0"); + public void testCountAndCountDistinctFolding() { + PhysicalPlan p = optimizeAndPlan("SELECT COUNT(DISTINCT keyword) dkey, COUNT(keyword) key FROM test"); assertEquals(EsQueryExec.class, p.getClass()); EsQueryExec ee = (EsQueryExec) p; - assertEquals(1, ee.output().size()); - assertThat(ee.output().get(0).toString(), startsWith("cnt{a->")); + assertEquals(2, ee.output().size()); + assertThat(ee.output().get(0).toString(), startsWith("dkey{a->")); + assertThat(ee.output().get(1).toString(), startsWith("key{a->")); Collection subAggs = ee.queryContainer().aggs().asAggBuilder().getSubAggregations(); - assertEquals(1, subAggs.size()); + assertEquals(2, subAggs.size()); assertTrue(subAggs.toArray()[0] instanceof CardinalityAggregationBuilder); + assertTrue(subAggs.toArray()[1] instanceof FilterAggregationBuilder); + + CardinalityAggregationBuilder cardinalityKeyword = (CardinalityAggregationBuilder) subAggs.toArray()[0]; + assertEquals("keyword", cardinalityKeyword.field()); + + FilterAggregationBuilder existsKeyword = (FilterAggregationBuilder) subAggs.toArray()[1]; + assertTrue(existsKeyword.getFilter() instanceof ExistsQueryBuilder); + assertEquals("keyword", ((ExistsQueryBuilder) existsKeyword.getFilter()).fieldName()); + + assertThat(ee.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""), + endsWith("{\"filter\":{\"exists\":{\"field\":\"keyword\",\"boost\":1.0}}}}}}")); + } + + public void testAllCountVariantsWithHavingGenerateCorrectAggregations() { + PhysicalPlan p = optimizeAndPlan("SELECT AVG(int), COUNT(keyword) ln, COUNT(distinct keyword) dln, COUNT(some.dotted.field) fn," + + "COUNT(distinct some.dotted.field) dfn, COUNT(*) ccc FROM test GROUP BY bool " + + "HAVING dln > 3 AND ln > 32 AND dfn > 1 AND fn > 2 AND ccc > 5 AND AVG(int) > 50000"); + assertEquals(EsQueryExec.class, p.getClass()); + EsQueryExec ee = (EsQueryExec) p; + assertEquals(6, ee.output().size()); + assertThat(ee.output().get(0).toString(), startsWith("AVG(int){a->")); + assertThat(ee.output().get(1).toString(), startsWith("ln{a->")); + assertThat(ee.output().get(2).toString(), startsWith("dln{a->")); + assertThat(ee.output().get(3).toString(), startsWith("fn{a->")); + assertThat(ee.output().get(4).toString(), startsWith("dfn{a->")); + assertThat(ee.output().get(5).toString(), startsWith("ccc{a->")); - CardinalityAggregationBuilder cardinalityAgg = (CardinalityAggregationBuilder) subAggs.toArray()[0]; - assertEquals("keyword", cardinalityAgg.field()); + Collection subAggs = ee.queryContainer().aggs().asAggBuilder().getSubAggregations(); + assertEquals(5, subAggs.size()); + assertTrue(subAggs.toArray()[0] instanceof AvgAggregationBuilder); + assertTrue(subAggs.toArray()[1] instanceof FilterAggregationBuilder); + assertTrue(subAggs.toArray()[2] instanceof CardinalityAggregationBuilder); + assertTrue(subAggs.toArray()[3] instanceof FilterAggregationBuilder); + assertTrue(subAggs.toArray()[4] instanceof CardinalityAggregationBuilder); + + AvgAggregationBuilder avgInt = (AvgAggregationBuilder) subAggs.toArray()[0]; + assertEquals("int", avgInt.field()); + + FilterAggregationBuilder existsKeyword = (FilterAggregationBuilder) subAggs.toArray()[1]; + assertTrue(existsKeyword.getFilter() instanceof ExistsQueryBuilder); + assertEquals("keyword", ((ExistsQueryBuilder) existsKeyword.getFilter()).fieldName()); + + CardinalityAggregationBuilder cardinalityKeyword = (CardinalityAggregationBuilder) subAggs.toArray()[2]; + assertEquals("keyword", cardinalityKeyword.field()); + + FilterAggregationBuilder existsDottedField = (FilterAggregationBuilder) subAggs.toArray()[3]; + assertTrue(existsDottedField.getFilter() instanceof ExistsQueryBuilder); + assertEquals("some.dotted.field", ((ExistsQueryBuilder) existsDottedField.getFilter()).fieldName()); + + CardinalityAggregationBuilder cardinalityDottedField = (CardinalityAggregationBuilder) subAggs.toArray()[4]; + assertEquals("some.dotted.field", cardinalityDottedField.field()); + assertThat(ee.queryContainer().aggs().asAggBuilder().toString().replaceAll("\\s+", ""), - endsWith("{\"buckets_path\":{\"a0\":\"" + cardinalityAgg.getName() +"\"},\"script\":{" - + "\"source\":\"InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.eq(params.a0,params.v0))\"," - + "\"lang\":\"painless\",\"params\":{\"v0\":0}},\"gap_policy\":\"skip\"}}}}}")); + endsWith("{\"buckets_path\":{" + + "\"a0\":\"" + cardinalityKeyword.getName() + "\"," + + "\"a1\":\"" + existsKeyword.getName() + "._count\"," + + "\"a2\":\"" + cardinalityDottedField.getName() + "\"," + + "\"a3\":\"" + existsDottedField.getName() + "._count\"," + + "\"a4\":\"_count\"," + + "\"a5\":\"" + avgInt.getName() + "\"}," + + "\"script\":{\"source\":\"" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.and(" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.and(" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.and(" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.and(" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.and(" + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a0,params.v0))," + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a1,params.v1))))," + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a2,params.v2))))," + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a3,params.v3))))," + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a4,params.v4))))," + + "InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gt(params.a5,params.v5))))\"," + + "\"lang\":\"painless\",\"params\":{\"v0\":3,\"v1\":32,\"v2\":1,\"v3\":2,\"v4\":5,\"v5\":50000}}," + + "\"gap_policy\":\"skip\"}}}}}")); } } From cfe05e4e8d1142cfaa9b91d22764654f6d6c2bcd Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 9 Jan 2019 12:17:47 +0100 Subject: [PATCH 010/121] [CCR] Added more logging. --- .../elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java index 296dc1b3187f8..03918d80d9ec7 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java @@ -241,6 +241,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }; newAutoFollowers.put(remoteCluster, autoFollower); + LOGGER.info("starting auto follower for remote cluster [{}]", remoteCluster); autoFollower.start(); } @@ -251,9 +252,10 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS boolean exist = autoFollowMetadata.getPatterns().values().stream() .anyMatch(pattern -> pattern.getRemoteCluster().equals(remoteCluster)); if (exist == false) { + LOGGER.info("removing auto follower for remote cluster [{}]", remoteCluster); removedRemoteClusters.add(remoteCluster); } else if (autoFollower.remoteClusterConnectionMissing) { - LOGGER.info("Retrying auto follower [{}] after remote cluster connection was missing", remoteCluster); + LOGGER.info("retrying auto follower [{}] after remote cluster connection was missing", remoteCluster); autoFollower.remoteClusterConnectionMissing = false; autoFollower.start(); } From 93c772225ab2c9a01eeb262ed823fe5f6ded41ab Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 10 Jan 2019 09:48:26 +0100 Subject: [PATCH 011/121] [CCR] Resume follow Api should not require a request body (#37217) Closes #37022 --- .../rest-api-spec/test/ccr/follow_and_unfollow.yml | 1 - .../xpack/ccr/rest/RestResumeFollowAction.java | 10 ++++++++-- .../resources/rest-api-spec/api/ccr.resume_follow.json | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/follow_and_unfollow.yml b/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/follow_and_unfollow.yml index d50bc52bc3620..f73f5c6dfb2d3 100644 --- a/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/follow_and_unfollow.yml +++ b/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/follow_and_unfollow.yml @@ -52,7 +52,6 @@ - do: ccr.resume_follow: index: bar - body: {} - is_true: acknowledged - do: diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/rest/RestResumeFollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/rest/RestResumeFollowAction.java index 62b3f6323ab88..ce2eab52e0cab 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/rest/RestResumeFollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/rest/RestResumeFollowAction.java @@ -37,8 +37,14 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient } static Request createRequest(RestRequest restRequest) throws IOException { - try (XContentParser parser = restRequest.contentOrSourceParamParser()) { - return Request.fromXContent(parser, restRequest.param("index")); + if (restRequest.hasContentOrSourceParam()) { + try (XContentParser parser = restRequest.contentOrSourceParamParser()) { + return Request.fromXContent(parser, restRequest.param("index")); + } + } else { + Request request = new Request(); + request.setFollowerIndex(restRequest.param("index")); + return request; } } } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/api/ccr.resume_follow.json b/x-pack/plugin/src/test/resources/rest-api-spec/api/ccr.resume_follow.json index 61bdf82372fc0..61e3b8580fc2c 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/api/ccr.resume_follow.json +++ b/x-pack/plugin/src/test/resources/rest-api-spec/api/ccr.resume_follow.json @@ -15,7 +15,7 @@ }, "body": { "description" : "The name of the leader index and other optional ccr related parameters", - "required" : true + "required" : false } } } From d62a353017c4a1ff30ab0a77174c93ba78d00e0a Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 10 Jan 2019 14:04:55 +0100 Subject: [PATCH 012/121] [HLRC] Ignore unknown fields in responses of CCR APIs (#36821) Otherwise hlrc fails if new fields are introduced in ccr api responses in future versions. --- .../java/org/elasticsearch/client/ccr/AutoFollowStats.java | 6 +++++- .../org/elasticsearch/client/ccr/CcrStatsResponse.java | 4 +++- .../client/ccr/GetAutoFollowPatternResponse.java | 6 +++--- .../org/elasticsearch/client/ccr/IndicesFollowStats.java | 7 ++++++- .../org/elasticsearch/client/ccr/PutFollowResponse.java | 2 +- .../elasticsearch/client/ccr/CcrStatsResponseTests.java | 2 +- .../elasticsearch/client/ccr/FollowStatsResponseTests.java | 2 +- .../client/ccr/GetAutoFollowPatternResponseTests.java | 2 +- .../elasticsearch/client/ccr/PutFollowRequestTests.java | 2 +- .../elasticsearch/client/ccr/PutFollowResponseTests.java | 2 +- 10 files changed, 23 insertions(+), 12 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java index b442336ca4dd2..2c458f47e5be9 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/AutoFollowStats.java @@ -45,7 +45,9 @@ public final class AutoFollowStats { static final ParseField LAST_SEEN_METADATA_VERSION = new ParseField("last_seen_metadata_version"); @SuppressWarnings("unchecked") - static final ConstructingObjectParser STATS_PARSER = new ConstructingObjectParser<>("auto_follow_stats", + static final ConstructingObjectParser STATS_PARSER = new ConstructingObjectParser<>( + "auto_follow_stats", + true, args -> new AutoFollowStats( (Long) args[0], (Long) args[1], @@ -63,11 +65,13 @@ public final class AutoFollowStats { private static final ConstructingObjectParser, Void> AUTO_FOLLOW_EXCEPTIONS_PARSER = new ConstructingObjectParser<>( "auto_follow_stats_errors", + true, args -> new AbstractMap.SimpleEntry<>((String) args[0], (ElasticsearchException) args[1])); private static final ConstructingObjectParser, Void> AUTO_FOLLOWED_CLUSTERS_PARSER = new ConstructingObjectParser<>( "auto_followed_clusters", + true, args -> new AbstractMap.SimpleEntry<>((String) args[0], new AutoFollowedCluster((Long) args[1], (Long) args[2]))); static { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java index 889a96683bfb3..54f79892c1d08 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/CcrStatsResponse.java @@ -28,7 +28,9 @@ public final class CcrStatsResponse { static final ParseField AUTO_FOLLOW_STATS_FIELD = new ParseField("auto_follow_stats"); static final ParseField FOLLOW_STATS_FIELD = new ParseField("follow_stats"); - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices", + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "indices", + true, args -> { AutoFollowStats autoFollowStats = (AutoFollowStats) args[0]; IndicesFollowStats indicesFollowStats = (IndicesFollowStats) args[1]; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java index ce42c98e57c41..d05ab3f3ee363 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java @@ -42,7 +42,7 @@ public final class GetAutoFollowPatternResponse { static final ParseField PATTERN_FIELD = new ParseField("pattern"); private static final ConstructingObjectParser, Void> ENTRY_PARSER = new ConstructingObjectParser<>( - "get_auto_follow_pattern_response", args -> new AbstractMap.SimpleEntry<>((String) args[0], (Pattern) args[1])); + "get_auto_follow_pattern_response", true, args -> new AbstractMap.SimpleEntry<>((String) args[0], (Pattern) args[1])); static { ENTRY_PARSER.declareString(ConstructingObjectParser.constructorArg(), NAME_FIELD); @@ -50,7 +50,7 @@ public final class GetAutoFollowPatternResponse { } private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "get_auto_follow_pattern_response", args -> { + "get_auto_follow_pattern_response", true, args -> { @SuppressWarnings("unchecked") List> entries = (List>) args[0]; return new GetAutoFollowPatternResponse(new TreeMap<>(entries.stream() @@ -92,7 +92,7 @@ public static class Pattern extends FollowConfig { @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "pattern", args -> new Pattern((String) args[0], (List) args[1], (String) args[2])); + "pattern", true, args -> new Pattern((String) args[0], (List) args[1], (String) args[2])); static { PARSER.declareString(ConstructingObjectParser.constructorArg(), PutFollowRequest.REMOTE_CLUSTER_FIELD); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java index 02e2fc4f4ed18..7d3af08577b16 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/IndicesFollowStats.java @@ -41,6 +41,7 @@ public final class IndicesFollowStats { private static final ConstructingObjectParser>, Void> ENTRY_PARSER = new ConstructingObjectParser<>( "entry", + true, args -> { String index = (String) args[0]; @SuppressWarnings("unchecked") @@ -54,7 +55,9 @@ public final class IndicesFollowStats { ENTRY_PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), ShardFollowStats.PARSER, SHARDS_FIELD); } - static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices", + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "indices", + true, args -> { @SuppressWarnings("unchecked") List>> entries = (List>>) args[0]; @@ -116,6 +119,7 @@ public static final class ShardFollowStats { static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "shard-follow-stats", + true, args -> new ShardFollowStats( (String) args[0], (String) args[1], @@ -152,6 +156,7 @@ public static final class ShardFollowStats { static final ConstructingObjectParser>, Void> READ_EXCEPTIONS_ENTRY_PARSER = new ConstructingObjectParser<>( "shard-follow-stats-read-exceptions-entry", + true, args -> new AbstractMap.SimpleEntry<>((long) args[0], Tuple.tuple((Integer) args[1], (ElasticsearchException)args[2]))); static { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutFollowResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutFollowResponse.java index 2d928b859882a..3841b868e73ab 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutFollowResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutFollowResponse.java @@ -33,7 +33,7 @@ public final class PutFollowResponse { static final ParseField INDEX_FOLLOWING_STARTED = new ParseField("index_following_started"); private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "put_follow_response", args -> new PutFollowResponse((boolean) args[0], (boolean) args[1], (boolean) args[2])); + "put_follow_response", true, args -> new PutFollowResponse((boolean) args[0], (boolean) args[1], (boolean) args[2])); static { PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), FOLLOW_INDEX_CREATED); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java index 8d53b5cde0827..f6fe0642fd288 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/CcrStatsResponseTests.java @@ -51,7 +51,7 @@ public void testFromXContent() throws IOException { CcrStatsResponseTests::createTestInstance, CcrStatsResponseTests::toXContent, CcrStatsResponse::fromXContent) - .supportsUnknownFields(false) + .supportsUnknownFields(true) .assertEqualsConsumer(CcrStatsResponseTests::assertEqualInstances) .assertToXContentEquivalence(false) .test(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java index c8d15bc508503..5ec3cb4edcf07 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowStatsResponseTests.java @@ -48,7 +48,7 @@ public void testFromXContent() throws IOException { FollowStatsResponseTests::createTestInstance, FollowStatsResponseTests::toXContent, FollowStatsResponse::fromXContent) - .supportsUnknownFields(false) + .supportsUnknownFields(true) .assertEqualsConsumer(FollowStatsResponseTests::assertEqualInstances) .assertToXContentEquivalence(false) .test(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java index b4a37286b4ace..f6f0f1747e2a2 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java @@ -43,7 +43,7 @@ public void testFromXContent() throws IOException { this::createTestInstance, GetAutoFollowPatternResponseTests::toXContent, GetAutoFollowPatternResponse::fromXContent) - .supportsUnknownFields(false) + .supportsUnknownFields(true) .test(); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowRequestTests.java index 0814278a0cf59..35353ce4a96f9 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowRequestTests.java @@ -31,7 +31,7 @@ public class PutFollowRequestTests extends AbstractXContentTestCase { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("test_parser", - (args) -> new PutFollowRequest((String) args[0], (String) args[1], (String) args[2])); + true, (args) -> new PutFollowRequest((String) args[0], (String) args[1], (String) args[2])); static { PARSER.declareString(ConstructingObjectParser.constructorArg(), PutFollowRequest.REMOTE_CLUSTER_FIELD); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowResponseTests.java index 48eb15717c599..00bcf535f08af 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutFollowResponseTests.java @@ -33,7 +33,7 @@ public void testFromXContent() throws IOException { this::createTestInstance, PutFollowResponseTests::toXContent, PutFollowResponse::fromXContent) - .supportsUnknownFields(false) + .supportsUnknownFields(true) .test(); } From 775f6e27d4ab4fa81ef4502fb869539797ec65e9 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 10 Jan 2019 15:02:30 +0100 Subject: [PATCH 013/121] [CCR] Make shard follow tasks more resilient for restarts (#37239) If a running shard follow task needs to be restarted and the remote connection seeds have changed then a shard follow task currently fails with a fatal error. The change creates the remote client lazily and adjusts the errors a shard follow task should retry. This issue was found in test failures in the recently added ccr rolling upgrade tests. The reason why this issue occurs more frequently in the rolling upgrade test is because ccr is setup in local mode (so remote connection seed will become stale) and all nodes are restarted, which forces the shard follow tasks to get restarted at some point during the test. Note that these tests cannot be enabled yet, because this change will need to be backported to 6.x first. (otherwise the issue still occurs on non upgraded nodes) I also changed the RestartIndexFollowingIT to setup remote cluster via persistent settings and to also restart the leader cluster. This way what happens during the ccr rolling upgrade qa tests, also happens in this test. Relates to #37231 --- .../xpack/ccr/action/ShardFollowNodeTask.java | 12 ++++---- .../ccr/action/ShardFollowTasksExecutor.java | 28 +++++++++++-------- .../xpack/ccr/RestartIndexFollowingIT.java | 28 ++++++++++++++++++- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java index 13b12d4b96f2b..b1d6467168c90 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java @@ -29,8 +29,7 @@ import org.elasticsearch.node.NodeClosedException; import org.elasticsearch.persistent.AllocatedPersistentTask; import org.elasticsearch.tasks.TaskId; -import org.elasticsearch.transport.NodeDisconnectedException; -import org.elasticsearch.transport.NodeNotConnectedException; +import org.elasticsearch.transport.ConnectTransportException; import org.elasticsearch.xpack.ccr.action.bulk.BulkShardOperationsResponse; import org.elasticsearch.xpack.core.ccr.ShardFollowNodeTaskStatus; @@ -448,7 +447,10 @@ static boolean shouldRetry(String remoteCluster, Exception e) { return true; } + // This is thrown when using a Client and its remote cluster alias went MIA String noSuchRemoteClusterMessage = "no such remote cluster: " + remoteCluster; + // This is thrown when creating a Client and the remote cluster does not exist: + String unknownClusterMessage = "unknown cluster alias [" + remoteCluster + "]"; final Throwable actual = ExceptionsHelper.unwrapCause(e); return actual instanceof ShardNotFoundException || actual instanceof IllegalIndexShardStateException || @@ -458,11 +460,11 @@ static boolean shouldRetry(String remoteCluster, Exception e) { actual instanceof ElasticsearchSecurityException || // If user does not have sufficient privileges actual instanceof ClusterBlockException || // If leader index is closed or no elected master actual instanceof IndexClosedException || // If follow index is closed - actual instanceof NodeDisconnectedException || - actual instanceof NodeNotConnectedException || + actual instanceof ConnectTransportException || actual instanceof NodeClosedException || (actual.getMessage() != null && actual.getMessage().contains("TransportService is closed")) || - (actual instanceof IllegalArgumentException && noSuchRemoteClusterMessage.equals(actual.getMessage())); + (actual instanceof IllegalArgumentException && (noSuchRemoteClusterMessage.equals(actual.getMessage()) || + unknownClusterMessage.equals(actual.getMessage()))); } // These methods are protected for testing purposes: diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java index 63fd80e6d48e6..c8a71e0e91a88 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTasksExecutor.java @@ -95,12 +95,6 @@ protected AllocatedPersistentTask createTask(long id, String type, String action PersistentTasksCustomMetaData.PersistentTask taskInProgress, Map headers) { ShardFollowTask params = taskInProgress.getParams(); - final Client remoteClient; - if (params.getRemoteCluster() != null) { - remoteClient = wrapClient(client.getRemoteClusterClient(params.getRemoteCluster()), params.getHeaders()); - } else { - remoteClient = wrapClient(client, params.getHeaders()); - } Client followerClient = wrapClient(client, params.getHeaders()); BiConsumer scheduler = (delay, command) -> { try { @@ -127,8 +121,7 @@ protected void innerUpdateMapping(LongConsumer handler, Consumer erro clusterStateRequest.clear(); clusterStateRequest.metaData(true); clusterStateRequest.indices(leaderIndex.getName()); - - remoteClient.admin().cluster().state(clusterStateRequest, ActionListener.wrap(clusterStateResponse -> { + CheckedConsumer onResponse = clusterStateResponse -> { IndexMetaData indexMetaData = clusterStateResponse.getState().metaData().getIndexSafe(leaderIndex); if (indexMetaData.getMappings().isEmpty()) { assert indexMetaData.getMappingVersion() == 1; @@ -146,7 +139,12 @@ protected void innerUpdateMapping(LongConsumer handler, Consumer erro followerClient.admin().indices().putMapping(putMappingRequest, ActionListener.wrap( putMappingResponse -> handler.accept(indexMetaData.getMappingVersion()), errorHandler)); - }, errorHandler)); + }; + try { + remoteClient(params).admin().cluster().state(clusterStateRequest, ActionListener.wrap(onResponse, errorHandler)); + } catch (Exception e) { + errorHandler.accept(e); + } } @Override @@ -190,7 +188,11 @@ protected void innerUpdateSettings(final LongConsumer finalHandler, final Consum } } }; - remoteClient.admin().cluster().state(clusterStateRequest, ActionListener.wrap(onResponse, errorHandler)); + try { + remoteClient(params).admin().cluster().state(clusterStateRequest, ActionListener.wrap(onResponse, errorHandler)); + } catch (Exception e) { + errorHandler.accept(e); + } } private void closeIndexUpdateSettingsAndOpenIndex(String followIndex, @@ -245,7 +247,7 @@ protected void innerSendShardChangesRequest(long from, int maxOperationCount, Co request.setMaxBatchSize(params.getMaxReadRequestSize()); request.setPollTimeout(params.getReadPollTimeout()); try { - remoteClient.execute(ShardChangesAction.INSTANCE, request, ActionListener.wrap(handler::accept, errorHandler)); + remoteClient(params).execute(ShardChangesAction.INSTANCE, request, ActionListener.wrap(handler::accept, errorHandler)); } catch (Exception e) { errorHandler.accept(e); } @@ -260,6 +262,10 @@ private String getLeaderShardHistoryUUID(ShardFollowTask params) { return recordedLeaderShardHistoryUUIDs[params.getLeaderShardId().id()]; } + private Client remoteClient(ShardFollowTask params) { + return wrapClient(client.getRemoteClusterClient(params.getRemoteCluster()), params.getHeaders()); + } + interface FollowerStatsInfoHandler { void accept(String followerHistoryUUID, long globalCheckpoint, long maxSeqNo); } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/RestartIndexFollowingIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/RestartIndexFollowingIT.java index 49fbe15ddabae..af666b045953e 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/RestartIndexFollowingIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/RestartIndexFollowingIT.java @@ -6,8 +6,11 @@ package org.elasticsearch.xpack.ccr; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.CcrIntegTestCase; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; @@ -27,9 +30,9 @@ protected int numberOfNodesPerCluster() { public void testFollowIndex() throws Exception { final String leaderIndexSettings = getIndexSettings(1, 0, singletonMap(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true")); - singletonMap(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"); assertAcked(leaderClient().admin().indices().prepareCreate("index1").setSource(leaderIndexSettings, XContentType.JSON)); ensureLeaderGreen("index1"); + setupRemoteCluster(); final PutFollowAction.Request followRequest = putFollow("index1", "index2"); followerClient().execute(PutFollowAction.INSTANCE, followRequest).get(); @@ -57,6 +60,29 @@ public void testFollowIndex() throws Exception { assertThat(followerClient().prepareSearch("index2").get().getHits().totalHits, equalTo(firstBatchNumDocs + secondBatchNumDocs)); }); + + getLeaderCluster().fullRestart(); + ensureLeaderGreen("index1"); + // Remote connection needs to be re-configured, because all the nodes in leader cluster have been restarted: + setupRemoteCluster(); + + final long thirdBatchNumDocs = randomIntBetween(2, 64); + for (int i = 0; i < thirdBatchNumDocs; i++) { + leaderClient().prepareIndex("index1", "doc").setSource("{}", XContentType.JSON).get(); + } + + assertBusy(() -> { + assertThat(followerClient().prepareSearch("index2").get().getHits().getTotalHits(), + equalTo(firstBatchNumDocs + secondBatchNumDocs + thirdBatchNumDocs)); + }); + } + + private void setupRemoteCluster() { + ClusterUpdateSettingsRequest updateSettingsRequest = new ClusterUpdateSettingsRequest(); + String masterNode = getLeaderCluster().getMasterName(); + String address = getLeaderCluster().getInstance(TransportService.class, masterNode).boundAddress().publishAddress().toString(); + updateSettingsRequest.persistentSettings(Settings.builder().put("cluster.remote.leader_cluster.seeds", address)); + assertAcked(followerClient().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); } } From d401b60216f56bb2608fcd64880595dc8df5a1e9 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 10 Jan 2019 17:28:00 +0000 Subject: [PATCH 014/121] [ML] Fix ML memory tracker for old jobs (#37311) Jobs created in version 6.1 or earlier can have a null model_memory_limit. If these are parsed from cluster state following a full cluster restart then we replace the null with 4096mb to make the meaning explicit. But if such jobs are streamed from an old node in a mixed version cluster this does not happen. Therefore we need to account for the possibility of a null model_memory_limit in the ML memory tracker. --- .../core/ml/job/config/AnalysisLimits.java | 2 +- .../xpack/ml/process/MlMemoryTracker.java | 18 ++++++++++-------- .../xpack/ml/process/MlMemoryTrackerTests.java | 9 ++++++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimits.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimits.java index 797df5892f82f..1e114ee0f7a46 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimits.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimits.java @@ -36,7 +36,7 @@ public class AnalysisLimits implements ToXContentObject, Writeable { * the old default value should be used. From 6.3 onwards, the value will always be explicit. */ public static final long DEFAULT_MODEL_MEMORY_LIMIT_MB = 1024L; - static final long PRE_6_1_DEFAULT_MODEL_MEMORY_LIMIT_MB = 4096L; + public static final long PRE_6_1_DEFAULT_MODEL_MEMORY_LIMIT_MB = 4096L; public static final long DEFAULT_CATEGORIZATION_EXAMPLES_LIMIT = 4; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/MlMemoryTracker.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/MlMemoryTracker.java index 0a195adc58077..414aa997fb7b7 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/MlMemoryTracker.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/MlMemoryTracker.java @@ -21,6 +21,7 @@ import org.elasticsearch.xpack.core.ml.MlMetadata; import org.elasticsearch.xpack.core.ml.MlTasks; import org.elasticsearch.xpack.core.ml.action.OpenJobAction; +import org.elasticsearch.xpack.core.ml.job.config.AnalysisLimits; import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.job.JobManager; @@ -276,15 +277,16 @@ public void refreshJobMemory(String jobId, ActionListener listener) { private void setJobMemoryToLimit(String jobId, ActionListener listener) { jobManager.getJob(jobId, ActionListener.wrap(job -> { - Long memoryLimitMb = job.getAnalysisLimits().getModelMemoryLimit(); - if (memoryLimitMb != null) { - Long memoryRequirementBytes = ByteSizeUnit.MB.toBytes(memoryLimitMb) + Job.PROCESS_MEMORY_OVERHEAD.getBytes(); - memoryRequirementByJob.put(jobId, memoryRequirementBytes); - listener.onResponse(memoryRequirementBytes); - } else { - memoryRequirementByJob.remove(jobId); - listener.onResponse(null); + Long memoryLimitMb = (job.getAnalysisLimits() != null) ? job.getAnalysisLimits().getModelMemoryLimit() : null; + // Although recent versions of the code enforce a non-null model_memory_limit + // when parsing, the job could have been streamed from an older version node in + // a mixed version cluster + if (memoryLimitMb == null) { + memoryLimitMb = AnalysisLimits.PRE_6_1_DEFAULT_MODEL_MEMORY_LIMIT_MB; } + Long memoryRequirementBytes = ByteSizeUnit.MB.toBytes(memoryLimitMb) + Job.PROCESS_MEMORY_OVERHEAD.getBytes(); + memoryRequirementByJob.put(jobId, memoryRequirementBytes); + listener.onResponse(memoryRequirementBytes); }, e -> { if (e instanceof ResourceNotFoundException) { // TODO: does this also happen if the .ml-config index exists but is unavailable? diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/MlMemoryTrackerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/MlMemoryTrackerTests.java index 197fa469bed7c..3e54994ac043b 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/MlMemoryTrackerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/MlMemoryTrackerTests.java @@ -123,9 +123,10 @@ public void testRefreshOne() { return null; }).when(jobResultsProvider).getEstablishedMemoryUsage(eq(jobId), any(), any(), any(Consumer.class), any()); - long modelMemoryLimitMb = 2; + boolean simulateVeryOldJob = randomBoolean(); + long recentJobModelMemoryLimitMb = 2; Job job = mock(Job.class); - when(job.getAnalysisLimits()).thenReturn(new AnalysisLimits(modelMemoryLimitMb, 4L)); + when(job.getAnalysisLimits()).thenReturn(simulateVeryOldJob ? null : new AnalysisLimits(recentJobModelMemoryLimitMb, 4L)); doAnswer(invocation -> { @SuppressWarnings("unchecked") ActionListener listener = (ActionListener) invocation.getArguments()[1]; @@ -141,7 +142,9 @@ public void testRefreshOne() { assertEquals(Long.valueOf(modelBytes + Job.PROCESS_MEMORY_OVERHEAD.getBytes()), memoryTracker.getJobMemoryRequirement(jobId)); } else { - assertEquals(Long.valueOf(ByteSizeUnit.MB.toBytes(modelMemoryLimitMb) + Job.PROCESS_MEMORY_OVERHEAD.getBytes()), + long expectedModelMemoryLimit = + simulateVeryOldJob ? AnalysisLimits.PRE_6_1_DEFAULT_MODEL_MEMORY_LIMIT_MB : recentJobModelMemoryLimitMb; + assertEquals(Long.valueOf(ByteSizeUnit.MB.toBytes(expectedModelMemoryLimit) + Job.PROCESS_MEMORY_OVERHEAD.getBytes()), memoryTracker.getJobMemoryRequirement(jobId)); } } else { From 92676cafdbbacd66023edc4de61f5f2824e3c26b Mon Sep 17 00:00:00 2001 From: lcawl Date: Thu, 10 Jan 2019 11:10:25 -0800 Subject: [PATCH 015/121] [DOCS] Remove unused screenshots --- docs/reference/ml/images/ml-data-dates.jpg | Bin 17402 -> 0 bytes docs/reference/ml/images/ml-data-keywords.jpg | Bin 17540 -> 0 bytes docs/reference/ml/images/ml-data-metrics.jpg | Bin 358164 -> 0 bytes .../reference/ml/images/ml-data-topmetrics.jpg | Bin 101513 -> 0 bytes docs/reference/ml/images/ml-start-feed.jpg | Bin 1346 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/reference/ml/images/ml-data-dates.jpg delete mode 100644 docs/reference/ml/images/ml-data-keywords.jpg delete mode 100644 docs/reference/ml/images/ml-data-metrics.jpg delete mode 100644 docs/reference/ml/images/ml-data-topmetrics.jpg delete mode 100644 docs/reference/ml/images/ml-start-feed.jpg diff --git a/docs/reference/ml/images/ml-data-dates.jpg b/docs/reference/ml/images/ml-data-dates.jpg deleted file mode 100644 index e00b765402aaeff7638ad4a8414d53d2d253e246..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17402 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 VACjp5X({|OBL3OAzd+d7{{z7R72*H@ diff --git a/docs/reference/ml/images/ml-data-metrics.jpg b/docs/reference/ml/images/ml-data-metrics.jpg deleted file mode 100644 index eeb83a76e1cb276fe9cb1987ccd4c0d710b8ea44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358164 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∿}`()%5$E2 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 z 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 Dn>jqY diff --git a/docs/reference/ml/images/ml-data-topmetrics.jpg b/docs/reference/ml/images/ml-data-topmetrics.jpg deleted file mode 100644 index 80eab8f2037308af69a7b4068dee005881ec13e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101513 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 Date: Thu, 10 Jan 2019 12:34:44 -0700 Subject: [PATCH 016/121] Link ILM management and policy information in ILM API documentation (#37324) Previously these were only linked in a circuitous way rather than being available from the top level API documentation and "Put Lifecycle" API docs. This makes them slightly easier to find for a user. --- docs/reference/ilm/apis/ilm-api.asciidoc | 4 +++- docs/reference/ilm/apis/put-lifecycle.asciidoc | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/reference/ilm/apis/ilm-api.asciidoc b/docs/reference/ilm/apis/ilm-api.asciidoc index dcc3d1962cb64..457ca3daf5d74 100644 --- a/docs/reference/ilm/apis/ilm-api.asciidoc +++ b/docs/reference/ilm/apis/ilm-api.asciidoc @@ -3,7 +3,9 @@ beta[] -You can use the following APIs to manage policies on indices. +You can use the following APIs to manage policies on indices. See +<> for more information +about Index Lifecycle Management. [float] [[ilm-api-policy-endpoint]] diff --git a/docs/reference/ilm/apis/put-lifecycle.asciidoc b/docs/reference/ilm/apis/put-lifecycle.asciidoc index 83facc6f42b01..21d48fd8cbddf 100644 --- a/docs/reference/ilm/apis/put-lifecycle.asciidoc +++ b/docs/reference/ilm/apis/put-lifecycle.asciidoc @@ -8,7 +8,8 @@ beta[] -Creates or updates lifecycle policy. +Creates or updates lifecycle policy. See <> +for definitions of policy components. ==== Request From 00b105c9666c21e3c89464115ec5552de1e907e8 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Thu, 10 Jan 2019 13:27:25 -0700 Subject: [PATCH 017/121] Ensure latch is counted down in ssl reload test (#37313) This change ensures we always countdown the latch in the SSLConfigurationReloaderTests to prevent the suite from timing out in case of an exception. Additionally, we also increase the logging of the resource watcher in case an IOException occurs. See #36053 --- .../core/ssl/SSLConfigurationReloaderTests.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java index 2290a34752819..cb9996ac90db5 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.http.MockResponse; import org.elasticsearch.test.http.MockWebServer; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -55,6 +56,7 @@ /** * Unit tests for the reloading of SSL configuration */ +@TestLogging("org.elasticsearch.watcher:TRACE") public class SSLConfigurationReloaderTests extends ESTestCase { private ThreadPool threadPool; @@ -435,20 +437,20 @@ void reloadSSLContext(SSLConfiguration configuration) { assertThat(sslService.sslContextHolder(config).sslContext(), sameInstance(context)); } - private void validateSSLConfigurationIsReloaded(Settings settings, Environment env, - Consumer preChecks, - Runnable modificationFunction, - Consumer postChecks) - throws Exception { + private void validateSSLConfigurationIsReloaded(Settings settings, Environment env, Consumer preChecks, + Runnable modificationFunction, Consumer postChecks) throws Exception { final CountDownLatch reloadLatch = new CountDownLatch(1); final SSLService sslService = new SSLService(settings, env); final SSLConfiguration config = sslService.getSSLConfiguration("xpack.ssl"); new SSLConfigurationReloader(env, sslService, resourceWatcherService) { @Override void reloadSSLContext(SSLConfiguration configuration) { - super.reloadSSLContext(configuration); - reloadLatch.countDown(); + try { + super.reloadSSLContext(configuration); + } finally { + reloadLatch.countDown(); + } } }; // Baseline checks From 54cfd6fd6b8a241d28febd1c926766a945c9cdf5 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 10 Jan 2019 22:18:53 +0200 Subject: [PATCH 018/121] SQL: Fix bug regarding alias fields with dots (#37279) Field of types aliases that have dots in name are returned without a hierarchy by field_caps, as oppose to the mapping api or field with concrete types, which in turn breaks IndexResolver. This commit fixes this by creating the backing hierarchy similar to the mapping api. Close #37224 (cherry picked from commit 83f7423cd65c661eeca46fc2f9dd874529ccff53) (cherry picked from commit d3dd70f8c566dcdaa5f6ef198569609a8411f431) --- .../xpack/sql/qa/jdbc/CsvSpecTestCase.java | 1 + .../xpack/sql/qa/jdbc/DataLoader.java | 4 + .../sql/qa/src/main/resources/alias.csv-spec | 10 +- .../qa/src/main/resources/command.csv-spec | 8 +- .../src/main/resources/field-alias.csv-spec | 129 ++++++++++++++++++ .../sql/analysis/index/IndexResolver.java | 17 ++- .../sql/plan/logical/command/ShowColumns.java | 1 + .../elasticsearch/xpack/sql/type/EsField.java | 2 +- .../analyzer/VerifierErrorMessagesTests.java | 27 +++- 9 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugin/sql/qa/src/main/resources/field-alias.csv-spec diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvSpecTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvSpecTestCase.java index abf56cee9c766..d8b6375e7ca96 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvSpecTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvSpecTestCase.java @@ -42,6 +42,7 @@ public static List readScriptSpec() throws Exception { tests.addAll(readScriptSpec("/nested.csv-spec", parser)); tests.addAll(readScriptSpec("/functions.csv-spec", parser)); tests.addAll(readScriptSpec("/math.csv-spec", parser)); + tests.addAll(readScriptSpec("/field-alias.csv-spec", parser)); return tests; } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java index 53669f9de0eb9..4985dda404dd7 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java @@ -105,6 +105,10 @@ private static void loadEmpDatasetIntoEs(RestClient client, String index, String if (extraFields) { createIndex.startObject("extra_gender").field("type", "keyword").endObject(); + createIndex.startObject("extra.info.gender") + .field("type", "alias") + .field("path", "gender") + .endObject(); } createIndex.startObject("birth_date").field("type", "date").endObject(); diff --git a/x-pack/plugin/sql/qa/src/main/resources/alias.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/alias.csv-spec index e87aaecf6f332..fe8e6e5da4e63 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/alias.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/alias.csv-spec @@ -36,6 +36,9 @@ dep.dep_name.keyword|VARCHAR |KEYWORD dep.from_date |TIMESTAMP |DATE dep.to_date |TIMESTAMP |DATE emp_no |INTEGER |INTEGER +extra |STRUCT |OBJECT +extra.info |STRUCT |OBJECT +extra.info.gender |VARCHAR |KEYWORD extra_gender |VARCHAR |KEYWORD extra_no |INTEGER |INTEGER first_name |VARCHAR |TEXT @@ -45,7 +48,7 @@ hire_date |TIMESTAMP |DATE languages |TINYINT |BYTE last_name |VARCHAR |TEXT last_name.keyword |VARCHAR |KEYWORD -salary |INTEGER |INTEGER +salary |INTEGER |INTEGER ; describePattern @@ -61,6 +64,9 @@ dep.dep_name.keyword|VARCHAR |KEYWORD dep.from_date |TIMESTAMP |DATE dep.to_date |TIMESTAMP |DATE emp_no |INTEGER |INTEGER +extra |STRUCT |OBJECT +extra.info |STRUCT |OBJECT +extra.info.gender |VARCHAR |KEYWORD extra_gender |VARCHAR |KEYWORD extra_no |INTEGER |INTEGER first_name |VARCHAR |TEXT @@ -70,7 +76,7 @@ hire_date |TIMESTAMP |DATE languages |TINYINT |BYTE last_name |VARCHAR |TEXT last_name.keyword |VARCHAR |KEYWORD -salary |INTEGER |INTEGER +salary |INTEGER |INTEGER ; showAlias diff --git a/x-pack/plugin/sql/qa/src/main/resources/command.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/command.csv-spec index 7c9c98f6d0446..c52a5f807bde1 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/command.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/command.csv-spec @@ -236,6 +236,9 @@ dep.dep_name.keyword|VARCHAR |KEYWORD dep.from_date |TIMESTAMP |DATE dep.to_date |TIMESTAMP |DATE emp_no |INTEGER |INTEGER +extra |STRUCT |OBJECT +extra.info |STRUCT |OBJECT +extra.info.gender |VARCHAR |KEYWORD extra_gender |VARCHAR |KEYWORD extra_no |INTEGER |INTEGER first_name |VARCHAR |TEXT @@ -261,6 +264,9 @@ dep.dep_name.keyword|VARCHAR |KEYWORD dep.from_date |TIMESTAMP |DATE dep.to_date |TIMESTAMP |DATE emp_no |INTEGER |INTEGER +extra |STRUCT |OBJECT +extra.info |STRUCT |OBJECT +extra.info.gender |VARCHAR |KEYWORD extra_gender |VARCHAR |KEYWORD extra_no |INTEGER |INTEGER first_name |VARCHAR |TEXT @@ -270,7 +276,7 @@ hire_date |TIMESTAMP |DATE languages |TINYINT |BYTE last_name |VARCHAR |TEXT last_name.keyword |VARCHAR |KEYWORD -salary |INTEGER |INTEGER +salary |INTEGER |INTEGER ; describeSimpleIdentifier diff --git a/x-pack/plugin/sql/qa/src/main/resources/field-alias.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/field-alias.csv-spec new file mode 100644 index 0000000000000..977c0e8309567 --- /dev/null +++ b/x-pack/plugin/sql/qa/src/main/resources/field-alias.csv-spec @@ -0,0 +1,129 @@ +// +// Tests testing field alias (introduced in ES 6.4) +// + +// filtering + +filterEquals +SELECT extra.info.gender gender FROM "test_emp_copy" WHERE gender = 'M' LIMIT 5; + + gender +--------------- +M +M +M +M +M + +; + +filterNotEquals +SELECT extra.info.gender gender FROM "test_emp_copy" WHERE gender <> 'M' ORDER BY gender LIMIT 5; + + gender +--------------- +F +F +F +F +F +; + +aggWithNullFilter +SELECT COUNT(*) count FROM test_emp_copy WHERE extra.info.gender IS NULL; + + count:l +--------------- +10 +; + +functionOverAlias +SELECT BIT_LENGTH(extra.info.gender) bit FROM test_emp_copy ORDER BY extra.info.gender LIMIT 1; + + bit +--------------- +8 +; + + +singlePercentileWithoutComma +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 97) p1 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d +null |10019.0 +F |10099.51 +M |10095.789999999999 +; + +singlePercentileWithComma +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 97.76) p1 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d +null |10019.0 +F |10099.7608 +M |10096.2232 +; + +multiplePercentilesOneWithCommaOneWithout +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 92.45) p1, PERCENTILE(emp_no, 91) p2 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d | p2:d +null |10018.745 |10018.599999999999 +F |10098.0085 |10096.119999999999 +M |10091.393 |10090.37 +; + +multiplePercentilesWithoutComma +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 91) p1, PERCENTILE(emp_no, 89) p2 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d | p2:d +null |10018.599999999999 |10018.4 +F |10096.119999999999 |10093.74 +M |10090.37 |10086.92 +; + +multiplePercentilesWithComma +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 85.7) p1, PERCENTILE(emp_no, 94.3) p2 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d | p2:d +null |10018.070000000002 |10018.929999999998 +F |10091.343 |10098.619 +M |10084.349 |10093.502 +; + +percentileRank +SELECT extra.info.gender AS gender, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | rank:d +null |100.0 +F |17.424242424242426 +M |15.350877192982457 +; + +multiplePercentileRanks +SELECT extra.info.gender AS gender, PERCENTILE_RANK(emp_no, 10030.0) rank1, PERCENTILE_RANK(emp_no, 10025) rank2 FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | rank1:d | rank2:d +null |100.0 |100.0 +F |21.445221445221442 |17.424242424242426 +M |21.929824561403507 |15.350877192982457 +; + +multiplePercentilesAndPercentileRank +SELECT extra.info.gender AS gender, PERCENTILE(emp_no, 97.76) p1, PERCENTILE(emp_no, 93.3) p2, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | p1:d | p2:d | rank:d +null |10019.0 |10018.83 |100.0 +F |10099.7608 |10098.289 |17.424242424242426 +M |10096.2232 |10092.362 |15.350877192982457 +; + +kurtosisAndSkewnessGroup +SELECT extra.info.gender AS gender, KURTOSIS(salary) k, SKEWNESS(salary) s FROM test_emp_copy GROUP BY extra.info.gender; + +gender:s | k:d | s:d + +null |2.2215791166941923 |-0.03373126000214023 +F |1.7873117044424276 |0.05504995122217512 +M |2.280646181070106 |0.44302407229580243 +; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java index 8d7d6b5bbee43..6f6bee0f6ba85 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.index.IndexNotFoundException; -import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DateEsField; import org.elasticsearch.xpack.sql.type.EsField; @@ -351,12 +350,18 @@ private static EsField createField(String fieldName, Map map = globalCaps.get(parentName); + Function fieldFunction; + + // lack of parent implies the field is an alias if (map == null) { - throw new SqlIllegalArgumentException("Cannot find field {}; this is likely a bug", parentName); + // as such, create the field manually + fieldFunction = s -> createField(s, DataType.OBJECT.name(), new TreeMap<>(), false); + } else { + FieldCapabilities parentCap = map.values().iterator().next(); + fieldFunction = s -> createField(s, parentCap.getType(), new TreeMap<>(), parentCap.isAggregatable()); } - FieldCapabilities parentCap = map.values().iterator().next(); - parent = createField(parentName, globalCaps, hierarchicalMapping, flattedMapping, - s -> createField(s, parentCap.getType(), new TreeMap<>(), parentCap.isAggregatable())); + + parent = createField(parentName, globalCaps, hierarchicalMapping, flattedMapping, fieldFunction); } parentProps = parent.getProperties(); } @@ -368,7 +373,7 @@ private static EsField createField(String fieldName, Map props, boolean isAggregateable) { DataType esType = DataType.fromTypeName(typeName); switch (esType) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java index 24f55b1d8eb54..a4f50f4b8ee1e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java @@ -80,6 +80,7 @@ private void fillInRows(Map mapping, String prefix, List mapping = new LinkedHashMap<>(); + + mapping.put("field", new EsField("field", DataType.OBJECT, + singletonMap("alias", new EsField("alias", DataType.KEYWORD, emptyMap(), true)), false)); + + IndexResolution resolution = IndexResolution.valid(new EsIndex("test", mapping)); + + // check the nested alias is seen + accept(resolution, "SELECT field.alias FROM test"); + // or its hierarhcy + accept(resolution, "SELECT field.* FROM test"); + + // check typos + assertEquals("1:8: Unknown column [field.alas], did you mean [field.alias]?", error(resolution, "SELECT field.alas FROM test")); + + // non-existing parents for aliases are not seen by the user + assertEquals("1:8: Cannot use field [field] type [object] only its subfields", error(resolution, "SELECT field FROM test")); + } + public void testMultipleColumnsWithWildcard1() { assertEquals("1:14: Unknown column [a]\n" + "line 1:17: Unknown column [b]\n" + From 9ca71b305c5862ea8c0b151c9d2c1b70406ee2b9 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Fri, 11 Jan 2019 09:22:40 +0200 Subject: [PATCH 019/121] [DOCS] Fix link to role mapping doc --- .../en/security/authentication/configuring-saml-realm.asciidoc | 2 +- x-pack/docs/en/security/authentication/saml-guide.asciidoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/docs/en/security/authentication/configuring-saml-realm.asciidoc b/x-pack/docs/en/security/authentication/configuring-saml-realm.asciidoc index 45b726578f6f8..87f13327ba9ae 100644 --- a/x-pack/docs/en/security/authentication/configuring-saml-realm.asciidoc +++ b/x-pack/docs/en/security/authentication/configuring-saml-realm.asciidoc @@ -221,7 +221,7 @@ Your SAML users cannot do anything until they are assigned roles. This can be do through either the {stack-ov}/saml-role-mapping.html[role mapping API], or with {stack-ov}/realm-chains.html#authorization_realms[authorization realms]. -NOTE: You cannot use {stack-ov}/defining-roles.html#roles-management-file[role mapping files] +NOTE: You cannot use {stack-ov}/mapping-roles.html#mapping-roles-file[role mapping files] to grant roles to users authenticating via SAML. -- diff --git a/x-pack/docs/en/security/authentication/saml-guide.asciidoc b/x-pack/docs/en/security/authentication/saml-guide.asciidoc index 11d3bedfae1fc..76e2da13f0eed 100644 --- a/x-pack/docs/en/security/authentication/saml-guide.asciidoc +++ b/x-pack/docs/en/security/authentication/saml-guide.asciidoc @@ -629,7 +629,7 @@ through either the {ref}/security-api-put-role-mapping.html[add role mapping API], or with <>. -NOTE: You cannot use {stack-ov}/defining-roles.html#roles-management-file[role mapping files] +NOTE: You cannot use {stack-ov}/mapping-roles.html#mapping-roles-file[role mapping files] to grant roles to users authenticating via SAML. This is an example of a simple role mapping that grants the `kibana_user` role From e048988cf0e72e9dc7f172281ad360b0a5b1d874 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Fri, 11 Jan 2019 09:37:18 +0100 Subject: [PATCH 020/121] Geo: Do not normalize the longitude with value -180 for Lucene shapes (#37299) Lucene based shapes should not normalize the longitude value -180 to 180. --- .../common/geo/builders/PolygonBuilder.java | 6 +- .../common/geo/GeoJsonShapeParserTests.java | 2 +- .../search/geo/GeoShapeIntegrationIT.java | 83 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java index 8f7876d2ba9f2..ac19642949c86 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java @@ -342,6 +342,8 @@ protected static org.apache.lucene.geo.Polygon polygonLucene(Coordinate[][] poly holes = new org.apache.lucene.geo.Polygon[polygon.length - 1]; for (int i = 0; i < holes.length; ++i) { Coordinate[] coords = polygon[i+1]; + //We do not have holes on the dateline as they get eliminated + //when breaking the polygon around it. double[] x = new double[coords.length]; double[] y = new double[coords.length]; for (int c = 0; c < coords.length; ++c) { @@ -357,7 +359,9 @@ protected static org.apache.lucene.geo.Polygon polygonLucene(Coordinate[][] poly double[] x = new double[shell.length]; double[] y = new double[shell.length]; for (int i = 0; i < shell.length; ++i) { - x[i] = normalizeLon(shell[i].x); + //Lucene Tessellator treats different +180 and -180 and we should keep the sign. + //normalizeLon method excludes -180. + x[i] = Math.abs(shell[i].x) > 180 ? normalizeLon(shell[i].x) : shell[i].x; y[i] = normalizeLat(shell[i].y); } diff --git a/server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java b/server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java index 973af6da2cd87..6ac700a1c6801 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java @@ -1107,7 +1107,7 @@ public void testParseGeometryCollection() throws IOException { ), new org.apache.lucene.geo.Polygon( new double[] {12.142857142857142d, -12.142857142857142d, -10d, 10d, 12.142857142857142d}, - new double[] {180d, 180d, -177d, -177d, 180d} + new double[] {-180d, -180d, -177d, -177d, -180d} ) }; assertGeometryEquals(luceneExpected, geometryCollectionGeoJson, false); diff --git a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java index 6181f8fc8b6c6..7dd0937f3993e 100644 --- a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java +++ b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java @@ -22,6 +22,7 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.geo.builders.PointBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -158,6 +159,88 @@ public void testIndexShapeRouting() throws Exception { assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); } + public void testIndexPolygonDateLine() throws Exception { + String mappingVector = "{\n" + + " \"properties\": {\n" + + " \"shape\": {\n" + + " \"type\": \"geo_shape\"\n" + + " }\n" + + " }\n" + + " }"; + + String mappingQuad = "{\n" + + " \"properties\": {\n" + + " \"shape\": {\n" + + " \"type\": \"geo_shape\",\n" + + " \"tree\": \"quadtree\"\n" + + " }\n" + + " }\n" + + " }"; + + + // create index + assertAcked(client().admin().indices().prepareCreate("vector").addMapping("doc", mappingVector, XContentType.JSON).get()); + ensureGreen(); + + assertAcked(client().admin().indices().prepareCreate("quad").addMapping("doc", mappingQuad, XContentType.JSON).get()); + ensureGreen(); + + String source = "{\n" + + " \"shape\" : \"POLYGON((179 0, -179 0, -179 2, 179 2, 179 0))\""+ + "}"; + + indexRandom(true, client().prepareIndex("quad", "doc", "0").setSource(source, XContentType.JSON)); + indexRandom(true, client().prepareIndex("vector", "doc", "0").setSource(source, XContentType.JSON)); + + SearchResponse searchResponse = client().prepareSearch("quad").setQuery( + geoShapeQuery("shape", new PointBuilder(-179.75, 1)) + ).get(); + + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch("quad").setQuery( + geoShapeQuery("shape", new PointBuilder(90, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + + searchResponse = client().prepareSearch("quad").setQuery( + geoShapeQuery("shape", new PointBuilder(-180, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + searchResponse = client().prepareSearch("quad").setQuery( + geoShapeQuery("shape", new PointBuilder(180, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch("vector").setQuery( + geoShapeQuery("shape", new PointBuilder(90, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + + searchResponse = client().prepareSearch("vector").setQuery( + geoShapeQuery("shape", new PointBuilder(-179.75, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch("vector").setQuery( + geoShapeQuery("shape", new PointBuilder(-180, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch("vector").setQuery( + geoShapeQuery("shape", new PointBuilder(180, 1)) + ).get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + } + private String findNodeName(String index) { ClusterState state = client().admin().cluster().prepareState().get().getState(); IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0); From c008065d650afac327a75400d831206a1c0b2387 Mon Sep 17 00:00:00 2001 From: iverase Date: Fri, 11 Jan 2019 10:13:55 +0100 Subject: [PATCH 021/121] fix compile error --- .../search/geo/GeoShapeIntegrationIT.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java index 7dd0937f3993e..4f834e32169b7 100644 --- a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java +++ b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java @@ -197,48 +197,48 @@ public void testIndexPolygonDateLine() throws Exception { ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); searchResponse = client().prepareSearch("quad").setQuery( geoShapeQuery("shape", new PointBuilder(90, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + assertThat(searchResponse.getHits().totalHits, equalTo(0L)); searchResponse = client().prepareSearch("quad").setQuery( geoShapeQuery("shape", new PointBuilder(-180, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); searchResponse = client().prepareSearch("quad").setQuery( geoShapeQuery("shape", new PointBuilder(180, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); searchResponse = client().prepareSearch("vector").setQuery( geoShapeQuery("shape", new PointBuilder(90, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + assertThat(searchResponse.getHits().totalHits, equalTo(0L)); searchResponse = client().prepareSearch("vector").setQuery( geoShapeQuery("shape", new PointBuilder(-179.75, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); searchResponse = client().prepareSearch("vector").setQuery( geoShapeQuery("shape", new PointBuilder(-180, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); searchResponse = client().prepareSearch("vector").setQuery( geoShapeQuery("shape", new PointBuilder(180, 1)) ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertThat(searchResponse.getHits().totalHits, equalTo(1L)); } private String findNodeName(String index) { From dafb93bc7d6334523ab48f403cb8345fd5294731 Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Fri, 11 Jan 2019 11:23:54 +0200 Subject: [PATCH 022/121] Fix artifactId in plugin poms (#37315) --- .../org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy index 440127c456247..4a08b8ed342a1 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy @@ -99,7 +99,7 @@ public class PluginBuildPlugin extends BuildPlugin { generatePOMTask.ext.pomFileName = "${project.archivesBaseName}-client-${project.versions.elasticsearch}.pom" } } else { - project.plugins.withType(MavenPublishPlugin).whenPluginAdded { + if (project.plugins.hasPlugin(MavenPublishPlugin)) { project.publishing.publications.nebula(MavenPublication).artifactId( project.pluginProperties.extension.name ) From 1180687e0dc0dc75582ad0e881b645c3b9829e4a Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 11 Jan 2019 11:25:20 +0100 Subject: [PATCH 023/121] Increase timeouts in UnicastZenPingTests Relates to #37268 --- .../discovery/zen/UnicastZenPingTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java b/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java index 683110bfb8084..e3aab679d36cf 100644 --- a/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java @@ -400,7 +400,7 @@ public BoundTransportAddress boundAddress() { Collections.singletonList("127.0.0.1"), limitPortCounts, transportService, - TimeValue.timeValueSeconds(1)); + TimeValue.timeValueSeconds(30)); assertThat(transportAddresses, hasSize(limitPortCounts)); final Set ports = new HashSet<>(); for (final TransportAddress address : transportAddresses) { @@ -443,7 +443,7 @@ public BoundTransportAddress boundAddress() { Collections.singletonList(NetworkAddress.format(loopbackAddress)), 10, transportService, - TimeValue.timeValueSeconds(1)); + TimeValue.timeValueSeconds(30)); assertThat(transportAddresses, hasSize(7)); final Set ports = new HashSet<>(); for (final TransportAddress address : transportAddresses) { @@ -493,7 +493,7 @@ public TransportAddress[] addressesFromString(String address, int perAddressLimi Arrays.asList(hostname), 1, transportService, - TimeValue.timeValueSeconds(1) + TimeValue.timeValueSeconds(30) ); assertThat(transportAddresses, empty()); @@ -543,7 +543,7 @@ public TransportAddress[] addressesFromString(String address, int perAddressLimi new TransportService(Settings.EMPTY, transport, threadPool, TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet()); closeables.push(transportService); - final TimeValue resolveTimeout = TimeValue.timeValueSeconds(randomIntBetween(1, 3)); + final TimeValue resolveTimeout = TimeValue.timeValueSeconds(randomIntBetween(3, 5)); try { final List transportAddresses = UnicastZenPing.resolveHostsLists( executorService, @@ -718,7 +718,7 @@ public BoundTransportAddress boundAddress() { Arrays.asList("127.0.0.1:9300:9300", "127.0.0.1:9301"), 1, transportService, - TimeValue.timeValueSeconds(1)); + TimeValue.timeValueSeconds(30)); assertThat(transportAddresses, hasSize(1)); // only one of the two is valid and will be used assertThat(transportAddresses.get(0).getAddress(), equalTo("127.0.0.1")); assertThat(transportAddresses.get(0).getPort(), equalTo(9301)); From f141f3a81f43453d338f24b84e42d95736cad6db Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 11 Jan 2019 13:22:35 +0000 Subject: [PATCH 024/121] [ML] Wait for autodetect to be ready in the datafeed (#37349) This is a reinforcement of #37227. It turns out that persistent tasks are not made stale if the node they were running on is restarted and the master node does not notice this. The main scenario where this happens is when minimum master nodes is the same as the number of nodes in the cluster, so the cluster cannot elect a master node when any node is restarted. When an ML node restarts we need the datafeeds for any jobs that were running on that node to not just wait until the jobs are allocated, but to wait for the autodetect process of the job to start up. In the case of reassignment of the job persistent task this was dealt with by the stale status test. But in the case where a node restarts but its persistent tasks are not reassigned we need a deeper test. Fixes #36810 --- .../xpack/ml/MachineLearning.java | 2 +- .../xpack/ml/datafeed/DatafeedManager.java | 29 ++++++++-- .../autodetect/AutodetectProcessManager.java | 27 +++++++-- .../ml/datafeed/DatafeedManagerTests.java | 55 +++++++++++++++++-- 4 files changed, 98 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index e52d566aa5fee..27f5cfb629684 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -426,7 +426,7 @@ public Collection createComponents(Client client, ClusterService cluster DatafeedJobBuilder datafeedJobBuilder = new DatafeedJobBuilder(client, settings, xContentRegistry, auditor, System::currentTimeMillis); DatafeedManager datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder, - System::currentTimeMillis, auditor); + System::currentTimeMillis, auditor, autodetectProcessManager); this.datafeedManager.set(datafeedManager); MlLifeCycleService mlLifeCycleService = new MlLifeCycleService(environment, clusterService, datafeedManager, autodetectProcessManager); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java index 7944657aaddc0..ed5c3bbc3bcc3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManager.java @@ -27,9 +27,11 @@ import org.elasticsearch.xpack.core.ml.action.StartDatafeedAction; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedState; import org.elasticsearch.xpack.core.ml.job.config.JobState; +import org.elasticsearch.xpack.core.ml.job.config.JobTaskState; import org.elasticsearch.xpack.core.ml.job.messages.Messages; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.action.TransportStartDatafeedAction; +import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; import org.elasticsearch.xpack.ml.notifications.Auditor; import java.util.ArrayList; @@ -62,16 +64,18 @@ public class DatafeedManager { private final ConcurrentMap runningDatafeedsOnThisNode = new ConcurrentHashMap<>(); private final DatafeedJobBuilder datafeedJobBuilder; private final TaskRunner taskRunner = new TaskRunner(); + private final AutodetectProcessManager autodetectProcessManager; private volatile boolean isolated; public DatafeedManager(ThreadPool threadPool, Client client, ClusterService clusterService, DatafeedJobBuilder datafeedJobBuilder, - Supplier currentTimeSupplier, Auditor auditor) { + Supplier currentTimeSupplier, Auditor auditor, AutodetectProcessManager autodetectProcessManager) { this.client = Objects.requireNonNull(client); this.clusterService = Objects.requireNonNull(clusterService); this.threadPool = threadPool; this.currentTimeSupplier = Objects.requireNonNull(currentTimeSupplier); this.auditor = Objects.requireNonNull(auditor); this.datafeedJobBuilder = Objects.requireNonNull(datafeedJobBuilder); + this.autodetectProcessManager = autodetectProcessManager; clusterService.addListener(taskRunner); } @@ -256,6 +260,21 @@ private JobState getJobState(PersistentTasksCustomMetaData tasks, TransportStart return MlTasks.getJobStateModifiedForReassignments(getJobId(datafeedTask), tasks); } + private boolean jobHasOpenAutodetectCommunicator(PersistentTasksCustomMetaData tasks, + TransportStartDatafeedAction.DatafeedTask datafeedTask) { + PersistentTasksCustomMetaData.PersistentTask jobTask = MlTasks.getJobTask(getJobId(datafeedTask), tasks); + if (jobTask == null) { + return false; + } + + JobTaskState state = (JobTaskState) jobTask.getState(); + if (state == null || state.isStatusStale(jobTask)) { + return false; + } + + return autodetectProcessManager.hasOpenAutodetectCommunicator(jobTask.getAllocationId()); + } + private TimeValue computeNextDelay(long next) { return new TimeValue(Math.max(1, next - currentTimeSupplier.get())); } @@ -446,7 +465,7 @@ private class TaskRunner implements ClusterStateListener { private void runWhenJobIsOpened(TransportStartDatafeedAction.DatafeedTask datafeedTask) { ClusterState clusterState = clusterService.state(); PersistentTasksCustomMetaData tasks = clusterState.getMetaData().custom(PersistentTasksCustomMetaData.TYPE); - if (getJobState(tasks, datafeedTask) == JobState.OPENED) { + if (getJobState(tasks, datafeedTask) == JobState.OPENED && jobHasOpenAutodetectCommunicator(tasks, datafeedTask)) { runTask(datafeedTask); } else { logger.info("Datafeed [{}] is waiting for job [{}] to be opened", @@ -485,10 +504,10 @@ public void clusterChanged(ClusterChangedEvent event) { continue; } JobState jobState = getJobState(currentTasks, datafeedTask); - if (jobState == JobState.OPENED) { - runTask(datafeedTask); - } else if (jobState == JobState.OPENING) { + if (jobState == JobState.OPENING || jobHasOpenAutodetectCommunicator(currentTasks, datafeedTask) == false) { remainingTasks.add(datafeedTask); + } else if (jobState == JobState.OPENED) { + runTask(datafeedTask); } else { logger.warn("Datafeed [{}] is stopping because job [{}] state is [{}]", datafeedTask.getDatafeedId(), getJobId(datafeedTask), jobState); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java index 88b04568732d6..470efeebdf32a 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java @@ -215,6 +215,13 @@ public void killAllProcessesOnThisNode() { */ public void persistJob(JobTask jobTask, Consumer handler) { AutodetectCommunicator communicator = getOpenAutodetectCommunicator(jobTask); + if (communicator == null) { + String message = String.format(Locale.ROOT, "Cannot persist because job [%s] does not have a corresponding autodetect process", + jobTask.getJobId()); + logger.debug(message); + handler.accept(ExceptionsHelper.conflictStatusException(message)); + return; + } communicator.persistJob((aVoid, e) -> handler.accept(e)); } @@ -242,7 +249,8 @@ public void processData(JobTask jobTask, AnalysisRegistry analysisRegistry, Inpu XContentType xContentType, DataLoadParams params, BiConsumer handler) { AutodetectCommunicator communicator = getOpenAutodetectCommunicator(jobTask); if (communicator == null) { - throw ExceptionsHelper.conflictStatusException("Cannot process data because job [" + jobTask.getJobId() + "] is not open"); + throw ExceptionsHelper.conflictStatusException("Cannot process data because job [" + jobTask.getJobId() + + "] does not have a corresponding autodetect process"); } communicator.writeToJob(input, analysisRegistry, xContentType, params, handler); } @@ -260,7 +268,8 @@ public void flushJob(JobTask jobTask, FlushJobParams params, ActionListener handler) { AutodetectCommunicator communicator = getOpenAutodetectCommunicator(jobTask); if (communicator == null) { - String message = "Cannot process update model debug config because job [" + jobTask.getJobId() + "] is not open"; + String message = "Cannot process update model debug config because job [" + jobTask.getJobId() + + "] does not have a corresponding autodetect process"; logger.debug(message); handler.accept(ExceptionsHelper.conflictStatusException(message)); return; @@ -667,6 +678,14 @@ private AutodetectCommunicator getOpenAutodetectCommunicator(JobTask jobTask) { return null; } + public boolean hasOpenAutodetectCommunicator(long jobAllocationId) { + ProcessContext processContext = processByAllocation.get(jobAllocationId); + if (processContext != null && processContext.getState() == ProcessContext.ProcessStateName.RUNNING) { + return processContext.getAutodetectCommunicator() != null; + } + return false; + } + public Optional jobOpenTime(JobTask jobTask) { AutodetectCommunicator communicator = getAutodetectCommunicator(jobTask); if (communicator == null) { diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java index 4b0d34fec79c8..eaa69de2c23b5 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedManagerTests.java @@ -39,6 +39,7 @@ import org.elasticsearch.xpack.ml.action.TransportStartDatafeedAction.DatafeedTask; import org.elasticsearch.xpack.ml.action.TransportStartDatafeedActionTests; import org.elasticsearch.xpack.ml.job.persistence.MockClientBuilder; +import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; import org.elasticsearch.xpack.ml.notifications.Auditor; import org.junit.Before; import org.mockito.ArgumentCaptor; @@ -48,6 +49,7 @@ import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import static org.elasticsearch.xpack.ml.action.TransportOpenJobActionTests.addJobTask; @@ -74,13 +76,14 @@ public class DatafeedManagerTests extends ESTestCase { private long currentTime = 120000; private Auditor auditor; private ArgumentCaptor capturedClusterStateListener = ArgumentCaptor.forClass(ClusterStateListener.class); + private AtomicBoolean hasOpenAutodetectCommunicator; @Before @SuppressWarnings("unchecked") public void setUpTests() { Job.Builder job = createDatafeedJob().setCreateTime(new Date()); - PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); + PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask(job.getId(), "node_id", JobState.OPENED, tasksBuilder); PersistentTasksCustomMetaData tasks = tasksBuilder.build(); DiscoveryNodes nodes = DiscoveryNodes.builder() @@ -128,7 +131,12 @@ public void setUpTests() { return null; }).when(datafeedJobBuilder).build(any(), any(), any()); - datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder, () -> currentTime, auditor); + hasOpenAutodetectCommunicator = new AtomicBoolean(true); + AutodetectProcessManager autodetectProcessManager = mock(AutodetectProcessManager.class); + doAnswer(invocation -> hasOpenAutodetectCommunicator.get()).when(autodetectProcessManager).hasOpenAutodetectCommunicator(anyLong()); + + datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder, () -> currentTime, auditor, + autodetectProcessManager); verify(clusterService).addListener(capturedClusterStateListener.capture()); } @@ -259,7 +267,7 @@ public void testDatafeedTaskWaitsUntilJobIsOpened() { // Verify datafeed has not started running yet as job is still opening verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); - tasksBuilder = PersistentTasksCustomMetaData.builder(); + tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.OPENING, tasksBuilder); addJobTask("another_job", "node_id", JobState.OPENED, tasksBuilder); ClusterState.Builder anotherJobCs = ClusterState.builder(clusterService.state()) @@ -270,7 +278,7 @@ public void testDatafeedTaskWaitsUntilJobIsOpened() { // Still no run verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); - tasksBuilder = PersistentTasksCustomMetaData.builder(); + tasksBuilder = PersistentTasksCustomMetaData.builder(); addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder); ClusterState.Builder jobOpenedCs = ClusterState.builder(clusterService.state()) .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); @@ -278,7 +286,44 @@ public void testDatafeedTaskWaitsUntilJobIsOpened() { capturedClusterStateListener.getValue().clusterChanged( new ClusterChangedEvent("_source", jobOpenedCs.build(), anotherJobCs.build())); - // Now it should run as the job state chanded to OPENED + // Now it should run as the job state changed to OPENED + verify(threadPool, times(1)).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + } + + public void testDatafeedTaskWaitsUntilAutodetectCommunicatorIsOpen() { + + hasOpenAutodetectCommunicator.set(false); + + PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder(); + addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder); + ClusterState.Builder cs = ClusterState.builder(clusterService.state()) + .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); + when(clusterService.state()).thenReturn(cs.build()); + + Consumer handler = mockConsumer(); + DatafeedTask task = createDatafeedTask("datafeed_id", 0L, 60000L); + datafeedManager.run(task, handler); + + // Verify datafeed has not started running yet as job doesn't have an open autodetect communicator + verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + + tasksBuilder = PersistentTasksCustomMetaData.builder(); + addJobTask("job_id", "node_id", JobState.OPENED, tasksBuilder); + addJobTask("another_job", "node_id", JobState.OPENED, tasksBuilder); + ClusterState.Builder anotherJobCs = ClusterState.builder(clusterService.state()) + .metaData(new MetaData.Builder().putCustom(PersistentTasksCustomMetaData.TYPE, tasksBuilder.build())); + + capturedClusterStateListener.getValue().clusterChanged(new ClusterChangedEvent("_source", anotherJobCs.build(), cs.build())); + + // Still no run + verify(threadPool, never()).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); + + hasOpenAutodetectCommunicator.set(true); + + capturedClusterStateListener.getValue().clusterChanged( + new ClusterChangedEvent("_source", cs.build(), anotherJobCs.build())); + + // Now it should run as the autodetect communicator is open verify(threadPool, times(1)).executor(MachineLearning.DATAFEED_THREAD_POOL_NAME); } From 28069db065239c2f7126b3784fdbf5ecbd2a6894 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Fri, 11 Jan 2019 07:59:15 -0800 Subject: [PATCH 025/121] Document Seq No powered optimistic concurrency control (#37284) Add documentation to describe the new sequence number powered optimistic concurrency control Relates #36148 Relates #10708 --- docs/reference/docs.asciidoc | 2 + docs/reference/docs/bulk.asciidoc | 11 ++ .../docs/concurrency-control.asciidoc | 114 ++++++++++++ docs/reference/docs/delete.asciidoc | 10 + docs/reference/docs/index_.asciidoc | 175 +++++++++--------- 5 files changed, 227 insertions(+), 85 deletions(-) create mode 100644 docs/reference/docs/concurrency-control.asciidoc diff --git a/docs/reference/docs.asciidoc b/docs/reference/docs.asciidoc index a8ab282853c8f..5c4c471b0a131 100644 --- a/docs/reference/docs.asciidoc +++ b/docs/reference/docs.asciidoc @@ -50,3 +50,5 @@ include::docs/termvectors.asciidoc[] include::docs/multi-termvectors.asciidoc[] include::docs/refresh.asciidoc[] + +include::docs/concurrency-control.asciidoc[] diff --git a/docs/reference/docs/bulk.asciidoc b/docs/reference/docs/bulk.asciidoc index 7ee634ccef649..0aae2365d965e 100644 --- a/docs/reference/docs/bulk.asciidoc +++ b/docs/reference/docs/bulk.asciidoc @@ -197,6 +197,17 @@ size for your particular workload. If using the HTTP API, make sure that the client does not send HTTP chunks, as this will slow things down. +[float] +[[bulk-optimistic-concurrency-control]] +=== Optimistic Concurrency Control + +Each `index` and `delete` action within a bulk API call may include the +`if_seq_no` and `if_primary_term` parameters in their respective action +and meta data lines. The `if_seq_no` and `if_primary_term` parameters control +how operations are executed, based on the last modification to existing +documents. See <> for more details. + + [float] [[bulk-versioning]] === Versioning diff --git a/docs/reference/docs/concurrency-control.asciidoc b/docs/reference/docs/concurrency-control.asciidoc new file mode 100644 index 0000000000000..d457b14068e26 --- /dev/null +++ b/docs/reference/docs/concurrency-control.asciidoc @@ -0,0 +1,114 @@ +[[optimistic-concurrency-control]] +== Optimistic concurrency control + +Elasticsearch is distributed. When documents are created, updated, or deleted, +the new version of the document has to be replicated to other nodes in the cluster. +Elasticsearch is also asynchronous and concurrent, meaning that these replication +requests are sent in parallel, and may arrive at their destination out of sequence. +Elasticsearch needs a way of ensuring that an older version of a document never +overwrites a newer version. + + +To ensure an older version of a document doesn't overwrite a newer version, every +operation performed to a document is assigned a sequence number by the primary +shard that coordinates that change. The sequence number is increased with each +operation and thus newer operations are guaranteed to have a higher sequence +number than older operations. Elasticsearch can then use the sequence number of +operations to make sure they never override a newer document version is never +overridden by a change that has a smaller sequence number assigned to it. + +For example, the following indexing command will create a document and assign it +an initial sequence number and primary term: + +[source,js] +-------------------------------------------------- +PUT products/_doc/1567 +{ + "product" : "r2d2", + "details" : "A resourceful astromech droid" +} +-------------------------------------------------- +// CONSOLE + +You can see the assigned sequence number and primary term in the +the `_seq_no` and `_primary_term` fields of the response: + +[source,js] +-------------------------------------------------- +{ + "_shards" : { + "total" : 2, + "failed" : 0, + "successful" : 1 + }, + "_index" : "products", + "_type" : "_doc", + "_id" : "1567", + "_version" : 1, + "_seq_no" : 362, + "_primary_term" : 2, + "result" : "created" +} +-------------------------------------------------- +// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 2/"_primary_term" : $body._primary_term/] + + +Elasticsearch keeps tracks of the sequence number and primary of the last +operation to have changed each of the document it stores. The sequence number +and primary term are returned in the `_seq_no` and `_primary_term` fields in +the response of the <>: + +[source,js] +-------------------------------------------------- +GET products/_doc/1567 +-------------------------------------------------- +// CONSOLE +// TEST[continued] + +returns: + +[source,js] +-------------------------------------------------- +{ + "_index" : "products", + "_type" : "_doc", + "_id" : "1567", + "_version" : 1, + "_seq_no" : 362, + "_primary_term" : 2, + "found": true, + "_source" : { + "product" : "r2d2", + "details" : "A resourceful astromech droid" + } +} +-------------------------------------------------- +// TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 2/"_primary_term" : $body._primary_term/] + + +Note: The <> can return the `_seq_no` and `_primary_term` +for each search hit by requesting the `_seq_no` and `_primary_term` <>. + +The sequence number and the primary term uniquely identify a change. By noting down +the sequence number and primary term returned, you can make sure to only change the +document if no other change was made to it since you retrieved it. This +is done by setting the `if_seq_no` and `if_primary_term` parameters of either the +<> or the <>. + +For example, the following indexing call will make sure to add a tag to the +document without losing any potential change to the description or an addition +of another tag by another API: + +[source,js] +-------------------------------------------------- +PUT products/_doc/1567?if_seq_no=362&if_primary_term=2 +{ + "product" : "r2d2", + "details" : "A resourceful astromech droid", + "tags": ["droid"] +} +-------------------------------------------------- +// CONSOLE +// TEST[continued] +// TEST[catch: conflict] + diff --git a/docs/reference/docs/delete.asciidoc b/docs/reference/docs/delete.asciidoc index 49f31eb2d75fb..3a4559773613c 100644 --- a/docs/reference/docs/delete.asciidoc +++ b/docs/reference/docs/delete.asciidoc @@ -35,6 +35,16 @@ The result of the above delete operation is: // TESTRESPONSE[s/"_primary_term" : 1/"_primary_term" : $body._primary_term/] // TESTRESPONSE[s/"_seq_no" : 5/"_seq_no" : $body._seq_no/] +[float] +[[optimistic-concurrency-control-delete]] +=== Optimistic concurrency control + +Delete operations can be made optional and only be performed if the last +modification to the document was assigned the sequence number and primary +term specified by the `if_seq_no` and `if_primary_term` parameters. If a +mismatch is detected, the operation will result in a `VersionConflictException` +and a status code of 409. See <> for more details. + [float] [[delete-versioning]] === Versioning diff --git a/docs/reference/docs/index_.asciidoc b/docs/reference/docs/index_.asciidoc index b0ddb483d9f72..e52aa7faa2b83 100644 --- a/docs/reference/docs/index_.asciidoc +++ b/docs/reference/docs/index_.asciidoc @@ -78,91 +78,6 @@ Automatic index creation can include a pattern based white/black list, for example, set `action.auto_create_index` to `+aaa*,-bbb*,+ccc*,-*` (+ meaning allowed, and - meaning disallowed). -[float] -[[index-versioning]] -=== Versioning - -Each indexed document is given a version number. The associated -`version` number is returned as part of the response to the index API -request. The index API optionally allows for -http://en.wikipedia.org/wiki/Optimistic_concurrency_control[optimistic -concurrency control] when the `version` parameter is specified. This -will control the version of the document the operation is intended to be -executed against. A good example of a use case for versioning is -performing a transactional read-then-update. Specifying a `version` from -the document initially read ensures no changes have happened in the -meantime (when reading in order to update, it is recommended to set -`preference` to `_primary`). For example: - -[source,js] --------------------------------------------------- -PUT twitter/_doc/1?version=2 -{ - "message" : "elasticsearch now has versioning support, double cool!" -} --------------------------------------------------- -// CONSOLE -// TEST[continued] -// TEST[catch: conflict] - -*NOTE:* versioning is completely real time, and is not affected by the -near real time aspects of search operations. If no version is provided, -then the operation is executed without any version checks. - -By default, internal versioning is used that starts at 1 and increments -with each update, deletes included. Optionally, the version number can be -supplemented with an external value (for example, if maintained in a -database). To enable this functionality, `version_type` should be set to -`external`. The value provided must be a numeric, long value greater or equal to 0, -and less than around 9.2e+18. When using the external version type, instead -of checking for a matching version number, the system checks to see if -the version number passed to the index request is greater than the -version of the currently stored document. If true, the document will be -indexed and the new version number used. If the value provided is less -than or equal to the stored document's version number, a version -conflict will occur and the index operation will fail. - -WARNING: External versioning supports the value 0 as a valid version number. -This allows the version to be in sync with an external versioning system -where version numbers start from zero instead of one. It has the side effect -that documents with version number equal to zero cannot neither be updated -using the <> nor be deleted -using the <> as long as their -version number is equal to zero. - -A nice side effect is that there is no need to maintain strict ordering -of async indexing operations executed as a result of changes to a source -database, as long as version numbers from the source database are used. -Even the simple case of updating the Elasticsearch index using data from -a database is simplified if external versioning is used, as only the -latest version will be used if the index operations are out of order for -whatever reason. - -[float] -==== Version types - -Next to the `internal` & `external` version types explained above, Elasticsearch -also supports other types for specific use cases. Here is an overview of -the different version types and their semantics. - -`internal`:: only index the document if the given version is identical to the version -of the stored document. - -`external` or `external_gt`:: only index the document if the given version is strictly higher -than the version of the stored document *or* if there is no existing document. The given -version will be used as the new version and will be stored with the new document. The supplied -version must be a non-negative long number. - -`external_gte`:: only index the document if the given version is *equal* or higher -than the version of the stored document. If there is no existing document -the operation will succeed as well. The given version will be used as the new version -and will be stored with the new document. The supplied version must be a non-negative long number. - -*NOTE*: The `external_gte` version type is meant for special use cases and -should be used with care. If used incorrectly, it can result in loss of data. -There is another option, `force`, which is deprecated because it can cause -primary and replica shards to diverge. - [float] [[operation-type]] === Operation Type @@ -238,6 +153,16 @@ The result of the above index operation is: -------------------------------------------------- // TESTRESPONSE[s/W0tpsmIBdwcYyG50zbta/$body._id/ s/"successful" : 2/"successful" : 1/] +[float] +[[optimistic-concurrency-control-index]] +=== Optimistic concurrency control + +Index operations can be made optional and only be performed if the last +modification to the document was assigned the sequence number and primary +term specified by the `if_seq_no` and `if_primary_term` parameters. If a +mismatch is detected, the operation will result in a `VersionConflictException` +and a status code of 409. See <> for more details. + [float] [[index-routing]] === Routing @@ -380,3 +305,83 @@ PUT twitter/_doc/1?timeout=5m } -------------------------------------------------- // CONSOLE + +[float] +[[index-versioning]] +=== Versioning + +Each indexed document is given a version number. By default, +internal versioning is used that starts at 1 and increments +with each update, deletes included. Optionally, the version number can be +set to an external value (for example, if maintained in a +database). To enable this functionality, `version_type` should be set to +`external`. The value provided must be a numeric, long value greater or equal to 0, +and less than around 9.2e+18. + +When using the external version type, the system checks to see if +the version number passed to the index request is greater than the +version of the currently stored document. If true, the document will be +indexed and the new version number used. If the value provided is less +than or equal to the stored document's version number, a version +conflict will occur and the index operation will fail. For example: + +[source,js] +-------------------------------------------------- +PUT twitter/_doc/1?version=2&version_type=external +{ + "message" : "elasticsearch now has versioning support, double cool!" +} +-------------------------------------------------- +// CONSOLE +// TEST[continued] + +*NOTE:* versioning is completely real time, and is not affected by the +near real time aspects of search operations. If no version is provided, +then the operation is executed without any version checks. + +The above will succeed since the the supplied version of 2 is higher than +the current document version of 1. If the document was already updated +and it's version was set to 2 or higher, the indexing command will fail +and result in a conflict (409 http status code). + +WARNING: External versioning supports the value 0 as a valid version number. +This allows the version to be in sync with an external versioning system +where version numbers start from zero instead of one. It has the side effect +that documents with version number equal to zero cannot neither be updated +using the <> nor be deleted +using the <> as long as their +version number is equal to zero. + +A nice side effect is that there is no need to maintain strict ordering +of async indexing operations executed as a result of changes to a source +database, as long as version numbers from the source database are used. +Even the simple case of updating the Elasticsearch index using data from +a database is simplified if external versioning is used, as only the +latest version will be used if the index operations are out of order for +whatever reason. + +[float] +==== Version types + +Next to the `external` version type explained above, Elasticsearch +also supports other types for specific use cases. Here is an overview of +the different version types and their semantics. + +`internal`:: only index the document if the given version is identical to the version +of the stored document. + +`external` or `external_gt`:: only index the document if the given version is strictly higher +than the version of the stored document *or* if there is no existing document. The given +version will be used as the new version and will be stored with the new document. The supplied +version must be a non-negative long number. + +`external_gte`:: only index the document if the given version is *equal* or higher +than the version of the stored document. If there is no existing document +the operation will succeed as well. The given version will be used as the new version +and will be stored with the new document. The supplied version must be a non-negative long number. + +*NOTE*: The `external_gte` version type is meant for special use cases and +should be used with care. If used incorrectly, it can result in loss of data. +There is another option, `force`, which is deprecated because it can cause +primary and replica shards to diverge. + From 3e98069514ccbcc69cf709b0611eb3c801b65c8c Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Fri, 11 Jan 2019 08:07:35 -0800 Subject: [PATCH 026/121] followup to #37284 with additional feedback --- docs/reference/docs/concurrency-control.asciidoc | 4 ++-- docs/reference/docs/index_.asciidoc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/reference/docs/concurrency-control.asciidoc b/docs/reference/docs/concurrency-control.asciidoc index d457b14068e26..e695e6b5127c9 100644 --- a/docs/reference/docs/concurrency-control.asciidoc +++ b/docs/reference/docs/concurrency-control.asciidoc @@ -14,8 +14,8 @@ operation performed to a document is assigned a sequence number by the primary shard that coordinates that change. The sequence number is increased with each operation and thus newer operations are guaranteed to have a higher sequence number than older operations. Elasticsearch can then use the sequence number of -operations to make sure they never override a newer document version is never -overridden by a change that has a smaller sequence number assigned to it. +operations to make sure a newer document version is never overridden by +a change that has a smaller sequence number assigned to it. For example, the following indexing command will create a document and assign it an initial sequence number and primary term: diff --git a/docs/reference/docs/index_.asciidoc b/docs/reference/docs/index_.asciidoc index e52aa7faa2b83..678e9c69c6ec1 100644 --- a/docs/reference/docs/index_.asciidoc +++ b/docs/reference/docs/index_.asciidoc @@ -341,13 +341,13 @@ then the operation is executed without any version checks. The above will succeed since the the supplied version of 2 is higher than the current document version of 1. If the document was already updated -and it's version was set to 2 or higher, the indexing command will fail +and its version was set to 2 or higher, the indexing command will fail and result in a conflict (409 http status code). WARNING: External versioning supports the value 0 as a valid version number. This allows the version to be in sync with an external versioning system where version numbers start from zero instead of one. It has the side effect -that documents with version number equal to zero cannot neither be updated +that documents with version number equal to zero can neither be updated using the <> nor be deleted using the <> as long as their version number is equal to zero. @@ -357,7 +357,7 @@ of async indexing operations executed as a result of changes to a source database, as long as version numbers from the source database are used. Even the simple case of updating the Elasticsearch index using data from a database is simplified if external versioning is used, as only the -latest version will be used if the index operations are out of order for +latest version will be used if the index operations arrive out of order for whatever reason. [float] From c5590b2a7ecc9a0e66c921d8eabf99ed65e94c89 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Fri, 11 Jan 2019 17:26:01 +0100 Subject: [PATCH 027/121] Test fix, wait for auto follower to have stopped in the background Relates to #36761 --- .../test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java index 6c85b2cb4891e..7198f429f4465 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java @@ -139,7 +139,13 @@ public void testAutoFollowManyIndices() throws Exception { assertThat(autoFollowStats.getNumberOfSuccessfulFollowIndices(), equalTo((long) expectedVal1)); }); + // Delete auto follow pattern and make sure that in the background the auto follower has stopped + // then the leader index created after that should never be auto followed: deleteAutoFollowPatternSetting(); + assertBusy(() -> { + AutoFollowStats autoFollowStats = getAutoFollowStats(); + assertThat(autoFollowStats.getAutoFollowedClusters().size(), equalTo(0)); + }); createLeaderIndex("logs-does-not-count", leaderIndexSettings); putAutoFollowPatterns("my-pattern", new String[] {"logs-*"}); From 27e178f420d34d801e7285d77a6128a4d5626c9a Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Fri, 11 Jan 2019 09:35:38 -0700 Subject: [PATCH 028/121] Mute testRoundRobinWithFailures (#32190) --- .../authc/ldap/support/SessionFactoryLoadBalancingTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java index f8bfa241736b9..143e12002a6e4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java @@ -75,6 +75,7 @@ public void testRoundRobin() throws Exception { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/32190") public void testRoundRobinWithFailures() throws Exception { assumeTrue("at least one ldap server should be present for this test", ldapServers.length > 1); logger.debug("using [{}] ldap servers, urls {}", ldapServers.length, ldapUrls()); From b71d5038d7c1f24244bd1d33ef412ced3074da63 Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Fri, 11 Jan 2019 23:08:00 +0200 Subject: [PATCH 029/121] SQL: Make `FULL` non-reserved keyword in the grammar (#37377) Since `full` can be common as a field name or part of a field name (e.g.: `full.name` or `name.full`), it's nice if it's not a reserved keyword of the grammar so a user can use it without resorting to quotes. Fixes: #37376 --- x-pack/plugin/sql/src/main/antlr/SqlBase.g4 | 2 +- .../xpack/sql/parser/SqlBaseParser.java | 24 +++++++++++++------ .../xpack/sql/parser/SqlParserTests.java | 22 +++++++++++++++++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 index 3bed074b03a2f..6435d80d04073 100644 --- a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 +++ b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 @@ -342,7 +342,7 @@ nonReserved | CATALOGS | COLUMNS | CURRENT | DAY | DEBUG | EXECUTABLE | EXPLAIN - | FIRST | FORMAT | FUNCTIONS + | FIRST | FORMAT | FULL | FUNCTIONS | GRAPHVIZ | HOUR | INTERVAL diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java index 323aeea30eaf2..8ba886404536b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java @@ -811,6 +811,7 @@ public final StatementContext statement() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -887,6 +888,7 @@ public final StatementContext statement() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -957,6 +959,7 @@ public final StatementContext statement() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -1140,6 +1143,7 @@ public final StatementContext statement() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -2056,7 +2060,7 @@ public final GroupingExpressionsContext groupingExpressions() throws Recognition match(T__0); setState(332); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { { setState(324); expression(); @@ -3870,6 +3874,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti case FALSE: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -4257,7 +4262,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce { setState(581); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { { setState(578); qualifiedName(); @@ -4756,6 +4761,7 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -4864,7 +4870,7 @@ public final FunctionTemplateContext functionTemplate() throws RecognitionExcept match(T__0); setState(664); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ALL) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ALL) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { { setState(654); _la = _input.LA(1); @@ -4967,6 +4973,7 @@ public final FunctionNameContext functionName() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -5809,6 +5816,7 @@ public final IdentifierContext identifier() throws RecognitionException { case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -5899,7 +5907,7 @@ public final TableIdentifierContext tableIdentifier() throws RecognitionExceptio { setState(739); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) { { setState(736); ((TableIdentifierContext)_localctx).catalog = identifier(); @@ -6101,6 +6109,7 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce case EXPLAIN: case FIRST: case FORMAT: + case FULL: case FUNCTIONS: case GRAPHVIZ: case HOUR: @@ -6300,6 +6309,7 @@ public static class NonReservedContext extends ParserRuleContext { public TerminalNode EXPLAIN() { return getToken(SqlBaseParser.EXPLAIN, 0); } public TerminalNode FIRST() { return getToken(SqlBaseParser.FIRST, 0); } public TerminalNode FORMAT() { return getToken(SqlBaseParser.FORMAT, 0); } + public TerminalNode FULL() { return getToken(SqlBaseParser.FULL, 0); } public TerminalNode FUNCTIONS() { return getToken(SqlBaseParser.FUNCTIONS, 0); } public TerminalNode GRAPHVIZ() { return getToken(SqlBaseParser.GRAPHVIZ, 0); } public TerminalNode HOUR() { return getToken(SqlBaseParser.HOUR, 0); } @@ -6353,7 +6363,7 @@ public final NonReservedContext nonReserved() throws RecognitionException { { setState(765); _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)))) != 0)) ) { + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); @@ -6464,8 +6474,8 @@ private boolean valueExpression_sempred(ValueExpressionContext _localctx, int pr "\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhjl\2\22\b\2\7\7\t\t\36\36\66\66AAEE\4"+ "\2((SS\4\2\t\tAA\4\2%%--\3\2\32\33\3\2mn\4\2\7\7vv\4\2\r\r\32\32\4\2#"+ "#\62\62\4\2\7\7\34\34\3\2oq\3\2fl\4\2\"\"TT\7\2\27\30+,8;LM\\]\3\2tu\31"+ - "\2\b\t\22\23\25\25\27\27\31\31\36\36 #$\'(++//\62\62\65\6688::AAEGIL"+ - "OPRSVWYY\\\\\u035f\2n\3\2\2\2\4q\3\2\2\2\6\u00de\3\2\2\2\b\u00e9\3\2\2"+ + "\2\b\t\22\23\25\25\27\27\31\31\36\36 #$&(++//\62\62\65\6688::AAEGILO"+ + "PRSVWYY\\\\\u035f\2n\3\2\2\2\4q\3\2\2\2\6\u00de\3\2\2\2\b\u00e9\3\2\2"+ "\2\n\u00ed\3\2\2\2\f\u0102\3\2\2\2\16\u0109\3\2\2\2\20\u010b\3\2\2\2\22"+ "\u0113\3\2\2\2\24\u012f\3\2\2\2\26\u0139\3\2\2\2\30\u0143\3\2\2\2\32\u0152"+ "\3\2\2\2\34\u0154\3\2\2\2\36\u015a\3\2\2\2 \u015c\3\2\2\2\"\u0163\3\2"+ diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java index 8e0074798a503..199b4e119d81b 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.StringJoiner; import static java.util.Collections.nCopies; import static java.util.stream.Collectors.toList; @@ -67,6 +68,27 @@ public void testSelectRightFunction() { assertEquals("RIGHT", f.functionName()); } + public void testsSelectNonReservedKeywords() { + String[] reserved = new String[] { + "ANALYZE", "ANALYZED", "CATALOGS", "COLUMNS", "CURRENT", "DAY", "DEBUG", "EXECUTABLE", "EXPLAIN", + "FIRST", "FORMAT", "FULL", "FUNCTIONS", "GRAPHVIZ", "HOUR", "INTERVAL", "LAST", "LIMIT", + "MAPPED", "MINUTE", "MONTH", "OPTIMIZED", "PARSED", "PHYSICAL", "PLAN", "QUERY", "RLIKE", + "SCHEMAS", "SECOND", "SHOW", "SYS", "TABLES", "TEXT", "TYPE", "TYPES", "VERIFY", "YEAR"}; + StringJoiner sj = new StringJoiner(","); + for (String s : reserved) { + sj.add(s); + } + + Project project = project(parseStatement("SELECT " + sj.toString() + " FROM foo")); + assertEquals(reserved.length, project.projections().size()); + + for (int i = 0; i < project.projections().size(); i++) { + NamedExpression ne = project.projections().get(i); + assertEquals(UnresolvedAttribute.class, ne.getClass()); + assertEquals(reserved[i], ne.name()); + } + } + public void testOrderByField() { Order.OrderDirection dir = randomFrom(Order.OrderDirection.values()); OrderBy ob = orderBy(parseStatement("SELECT * FROM foo ORDER BY bar" + stringForDirection(dir))); From 251837e1c582d9d7ee041cd498678adb3275fbfd Mon Sep 17 00:00:00 2001 From: Peter Dyson Date: Sun, 13 Jan 2019 16:24:34 +1000 Subject: [PATCH 030/121] [DOCS] copy_to only works one level deep, not recursively (#37249) --- docs/reference/mapping/params/copy-to.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference/mapping/params/copy-to.asciidoc b/docs/reference/mapping/params/copy-to.asciidoc index 3e90e17516c41..e02ddd5b66c58 100644 --- a/docs/reference/mapping/params/copy-to.asciidoc +++ b/docs/reference/mapping/params/copy-to.asciidoc @@ -62,3 +62,7 @@ Some important points: * It is the field _value_ which is copied, not the terms (which result from the analysis process). * The original <> field will not be modified to show the copied values. * The same value can be copied to multiple fields, with `"copy_to": [ "field_1", "field_2" ]` +* You cannot copy recursively via intermediary fields such as a `copy_to` on +`field_1` to `field_2` and `copy_to` on `field_2` to `field_3` expecting +indexing into `field_1` will eventuate in `field_3`, instead use copy_to +directly to multiple fields from the originating field. \ No newline at end of file From ab08f3a2fb9df15bcf3a1b44318dc4a2facbdfdc Mon Sep 17 00:00:00 2001 From: Jiyu-Zhang-Zendesk <36034626+Jiyu-Zhang-Zendesk@users.noreply.github.com> Date: Mon, 14 Jan 2019 11:20:25 +0100 Subject: [PATCH 031/121] Update analysis.asciidoc (#37404) STConvert plugin is made by Medcl to convert between Simplified Chinese and Traditional Chinese. It's widely used by the Search Community for Chinese --- docs/plugins/analysis.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plugins/analysis.asciidoc b/docs/plugins/analysis.asciidoc index 875c87124ef45..0a0bbf090495f 100644 --- a/docs/plugins/analysis.asciidoc +++ b/docs/plugins/analysis.asciidoc @@ -58,6 +58,7 @@ A number of analysis plugins have been contributed by our community: * https://github.com/ofir123/elasticsearch-network-analysis[Network Addresses Analysis Plugin] (by Ofir123) * https://github.com/medcl/elasticsearch-analysis-string2int[String2Integer Analysis Plugin] (by Medcl) * https://github.com/ZarHenry96/elasticsearch-dandelion-plugin[Dandelion Analysis Plugin] (by ZarHenry96) +* https://github.com/medcl/elasticsearch-analysis-stconvert[STConvert Analysis Plugin] (by Medcl) include::analysis-icu.asciidoc[] From 6a128e251e717c71e0ac328b3734590e1e80e3ad Mon Sep 17 00:00:00 2001 From: Georgi Ivanov Date: Mon, 14 Jan 2019 13:03:00 +0100 Subject: [PATCH 032/121] Update the scroll example in the docs (#37394) Update the scroll example ascii and Java docs, so it is more clear when to consume the scroll documents. Before this change the user could loose the first results if one uses copy & paste. --- .../client/documentation/SearchDocumentationIT.java | 4 ++-- docs/java-rest/high-level/search/scroll.asciidoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java index e97620b50d4e6..b6fbf759848ac 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java @@ -709,12 +709,12 @@ public void onFailure(Exception e) { SearchHit[] searchHits = searchResponse.getHits().getHits(); while (searchHits != null && searchHits.length > 0) { // <2> - SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); // <3> + // <3> + SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); // <4> scrollRequest.scroll(scroll); searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT); scrollId = searchResponse.getScrollId(); searchHits = searchResponse.getHits().getHits(); - // <4> } ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); // <5> diff --git a/docs/java-rest/high-level/search/scroll.asciidoc b/docs/java-rest/high-level/search/scroll.asciidoc index 8a6d9830f88d6..8285243103abe 100644 --- a/docs/java-rest/high-level/search/scroll.asciidoc +++ b/docs/java-rest/high-level/search/scroll.asciidoc @@ -128,9 +128,9 @@ include-tagged::{doc-tests}/SearchDocumentationIT.java[search-scroll-example] <1> Initialize the search context by sending the initial `SearchRequest` <2> Retrieve all the search hits by calling the Search Scroll api in a loop until no documents are returned -<3> Create a new `SearchScrollRequest` holding the last returned scroll +<3> Process the returned search results +<4> Create a new `SearchScrollRequest` holding the last returned scroll identifier and the scroll interval -<4> Process the returned search results <5> Clear the scroll context once the scroll is completed [[java-rest-high-clear-scroll]] From 455f2acf6a55b88d56ae41cadf4ce9c7970b158e Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Fri, 11 Jan 2019 10:15:39 +0100 Subject: [PATCH 033/121] Increase assertBusy timeouts for RefreshListenersTests --- .../org/elasticsearch/index/shard/RefreshListenersTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java b/server/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java index cbc08b19e8a07..d07c3df8c12bb 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/RefreshListenersTests.java @@ -71,6 +71,7 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -279,7 +280,7 @@ public void testConcurrentRefresh() throws Exception { if (immediate) { assertNotNull(listener.forcedRefresh.get()); } else { - assertBusy(() -> assertNotNull(listener.forcedRefresh.get())); + assertBusy(() -> assertNotNull(listener.forcedRefresh.get()), 1, TimeUnit.MINUTES); } assertFalse(listener.forcedRefresh.get()); listener.assertNoError(); @@ -314,7 +315,7 @@ public void testLotsOfThreads() throws Exception { DummyRefreshListener listener = new DummyRefreshListener(); listeners.addOrNotify(index.getTranslogLocation(), listener); - assertBusy(() -> assertNotNull("listener never called", listener.forcedRefresh.get())); + assertBusy(() -> assertNotNull("listener never called", listener.forcedRefresh.get()), 1, TimeUnit.MINUTES); if (threadCount < maxListeners) { assertFalse(listener.forcedRefresh.get()); } From d69388f70b4ad880883b461946e6f9df6b9068ec Mon Sep 17 00:00:00 2001 From: David Kyle Date: Mon, 14 Jan 2019 16:39:47 +0000 Subject: [PATCH 034/121] [ML] Use String rep of Version in map for serialisation (#37416) --- .../org/elasticsearch/xpack/ml/MlConfigMigrator.java | 3 ++- .../elasticsearch/xpack/ml/MlConfigMigratorTests.java | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java index 184ee44cf376c..f497a6c7063fe 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java @@ -402,7 +402,8 @@ private void createConfigIndex(ActionListener listener) { public static Job updateJobForMigration(Job job) { Job.Builder builder = new Job.Builder(job); Map custom = job.getCustomSettings() == null ? new HashMap<>() : new HashMap<>(job.getCustomSettings()); - custom.put(MIGRATED_FROM_VERSION, job.getJobVersion()); + String version = job.getJobVersion() != null ? job.getJobVersion().toString() : null; + custom.put(MIGRATED_FROM_VERSION, version); builder.setCustomSettings(custom); // Increase the model memory limit for 6.1 - 6.3 jobs Version jobVersion = job.getJobVersion(); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlConfigMigratorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlConfigMigratorTests.java index d9ea035e58234..cff299b9fa1aa 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlConfigMigratorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlConfigMigratorTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.persistent.PersistentTasksCustomMetaData; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.ml.MlMetadata; @@ -21,9 +22,11 @@ import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.core.ml.job.config.JobTests; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -125,7 +128,7 @@ public void testUpdateJobForMigration() { Job migratedJob = MlConfigMigrator.updateJobForMigration(oldJob.build()); assertEquals(Version.CURRENT, migratedJob.getJobVersion()); assertTrue(migratedJob.getCustomSettings().containsKey(MlConfigMigrator.MIGRATED_FROM_VERSION)); - assertEquals(oldVersion, migratedJob.getCustomSettings().get(MlConfigMigrator.MIGRATED_FROM_VERSION)); + assertEquals(oldVersion.toString(), migratedJob.getCustomSettings().get(MlConfigMigrator.MIGRATED_FROM_VERSION)); } public void testUpdateJobForMigration_GivenV54Job() { @@ -138,6 +141,12 @@ public void testUpdateJobForMigration_GivenV54Job() { assertTrue(migratedJob.getCustomSettings().containsKey(MlConfigMigrator.MIGRATED_FROM_VERSION)); } + public void testSerialisationOfUpdatedJob() throws IOException { + Job migratedJob = MlConfigMigrator.updateJobForMigration(JobTests.buildJobBuilder("pre-migration").build(new Date())); + Job copy = copyWriteable(migratedJob, new NamedWriteableRegistry(Collections.emptyList()), Job::new, Version.CURRENT); + assertEquals(migratedJob, copy); + } + public void testFilterFailedJobConfigWrites() { List jobs = new ArrayList<>(); jobs.add(JobTests.buildJobBuilder("foo").build()); From 46450c6dd7552a78ba41e1708b9563ae0395d7d6 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Mon, 14 Jan 2019 13:55:40 -0500 Subject: [PATCH 035/121] Docs: remove reference to deprecated ShapeBuilders (#37233) ShapeBuilders were deprecated in 6.2, but we are still referring to them in docs. This PR replaces `ShapeBuilders.newMultiPoint` with `new MultiPointBuilder`. Closes #37164 --- .../client/documentation/QueryDSLDocumentationTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java index 7a93c0904791b..f8126ad509da0 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/QueryDSLDocumentationTests.java @@ -23,7 +23,7 @@ import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.builders.CoordinatesBuilder; -import org.elasticsearch.common.geo.builders.ShapeBuilders; +import org.elasticsearch.common.geo.builders.MultiPointBuilder; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; @@ -190,7 +190,7 @@ public void testGeoShape() throws IOException { // tag::geo_shape GeoShapeQueryBuilder qb = geoShapeQuery( "pin.location", // <1> - ShapeBuilders.newMultiPoint( // <2> + new MultiPointBuilder( // <2> new CoordinatesBuilder() .coordinate(0, 0) .coordinate(0, 10) From 3bf1f029cc5b3e430597a58528204509689acaac Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Mon, 14 Jan 2019 11:40:11 -0500 Subject: [PATCH 036/121] Relax assertSameDocIdsOnShards assertion If the checking node no longer holds the shard copy, the assertion assertSameDocIdsOnShards might fail. This is too harsh since the assertion is to ensure the consistency between active copies. --- .../test/InternalTestCluster.java | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java index be5729b9560a3..6dd225d360045 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java @@ -135,7 +135,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -1284,26 +1283,24 @@ public void assertConsistentHistoryBetweenTranslogAndLuceneIndex() throws IOExce } } + private IndexShard getShardOrNull(ClusterState clusterState, ShardRouting shardRouting) { + if (shardRouting == null || shardRouting.assignedToNode() == false) { + return null; + } + final DiscoveryNode assignedNode = clusterState.nodes().get(shardRouting.currentNodeId()); + if (assignedNode == null) { + return null; + } + return getInstance(IndicesService.class, assignedNode.getName()).getShardOrNull(shardRouting.shardId()); + } + public void assertSeqNos() throws Exception { - final BiFunction getInstanceShardInstance = (clusterState, shardRouting) -> { - if (shardRouting.assignedToNode() == false) { - return null; - } - final DiscoveryNode assignedNode = clusterState.nodes().get(shardRouting.currentNodeId()); - if (assignedNode == null) { - return null; - } - return getInstance(IndicesService.class, assignedNode.getName()).getShardOrNull(shardRouting.shardId()); - }; assertBusy(() -> { final ClusterState state = clusterService().state(); for (ObjectObjectCursor indexRoutingTable : state.routingTable().indicesRouting()) { for (IntObjectCursor indexShardRoutingTable : indexRoutingTable.value.shards()) { ShardRouting primaryShardRouting = indexShardRoutingTable.value.primaryShard(); - if (primaryShardRouting == null) { - continue; - } - final IndexShard primaryShard = getInstanceShardInstance.apply(state, primaryShardRouting); + final IndexShard primaryShard = getShardOrNull(state, primaryShardRouting); if (primaryShard == null) { continue; //just ignore - shard movement } @@ -1318,7 +1315,7 @@ public void assertSeqNos() throws Exception { assertThat(primaryShardRouting + " should have set the global checkpoint", primarySeqNoStats.getGlobalCheckpoint(), not(equalTo(SequenceNumbers.UNASSIGNED_SEQ_NO))); for (ShardRouting replicaShardRouting : indexShardRoutingTable.value.replicaShards()) { - final IndexShard replicaShard = getInstanceShardInstance.apply(state, replicaShardRouting); + final IndexShard replicaShard = getShardOrNull(state, replicaShardRouting); if (replicaShard == null) { continue; //just ignore - shard movement } @@ -1347,12 +1344,10 @@ public void assertSameDocIdsOnShards() throws Exception { for (ObjectObjectCursor indexRoutingTable : state.routingTable().indicesRouting()) { for (IntObjectCursor indexShardRoutingTable : indexRoutingTable.value.shards()) { ShardRouting primaryShardRouting = indexShardRoutingTable.value.primaryShard(); - if (primaryShardRouting == null || primaryShardRouting.assignedToNode() == false) { + IndexShard primaryShard = getShardOrNull(state, primaryShardRouting); + if (primaryShard == null) { continue; } - DiscoveryNode primaryNode = state.nodes().get(primaryShardRouting.currentNodeId()); - IndexShard primaryShard = getInstance(IndicesService.class, primaryNode.getName()) - .indexServiceSafe(primaryShardRouting.index()).getShard(primaryShardRouting.id()); final List docsOnPrimary; try { docsOnPrimary = IndexShardTestCase.getDocIdAndSeqNos(primaryShard); @@ -1360,12 +1355,10 @@ public void assertSameDocIdsOnShards() throws Exception { continue; } for (ShardRouting replicaShardRouting : indexShardRoutingTable.value.replicaShards()) { - if (replicaShardRouting.assignedToNode() == false) { + IndexShard replicaShard = getShardOrNull(state, replicaShardRouting); + if (replicaShard == null) { continue; } - DiscoveryNode replicaNode = state.nodes().get(replicaShardRouting.currentNodeId()); - IndexShard replicaShard = getInstance(IndicesService.class, replicaNode.getName()) - .indexServiceSafe(replicaShardRouting.index()).getShard(replicaShardRouting.id()); final List docsOnReplica; try { docsOnReplica = IndexShardTestCase.getDocIdAndSeqNos(replicaShard); From 874b5ecba3e95d654dd0a04b7d19f9e9f050ba87 Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Tue, 15 Jan 2019 09:41:41 +0200 Subject: [PATCH 037/121] SQL: Fix issue with field names containing "." (#37364) Adjust FieldExtractor to handle fields which contain `.` in their name, regardless where they fall in, in the document hierarchy. E.g.: ``` { "a.b": "Elastic Search" } { "a": { "b.c": "Elastic Search" } } { "a.b": { "c": { "d.e" : "Elastic Search" } } } ``` Fixes: #37128 --- .../search/extractor/FieldHitExtractor.java | 52 +++++++-- .../extractor/FieldHitExtractorTests.java | 105 +++++++++++++++++- 2 files changed, 143 insertions(+), 14 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java index 07bf0f15b0bd3..7cce05652dfc0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.execution.search.extractor; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -16,9 +17,12 @@ import org.joda.time.DateTime; import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.StringJoiner; /** * Extractor for ES fields. Works for both 'normal' fields but also nested ones (which require hitName to be set). @@ -141,17 +145,43 @@ private Object unwrapMultiValue(Object values) { @SuppressWarnings("unchecked") Object extractFromSource(Map map) { - Object value = map; - boolean first = true; - // each node is a key inside the map - for (String node : path) { - if (value == null) { - return null; - } else if (first || value instanceof Map) { - first = false; - value = ((Map) value).get(node); - } else { - throw new SqlIllegalArgumentException("Cannot extract value [{}] from source", fieldName); + Object value = null; + + // Used to avoid recursive method calls + // Holds the sub-maps in the document hierarchy that are pending to be inspected. + // along with the current index of the `path`. + Deque>> queue = new ArrayDeque<>(); + queue.add(new Tuple<>(-1, map)); + + while (!queue.isEmpty()) { + Tuple> tuple = queue.removeLast(); + int idx = tuple.v1(); + Map subMap = tuple.v2(); + + // Find all possible entries by examining all combinations under the current level ("idx") of the "path" + // e.g.: If the path == "a.b.c.d" and the idx == 0, we need to check the current subMap against the keys: + // "b", "b.c" and "b.c.d" + StringJoiner sj = new StringJoiner("."); + for (int i = idx + 1; i < path.length; i++) { + sj.add(path[i]); + Object node = subMap.get(sj.toString()); + if (node instanceof Map) { + // Add the sub-map to the queue along with the current path index + queue.add(new Tuple<>(i, (Map) node)); + } else if (node != null) { + if (i < path.length - 1) { + // If we reach a concrete value without exhausting the full path, something is wrong with the mapping + // e.g.: map is {"a" : { "b" : "value }} and we are looking for a path: "a.b.c.d" + throw new SqlIllegalArgumentException("Cannot extract value [{}] from source", fieldName); + } + if (value != null) { + // A value has already been found so this means that there are more than one + // values in the document for the same path but different hierarchy. + // e.g.: {"a" : {"b" : {"c" : "value"}}}, {"a.b" : {"c" : "value"}}, ... + throw new SqlIllegalArgumentException("Multiple values (returned by [{}]) are not supported", fieldName); + } + value = node; + } } } return unwrapMultiValue(value); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java index 5c3478eaea343..4f562e82b5c21 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java @@ -21,8 +21,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.function.Supplier; import static java.util.Arrays.asList; @@ -47,7 +49,7 @@ protected Reader instanceReader() { } @Override - protected FieldHitExtractor mutateInstance(FieldHitExtractor instance) throws IOException { + protected FieldHitExtractor mutateInstance(FieldHitExtractor instance) { return new FieldHitExtractor(instance.fieldName() + "mutated", null, true, instance.hitName()); } @@ -237,7 +239,104 @@ public void testMultiValuedSource() { assertThat(ex.getMessage(), is("Arrays (returned by [a]) are not supported")); } - public Object randomValue() { + public void testFieldWithDots() { + FieldHitExtractor fe = new FieldHitExtractor("a.b", null, false); + Object value = randomValue(); + Map map = singletonMap("a.b", value); + assertEquals(value, fe.extractFromSource(map)); + } + + public void testNestedFieldWithDots() { + FieldHitExtractor fe = new FieldHitExtractor("a.b.c", null, false); + Object value = randomValue(); + Map map = singletonMap("a", singletonMap("b.c", value)); + assertEquals(value, fe.extractFromSource(map)); + } + + public void testNestedFieldWithDotsWithNestedField() { + FieldHitExtractor fe = new FieldHitExtractor("a.b.c.d", null, false); + Object value = randomValue(); + Map map = singletonMap("a", singletonMap("b.c", singletonMap("d", value))); + assertEquals(value, fe.extractFromSource(map)); + } + + public void testNestedFieldWithDotsWithNestedFieldWithDots() { + FieldHitExtractor fe = new FieldHitExtractor("a.b.c.d.e", null, false); + Object value = randomValue(); + Map map = singletonMap("a", singletonMap("b.c", singletonMap("d.e", value))); + assertEquals(value, fe.extractFromSource(map)); + } + + public void testNestedFieldsWithDotsAndRandomHiearachy() { + String[] path = new String[100]; + StringJoiner sj = new StringJoiner("."); + for (int i = 0; i < 100; i++) { + path[i] = randomAlphaOfLength(randomIntBetween(1, 10)); + sj.add(path[i]); + } + FieldHitExtractor fe = new FieldHitExtractor(sj.toString(), null, false); + + List paths = new ArrayList<>(path.length); + int start = 0; + while (start < path.length) { + int end = randomIntBetween(start + 1, path.length); + sj = new StringJoiner("."); + for (int j = start; j < end; j++) { + sj.add(path[j]); + } + paths.add(sj.toString()); + start = end; + } + + Object value = randomValue(); + Map map = singletonMap(paths.get(paths.size() - 1), value); + for (int i = paths.size() - 2; i >= 0; i--) { + map = singletonMap(paths.get(i), map); + } + assertEquals(value, fe.extractFromSource(map)); + } + + public void testExtractSourceIncorrectPathWithFieldWithDots() { + FieldHitExtractor fe = new FieldHitExtractor("a.b.c.d.e", null, false); + Object value = randomNonNullValue(); + Map map = singletonMap("a", singletonMap("b.c", singletonMap("d", value))); + SqlException ex = expectThrows(SqlException.class, () -> fe.extractFromSource(map)); + assertThat(ex.getMessage(), is("Cannot extract value [a.b.c.d.e] from source")); + } + + public void testFieldWithDotsAndCommonPrefix() { + FieldHitExtractor fe1 = new FieldHitExtractor("a.d", null, false); + FieldHitExtractor fe2 = new FieldHitExtractor("a.b.c", null, false); + Object value = randomNonNullValue(); + Map map = new HashMap<>(); + map.put("a", singletonMap("d", value)); + map.put("a.b", singletonMap("c", value)); + assertEquals(value, fe1.extractFromSource(map)); + assertEquals(value, fe2.extractFromSource(map)); + } + + public void testFieldWithDotsAndCommonPrefixes() { + FieldHitExtractor fe1 = new FieldHitExtractor("a1.b.c.d1.e.f.g1", null, false); + FieldHitExtractor fe2 = new FieldHitExtractor("a2.b.c.d2.e.f.g2", null, false); + Object value = randomNonNullValue(); + Map map = new HashMap<>(); + map.put("a1", singletonMap("b.c", singletonMap("d1", singletonMap("e.f", singletonMap("g1", value))))); + map.put("a2", singletonMap("b.c", singletonMap("d2", singletonMap("e.f", singletonMap("g2", value))))); + assertEquals(value, fe1.extractFromSource(map)); + assertEquals(value, fe2.extractFromSource(map)); + } + + public void testFieldWithDotsAndSamePathButDifferentHierarchy() { + FieldHitExtractor fe = new FieldHitExtractor("a.b.c.d.e.f.g", null, false); + Object value = randomNonNullValue(); + Map map = new HashMap<>(); + map.put("a.b", singletonMap("c", singletonMap("d.e", singletonMap("f.g", value)))); + map.put("a", singletonMap("b.c", singletonMap("d.e", singletonMap("f", singletonMap("g", value))))); + SqlException ex = expectThrows(SqlException.class, () -> fe.extractFromSource(map)); + assertThat(ex.getMessage(), is("Multiple values (returned by [a.b.c.d.e.f.g]) are not supported")); + } + + private Object randomValue() { Supplier value = randomFrom(Arrays.asList( () -> randomAlphaOfLength(10), ESTestCase::randomLong, @@ -246,7 +345,7 @@ public Object randomValue() { return value.get(); } - public Object randomNonNullValue() { + private Object randomNonNullValue() { Supplier value = randomFrom(Arrays.asList( () -> randomAlphaOfLength(10), ESTestCase::randomLong, From ca1b9458aff6c4567565c7a1a00c30de3d4bc019 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Tue, 15 Jan 2019 11:43:09 +0100 Subject: [PATCH 038/121] Update the Flush API documentation (#33551) The semantics of the API changed considerably since the documentation was written. The main change is to remove references to memory reduction (this is related to refresh). Instead, flush refers to recovery times. I also removed the references to trimming the translog as the translog may be required for other purposes (operation history for ops based recovery and complement ongoing file based recoveries). Closes #32869 --- docs/reference/indices/flush.asciidoc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/reference/indices/flush.asciidoc b/docs/reference/indices/flush.asciidoc index db1f7c2fe00a9..72657525f9481 100644 --- a/docs/reference/indices/flush.asciidoc +++ b/docs/reference/indices/flush.asciidoc @@ -2,11 +2,12 @@ == Flush The flush API allows to flush one or more indices through an API. The -flush process of an index basically frees memory from the index by -flushing data to the index storage and clearing the internal -<>. By -default, Elasticsearch uses memory heuristics in order to automatically -trigger flush operations as required in order to clear memory. +flush process of an index makes sure that any data that is currently only +persisted in the <> is also permanently +persisted in Lucene. This reduces recovery times as that data doesn't need to be +reindexed from the transaction logs after the Lucene indexed is opened. By +default, Elasticsearch uses heuristics in order to automatically +trigger flushes as required. It is rare for users to need to call the API directly. [source,js] -------------------------------------------------- From 188e8e262c375ed4bde07b408a6cf5048a0927af Mon Sep 17 00:00:00 2001 From: David Kyle Date: Tue, 15 Jan 2019 11:07:24 +0000 Subject: [PATCH 039/121] [ML][CI] Unmute MlMigrationIT.testConfigMigration Resolved by #36810 --- .../test/java/org/elasticsearch/upgrades/MlMigrationIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMigrationIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMigrationIT.java index 46ecb745bce41..add428611431b 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMigrationIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMigrationIT.java @@ -92,8 +92,7 @@ private void createTestIndex() throws IOException { "}}}}"); client().performRequest(createTestIndex); } - - @AwaitsFix(bugUrl="https://github.com/elastic/elasticsearch/issues/36935") + public void testConfigMigration() throws Exception { if (UPGRADED_FROM_VERSION.onOrAfter(Version.V_6_6_0)) { // We are testing migration of ml config defined in the clusterstate From 76ea5a4d4305812b7e3a657ae51ac4e9f2d49d58 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 15 Jan 2019 12:48:21 +0100 Subject: [PATCH 040/121] Fix IndexShardTestCase.recoverReplica(IndexShard, IndexShard, boolean) (#37414) This commit fixes the IndexShardTestCase.recoverReplica(IndexShard, IndexShard, boolean) method where the startReplica parameter was not correctly propagated and the value true always used instead. --- .../java/org/elasticsearch/index/shard/IndexShardTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index cf59dff652b40..61f6651cdec0b 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -526,7 +526,7 @@ protected void recoverReplica(IndexShard replica, IndexShard primary, boolean st recoverReplica(replica, primary, (r, sourceNode) -> new RecoveryTarget(r, sourceNode, recoveryListener, version -> { }), - true, true); + true, startReplica); } /** recovers a replica from the given primary **/ From d5b2c9e61a63ddb4e1cdb9bd7a61cef22a00e480 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Mon, 14 Jan 2019 14:27:26 +0100 Subject: [PATCH 041/121] unmuted test Relates to #37014 --- .../java/org/elasticsearch/xpack/CcrIntegTestCase.java | 4 +++- .../org/elasticsearch/xpack/CcrSingleNodeTestCase.java | 6 ++++++ .../elasticsearch/xpack/ccr/LocalIndexFollowingIT.java | 8 +++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java index 29e5ea1498321..e6a35349dcce0 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java @@ -566,9 +566,11 @@ static void removeCCRRelatedMetadataFromClusterState(ClusterService clusterServi clusterService.submitStateUpdateTask("remove-ccr-related-metadata", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { + AutoFollowMetadata empty = + new AutoFollowMetadata(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap()); ClusterState.Builder newState = ClusterState.builder(currentState); newState.metaData(MetaData.builder(currentState.getMetaData()) - .removeCustom(AutoFollowMetadata.TYPE) + .putCustom(AutoFollowMetadata.TYPE, empty) .removeCustom(PersistentTasksCustomMetaData.TYPE) .build()); return newState.build(); diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrSingleNodeTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrSingleNodeTestCase.java index 2fb1f868dd7fb..e77a672f1fddb 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrSingleNodeTestCase.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrSingleNodeTestCase.java @@ -18,7 +18,9 @@ import org.elasticsearch.xpack.ccr.CcrSettings; import org.elasticsearch.xpack.ccr.LocalStateCcr; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.ccr.AutoFollowStats; import org.elasticsearch.xpack.core.ccr.ShardFollowNodeTaskStatus; +import org.elasticsearch.xpack.core.ccr.action.CcrStatsAction; import org.elasticsearch.xpack.core.ccr.action.FollowStatsAction; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; import org.elasticsearch.xpack.core.ccr.action.ResumeFollowAction; @@ -79,6 +81,10 @@ public void removeLocalRemote() { assertAcked(client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); } + protected AutoFollowStats getAutoFollowStats() { + return client().execute(CcrStatsAction.INSTANCE, new CcrStatsAction.Request()).actionGet().getAutoFollowStats(); + } + protected ResumeFollowAction.Request getResumeFollowRequest(String followerIndex) { ResumeFollowAction.Request request = new ResumeFollowAction.Request(); request.setFollowerIndex(followerIndex); diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/LocalIndexFollowingIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/LocalIndexFollowingIT.java index d7dd59c91fc96..c508ab0ca7e0a 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/LocalIndexFollowingIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/LocalIndexFollowingIT.java @@ -93,7 +93,6 @@ public void testDoNotCreateFollowerIfLeaderDoesNotHaveSoftDeletes() throws Excep assertThat(client().admin().indices().prepareExists("follower-index").get().isExists(), equalTo(false)); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/37014") public void testRemoveRemoteConnection() throws Exception { PutAutoFollowPatternAction.Request request = new PutAutoFollowPatternAction.Request(); request.setName("my_pattern"); @@ -102,6 +101,7 @@ public void testRemoveRemoteConnection() throws Exception { request.setFollowIndexNamePattern("copy-{{leader_index}}"); request.setReadPollTimeout(TimeValue.timeValueMillis(10)); assertTrue(client().execute(PutAutoFollowPatternAction.INSTANCE, request).actionGet().isAcknowledged()); + long previousNumberOfSuccessfulFollowedIndices = getAutoFollowStats().getNumberOfSuccessfulFollowIndices(); Settings leaderIndexSettings = Settings.builder() .put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true) @@ -112,7 +112,8 @@ public void testRemoveRemoteConnection() throws Exception { client().prepareIndex("logs-20200101", "doc").setSource("{}", XContentType.JSON).get(); assertBusy(() -> { CcrStatsAction.Response response = client().execute(CcrStatsAction.INSTANCE, new CcrStatsAction.Request()).actionGet(); - assertThat(response.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), equalTo(1L)); + assertThat(response.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), + equalTo(previousNumberOfSuccessfulFollowedIndices + 1)); assertThat(response.getFollowStats().getStatsResponses().size(), equalTo(1)); assertThat(response.getFollowStats().getStatsResponses().get(0).status().followerGlobalCheckpoint(), equalTo(0L)); }); @@ -128,7 +129,8 @@ public void testRemoveRemoteConnection() throws Exception { client().prepareIndex("logs-20200101", "doc").setSource("{}", XContentType.JSON).get(); assertBusy(() -> { CcrStatsAction.Response response = client().execute(CcrStatsAction.INSTANCE, new CcrStatsAction.Request()).actionGet(); - assertThat(response.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), equalTo(2L)); + assertThat(response.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), + equalTo(previousNumberOfSuccessfulFollowedIndices + 2)); FollowStatsAction.StatsRequest statsRequest = new FollowStatsAction.StatsRequest(); statsRequest.setIndices(new String[]{"copy-logs-20200101"}); From a8606de718e83cb2c55979f4751912bd5498e04c Mon Sep 17 00:00:00 2001 From: hydrogen666 Date: Tue, 15 Jan 2019 20:42:01 +0800 Subject: [PATCH 042/121] Use executor `SAME` to handle search related handlers (#37427) The executor was missed in the backport of #33732- Due to the internal forking to search or search_throttled threadpool there is no reason to fork to the search thread pool twice. Closes #37392 Relates to #33732 --- .../action/search/SearchTransportService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 0eee0ad7ea624..41ed09071ac57 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -358,7 +358,7 @@ public void messageReceived(ShardSearchTransportRequest request, TransportChanne TransportActionProxy.registerProxyActionWithDynamicResponseType(transportService, QUERY_ACTION_NAME, (request) -> ((ShardSearchRequest)request).numberOfShards() == 1 ? QueryFetchSearchResult::new : QuerySearchResult::new); - transportService.registerRequestHandler(QUERY_ID_ACTION_NAME, QuerySearchRequest::new, ThreadPool.Names.SEARCH, + transportService.registerRequestHandler(QUERY_ID_ACTION_NAME, QuerySearchRequest::new, ThreadPool.Names.SAME, new TaskAwareTransportRequestHandler() { @Override public void messageReceived(QuerySearchRequest request, TransportChannel channel, Task task) { @@ -368,7 +368,7 @@ public void messageReceived(QuerySearchRequest request, TransportChannel channel }); TransportActionProxy.registerProxyAction(transportService, QUERY_ID_ACTION_NAME, QuerySearchResult::new); - transportService.registerRequestHandler(QUERY_SCROLL_ACTION_NAME, InternalScrollSearchRequest::new, ThreadPool.Names.SEARCH, + transportService.registerRequestHandler(QUERY_SCROLL_ACTION_NAME, InternalScrollSearchRequest::new, ThreadPool.Names.SAME, new TaskAwareTransportRequestHandler() { @Override public void messageReceived(InternalScrollSearchRequest request, TransportChannel channel, Task task) { @@ -378,7 +378,7 @@ public void messageReceived(InternalScrollSearchRequest request, TransportChanne }); TransportActionProxy.registerProxyAction(transportService, QUERY_SCROLL_ACTION_NAME, ScrollQuerySearchResult::new); - transportService.registerRequestHandler(QUERY_FETCH_SCROLL_ACTION_NAME, InternalScrollSearchRequest::new, ThreadPool.Names.SEARCH, + transportService.registerRequestHandler(QUERY_FETCH_SCROLL_ACTION_NAME, InternalScrollSearchRequest::new, ThreadPool.Names.SAME, new TaskAwareTransportRequestHandler() { @Override public void messageReceived(InternalScrollSearchRequest request, TransportChannel channel, Task task) { @@ -388,7 +388,7 @@ public void messageReceived(InternalScrollSearchRequest request, TransportChanne }); TransportActionProxy.registerProxyAction(transportService, QUERY_FETCH_SCROLL_ACTION_NAME, ScrollQueryFetchSearchResult::new); - transportService.registerRequestHandler(FETCH_ID_SCROLL_ACTION_NAME, ShardFetchRequest::new, ThreadPool.Names.SEARCH, + transportService.registerRequestHandler(FETCH_ID_SCROLL_ACTION_NAME, ShardFetchRequest::new, ThreadPool.Names.SAME, new TaskAwareTransportRequestHandler() { @Override public void messageReceived(ShardFetchRequest request, TransportChannel channel, Task task){ @@ -398,7 +398,7 @@ public void messageReceived(ShardFetchRequest request, TransportChannel channel, }); TransportActionProxy.registerProxyAction(transportService, FETCH_ID_SCROLL_ACTION_NAME, FetchSearchResult::new); - transportService.registerRequestHandler(FETCH_ID_ACTION_NAME, ShardFetchSearchRequest::new, ThreadPool.Names.SEARCH, true, true, + transportService.registerRequestHandler(FETCH_ID_ACTION_NAME, ShardFetchSearchRequest::new, ThreadPool.Names.SAME, true, true, new TaskAwareTransportRequestHandler() { @Override public void messageReceived(ShardFetchSearchRequest request, TransportChannel channel, Task task) { From 6add7cfa858fa79c86db0f849849e9d90774f87b Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 15 Jan 2019 15:03:09 +0100 Subject: [PATCH 043/121] Fix SourceOnlySnapshotIT (#37461) The SourceOnlySnapshotIT class tests a source only repository using the following scenario: starts a master node starts a data node creates a source only repository creates an index with documents snapshots the index to the source only repository deletes the index stops the data node starts a new data node restores the index Thanks to ESIntegTestCase the index is sometimes created using a custom data path. With such a setting, when a shard is assigned to one of the data node of the cluster the shard path is resolved using the index custom data path and the node's lock id by the NodeEnvironment#resolveCustomLocation(). It should work nicely but in SourceOnlySnapshotIT.snashotAndRestore(), b efore the change in this PR, the last data node was restarted using a different path.home. At startup time this node was assigned a node lock based on other locks in the data directory of this temporary path.home which is empty. So it always got the 0 lock id. And when this new data node is assigned a shard for the index and resolves it against the index custom data path, it also uses the node lock id 0 which conflicts with another node of the cluster, resulting in various errors with the most obvious one being LockObtainFailedException. This commit removes the temporary home path for the last data node so that it uses the same path home as other nodes of the cluster and then got assigned a correct node lock id at startup. Closes #36330 Closes #36276 --- .../snapshots/SourceOnlySnapshotIT.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/SourceOnlySnapshotIT.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/SourceOnlySnapshotIT.java index 737e2e26970b7..e568bbdc82d5f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/SourceOnlySnapshotIT.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/SourceOnlySnapshotIT.java @@ -28,7 +28,6 @@ import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.mapper.SeqNoFieldMapper; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.node.Node; import org.elasticsearch.plugins.EnginePlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.RepositoryPlugin; @@ -55,6 +54,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +@ESIntegTestCase.ClusterScope(numDataNodes = 0) public class SourceOnlySnapshotIT extends ESIntegTestCase { @Override @@ -208,12 +208,13 @@ private void assertHits(String index, int numDocsExpected, boolean sourceHadDele client().prepareClearScroll().addScrollId(searchResponse.getScrollId()).get(); } } - } - private IndexRequestBuilder[] snashotAndRestore(String sourceIdx, int numShards, boolean minimal, boolean requireRouting, boolean - useNested) - throws ExecutionException, InterruptedException, IOException { + private IndexRequestBuilder[] snashotAndRestore(final String sourceIdx, + final int numShards, + final boolean minimal, + final boolean requireRouting, + final boolean useNested) throws InterruptedException, IOException, ExecutionException { logger.info("--> starting a master node and a data node"); internalCluster().startMasterOnlyNode(); internalCluster().startDataOnlyNode(); @@ -277,12 +278,8 @@ private IndexRequestBuilder[] snashotAndRestore(String sourceIdx, int numShards, internalCluster().stopRandomDataNode(); client().admin().cluster().prepareHealth().setTimeout("30s").setWaitForNodes("1"); - logger.info("--> start a new data node"); - final Settings dataSettings = Settings.builder() - .put(Node.NODE_NAME_SETTING.getKey(), randomAlphaOfLength(5)) - .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) // to get a new node id - .build(); - internalCluster().startDataOnlyNode(dataSettings); + final String newDataNode = internalCluster().startDataOnlyNode(); + logger.info("--> start a new data node " + newDataNode); client().admin().cluster().prepareHealth().setTimeout("30s").setWaitForNodes("2"); logger.info("--> restore the index and ensure all shards are allocated"); From 76bf41a8c2f37339e7cdf08a440383a0cfaa6010 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Tue, 15 Jan 2019 07:52:58 -0700 Subject: [PATCH 044/121] Remove SslNullCipherTests from codebase (#37431) This change deletes the SslNullCipherTests from our codebase since it will have issues with newer JDK versions and it is essentially testing JDK functionality rather than our own. The upstream JDK issue for disabling these ciphers by default is https://bugs.openjdk.java.net/browse/JDK-8212823. Closes #37403 --- .../transport/ssl/SslNullCipherTests.java | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslNullCipherTests.java diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslNullCipherTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslNullCipherTests.java deleted file mode 100644 index 7427c5a67e92d..0000000000000 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslNullCipherTests.java +++ /dev/null @@ -1,55 +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.xpack.security.transport.ssl; - -import org.elasticsearch.action.DocWriteResponse.Result; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.client.Client; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.test.SecurityIntegTestCase; -import org.junit.BeforeClass; - -/** - * An extremely simple test that shows SSL will work with a cipher that does not perform encryption - */ -public class SslNullCipherTests extends SecurityIntegTestCase { - - @BeforeClass - public static void muteInFips() { - assumeFalse("Can't run in a FIPS JVM", inFipsJvm()); - } - - @Override - public boolean transportSSLEnabled() { - return true; - } - - @Override - public Settings nodeSettings(int nodeOrdinal) { - Settings settings = super.nodeSettings(nodeOrdinal); - Settings.Builder builder = Settings.builder() - .put(settings); - builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256"); - return builder.build(); - } - - @Override - public Settings transportClientSettings() { - Settings settings = super.transportClientSettings(); - Settings.Builder builder = Settings.builder() - .put(settings); - - builder.put("xpack.security.transport.ssl.cipher_suites", "TLS_RSA_WITH_NULL_SHA256"); - return builder.build(); - } - - public void testClusterIsFormed() { - ensureGreen(); - Client client = internalCluster().transportClient(); - IndexResponse response = client.prepareIndex("index", "type").setSource("foo", "bar").get(); - assertEquals(Result.CREATED, response.getResult()); - } -} From 7170ebe6299382ad984cf347948e10ac94e4c382 Mon Sep 17 00:00:00 2001 From: niloct Date: Tue, 15 Jan 2019 13:04:32 -0200 Subject: [PATCH 045/121] Update delete-by-query.asciidoc (#37370) Tried my best to clarify sentence on `_delete_by_query` docs. --- docs/reference/docs/delete-by-query.asciidoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/reference/docs/delete-by-query.asciidoc b/docs/reference/docs/delete-by-query.asciidoc index 0a71222a3b3f0..0953ec7d2d904 100644 --- a/docs/reference/docs/delete-by-query.asciidoc +++ b/docs/reference/docs/delete-by-query.asciidoc @@ -383,8 +383,9 @@ POST _tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel The task ID can be found using the <>. -Cancellation should happen quickly but might take a few seconds. The task status -API above will continue to list the task until it is wakes to cancel itself. +Cancellation should happen quickly but might take a few seconds. The task status +API above will continue to list the delete by query task until this task checks that it +has been cancelled and terminates itself. [float] From 850fbec88657c1c74b58d667b31741ee5bdd22ea Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Tue, 15 Jan 2019 16:24:19 +0100 Subject: [PATCH 046/121] When removing an AutoFollower also mark it as removed. (#37402) Currently when there are no more auto follow patterns for a remote cluster then the AutoFollower instance for this remote cluster will be removed. If a new auto follow pattern for this remote cluster gets added quickly enough after the last delete then there may be two AutoFollower instance running for this remote cluster instead of one. Each AutoFollower instance stops automatically after it sees in the start() method that there are no more auto follow patterns for the remote cluster it is tracking. However when an auto follow pattern gets removed and then added back quickly enough then old AutoFollower may never detect that at some point there were no auto follow patterns for the remote cluster it is monitoring. The creation and removal of an AutoFollower instance happens independently in the `updateAutoFollowers()` as part of a cluster state update. By adding the `removed` field, an AutoFollower instance will not miss the fact there were no auto follow patterns at some point in time. The `updateAutoFollowers()` method now marks an AutoFollower instance as removed when it sees that there are no more patterns for a remote cluster. The updateAutoFollowers() method can then safely start a new AutoFollower instance. Relates to #36761 --- .../ccr/action/AutoFollowCoordinator.java | 33 +++++++++++++++++++ .../elasticsearch/xpack/ccr/AutoFollowIT.java | 7 +++- .../action/AutoFollowCoordinatorTests.java | 13 ++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java index 03918d80d9ec7..5333c51e52df3 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinator.java @@ -253,6 +253,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS .anyMatch(pattern -> pattern.getRemoteCluster().equals(remoteCluster)); if (exist == false) { LOGGER.info("removing auto follower for remote cluster [{}]", remoteCluster); + autoFollower.removed = true; removedRemoteClusters.add(remoteCluster); } else if (autoFollower.remoteClusterConnectionMissing) { LOGGER.info("retrying auto follower [{}] after remote cluster connection was missing", remoteCluster); @@ -260,11 +261,25 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS autoFollower.start(); } } + assert assertNoOtherActiveAutoFollower(newAutoFollowers); this.autoFollowers = autoFollowers .copyAndPutAll(newAutoFollowers) .copyAndRemoveAll(removedRemoteClusters); } + private boolean assertNoOtherActiveAutoFollower(Map newAutoFollowers) { + for (AutoFollower newAutoFollower : newAutoFollowers.values()) { + AutoFollower previousInstance = autoFollowers.get(newAutoFollower.remoteCluster); + assert previousInstance == null || previousInstance.removed; + } + return true; + } + + + Map getAutoFollowers() { + return autoFollowers; + } + @Override public void clusterChanged(ClusterChangedEvent event) { if (event.localNodeMaster()) { @@ -290,6 +305,7 @@ abstract static class AutoFollower { private volatile long lastAutoFollowTimeInMillis = -1; private volatile long metadataVersion = 0; private volatile boolean remoteClusterConnectionMissing = false; + volatile boolean removed = false; private volatile CountDown autoFollowPatternsCountDown; private volatile AtomicArray autoFollowResults; @@ -304,6 +320,17 @@ abstract static class AutoFollower { } void start() { + if (removed) { + // This check exists to avoid two AutoFollower instances a single remote cluster. + // (If an auto follow pattern is deleted and then added back quickly enough then + // the old AutoFollower instance still sees that there is an auto follow pattern + // for the remote cluster it is tracking and will continue to operate, while in + // the meantime in updateAutoFollowers() method another AutoFollower instance has been + // started for the same remote cluster.) + LOGGER.info("AutoFollower instance for cluster [{}] has been removed", remoteCluster); + return; + } + lastAutoFollowTimeInMillis = relativeTimeProvider.getAsLong(); final ClusterState clusterState = followerClusterStateSupplier.get(); final AutoFollowMetadata autoFollowMetadata = clusterState.metaData().custom(AutoFollowMetadata.TYPE); @@ -325,6 +352,12 @@ void start() { this.autoFollowResults = new AtomicArray<>(patterns.size()); getRemoteClusterState(remoteCluster, metadataVersion + 1, (remoteClusterStateResponse, remoteError) -> { + // Also check removed flag here, as it may take a while for this remote cluster state api call to return: + if (removed) { + LOGGER.info("AutoFollower instance for cluster [{}] has been removed", remoteCluster); + return; + } + if (remoteClusterStateResponse != null) { assert remoteError == null; if (remoteClusterStateResponse.isWaitForTimedOut()) { diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java index 7198f429f4465..07a77e331a682 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java @@ -157,15 +157,20 @@ public void testAutoFollowManyIndices() throws Exception { int expectedVal2 = numIndices; MetaData[] metaData = new MetaData[1]; + AutoFollowStats[] autoFollowStats = new AutoFollowStats[1]; try { assertBusy(() -> { metaData[0] = followerClient().admin().cluster().prepareState().get().getState().metaData(); + autoFollowStats[0] = getAutoFollowStats(); int count = (int) Arrays.stream(metaData[0].getConcreteAllIndices()).filter(s -> s.startsWith("copy-")).count(); assertThat(count, equalTo(expectedVal2)); + // Ensure that there are no auto follow errors: + // (added specifically to see that there are no leader indices auto followed multiple times) + assertThat(autoFollowStats[0].getRecentAutoFollowErrors().size(), equalTo(0)); }); } catch (AssertionError ae) { logger.warn("metadata={}", Strings.toString(metaData[0])); - logger.warn("auto follow stats={}", Strings.toString(getAutoFollowStats())); + logger.warn("auto follow stats={}", Strings.toString(autoFollowStats[0])); throw ae; } } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java index 5fbaf8c76ba11..6f6b7e166cdbe 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java @@ -620,6 +620,10 @@ public void testUpdateAutoFollowers() { assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().size(), equalTo(2)); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().get("remote1"), notNullValue()); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().get("remote2"), notNullValue()); + // Get a reference to auto follower that will get removed, so that we can assert that it has been marked as removed, + // when pattern 1 and 3 are moved. (To avoid a edge case where multiple auto follow coordinators for the same remote cluster) + AutoFollowCoordinator.AutoFollower removedAutoFollower1 = autoFollowCoordinator.getAutoFollowers().get("remote1"); + assertThat(removedAutoFollower1.removed, is(false)); // Remove patterns 1 and 3: patterns.remove("pattern1"); patterns.remove("pattern3"); @@ -630,6 +634,7 @@ public void testUpdateAutoFollowers() { autoFollowCoordinator.updateAutoFollowers(clusterState); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().size(), equalTo(1)); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().get("remote2"), notNullValue()); + assertThat(removedAutoFollower1.removed, is(true)); // Add pattern 4: patterns.put("pattern4", new AutoFollowPattern("remote1", Collections.singletonList("metrics-*"), null, null, null, null, null, null, null, null, null, null, null)); @@ -641,7 +646,13 @@ public void testUpdateAutoFollowers() { assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().size(), equalTo(2)); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().get("remote1"), notNullValue()); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().get("remote2"), notNullValue()); + // Get references to auto followers that will get removed, so that we can assert that those have been marked as removed, + // when pattern 2 and 4 are moved. (To avoid a edge case where multiple auto follow coordinators for the same remote cluster) + removedAutoFollower1 = autoFollowCoordinator.getAutoFollowers().get("remote1"); + AutoFollower removedAutoFollower2 = autoFollowCoordinator.getAutoFollowers().get("remote2"); // Remove patterns 2 and 4: + assertThat(removedAutoFollower1.removed, is(false)); + assertThat(removedAutoFollower2.removed, is(false)); patterns.remove("pattern2"); patterns.remove("pattern4"); clusterState = ClusterState.builder(new ClusterName("remote")) @@ -650,6 +661,8 @@ public void testUpdateAutoFollowers() { .build(); autoFollowCoordinator.updateAutoFollowers(clusterState); assertThat(autoFollowCoordinator.getStats().getAutoFollowedClusters().size(), equalTo(0)); + assertThat(removedAutoFollower1.removed, is(true)); + assertThat(removedAutoFollower2.removed, is(true)); } public void testUpdateAutoFollowersNoPatterns() { From 932b3df2983e8258c2db539d3728813321e4eb58 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 15 Jan 2019 08:46:36 -0800 Subject: [PATCH 047/121] [DOCS] Edits rollup API description (#37444) --- docs/reference/rollup/apis/rollup-caps.asciidoc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/reference/rollup/apis/rollup-caps.asciidoc b/docs/reference/rollup/apis/rollup-caps.asciidoc index 73816df6e1e56..5739250188f08 100644 --- a/docs/reference/rollup/apis/rollup-caps.asciidoc +++ b/docs/reference/rollup/apis/rollup-caps.asciidoc @@ -8,16 +8,19 @@ experimental[] -This API returns the rollup capabilities that have been configured for an index or index pattern. This API is useful -because a rollup job is often configured to rollup only a subset of fields from the source index. Furthermore, only -certain aggregations can be configured for various fields, leading to a limited subset of functionality depending on -that configuration. +This API returns the capabilities of any rollup jobs that have been configured +for a specific index or index pattern. -This API will allow you to inspect an index and determine: +This API is useful because a rollup job is often configured to rollup only a +subset of fields from the source index. Furthermore, only certain aggregations +can be configured for various fields, leading to a limited subset of +functionality depending on that configuration. + +This API enables you to inspect an index and determine: 1. Does this index have associated rollup data somewhere in the cluster? -2. If yes to the first question, what fields were rolled up, what aggregations can be performed, and where does the data -live? +2. If yes to the first question, what fields were rolled up, what aggregations +can be performed, and where does the data live? ==== Request From 2e074e5fc50483164edf190ba5642d58bce0a312 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 15 Jan 2019 10:53:29 -0800 Subject: [PATCH 048/121] [DOCS] Adds CCR screenshots (#37439) --- docs/reference/ccr/getting-started.asciidoc | 13 +++++++++++++ .../ccr/images/auto-follow-patterns.jpg | Bin 0 -> 171080 bytes docs/reference/ccr/images/remote-clusters.jpg | Bin 0 -> 121887 bytes 3 files changed, 13 insertions(+) create mode 100644 docs/reference/ccr/images/auto-follow-patterns.jpg create mode 100644 docs/reference/ccr/images/remote-clusters.jpg diff --git a/docs/reference/ccr/getting-started.asciidoc b/docs/reference/ccr/getting-started.asciidoc index d9684a38762b2..b44153a74bc4a 100644 --- a/docs/reference/ccr/getting-started.asciidoc +++ b/docs/reference/ccr/getting-started.asciidoc @@ -155,6 +155,13 @@ remote cluster. alias `leader` <2> This shows the number of nodes in the remote cluster the local cluster is connected to. + +Alternatively, you can manage remote clusters on the +*Management / Elasticsearch / Remote Clusters* page in {kib}: + +[role="screenshot"] +image::ml/images/remote-clusters.jpg["The Remote Clusters page in {kib}"] + [float] [[ccr-getting-started-leader-index]] @@ -331,3 +338,9 @@ DELETE /_ccr/auto_follow/beats // TEST[continued] ////////////////////////// + +Alternatively, you can manage auto-follow patterns on the +*Management / Elasticsearch / Cross Cluster Replication* page in {kib}: + +[role="screenshot"] +image::ml/images/auto-follow-patterns.jpg["The Auto-follow patterns page in {kib}"] diff --git a/docs/reference/ccr/images/auto-follow-patterns.jpg b/docs/reference/ccr/images/auto-follow-patterns.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc32d8fadfae195ad06a1ce3c4e2bb1e618662b3 GIT binary patch literal 171080 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

;j-?xXMsI>2ugFKp!)wqDgrOSEz- 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

2=D=HR+a)9XrCvGKrM;l}?fh| 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 z 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 Re_)6>(eV!_I;c}U{|4K=QXT*R literal 0 HcmV?d00001 From 6c8d04f40ac102985a7251aa96aca529f09df5bc Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 15 Jan 2019 11:11:01 -0800 Subject: [PATCH 049/121] [DOCS] Intro for adding nodes (#37202) --- docs/reference/setup.asciidoc | 2 ++ docs/reference/setup/add-nodes.asciidoc | 37 ++++++++++++++++++++++ docs/reference/setup/images/elas_0202.png | Bin 0 -> 10416 bytes docs/reference/setup/images/elas_0204.png | Bin 0 -> 20605 bytes 4 files changed, 39 insertions(+) create mode 100644 docs/reference/setup/add-nodes.asciidoc create mode 100644 docs/reference/setup/images/elas_0202.png create mode 100644 docs/reference/setup/images/elas_0204.png diff --git a/docs/reference/setup.asciidoc b/docs/reference/setup.asciidoc index 60e3c1dac2948..0d04fd37ffba6 100644 --- a/docs/reference/setup.asciidoc +++ b/docs/reference/setup.asciidoc @@ -58,3 +58,5 @@ include::setup/bootstrap-checks.asciidoc[] include::setup/starting.asciidoc[] include::setup/stopping.asciidoc[] + +include::setup/add-nodes.asciidoc[] diff --git a/docs/reference/setup/add-nodes.asciidoc b/docs/reference/setup/add-nodes.asciidoc new file mode 100644 index 0000000000000..eab4e131ad417 --- /dev/null +++ b/docs/reference/setup/add-nodes.asciidoc @@ -0,0 +1,37 @@ +[[add-elasticsearch-nodes]] +== Adding nodes to your cluster + +When you start an instance of {es}, you are starting a _node_. An {es} _cluster_ +is a group of nodes that have the same `cluster.name` attribute. As nodes join +or leave a cluster, the cluster automatically reorganizes itself to evenly +distribute the data across the available nodes. + +If you are running a single instance of {es}, you have a cluster of one node. +All primary shards reside on the single node. No replica shards can be +allocated, therefore the cluster state remains yellow. The cluster is fully +functional but is at risk of data loss in the event of a failure. + +image::setup/images/elas_0202.png["A cluster with one node and three primary shards"] + +You add nodes to a cluster to increase its capacity and reliability. By default, +a node is both a data node and eligible to be elected as the master node that +controls the cluster. You can also configure a new node for a specific purpose, +such as handling ingest requests. For more information, see +<>. + +When you add more nodes to a cluster, it automatically allocates replica shards. +When all primary and replica shards are active, the cluster state changes to +green. + +image::setup/images/elas_0204.png["A cluster with three nodes"] + +To add a node to a cluster: + +. Set up a new {es} instance. +. Specify the name of the cluster in its `cluster.name` attribute. For example, +to add a node to the `logging-prod` cluster, set `cluster.name: "logging-prod"` +in `elasticsearch.yml`. +. Start {es}. The node automatically discovers and joins the specified cluster. + +For more information about discovery and shard allocation, see +<> and <>. diff --git a/docs/reference/setup/images/elas_0202.png b/docs/reference/setup/images/elas_0202.png new file mode 100644 index 0000000000000000000000000000000000000000..36b31cc378197c8adaa81959662b085061e1f623 GIT binary patch literal 10416 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 Suj8@`9*BY2&B`11pZy=Vr5X4D literal 0 HcmV?d00001 diff --git a/docs/reference/setup/images/elas_0204.png b/docs/reference/setup/images/elas_0204.png new file mode 100644 index 0000000000000000000000000000000000000000..788d46e09f295d883667d2624bc698944394cd66 GIT binary patch literal 20605 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 literal 0 HcmV?d00001 From 6673956513b5facd954f64d04a09cd4111935ed0 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 15 Jan 2019 12:20:32 -0800 Subject: [PATCH 050/121] [DOCS] Fixes link to X-Pack settings --- docs/reference/settings/configuring-xes.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/settings/configuring-xes.asciidoc b/docs/reference/settings/configuring-xes.asciidoc index e075d6e695b46..29c6b95dddf0f 100644 --- a/docs/reference/settings/configuring-xes.asciidoc +++ b/docs/reference/settings/configuring-xes.asciidoc @@ -5,7 +5,7 @@ {xpack} Settings ++++ -include::{asciidoc-dir}/../../shared/settings61.asciidoc[] +include::{asciidoc-dir}/../../shared/settings.asciidoc[] include::license-settings.asciidoc[] include::ml-settings.asciidoc[] include::notification-settings.asciidoc[] From b0cd0b7ffaad49b1272c757065d2c77f15bf5da1 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Tue, 15 Jan 2019 14:03:21 -0700 Subject: [PATCH 051/121] Consistently use loopback address for ssl profile (#37487) This change fixes failures in the SslMultiPortTests where we attempt to connect to a profile on a port it is listening on but the connection fails. The failure is due to the profile being bound to multiple addresses and randomization will pick one of these addresses to determine the listening port. However, the address we get the port for may not be the address we are actually connecting to. In order to resolve this, the test now sets the bind host for profiles to the loopback address and uses the same address for connecting. Closes #37481 --- .../transport/ssl/SslMultiPortTests.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslMultiPortTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslMultiPortTests.java index d3ab5d092ab5b..1317218474a1a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslMultiPortTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslMultiPortTests.java @@ -7,6 +7,7 @@ import org.elasticsearch.client.transport.NoNodeAvailableException; import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.test.SecurityIntegTestCase; @@ -33,11 +34,13 @@ public class SslMultiPortTests extends SecurityIntegTestCase { private static int randomClientPort; private static int randomNoClientAuthPort; + private static InetAddress localAddress; @BeforeClass public static void getRandomPort() { randomClientPort = randomIntBetween(49000, 65500); // ephemeral port randomNoClientAuthPort = randomIntBetween(49000, 65500); + localAddress = InetAddress.getLoopbackAddress(); } /** @@ -66,10 +69,10 @@ protected Settings nodeSettings(int nodeOrdinal) { // client set up here .put("transport.profiles.client.port", randomClientPortRange) // make sure this is "localhost", no matter if ipv4 or ipv6, but be consistent - .put("transport.profiles.client.bind_host", "localhost") + .put("transport.profiles.client.bind_host", NetworkAddress.format(localAddress)) .put("transport.profiles.client.xpack.security.ssl.certificate_authorities", trustCert.toAbsolutePath()) .put("transport.profiles.no_client_auth.port", randomNoClientAuthPortRange) - .put("transport.profiles.no_client_auth.bind_host", "localhost") + .put("transport.profiles.no_client_auth.bind_host", NetworkAddress.format(localAddress)) .put("transport.profiles.no_client_auth.xpack.security.ssl.client_authentication", SSLClientAuth.NONE) .build(); logger.info("node {} settings:\n{}", nodeOrdinal, settings); @@ -116,7 +119,7 @@ public void testThatStandardTransportClientCanConnectToNoClientAuthProfile() thr .put("node.name", "programmatic_transport_client") .put("cluster.name", internalCluster().getClusterName()) .build(), LocalStateSecurity.class)) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("no_client_auth"))); assertGreenClusterState(transportClient); } @@ -131,7 +134,7 @@ public void testThatStandardTransportClientCanConnectToNoClientAuthProfile() thr */ public void testThatStandardTransportClientCannotConnectToClientProfile() throws Exception { try (TransportClient transportClient = createTransportClient(Settings.EMPTY)) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client"))); + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("client"))); transportClient.admin().cluster().prepareHealth().get(); fail("Expected NoNodeAvailableException"); } catch (NoNodeAvailableException e) { @@ -153,7 +156,7 @@ public void testThatProfileTransportClientCanConnectToClientProfile() throws Exc "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient-client-profile.crt", Arrays.asList("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt")); try (TransportClient transportClient = createTransportClient(builder.build())) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client"))); + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("client"))); assertGreenClusterState(transportClient); } } @@ -173,7 +176,7 @@ public void testThatProfileTransportClientCanConnectToNoClientAuthProfile() thro "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient-client-profile.crt", Arrays.asList("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt")); try (TransportClient transportClient = createTransportClient(builder.build())) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("no_client_auth"))); assertGreenClusterState(transportClient); } @@ -233,7 +236,7 @@ public void testThatTransportClientCannotConnectToClientProfile() throws Excepti .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client"))); + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("client"))); assertGreenClusterState(transportClient); fail("Expected NoNodeAvailableException"); } catch (NoNodeAvailableException e) { @@ -252,7 +255,7 @@ public void testThatTransportClientCannotConnectToNoClientAuthProfile() throws E .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("no_client_auth"))); assertGreenClusterState(transportClient); fail("Expected NoNodeAvailableException"); @@ -275,7 +278,7 @@ public void testThatTransportClientWithOnlyTruststoreCanConnectToNoClientAuthPro .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("no_client_auth"))); } } @@ -296,7 +299,7 @@ public void testThatTransportClientWithOnlyTruststoreCannotConnectToClientProfil .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client"))); + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("client"))); assertGreenClusterState(transportClient); fail("Expected NoNodeAvailableException"); } catch (NoNodeAvailableException e) { @@ -364,7 +367,7 @@ public void testThatSSLTransportClientWithNoTruststoreCannotConnectToClientProfi .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client"))); + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("client"))); assertGreenClusterState(transportClient); fail("Expected NoNodeAvailableException"); } catch (NoNodeAvailableException e) { @@ -386,7 +389,7 @@ public void testThatSSLTransportClientWithNoTruststoreCannotConnectToNoClientAut .build(); try (TransportClient transportClient = new TestXPackTransportClient(settings, Collections.singletonList(LocalStateSecurity.class))) { - transportClient.addTransportAddress(new TransportAddress(InetAddress.getLoopbackAddress(), + transportClient.addTransportAddress(new TransportAddress(localAddress, getProfilePort("no_client_auth"))); assertGreenClusterState(transportClient); fail("Expected NoNodeAvailableException"); @@ -396,8 +399,14 @@ public void testThatSSLTransportClientWithNoTruststoreCannotConnectToNoClientAut } private static int getProfilePort(String profile) { - TransportAddress transportAddress = - randomFrom(internalCluster().getInstance(Transport.class).profileBoundAddresses().get(profile).boundAddresses()); - return transportAddress.address().getPort(); + TransportAddress[] transportAddresses = + internalCluster().getInstance(Transport.class).profileBoundAddresses().get(profile).boundAddresses(); + for (TransportAddress address : transportAddresses) { + if (address.address().getAddress().equals(localAddress)) { + return address.address().getPort(); + } + } + throw new IllegalStateException("failed to find transport address equal to [" + NetworkAddress.format(localAddress) + "] " + + " in the following bound addresses " + Arrays.toString(transportAddresses)); } } From 0721448096b2a109e2d020a5f79877d798d7a150 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 15 Jan 2019 14:47:53 -0800 Subject: [PATCH 052/121] [DOCS] Adds placeholders for 6.6.0 release notes and highlights --- docs/reference/release-notes.asciidoc | 2 + docs/reference/release-notes/6.6.asciidoc | 52 +++++++++++++++++++ .../release-notes/highlights-6.6.0.asciidoc | 9 ++++ .../release-notes/highlights.asciidoc | 2 + 4 files changed, 65 insertions(+) create mode 100644 docs/reference/release-notes/6.6.asciidoc create mode 100644 docs/reference/release-notes/highlights-6.6.0.asciidoc diff --git a/docs/reference/release-notes.asciidoc b/docs/reference/release-notes.asciidoc index 697efd116180b..f4052e9b5ba6f 100644 --- a/docs/reference/release-notes.asciidoc +++ b/docs/reference/release-notes.asciidoc @@ -5,6 +5,7 @@ -- This section summarizes the changes in each release. +* <> * <> * <> * <> @@ -39,6 +40,7 @@ This section summarizes the changes in each release. -- +include::release-notes/6.6.asciidoc[] include::release-notes/6.5.asciidoc[] include::release-notes/6.4.asciidoc[] include::release-notes/6.3.asciidoc[] diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc new file mode 100644 index 0000000000000..1e6618c9c1d96 --- /dev/null +++ b/docs/reference/release-notes/6.6.asciidoc @@ -0,0 +1,52 @@ +//// +// To add a release, copy and paste the following text, uncomment the relevant +// sections, and add a link to the new section in the list of releases in +// ../release-notes.asciidoc. Note that release subheads must be floated and +// sections cannot be empty. +// TEMPLATE + +// [[release-notes-n.n.n]] +// == {es} version n.n.n + +// coming[n.n.n] + +// Also see <>. + +// [float] +// [[breaking-n.n.n]] +// === Breaking Changes + +// [float] +// [[breaking-java-n.n.n]] +// === Breaking Java Changes + +// [float] +// [[deprecation-n.n.n]] +// === Deprecations + +// [float] +// [[feature-n.n.n]] +// === New Features + +// [float] +// [[enhancement-n.n.n]] +// === Enhancements + +// [float] +// [[bug-n.n.n]] +// === Bug Fixes + +// [float] +// [[regression-n.n.n]] +// === Regressions + +// [float] +// === Known Issues +//// + +[[release-notes-6.6.0]] +== {es} version 6.6.0 + +Also see <>. + +coming[6.6.0] \ No newline at end of file diff --git a/docs/reference/release-notes/highlights-6.6.0.asciidoc b/docs/reference/release-notes/highlights-6.6.0.asciidoc new file mode 100644 index 0000000000000..47fcbe3f89a4e --- /dev/null +++ b/docs/reference/release-notes/highlights-6.6.0.asciidoc @@ -0,0 +1,9 @@ +[[release-highlights-6.6.0]] +== 6.6.0 release highlights +++++ +6.6.0 +++++ + +See also <>. + +coming[6.6.0] \ No newline at end of file diff --git a/docs/reference/release-notes/highlights.asciidoc b/docs/reference/release-notes/highlights.asciidoc index 85fda6152919e..e10eff390e163 100644 --- a/docs/reference/release-notes/highlights.asciidoc +++ b/docs/reference/release-notes/highlights.asciidoc @@ -9,12 +9,14 @@ This section summarizes the most important changes in each release. For the full list, see <> and <>. +* <> * <> * <> * <> -- +include::highlights-6.6.0.asciidoc[] include::highlights-6.5.0.asciidoc[] include::highlights-6.4.0.asciidoc[] include::highlights-6.3.0.asciidoc[] \ No newline at end of file From 9a1b2eb3411e52e4d7951131ca70684b88970668 Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 16 Jan 2019 08:38:46 +0000 Subject: [PATCH 053/121] [Ml] Prevent config snapshot failure blocking migration (#37493) --- .../xpack/ml/MlConfigMigrator.java | 10 +++- .../ml/integration/MlConfigMigratorIT.java | 56 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java index f497a6c7063fe..28e6f34cd9e3f 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlConfigMigrator.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.persistent.PersistentTasksCustomMetaData; import org.elasticsearch.xpack.core.ml.MlMetadata; import org.elasticsearch.xpack.core.ml.MlTasks; @@ -369,7 +370,14 @@ public void snapshotMlMeta(MlMetadata mlMetadata, ActionListener listen indexResponse -> { listener.onResponse(indexResponse.getResult() == DocWriteResponse.Result.CREATED); }, - listener::onFailure), + e -> { + if (e instanceof VersionConflictEngineException) { + // the snapshot already exists + listener.onResponse(Boolean.TRUE); + } else { + listener.onFailure(e); + } + }), client::index ); } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/integration/MlConfigMigratorIT.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/integration/MlConfigMigratorIT.java index 87c0e4ac824ce..55e8dc163dc4d 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/integration/MlConfigMigratorIT.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/integration/MlConfigMigratorIT.java @@ -6,8 +6,11 @@ package org.elasticsearch.xpack.ml.integration; import org.elasticsearch.Version; +import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateUpdateTask; @@ -178,6 +181,59 @@ public void testMigrateConfigs() throws InterruptedException, IOException { assertEquals("df-1", datafeedsHolder.get().get(0).getId()); } + public void testExistingSnapshotDoesNotBlockMigration() throws InterruptedException { + // index a doc with the same Id as the config snapshot + IndexRequestBuilder indexRequest = client().prepareIndex(AnomalyDetectorsIndex.jobStateIndexName(), + ElasticsearchMappings.DOC_TYPE, "ml-config") + .setSource(Collections.singletonMap("a_field", "a_value")) + .setOpType(DocWriteRequest.OpType.CREATE) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + + indexRequest.execute().actionGet(); + + // define the configs + MlMetadata.Builder mlMetadata = new MlMetadata.Builder(); + mlMetadata.putJob(buildJobBuilder("job-foo").build(), false); + + MetaData.Builder metaData = MetaData.builder(); + RoutingTable.Builder routingTable = RoutingTable.builder(); + addMlConfigIndex(metaData, routingTable); + ClusterState clusterState = ClusterState.builder(new ClusterName("_name")) + .metaData(metaData.putCustom(MlMetadata.TYPE, mlMetadata.build())) + .routingTable(routingTable.build()) + .build(); + + doAnswer(invocation -> { + ClusterStateUpdateTask listener = (ClusterStateUpdateTask) invocation.getArguments()[1]; + listener.clusterStateProcessed("source", mock(ClusterState.class), mock(ClusterState.class)); + return null; + }).when(clusterService).submitStateUpdateTask(eq("remove-migrated-ml-configs"), any()); + + AtomicReference exceptionHolder = new AtomicReference<>(); + AtomicReference responseHolder = new AtomicReference<>(); + + // do the migration + MlConfigMigrator mlConfigMigrator = new MlConfigMigrator(nodeSettings(), client(), clusterService); + // writing the snapshot should fail because the doc already exists + // in which case the migration should continue + blockingCall(actionListener -> mlConfigMigrator.migrateConfigsWithoutTasks(clusterState, actionListener), + responseHolder, exceptionHolder); + + assertNull(exceptionHolder.get()); + assertTrue(responseHolder.get()); + + // check the jobs have been migrated + AtomicReference> jobsHolder = new AtomicReference<>(); + JobConfigProvider jobConfigProvider = new JobConfigProvider(client()); + blockingCall(actionListener -> jobConfigProvider.expandJobs("*", true, true, actionListener), + jobsHolder, exceptionHolder); + + assertNull(exceptionHolder.get()); + assertThat(jobsHolder.get(), hasSize(1)); + assertTrue(jobsHolder.get().get(0).build().getCustomSettings().containsKey(MlConfigMigrator.MIGRATED_FROM_VERSION)); + assertEquals("job-foo", jobsHolder.get().get(0).build().getId()); + } + public void testMigrateConfigs_GivenLargeNumberOfJobsAndDatafeeds() throws InterruptedException { int jobCount = randomIntBetween(150, 201); int datafeedCount = randomIntBetween(150, jobCount); From 50f4f2691290ed5160444ba7f5669eee580a1c50 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 16 Jan 2019 13:49:41 -0800 Subject: [PATCH 054/121] [DOCS] Edits and adds missing 6.5.0 release notes (#37553) --- docs/reference/release-notes/6.5.asciidoc | 53 ++++++++++++++--------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/docs/reference/release-notes/6.5.asciidoc b/docs/reference/release-notes/6.5.asciidoc index 362ab368ba1fa..ff6de87f6ab21 100644 --- a/docs/reference/release-notes/6.5.asciidoc +++ b/docs/reference/release-notes/6.5.asciidoc @@ -258,9 +258,8 @@ Java High Level REST Client:: * add start trial API to HLRC {pull}32799[#32799] Machine Learning:: -* [ML] Label anomalies with multi_bucket_impact {pull}34233[#34233] -* [ML] Add a file structure determination endpoint {pull}33471[#33471] -* [ML] Partition-wise maximum scores {pull}32748[#32748] +* Add a file structure determination endpoint {pull}33471[#33471] +* Partition-wise maximum scores {pull}32748[#32748] Mapping:: * New Annotated_text field type {pull}30364[#30364] (issue: {issue}29467[#29467]) @@ -446,18 +445,34 @@ Logging:: * Logging: Make node name consistent in logger {pull}31588[#31588] Machine Learning:: -* ML: Adding support for lazy nodes (#29991) {pull}34538[#34538] (issue: {issue}29991[#29991]) -* [ML] Add an ingest pipeline definition to structure finder {pull}34350[#34350] -* [ML] Add a timeout option to file structure finder {pull}34117[#34117] -* [ML] Allow asynchronous job deletion {pull}34058[#34058] (issue: {issue}32836[#32836]) -* Make certain ML node settings dynamic (#33565) {pull}33961[#33961] (issue: {issue}33565[#33565]) -* [ML] Display integers without .0 in file structure field stats {pull}33947[#33947] -* [ML] Return both Joda and Java formats from structure finder {pull}33900[#33900] -* Adding node_count to ML Usage (#33850) {pull}33863[#33863] (issue: {issue}33850[#33850]) +* Adding support for lazy nodes {pull}34538[#34538] (issue: {issue}29991[#29991]) +* Add an ingest pipeline definition to structure finder {pull}34350[#34350] +* Add a timeout option to file structure finder {pull}34117[#34117] +* Allow asynchronous job deletion {pull}34058[#34058] (issue: {issue}32836[#32836]) +* Make certain ML node settings dynamic {pull}33961[#33961] (issue: {issue}33565[#33565]) +* Display integers without .0 in file structure field stats {pull}33947[#33947] +* Return both Joda and Java formats from structure finder {pull}33900[#33900] +* Adding node_count to ML Usage {pull}33863[#33863] (issue: {issue}33850[#33850]) * Delete custom index if the only contained job is deleted {pull}33788[#33788] (issue: {issue}30075[#30075]) -* [ML] Allow overrides for some file structure detection decisions {pull}33630[#33630] -* [ML] Minor improvements to categorization Grok pattern creation {pull}33353[#33353] -* [ML] Delete forecast API (#31134) {pull}33218[#33218] (issue: {issue}31134[#31134]) +* Allow overrides for some file structure detection decisions {pull}33630[#33630] +* Minor improvements to categorization Grok pattern creation {pull}33353[#33353] +* Delete forecast API {pull}33218[#33218] (issue: {issue}31134[#31134]) +* Perform anomaly detection on features derived from multiple bucket values to +improve the robustness of detection with respect to misconfigured bucket lengths +and to improve the detection of long lasting anomalies. {ml-pull}175[#175] +* Support decomposing a time series into a piecewise linear trend and with +piecewise constant scaling of the periodic components. This extends our +decomposition functionality to handle the same types of change points that our +modelling capabilities do. {ml-pull}198[#198] +* Increased independence of anomaly scores across partitions. {ml-pull}182[#182] +* Avoid potential false positives at model start up when first detecting new +components of the time series decomposition. {ml-pull}218[#218] +* Add a new label (`multi_bucket_impact`) to record level anomaly results. The +value is on a scale of -5 to +5 where -5 means the anomaly is purely single +bucket and +5 means the anomaly is purely multi bucket. {pull}34233[#34233], +{ml-pull}230[#230] +* Improve our ability to detect change points in the presence of outliers. +{ml-pull}265[#265] Mapping:: * Preserve the order of nested documents in the Lucene index {pull}34225[#34225] (issue: {issue}33587[#33587]) @@ -677,14 +692,10 @@ Logging:: * Logging: Configure the node name when we have it {pull}32983[#32983] (issue: {issue}32793[#32793]) Machine Learning:: -* [ML] Prevent notifications being created on deletion of a non existent job {pull}35337[#35337] (issues: {issue}34058[#34058], {issue}35336[#35336]) -* [ML] Prevent default job values overwriting nulled fields {pull}34804[#34804] * Handle pre-6.x time fields {pull}34373[#34373] -* [ML] Get job stats request should filter non-ML job tasks {pull}33516[#33516] (issue: {issue}33515[#33515]) -* [ML] Prevent NPE parsing the stop datafeed request. {pull}33347[#33347] -* [ML] fix updating opened jobs scheduled events (#31651) {pull}32881[#32881] (issue: {issue}31651[#31651]) -* Clear Job#finished_time when it is opened (#32605) {pull}32755[#32755] -* [ML] Fix thread leak when waiting for job flush (#32196) {pull}32541[#32541] (issue: {issue}32196[#32196]) +* Fix updating opened jobs scheduled events {pull}32881[#32881] (issue: {issue}31651[#31651]) +* Clear Job#finished_time when it is opened {pull}32755[#32755] +* Fix thread leak when waiting for job flush {pull}32541[#32541] (issue: {issue}32196[#32196]) Mapping:: * Fix field mapping updates with similarity {pull}33634[#33634] (issue: {issue}33611[#33611]) From 5525a6f35a058a02dad8bf8359f12cf07a30f313 Mon Sep 17 00:00:00 2001 From: Torgeir Thoresen Date: Thu, 17 Jan 2019 10:22:49 +0100 Subject: [PATCH 055/121] Fix erroneous docstrings for abstract bulk by scroll request (#37517) --- .../index/reindex/AbstractBulkByScrollRequest.java | 4 ++-- .../index/reindex/AbstractBulkByScrollRequestBuilder.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java b/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java index f57d7dff1cb62..fa4eb69ea9be6 100644 --- a/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java +++ b/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequest.java @@ -185,14 +185,14 @@ public Self setSize(int size) { } /** - * Should version conflicts cause aborts? Defaults to false. + * Whether or not version conflicts cause the action to abort. */ public boolean isAbortOnVersionConflict() { return abortOnVersionConflict; } /** - * Should version conflicts cause aborts? Defaults to false. + * Set whether or not version conflicts cause the action to abort. */ public Self setAbortOnVersionConflict(boolean abortOnVersionConflict) { this.abortOnVersionConflict = abortOnVersionConflict; diff --git a/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java b/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java index e3c5bd2197a94..449c023696677 100644 --- a/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/reindex/AbstractBulkByScrollRequestBuilder.java @@ -75,7 +75,7 @@ public Self size(int size) { } /** - * Should we version conflicts cause the action to abort? + * Set whether or not version conflicts cause the action to abort. */ public Self abortOnVersionConflict(boolean abortOnVersionConflict) { request.setAbortOnVersionConflict(abortOnVersionConflict); From ec5a529a4e585ae6080af585b44848cd198132e2 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 17 Jan 2019 13:54:48 +0100 Subject: [PATCH 056/121] Mute TransportClientNodesServiceTests#testListenerFailures Relates to #37567 --- .../client/transport/TransportClientNodesServiceTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java b/server/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java index c95d4b9e74f21..36c449e791852 100644 --- a/server/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java +++ b/server/src/test/java/org/elasticsearch/client/transport/TransportClientNodesServiceTests.java @@ -223,6 +223,7 @@ public void close() { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/37567") public void testListenerFailures() throws InterruptedException { int iters = iterations(10, 100); for (int i = 0; i Date: Thu, 17 Jan 2019 11:51:17 +0100 Subject: [PATCH 057/121] Decreased time out in test Relates to #37378 --- .../action/admin/cluster/state/ClusterStateApiTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/state/ClusterStateApiTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/state/ClusterStateApiTests.java index fb823d3657e19..e061e7a08dd89 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/state/ClusterStateApiTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/state/ClusterStateApiTests.java @@ -62,7 +62,7 @@ public void testWaitForMetaDataVersion() throws Exception { // Verify that the timed out property has been set" metadataVersion = response.getState().getMetaData().version(); clusterStateRequest.waitForMetaDataVersion(metadataVersion + 1); - clusterStateRequest.waitForTimeout(TimeValue.timeValueSeconds(1)); // Fail fast + clusterStateRequest.waitForTimeout(TimeValue.timeValueMillis(500)); // Fail fast ActionFuture future3 = client().admin().cluster().state(clusterStateRequest); assertBusy(() -> { assertThat(future3.isDone(), is(true)); From ab51f112c2abe76500bb27fdbd03aa5546a2ae35 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 17 Jan 2019 13:14:06 +0100 Subject: [PATCH 058/121] Moved ccr integration to the package with other ccr integration tests. --- .../org/elasticsearch/xpack/ccr/{action => }/FollowStatsIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/{action => }/FollowStatsIT.java (99%) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/FollowStatsIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowStatsIT.java similarity index 99% rename from x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/FollowStatsIT.java rename to x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowStatsIT.java index bf6f080099088..409746f9d851b 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/FollowStatsIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowStatsIT.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.ccr.action; +package org.elasticsearch.xpack.ccr; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; From 657b120c3b58d4871157cebe714065c1cdfb92f9 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Thu, 17 Jan 2019 14:04:41 +0100 Subject: [PATCH 059/121] Added fatal_exception field for ccr stats in monitoring mapping. (#37563) --- .../collector/ccr/FollowStatsMonitoringDocTests.java | 7 ++++++- .../plugin/core/src/main/resources/monitoring-es.json | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/monitoring/collector/ccr/FollowStatsMonitoringDocTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/monitoring/collector/ccr/FollowStatsMonitoringDocTests.java index 410d573e1b4c0..33affe45fc46c 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/monitoring/collector/ccr/FollowStatsMonitoringDocTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/monitoring/collector/ccr/FollowStatsMonitoringDocTests.java @@ -230,7 +230,7 @@ public void testShardFollowNodeTaskStatusFieldsMapped() throws IOException { 10, fetchExceptions, 2, - null); + new ElasticsearchException("fatal error")); XContentBuilder builder = jsonBuilder(); builder.value(status); Map serializedStatus = XContentHelper.convertToMap(XContentType.JSON.xContent(), Strings.toString(builder), false); @@ -266,6 +266,11 @@ public void testShardFollowNodeTaskStatusFieldsMapped() throws IOException { assertThat(exceptionFieldMapping.size(), equalTo(2)); assertThat(XContentMapValues.extractValue("type.type", exceptionFieldMapping), equalTo("keyword")); assertThat(XContentMapValues.extractValue("reason.type", exceptionFieldMapping), equalTo("text")); + } else if (fieldName.equals("fatal_exception")) { + assertThat(fieldType, equalTo("object")); + assertThat(((Map) fieldMapping.get("properties")).size(), equalTo(2)); + assertThat(XContentMapValues.extractValue("properties.type.type", fieldMapping), equalTo("keyword")); + assertThat(XContentMapValues.extractValue("properties.reason.type", fieldMapping), equalTo("text")); } else { fail("unexpected field value type [" + fieldValue.getClass() + "] for field [" + fieldName + "]"); } diff --git a/x-pack/plugin/core/src/main/resources/monitoring-es.json b/x-pack/plugin/core/src/main/resources/monitoring-es.json index 6f345385e4863..c23da39ecdefe 100644 --- a/x-pack/plugin/core/src/main/resources/monitoring-es.json +++ b/x-pack/plugin/core/src/main/resources/monitoring-es.json @@ -1028,6 +1028,17 @@ }, "time_since_last_read_millis": { "type": "long" + }, + "fatal_exception": { + "type": "object", + "properties": { + "type" : { + "type": "keyword" + }, + "reason": { + "type": "text" + } + } } } }, From b83b3de518e508d195ddca2bb66fcd7293045227 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 17 Jan 2019 08:21:37 -0800 Subject: [PATCH 060/121] [DOCS] Adds limitation to the get jobs API (#37549) --- docs/reference/ml/apis/get-job.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/ml/apis/get-job.asciidoc b/docs/reference/ml/apis/get-job.asciidoc index e9836911d4047..3faad1062d7ea 100644 --- a/docs/reference/ml/apis/get-job.asciidoc +++ b/docs/reference/ml/apis/get-job.asciidoc @@ -27,6 +27,8 @@ group name, a comma-separated list of jobs, or a wildcard expression. You can get information for all jobs by using `_all`, by specifying `*` as the ``, or by omitting the ``. +IMPORTANT: This API returns a maximum of 10,000 jobs. + ==== Path Parameters From 58899abe59b152d388664cf090cff85c8076ffaa Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 17 Jan 2019 10:47:15 -0800 Subject: [PATCH 061/121] [DOCS] Adds size limitation to the get datafeeds APIs (#37578) --- docs/reference/ml/apis/get-datafeed-stats.asciidoc | 1 + docs/reference/ml/apis/get-datafeed.asciidoc | 2 ++ docs/reference/ml/apis/get-job-stats.asciidoc | 2 ++ 3 files changed, 5 insertions(+) diff --git a/docs/reference/ml/apis/get-datafeed-stats.asciidoc b/docs/reference/ml/apis/get-datafeed-stats.asciidoc index fda79fe913ce4..80cb4830026f7 100644 --- a/docs/reference/ml/apis/get-datafeed-stats.asciidoc +++ b/docs/reference/ml/apis/get-datafeed-stats.asciidoc @@ -32,6 +32,7 @@ statistics for all {dfeeds} by using `_all`, by specifying `*` as the If the {dfeed} is stopped, the only information you receive is the `datafeed_id` and the `state`. +IMPORTANT: This API returns a maximum of 10,000 {dfeeds}. ==== Path Parameters diff --git a/docs/reference/ml/apis/get-datafeed.asciidoc b/docs/reference/ml/apis/get-datafeed.asciidoc index b859a6a80872e..ad31d756f7730 100644 --- a/docs/reference/ml/apis/get-datafeed.asciidoc +++ b/docs/reference/ml/apis/get-datafeed.asciidoc @@ -27,6 +27,8 @@ comma-separated list of {dfeeds} or a wildcard expression. You can get information for all {dfeeds} by using `_all`, by specifying `*` as the ``, or by omitting the ``. +IMPORTANT: This API returns a maximum of 10,000 {dfeeds}. + ==== Path Parameters `feed_id`:: diff --git a/docs/reference/ml/apis/get-job-stats.asciidoc b/docs/reference/ml/apis/get-job-stats.asciidoc index 62a68db86fa06..2e617c5dfcaad 100644 --- a/docs/reference/ml/apis/get-job-stats.asciidoc +++ b/docs/reference/ml/apis/get-job-stats.asciidoc @@ -29,6 +29,8 @@ group name, a comma-separated list of jobs, or a wildcard expression. You can get statistics for all jobs by using `_all`, by specifying `*` as the ``, or by omitting the ``. +IMPORTANT: This API returns a maximum of 10,000 jobs. + ==== Path Parameters From 01da8dabddf6bc10a212811fc8cc1387927eed2a Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 17 Jan 2019 11:57:46 -0800 Subject: [PATCH 062/121] [DOCS] Add ML breaking change for 6.6 (#37582) --- docs/reference/migration/migrate_6_6.asciidoc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/reference/migration/migrate_6_6.asciidoc b/docs/reference/migration/migrate_6_6.asciidoc index e173d04870ea4..95e34b70a3777 100644 --- a/docs/reference/migration/migrate_6_6.asciidoc +++ b/docs/reference/migration/migrate_6_6.asciidoc @@ -7,7 +7,10 @@ This section discusses the changes that you need to be aware of when migrating your application to Elasticsearch 6.6. +* <> +* <> * <> +* <> See also <> and <>. @@ -81,6 +84,7 @@ GET /stackoverflow/_search // TEST[setup:stackoverflow] [float] +[[breaking_66_setting_changes]] === Settings changes [float] @@ -108,6 +112,7 @@ the cluster settings API. `xpack.notification.slack.account..secure_url` [float] +[[breaking_66_mapping_changes]] === Mappings changes [float] @@ -136,4 +141,13 @@ previously created indexes. The following type parameters are deprecated for the `geo_shape` field type: `tree`, `precision`, `tree_levels`, `distance_error_pct`, `points_only`, and `strategy`. They -will be removed in a future version. \ No newline at end of file +will be removed in a future version. + +[float] +[[breaking_66_ml_changes]] +=== Machine learning changes + +The get jobs API and get job stats API can retrieve a maximum of 10,000 jobs. +Likewise, the get datafeeds API and get datafeed stats API can retrieve a +maximum of 10,000 datafeeds. Prior to version 6.6, there were no limits on the +results from these APIs. From 8a8a51dc8ffd5a9e28db0a840bff3329a988743b Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 18 Jan 2019 00:28:48 -0800 Subject: [PATCH 063/121] Minor docs cleanup (#37595) Not all terminals are accessed via SSH. --- docs/reference/cat.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/cat.asciidoc b/docs/reference/cat.asciidoc index 7a2262b7962bb..0da752aba42d2 100644 --- a/docs/reference/cat.asciidoc +++ b/docs/reference/cat.asciidoc @@ -9,8 +9,8 @@ JSON is great... for computers. Even if it's pretty-printed, trying to find relationships in the data is tedious. Human eyes, especially -when looking at an ssh terminal, need compact and aligned text. The -cat API aims to meet this need. +when looking at a terminal, need compact and aligned text. The cat API +aims to meet this need. All the cat commands accept a query string parameter `help` to see all the headers and info they provide, and the `/_cat` command alone lists all From bf15701462befb8e76f39d40bf39ca8bae94e39b Mon Sep 17 00:00:00 2001 From: lcawl Date: Fri, 18 Jan 2019 08:52:54 -0800 Subject: [PATCH 064/121] [DOCS] Add ml-cpp PRs to release notes --- docs/reference/release-notes/6.6.asciidoc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc index 1e6618c9c1d96..3a56dd2027263 100644 --- a/docs/reference/release-notes/6.6.asciidoc +++ b/docs/reference/release-notes/6.6.asciidoc @@ -49,4 +49,14 @@ Also see <>. -coming[6.6.0] \ No newline at end of file +coming[6.6.0] + +[[bug-6.6.0]] +[float] +=== Bug fixes + +Machine Learning:: +* Fix cause of "Sample out of bounds" error message. {ml-pull}335[#335] +* Fix hang when closing a job or creating a forecast. This problem occurs if you +created a forecast for a large job and temporary storage was not cleaned up. +{ml-pull}352[#352] (issue: {ml-issue}350[#350]) \ No newline at end of file From c76abc2fbe9e927cc4cd5d928de968b5cb1e9a5b Mon Sep 17 00:00:00 2001 From: lcawl Date: Fri, 18 Jan 2019 12:08:34 -0800 Subject: [PATCH 065/121] [DOCS] Remove coming tags for 6.6 --- docs/reference/release-notes/6.6.asciidoc | 2 -- docs/reference/release-notes/highlights-6.6.0.asciidoc | 2 -- 2 files changed, 4 deletions(-) diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc index 3a56dd2027263..f04df77f18333 100644 --- a/docs/reference/release-notes/6.6.asciidoc +++ b/docs/reference/release-notes/6.6.asciidoc @@ -49,8 +49,6 @@ Also see <>. -coming[6.6.0] - [[bug-6.6.0]] [float] === Bug fixes diff --git a/docs/reference/release-notes/highlights-6.6.0.asciidoc b/docs/reference/release-notes/highlights-6.6.0.asciidoc index 47fcbe3f89a4e..38eb2ae8aebe2 100644 --- a/docs/reference/release-notes/highlights-6.6.0.asciidoc +++ b/docs/reference/release-notes/highlights-6.6.0.asciidoc @@ -5,5 +5,3 @@ ++++ See also <>. - -coming[6.6.0] \ No newline at end of file From 595eab3c42b2ae9dfe8d0be30e421f67c3a35857 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Fri, 18 Jan 2019 14:03:48 -0500 Subject: [PATCH 066/121] SQL: fix object extraction from sources (#37502) Throws an exception if hit extractor tries to retrieve unsupported object. For example, selecting "a" from `{"a": {"b": "c"}}` now throws an exception instead of returning null. Relates to #37364 --- .../search/extractor/FieldHitExtractor.java | 10 ++++++++-- .../extractor/FieldHitExtractorTests.java | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java index 7cce05652dfc0..23130ba780a29 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java @@ -166,8 +166,14 @@ Object extractFromSource(Map map) { sj.add(path[i]); Object node = subMap.get(sj.toString()); if (node instanceof Map) { - // Add the sub-map to the queue along with the current path index - queue.add(new Tuple<>(i, (Map) node)); + if (i < path.length - 1) { + // Add the sub-map to the queue along with the current path index + queue.add(new Tuple<>(i, (Map) node)); + } else { + // We exhausted the path and got a map + // If it is an object - it will be handled in the value extractor + value = node; + } } else if (node != null) { if (i < path.length - 1) { // If we reach a concrete value without exhausting the full path, something is wrong with the mapping diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java index 4f562e82b5c21..429764903a261 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java @@ -336,6 +336,24 @@ public void testFieldWithDotsAndSamePathButDifferentHierarchy() { assertThat(ex.getMessage(), is("Multiple values (returned by [a.b.c.d.e.f.g]) are not supported")); } + public void testObjectsForSourceValue() throws IOException { + String fieldName = randomAlphaOfLength(5); + FieldHitExtractor fe = new FieldHitExtractor(fieldName, null, false); + SearchHit hit = new SearchHit(1); + XContentBuilder source = JsonXContent.contentBuilder(); + source.startObject(); { + source.startObject(fieldName); { + source.field("b", "c"); + } + source.endObject(); + } + source.endObject(); + BytesReference sourceRef = BytesReference.bytes(source); + hit.sourceRef(sourceRef); + SqlException ex = expectThrows(SqlException.class, () -> fe.extract(hit)); + assertThat(ex.getMessage(), is("Objects (returned by [" + fieldName + "]) are not supported")); + } + private Object randomValue() { Supplier value = randomFrom(Arrays.asList( () -> randomAlphaOfLength(10), From bfac71038c6ebd450d6023809798b0867e5d644b Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Mon, 21 Jan 2019 07:54:51 +0100 Subject: [PATCH 067/121] Do not set fatal exception when shard follow task is stopped. (#37603) When shard follow task is cancelled while fetching operations then the fatal exception field should not be set. --- .../xpack/ccr/action/ShardFollowNodeTask.java | 15 +++++---- .../ccr/action/ShardFollowNodeTaskTests.java | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java index b1d6467168c90..a1a0e25e126d0 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTask.java @@ -418,12 +418,15 @@ private void updateSettings(final LongConsumer handler, final AtomicInteger retr private void handleFailure(Exception e, AtomicInteger retryCounter, Runnable task) { assert e != null; - if (shouldRetry(params.getRemoteCluster(), e) && isStopped() == false) { - int currentRetry = retryCounter.incrementAndGet(); - LOGGER.debug(new ParameterizedMessage("{} error during follow shard task, retrying [{}]", - params.getFollowShardId(), currentRetry), e); - long delay = computeDelay(currentRetry, params.getReadPollTimeout().getMillis()); - scheduler.accept(TimeValue.timeValueMillis(delay), task); + if (shouldRetry(params.getRemoteCluster(), e)) { + if (isStopped() == false) { + // Only retry is the shard follow task is not stopped. + int currentRetry = retryCounter.incrementAndGet(); + LOGGER.debug(new ParameterizedMessage("{} error during follow shard task, retrying [{}]", + params.getFollowShardId(), currentRetry), e); + long delay = computeDelay(currentRetry, params.getReadPollTimeout().getMillis()); + scheduler.accept(TimeValue.timeValueMillis(delay), task); + } } else { fatalException = ExceptionsHelper.convertToElastic(e); LOGGER.warn("shard follow task encounter non-retryable error", e); diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTaskTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTaskTests.java index cf7f901aaba33..9929241fc23c8 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTaskTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/ShardFollowNodeTaskTests.java @@ -40,7 +40,9 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; public class ShardFollowNodeTaskTests extends ESTestCase { @@ -287,6 +289,35 @@ public void testReceiveRetryableError() { assertThat(status.leaderGlobalCheckpoint(), equalTo(63L)); } + public void testFatalExceptionNotSetWhenStoppingWhileFetchingOps() { + ShardFollowTaskParams params = new ShardFollowTaskParams(); + params.maxReadRequestOperationCount = 64; + params.maxOutstandingReadRequests = 1; + params.maxOutstandingWriteRequests = 1; + ShardFollowNodeTask task = createShardFollowTask(params); + startTask(task, 63, -1); + + readFailures.add(new ShardNotFoundException(new ShardId("leader_index", "", 0))); + + mappingVersions.add(1L); + leaderGlobalCheckpoints.add(63L); + maxSeqNos.add(63L); + responseSizes.add(64); + simulateResponse.set(true); + beforeSendShardChangesRequest = status -> { + // Cancel just before attempting to fetch operations: + task.onCancelled(); + }; + task.coordinateReads(); + + assertThat(task.isStopped(), is(true)); + ShardFollowNodeTaskStatus status = task.getStatus(); + assertThat(status.getFatalException(), nullValue()); + assertThat(status.failedReadRequests(), equalTo(1L)); + assertThat(status.successfulReadRequests(), equalTo(0L)); + assertThat(status.readExceptions().size(), equalTo(1)); + } + public void testEmptyShardChangesResponseShouldClearFetchException() { ShardFollowTaskParams params = new ShardFollowTaskParams(); params.maxReadRequestOperationCount = 64; From 3a6e98418298fd94bd20ddb7c85b19850db7ac8d Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 21 Jan 2019 08:55:10 -0800 Subject: [PATCH 068/121] [DOCS] Adds ML PRs to 6.6 release notes (#37615) --- docs/reference/release-notes/6.6.asciidoc | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc index f04df77f18333..4c07e3fab8281 100644 --- a/docs/reference/release-notes/6.6.asciidoc +++ b/docs/reference/release-notes/6.6.asciidoc @@ -49,6 +49,31 @@ Also see <>. +[float] +[[feature-6.6.0]] +=== New features + +Machine Learning:: +* Store job configuration information in the new `.ml-config` index +{pull}36698[#36698] (issue: {issue}32905[#32905]) +* Determine when data is missing from a bucket due to ingest latency +{pull}35387[#35387] (issue: {issue}35131[#35131]) + +[float] +[[enhancement-6.6.0]] +=== Enhancements + +Machine Learning:: +* Create the {ml} annotations index {pull}36731[#36731] (issue: {issue}33376[#33376]) +* Add cluster setting to enable/disable config migration {pull}36700[#36700] +(issue: {issue}32905[#32905]) +* Enable the use of endpoints starting with `_ml` instead of `_xpack/ml` +{pull}36373[#36373] (issue: {issue}36315[#36315]) +* Add audits when deprecation warnings occur while datafeeds start +{pull}36233[#36233] +* Add lazy parsing for DatafeedConfig:Aggs,Query {pull}36117[#36117] +* Add support for rollup indexes in datafeeds {pull}34654[#34654] + [[bug-6.6.0]] [float] === Bug fixes @@ -57,4 +82,10 @@ Machine Learning:: * Fix cause of "Sample out of bounds" error message. {ml-pull}335[#335] * Fix hang when closing a job or creating a forecast. This problem occurs if you created a forecast for a large job and temporary storage was not cleaned up. -{ml-pull}352[#352] (issue: {ml-issue}350[#350]) \ No newline at end of file +{ml-pull}352[#352] (issue: {ml-issue}350[#350]) +* Wait for autodetect to be ready in the datafeed {pull}37349[#37349] +(issues: {issue}36810[#36810], {issue}37227[#37227]) +* Stop datafeeds when their jobs are stale {pull}37227[#37227] +(issue: {issue}36810[#36810]) +* Order get job stats API response by job id {pull}36841[#36841] +(issue: {issue}36683[#36683]) \ No newline at end of file From 94d28bf80f66c3a86df810ac3ac7c64969707d62 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 22 Jan 2019 10:01:51 +0200 Subject: [PATCH 069/121] Define constants for REST requests endpoints in tests (#37610) (cherry picked from commit 90ae556d97dc05f9a5a34b8bd08234acfd5ffe28) --- .../xpack/sql/qa/multi_node/RestSqlMultinodeIT.java | 3 ++- .../xpack/sql/qa/security/RestSqlSecurityIT.java | 3 ++- .../xpack/sql/qa/security/UserFunctionIT.java | 3 ++- .../elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java | 8 +++++--- .../xpack/sql/qa/rest/RestSqlUsageTestCase.java | 9 ++++++--- .../xpack/sql/client/JreHttpUrlConnection.java | 4 +++- .../java/org/elasticsearch/xpack/sql/proto/Protocol.java | 1 + .../xpack/sql/plugin/RestSqlTranslateAction.java | 5 +++-- 8 files changed, 24 insertions(+), 12 deletions(-) diff --git a/x-pack/plugin/sql/qa/multi-node/src/test/java/org/elasticsearch/xpack/sql/qa/multi_node/RestSqlMultinodeIT.java b/x-pack/plugin/sql/qa/multi-node/src/test/java/org/elasticsearch/xpack/sql/qa/multi_node/RestSqlMultinodeIT.java index f4e878436e639..c2aaf69c97b48 100644 --- a/x-pack/plugin/sql/qa/multi-node/src/test/java/org/elasticsearch/xpack/sql/qa/multi_node/RestSqlMultinodeIT.java +++ b/x-pack/plugin/sql/qa/multi-node/src/test/java/org/elasticsearch/xpack/sql/qa/multi_node/RestSqlMultinodeIT.java @@ -28,6 +28,7 @@ import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.randomMode; +import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; /** * Tests specific to multiple nodes. @@ -111,7 +112,7 @@ private void assertCount(RestClient client, int count) throws IOException { expected.put("columns", singletonList(columnInfo(mode, "COUNT(1)", "long", JDBCType.BIGINT, 20))); expected.put("rows", singletonList(singletonList(count))); - Request request = new Request("POST", "/_xpack/sql"); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); request.setJsonEntity("{\"query\": \"SELECT COUNT(*) FROM test\"" + mode(mode) + "}"); Map actual = responseToMap(client.performRequest(request)); diff --git a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java index 0f91b3f1b7ecc..1f24eb79a0c2b 100644 --- a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java +++ b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java @@ -32,6 +32,7 @@ import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.randomMode; +import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -180,7 +181,7 @@ private static Map runSql(@Nullable String asUser, String mode, } private static Map runSql(@Nullable String asUser, HttpEntity entity) throws IOException { - Request request = new Request("POST", "/_xpack/sql"); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); if (asUser != null) { RequestOptions.Builder options = request.getOptions().toBuilder(); options.addHeader("es-security-runas-user", asUser); diff --git a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java index 9f6cac39a7116..0aecfe350639f 100644 --- a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java +++ b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java @@ -37,6 +37,7 @@ import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.randomMode; +import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; public class UserFunctionIT extends ESRestTestCase { @@ -178,7 +179,7 @@ private Map runSql(String asUser, String mode, String sql) throw } private Map runSql(String asUser, HttpEntity entity) throws IOException { - Request request = new Request("POST", "/_xpack/sql"); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); if (asUser != null) { RequestOptions.Builder options = request.getOptions().toBuilder(); options.addHeader("es-security-runas-user", asUser); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java index 37ade9fe495a5..e9a7069b55166 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java @@ -50,6 +50,8 @@ * user rather than to the JDBC driver or CLI. */ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTestCase { + + public static String SQL_QUERY_REST_ENDPOINT = org.elasticsearch.xpack.sql.proto.Protocol.SQL_QUERY_REST_ENDPOINT; /** * Builds that map that is returned in the header for each column. */ @@ -311,7 +313,7 @@ private Map runSql(String mode, String sql, String suffix) throw } private Map runSql(HttpEntity sql, String suffix) throws IOException { - Request request = new Request("POST", "/_xpack/sql" + suffix); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT + suffix); request.addParameter("error_trace", "true"); // Helps with debugging in case something crazy happens on the server. request.addParameter("pretty", "true"); // Improves error reporting readability if (randomBoolean()) { @@ -642,7 +644,7 @@ private Tuple runSqlAsText(String sql, String accept) throws IOE * rather than the {@code format} parameter. */ private Tuple runSqlAsText(String suffix, HttpEntity entity, String accept) throws IOException { - Request request = new Request("POST", "/_xpack/sql" + suffix); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT + suffix); request.addParameter("error_trace", "true"); request.setEntity(entity); RequestOptions.Builder options = request.getOptions().toBuilder(); @@ -660,7 +662,7 @@ private Tuple runSqlAsText(String suffix, HttpEntity entity, Str * rather than an {@code Accept} header. */ private Tuple runSqlAsTextFormat(String sql, String format) throws IOException { - Request request = new Request("POST", "/_xpack/sql"); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); request.addParameter("error_trace", "true"); request.addParameter("format", format); request.setJsonEntity("{\"query\":\"" + sql + "\"}"); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java index 4afafdd444d07..7b9f52b2c90fb 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java @@ -26,6 +26,9 @@ import java.util.Locale; import java.util.Map; +import static org.elasticsearch.xpack.sql.proto.Protocol.SQL_QUERY_REST_ENDPOINT; +import static org.elasticsearch.xpack.sql.proto.Protocol.SQL_STATS_REST_ENDPOINT; +import static org.elasticsearch.xpack.sql.proto.Protocol.SQL_TRANSLATE_REST_ENDPOINT; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; public abstract class RestSqlUsageTestCase extends ESRestTestCase { @@ -226,7 +229,7 @@ private void index(List docs) throws IOException { } private Map getStats() throws UnsupportedOperationException, IOException { - Request request = new Request("GET", "/_xpack/sql/stats"); + Request request = new Request("GET", SQL_STATS_REST_ENDPOINT); Map responseAsMap; try (InputStream content = client().performRequest(request).getEntity().getContent()) { responseAsMap = XContentHelper.convertToMap(JsonXContent.jsonXContent, content, false); @@ -236,7 +239,7 @@ private Map getStats() throws UnsupportedOperationException, IOE } private void runTranslate(String sql) throws IOException { - Request request = new Request("POST", "/_xpack/sql/translate"); + Request request = new Request("POST", SQL_TRANSLATE_REST_ENDPOINT); if (randomBoolean()) { // We default to JSON but we force it randomly for extra coverage request.addParameter("format", "json"); @@ -276,7 +279,7 @@ private void assertTranslateQueryMetric(int expected, Map respon } private void runSql(String mode, String restClient, String sql) throws IOException { - Request request = new Request("POST", "/_xpack/sql"); + Request request = new Request("POST", SQL_QUERY_REST_ENDPOINT); request.addParameter("error_trace", "true"); // Helps with debugging in case something crazy happens on the server. request.addParameter("pretty", "true"); // Improves error reporting readability if (randomBoolean()) { diff --git a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/JreHttpUrlConnection.java b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/JreHttpUrlConnection.java index 3f894ae59af8e..59a6e82e9874d 100644 --- a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/JreHttpUrlConnection.java +++ b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/JreHttpUrlConnection.java @@ -35,6 +35,7 @@ import javax.sql.rowset.serial.SerialException; import static java.util.Collections.emptyMap; +import static org.elasticsearch.xpack.sql.proto.Protocol.SQL_QUERY_REST_ENDPOINT; /** * Low-level http client using the built-in {@link HttpURLConnection}. @@ -47,7 +48,8 @@ public class JreHttpUrlConnection implements Closeable { * error. */ public static final String SQL_STATE_BAD_SERVER = "bad_server"; - private static final String SQL_NOT_AVAILABLE_ERROR_MESSAGE = "request [/_xpack/sql] contains unrecognized parameter: [mode]"; + private static final String SQL_NOT_AVAILABLE_ERROR_MESSAGE = "request [" + SQL_QUERY_REST_ENDPOINT + + "] contains unrecognized parameter: [mode]"; public static R http(String path, String query, ConnectionConfiguration cfg, Function handler) { final URI uriPath = cfg.baseUri().resolve(path); // update path if needed diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/Protocol.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/Protocol.java index cc4bb92823160..77eb8c8113b9b 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/Protocol.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/Protocol.java @@ -28,5 +28,6 @@ public final class Protocol { */ public static final String CLEAR_CURSOR_REST_ENDPOINT = "/_xpack/sql/close"; public static final String SQL_QUERY_REST_ENDPOINT = "/_xpack/sql"; + public static final String SQL_TRANSLATE_REST_ENDPOINT = "/_xpack/sql/translate"; public static final String SQL_STATS_REST_ENDPOINT = "/_xpack/sql/stats"; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlTranslateAction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlTranslateAction.java index 0d58c7880d8bd..121e49f8a7a35 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlTranslateAction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlTranslateAction.java @@ -14,6 +14,7 @@ import org.elasticsearch.rest.action.RestToXContentListener; import org.elasticsearch.xpack.sql.action.SqlTranslateAction; import org.elasticsearch.xpack.sql.action.SqlTranslateRequest; +import org.elasticsearch.xpack.sql.proto.Protocol; import java.io.IOException; @@ -26,8 +27,8 @@ public class RestSqlTranslateAction extends BaseRestHandler { public RestSqlTranslateAction(Settings settings, RestController controller) { super(settings); - controller.registerHandler(GET, "/_xpack/sql/translate", this); - controller.registerHandler(POST, "/_xpack/sql/translate", this); + controller.registerHandler(GET, Protocol.SQL_TRANSLATE_REST_ENDPOINT, this); + controller.registerHandler(POST, Protocol.SQL_TRANSLATE_REST_ENDPOINT, this); } @Override From a8df1fdb1e331e32b69b9ec4901a1809420a878a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 22 Jan 2019 17:34:13 +0100 Subject: [PATCH 070/121] Fix potential NPE in UsersTool (#37660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It looks like the output of FileUserPasswdStore.parseFile shouldn't be wrapped into another map since its output can be null. Doing this wrapping after the null check (which potentially raises an exception) instead. --- .../xpack/security/authc/file/tool/UsersTool.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java index 9d4dfba327e5d..6007ef5fd6d03 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java @@ -7,6 +7,7 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; + import org.elasticsearch.cli.EnvironmentAwareCommand; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.LoggingAwareMultiCommand; @@ -221,7 +222,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th Path file = FileUserPasswdStore.resolveFile(env); FileAttributesChecker attributesChecker = new FileAttributesChecker(file); - Map users = new HashMap<>(FileUserPasswdStore.parseFile(file, null, env.settings())); + Map users = FileUserPasswdStore.parseFile(file, null, env.settings()); if (users == null) { throw new UserException(ExitCodes.CONFIG, "Configuration file [" + file + "] is missing"); } @@ -229,6 +230,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th throw new UserException(ExitCodes.NO_USER, "User [" + username + "] doesn't exist"); } final Hasher hasher = Hasher.resolve(XPackSettings.PASSWORD_HASHING_ALGORITHM.get(env.settings())); + users = new HashMap<>(users); // make modifiable users.put(username, hasher.hash(new SecureString(password))); FileUserPasswdStore.writeFile(users, file); From e23f01f3ae7866c6f054b972634c7396cfd6c9f2 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 22 Jan 2019 10:50:41 -0800 Subject: [PATCH 071/121] [DOCS] Adds anchor in bootstrap checks --- docs/reference/setup/bootstrap-checks.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/setup/bootstrap-checks.asciidoc b/docs/reference/setup/bootstrap-checks.asciidoc index 91e491de7fcaa..0df398969ecf9 100644 --- a/docs/reference/setup/bootstrap-checks.asciidoc +++ b/docs/reference/setup/bootstrap-checks.asciidoc @@ -21,6 +21,7 @@ Elasticsearch from running with incompatible settings. These checks are documented individually. [float] +[[dev-vs-prod-mode]] === Development vs. production mode By default, Elasticsearch binds to loopback addresses for <> From 3500dea312a436a16d8d85570c025e95e74585de Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 22 Jan 2019 12:23:27 -0800 Subject: [PATCH 072/121] [DOCS] Fixes links to new bootstrap anchor --- docs/reference/release-notes/6.0.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/release-notes/6.0.asciidoc b/docs/reference/release-notes/6.0.asciidoc index ac385b8c30174..4a063236c92d8 100644 --- a/docs/reference/release-notes/6.0.asciidoc +++ b/docs/reference/release-notes/6.0.asciidoc @@ -1798,11 +1798,11 @@ Search:: Security:: * A new bootstrap check enforces that TLS/SSL is required for inter-node communication when running in -{ref}/bootstrap-checks.html#_development_vs_production_mode[production mode]. See +{ref}/bootstrap-checks.html#dev-vs-prod-mode[production mode]. See {stack-ov}/encrypting-communications.html[Encrypting Communications]. * A new bootstrap check enforces that HTTPS is used by the built-in token service when running in -{ref}/bootstrap-checks.html#_development_vs_production_mode[production mode]. +{ref}/bootstrap-checks.html#dev-vs-prod-mode[production mode]. To disable the token service, set `xpack.security.authc.token.enabled` to `false` in your `elasticsearch.yml`. See <>. @@ -2591,7 +2591,7 @@ Search:: Security:: * A new bootstrap check enforces that default passwords are disabled for the built-in users when running in -{ref}/bootstrap-checks.html#_development_vs_production_mode[production mode]. +{ref}/bootstrap-checks.html#dev-vs-prod-mode[production mode]. You must set `xpack.security.authc.accept_default_password` to `false` in your `elasticsearch.yml`. For more information, see <> and {stack-ov}/setting-up-authentication.html[User authentication]. From d3bc751d56eca19cace7a1c6087204dedf8fda14 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Wed, 23 Jan 2019 12:25:54 -0500 Subject: [PATCH 073/121] Tests: disable testRandomGeoCollectionQuery on tiny polygons (#37579) Due to https://issues.apache.org/jira/browse/LUCENE-8634 this test may fail if a really tiny polygon is generated. This commit checks for tiny polygons and skips the final check, which is expected to fail until the lucene bug is fixed and new version of lucene is released. --- .../elasticsearch/search/geo/GeoShapeQueryTests.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java index 57997a5a17ccd..66ba1624bb84f 100644 --- a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java +++ b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java @@ -357,6 +357,9 @@ public void testQueryRandomGeoCollection() throws Exception { geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS); SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get(); assertSearchResponse(result); + assumeTrue("Skipping the check for the polygon with a degenerated dimension until " + +" https://issues.apache.org/jira/browse/LUCENE-8634 is fixed", + randomPoly.maxLat - randomPoly.minLat > 8.4e-8 && randomPoly.maxLon - randomPoly.minLon > 8.4e-8); assertHitCount(result, 1); } @@ -385,7 +388,8 @@ public void testRandomGeoCollectionQuery() throws Exception { } gcb.shape(new PolygonBuilder(cb)); - logger.info("Created Random GeometryCollection containing {} shapes", gcb.numShapes()); + logger.info("Created Random GeometryCollection containing {} shapes using {} tree", gcb.numShapes(), + usePrefixTrees ? "default" : "quadtree"); if (usePrefixTrees == false) { client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape") @@ -406,7 +410,11 @@ public void testRandomGeoCollectionQuery() throws Exception { geoShapeQueryBuilder.relation(ShapeRelation.INTERSECTS); SearchResponse result = client().prepareSearch("test").setTypes("type").setQuery(geoShapeQueryBuilder).get(); assertSearchResponse(result); - assertTrue(result.getHits().getTotalHits() > 0); + assumeTrue("Skipping the check for the polygon with a degenerated dimension until " + +" https://issues.apache.org/jira/browse/LUCENE-8634 is fixed", + randomPoly.maxLat - randomPoly.minLat > 8.4e-8 && randomPoly.maxLon - randomPoly.minLon > 8.4e-8); + assertTrue("query: " + geoShapeQueryBuilder.toString() + " doc: " + Strings.toString(docSource), + result.getHits().getTotalHits() > 0); } /** tests querying a random geometry collection with a point */ From b3d324be8a6065aaacb0b08359b4ef68386bd6a3 Mon Sep 17 00:00:00 2001 From: Paul Sanwald Date: Wed, 23 Jan 2019 16:19:55 -0500 Subject: [PATCH 074/121] add 6.6 release notes (#37715) Adding release notes for 6.6.0. --- docs/reference/release-notes/6.6.asciidoc | 647 ++++++++++++++++++++-- 1 file changed, 588 insertions(+), 59 deletions(-) diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc index 4c07e3fab8281..9cd54f2ee69bb 100644 --- a/docs/reference/release-notes/6.6.asciidoc +++ b/docs/reference/release-notes/6.6.asciidoc @@ -1,91 +1,620 @@ -//// -// To add a release, copy and paste the following text, uncomment the relevant -// sections, and add a link to the new section in the list of releases in -// ../release-notes.asciidoc. Note that release subheads must be floated and -// sections cannot be empty. -// TEMPLATE -// [[release-notes-n.n.n]] -// == {es} version n.n.n - -// coming[n.n.n] +[[release-notes-6.6.0]] +== {es} version 6.6.0 -// Also see <>. +coming[6.6.0] +Also see <>. -// [float] -// [[breaking-n.n.n]] -// === Breaking Changes +[[breaking-6.6.0]] +[float] +=== Breaking changes -// [float] -// [[breaking-java-n.n.n]] -// === Breaking Java Changes +Cross-cluster replication:: +* Change get autofollow patterns API response format {pull}36203[#36203] (issue: {issue}36049[#36049]) -// [float] -// [[deprecation-n.n.n]] -// === Deprecations +Ranking:: +* Forbid negative scores in function_score query {pull}35709[#35709] (issue: {issue}33309[#33309]) -// [float] -// [[feature-n.n.n]] -// === New Features +Search:: +* Remove the distinction between query and filter context in QueryBuilders {pull}35354[#35354] (issue: {issue}35293[#35293]) +* Throw a parsing exception when boost is set in span_or query (#28390) {pull}34112[#34112] (issue: {issue}28390[#28390]) -// [float] -// [[enhancement-n.n.n]] -// === Enhancements +Watcher:: +* Fix metric stats names {pull}34951[#34951] (issue: {issue}34865[#34865]) -// [float] -// [[bug-n.n.n]] -// === Bug Fixes +[[breaking-java-6.6.0]] +[float] +=== Breaking Java changes -// [float] -// [[regression-n.n.n]] -// === Regressions +Infra/Core:: +* Drop settings member from AbstractComponent {pull}35083[#35083] -// [float] -// === Known Issues -//// +Infra/Logging:: +* Drop last deprecated logger function {pull}35082[#35082] (issue: {issue}32174[#32174]) -[[release-notes-6.6.0]] -== {es} version 6.6.0 +Java High Level REST Client:: +* HLRC XPack Protocol clean up: Migration; Graph; Watcher {pull}34639[#34639] (issue: {issue}34451[#34451]) -Also see <>. +ZenDiscovery:: +* Make node field in JoinRequest private {pull}36405[#36405] +[[deprecation-6.6.0]] [float] +=== Deprecations + +Aggregations:: +* Deprecate dots in aggregation names {pull}31468[#31468] (issues: {issue}17600[#17600], {issue}19040[#19040]) + +Core:: +* Deprecate use of scientific notation in epoch time parsing {pull}36691[#36691] +* Add backcompat for joda time formats {pull}36531[#36531] + +Features:: +* Deprecation info API: 'fix' value for index.shard.check_on_startup {pull}36458[#36458] (issues: {issue}33194[#33194], {issue}36024[#36024]) +* Deprecation info API: negative index.unassigned.node_left.delayed_timeout {pull}36454[#36454] (issues: {issue}26828[#26828], {issue}36024[#36024]) + +Mapping:: +* Add warning about upcoming expanded fields limit {pull}34906[#34906] (issue: {issue}26541[#26541]) + +Scripting:: +* Adds deprecation logging to ScriptDocValues#getValues. {pull}34279[#34279] (issue: {issue}22919[#22919]) + +Search:: +* Warn in multi-search on unknown keys in meatdata {pull}36104[#36104] (issue: {issue}35938[#35938]) +* Deprecate the _termvector endpoint. {pull}36098[#36098] (issue: {issue}8484[#8484]) +* Deprecate `_source_include` and `_source_exclude` url parameters {pull}33475[#33475] (issue: {issue}22792[#22792]) + +Security:: +* Undeprecate /_license endpoints {pull}35974[#35974] (issue: {issue}35959[#35959]) + +Settings:: +* Deprecate setting index.optimize_auto_generated_id in 6.x {pull}28862[#28862] (issue: {issue}27600[#27600]) + +Watcher:: +* Watcher deprecate notification service settings {pull}36403[#36403] +* Undeprecate /_watcher endpoints {pull}36269[#36269] + [[feature-6.6.0]] +[float] === New features -Machine Learning:: -* Store job configuration information in the new `.ml-config` index -{pull}36698[#36698] (issue: {issue}32905[#32905]) -* Determine when data is missing from a bucket due to ingest latency -{pull}35387[#35387] (issue: {issue}35131[#35131]) +Aggregations:: +* Median absolute deviation agg {pull}34482[#34482] (issue: {issue}26681[#26681]) + +Analysis:: +* Add support for inlined user dictionary in Nori {pull}36123[#36123] (issue: {issue}35842[#35842]) +* Add a prebuilt ICU Analyzer {pull}34958[#34958] (issue: {issue}34285[#34285]) + +Index Lifecycle Management:: +* Adds Index lifecycle feature {pull}35193[#35193] + +Java High Level REST Client:: +* Add rollup search {pull}36334[#36334] (issue: {issue}29827[#29827]) + +Java Low Level REST Client:: +* Make warning behavior pluggable per request {pull}36345[#36345] +* Add PreferHasAttributeNodeSelector {pull}36005[#36005] + +Geo:: +* Fork Lucene's LatLonShape Classes to local lucene package {pull}36794[#36794] +* Integrate Lucene's LatLonShape (BKD Backed GeoShapes) as default `geo_shape` indexing approach {pull}35320[#35320] (issue: {issue}32039[#32039]) + +Machine learning:: +* Store job configuration information in the new `.ml-config` index {pull}36698[#36698] (issue: {issue}32905[#32905]) +* Determine when data is missing from a bucket due to ingest latency {pull}35387[#35387] (issue: {issue}35131[#35131]) + +Search:: +* Added soft limit to open scroll contexts #25244 {pull}36009[#36009] (issue: {issue}25244[#25244]) +* Make lucene's IntervalQuery available via the Query DSL {pull}32406[#32406] (issue: {issue}29636[#29636]) + +SQL:: +* Introduce HISTOGRAM grouping function {pull}36510[#36510] (issue: {issue}36509[#36509]) +* DATABASE() and USER() system functions {pull}35946[#35946] (issue: {issue}35863[#35863]) +* Introduce INTERVAL support {pull}35521[#35521] (issue: {issue}29990[#29990]) -[float] [[enhancement-6.6.0]] +[float] === Enhancements +Aggregations:: +* Enforce max_buckets limit only in the final reduction phase {pull}36152[#36152] (issues: {issue}32125[#32125], {issue}35921[#35921]) +* Histogram aggs: add empty buckets only in the final reduce step {pull}35921[#35921] +* Handles exists query in composite aggs {pull}35758[#35758] +* Added parent validation for auto date histogram {pull}35670[#35670] +* Allow unmapped fields in composite aggregations {pull}35331[#35331] (issue: {issue}35317[#35317]) +* Refactor children aggregator into a generic ParentJoinAggregator {pull}34845[#34845] (issue: {issue}34210[#34210]) +* Add parent-aggregation to parent-join module {pull}34210[#34210] (issue: {issue}9705[#9705]) + +Analysis:: +* Ensure TokenFilters only produce single tokens when parsing synonyms {pull}34331[#34331] (issue: {issue}34298[#34298]) + +Audit:: +* Add "request.id" to file audit logs {pull}35536[#35536] + +Authentication:: +* Invalidate Token API enhancements - HLRC {pull}36362[#36362] +* Add DEBUG/TRACE logs for LDAP bind {pull}36028[#36028] +* Add Tests for findSamlRealm {pull}35905[#35905] +* Add realm information for Authenticate API {pull}35648[#35648] +* Formal support for "password_hash" in Put User {pull}35242[#35242] (issue: {issue}34729[#34729]) +* Enhance Invalidate Token API {pull}35388[#35388] (issues: {issue}34556[#34556], {issue}35115[#35115]) + +Authorization:: +* Add origin_address to authentication_success {pull}36409[#36409] +* Improve exact index matching performance {pull}36017[#36017] +* `manage_token` privilege for `kibana_system` {pull}35751[#35751] +* Grant .tasks access to kibana_system role {pull}35573[#35573] +* Native roles store uses mget to retrieve roles {pull}33531[#33531] (issue: {issue}33205[#33205]) + +Build:: +* Sounds like typo in exception message {pull}35458[#35458] +* Improve validation of yaml suites {pull}34957[#34957] (issue: {issue}34735[#34735]) +* Enforce skip headers when needed {pull}34735[#34735] (issue: {issue}34650[#34650]) +* Improve validation of do sections {pull}34734[#34734] (issue: {issue}34651[#34651]) + +Core:: +* Override the JVM DNS cache policy {pull}36570[#36570] +* Added wait_for_metadata_version parameter to cluster state api. {pull}35535[#35535] +* Extract RunOnce into a dedicated class {pull}35489[#35489] + +Cross-cluster replication:: +* Add time since last auto follow fetch to auto follow stats {pull}36542[#36542] (issues: {issue}33007[#33007], {issue}35895[#35895]) +* Clean followed leader index UUIDs in auto follow metadata {pull}36408[#36408] (issue: {issue}33007[#33007]) +* Change AutofollowCoordinator to use wait_for_metadata_version {pull}36264[#36264] (issues: {issue}33007[#33007], {issue}35895[#35895]) +* Refactor AutoFollowCoordinator to track leader indices per remote cluster {pull}36031[#36031] (issues: {issue}33007[#33007], {issue}35895[#35895]) +* Refactor auto follow coordinator {pull}35895[#35895] (issue: {issue}33007[#33007]) + +CRUD:: +* Document Seq No powered optimistic concurrency control {pull}37284[#37284] (issues: {issue}10708[#10708], {issue}36148[#36148]) +* Rename seq# powered optimistic concurrency control parameters to ifSeqNo/ifPrimaryTerm {pull}36757[#36757] (issues: {issue}10708[#10708], {issue}36148[#36148]) +* Expose Sequence Number based Optimistic Concurrency Control in the rest layer {pull}36721[#36721] (issues: {issue}10708[#10708], {issue}36148[#36148]) +* Add doc's sequence number + primary term to GetResult and use it for updates {pull}36680[#36680] (issues: {issue}10708[#10708], {issue}36148[#36148]) +* Add seq no powered optimistic locking support to the index and delete transport actions {pull}36619[#36619] (issues: {issue}10708[#10708], {issue}36148[#36148]) + +Distributed:: +* Allow asynchronous block operations to be delayed in IndexShardOperationPermits {pull}35999[#35999] (issues: {issue}35540[#35540], {issue}35850[#35850]) +* TransportResyncReplicationAction should not honour blocks {pull}35795[#35795] (issues: {issue}35332[#35332], {issue}35597[#35597]) +* Add global and index level blocks to IndexSettings {pull}35695[#35695] (issues: {issue}35332[#35332], {issue}35597[#35597]) +* Expose all permits acquisition in IndexShard and TransportReplicationAction {pull}35540[#35540] (issue: {issue}33888[#33888]) +* Add a java level freeze/unfreeze API {pull}35353[#35353] (issues: {issue}34352[#34352], {issue}34357[#34357]) +* Check blocks while having index shard permit in TransportReplicationAction {pull}35332[#35332] (issue: {issue}33888[#33888]) +* Apply masterNodeTimeout to MasterNodeRequest transmission {pull}35235[#35235] + +Engine:: +* Add sequence numbers based optimistic concurrency control support to Engine {pull}36467[#36467] (issues: {issue}10708[#10708], {issue}36148[#36148]) +* Require soft-deletes when access changes snapshot {pull}36446[#36446] +* Use delCount of SegmentInfos to calculate numDocs {pull}36323[#36323] +* Always configure soft-deletes field of IndexWriterConfig {pull}36196[#36196] (issue: {issue}36141[#36141]) +* Always return false from `refreshNeeded` on ReadOnlyEngine {pull}35837[#35837] (issue: {issue}35785[#35785]) +* Add a `_freeze` / `_unfreeze` API {pull}35592[#35592] (issue: {issue}34352[#34352]) +* Engine.newChangesSnapshot may cause unneeded refreshes if called concurrently {pull}35169[#35169] +* Do not alloc full buffer for small change requests {pull}35158[#35158] +* Add IndexShardOperationPermits.asyncBlockOperations(ActionListener) {pull}34902[#34902] (issue: {issue}33888[#33888]) +* Add a frozen engine implementation {pull}34357[#34357] (issue: {issue}34352[#34352]) + +Features:: +* Deprecation check for discovery configuration {pull}36666[#36666] (issue: {issue}36024[#36024]) +* Simplify deprecation issue levels {pull}36326[#36326] +* Deprecation check for tribe node {pull}36240[#36240] (issue: {issue}36024[#36024]) +* Deprecation check for `:` in Cluster/Index name {pull}36185[#36185] (issue: {issue}36024[#36024]) +* Deprecation check for renamed bulk threadpool settings {pull}36662[#36662] (issue: {issue}36024[#36024]) +* Deprecation check for audit log prefix settings {pull}36661[#36661] (issue: {issue}36024[#36024]) +* Deprecation check for classic similarity {pull}36577[#36577] (issue: {issue}36024[#36024]) +* Deprecation check for HTTP pipelining {pull}36521[#36521] (issue: {issue}36024[#36024]) +* Deprecation check for index threadpool {pull}36520[#36520] (issue: {issue}36024[#36024]) +* Deprecation check for percolator.map_unmapped_fields_as_string {pull}36460[#36460] (issue: {issue}36024[#36024]) +* Deprecation check for http.enabled setting {pull}36394[#36394] (issues: {issue}29601[#29601], {issue}36024[#36024]) +* Deprecation check for File Discovery plugin {pull}36190[#36190] (issue: {issue}36024[#36024]) + +Geo:: +* Adds a name of the field to geopoint parsing errors {pull}36529[#36529] (issue: {issue}15965[#15965]) +* Add support to ShapeBuilders for building Lucene geometry {pull}35707[#35707] (issue: {issue}35320[#35320]) + +Ingest:: +* Make the ingest-geoip databases even lazier to load {pull}36679[#36679] +* ingest: grok fix duplicate patterns JAVACLASS and JAVAFILE {pull}35886[#35886] + +Java High Level REST Client:: +* Add HLRC support for pause follow API {pull}35216[#35216] (issue: {issue}33824[#33824]) +* Add security Create Token API {pull}34791[#34791] +* Add start rollup job support to HL REST Client {pull}34623[#34623] (issue: {issue}29827[#29827]) +* Add security authenticate API {pull}33552[#33552] +* Add get users action {pull}36332[#36332] (issue: {issue}29827[#29827]) +* Add delete template API {pull}36320[#36320] (issue: {issue}27205[#27205]) +* Implement get-user-privileges API {pull}36292[#36292] +* Get Deprecation Info API {pull}36279[#36279] (issue: {issue}29827[#29827]) +* Added support for Follow Stats API {pull}36253[#36253] (issue: {issue}33824[#33824]) +* Added support for CCR Stats API {pull}36213[#36213] (issue: {issue}33824[#33824]) +* Put Role {pull}36209[#36209] (issue: {issue}29827[#29827]) +* Add index templates exist API {pull}36132[#36132] (issue: {issue}27205[#27205]) +* Add support for CCR Get Auto Follow Pattern apis {pull}36049[#36049] (issue: {issue}33824[#33824]) +* Add support for CCR Delete Auto Follow Pattern API {pull}35981[#35981] (issue: {issue}33824[#33824]) +* Remove fromXContent from IndexUpgradeInfoResponse {pull}35934[#35934] +* Add delete expired data API {pull}35906[#35906] (issue: {issue}29827[#29827]) +* Execute watch API {pull}35868[#35868] (issue: {issue}29827[#29827]) +* Add ability to put user with a password hash {pull}35844[#35844] (issue: {issue}35242[#35242]) +* Add ML find file structure API {pull}35833[#35833] (issue: {issue}29827[#29827]) +* Add support for get roles API {pull}35787[#35787] (issue: {issue}29827[#29827]) +* Add support for CCR Put Auto Follow Pattern API {pull}35780[#35780] (issue: {issue}33824[#33824]) +* XPack ML info action {pull}35777[#35777] (issue: {issue}29827[#29827]) +* ML Delete event from Calendar {pull}35760[#35760] (issue: {issue}29827[#29827]) +* Add ML revert model snapshot API {pull}35750[#35750] (issue: {issue}29827[#29827]) +* ML Get Calendar Events {pull}35747[#35747] (issue: {issue}29827[#29827]) +* Add high-level REST client API for `_freeze` and `_unfreeze` {pull}35723[#35723] (issue: {issue}34352[#34352]) +* Fix issue in equals impl for GlobalOperationPrivileges {pull}35721[#35721] +* ML Delete job from calendar {pull}35713[#35713] (issue: {issue}29827[#29827]) +* ML Add Event To Calendar API {pull}35704[#35704] (issue: {issue}29827[#29827]) +* Add ML update model snapshot API (#35537) {pull}35694[#35694] (issue: {issue}29827[#29827]) +* Add support for CCR Unfollow API {pull}35693[#35693] (issue: {issue}33824[#33824]) +* Clean up PutLicenseResponse {pull}35689[#35689] (issue: {issue}35547[#35547]) +* Clean up StartBasicResponse {pull}35688[#35688] (issue: {issue}35547[#35547]) +* Add support for put privileges API {pull}35679[#35679] +* Add Job to Calendar API {pull}35666[#35666] (issue: {issue}29827[#29827]) +* Add support for CCR Resume Follow API {pull}35638[#35638] (issue: {issue}33824[#33824]) +* Add support for get application privileges API {pull}35556[#35556] (issue: {issue}29827[#29827]) +* Clean up XPackInfoResponse class and related tests {pull}35547[#35547] +* Add parameters to stopRollupJob API {pull}35545[#35545] (issue: {issue}34811[#34811]) +* Add ML delete model snapshot API {pull}35537[#35537] (issue: {issue}29827[#29827]) +* Add get watch API {pull}35531[#35531] (issue: {issue}29827[#29827]) +* Add ML Update Filter API {pull}35522[#35522] (issue: {issue}29827[#29827]) +* Add ML get filters api {pull}35502[#35502] (issue: {issue}29827[#29827]) +* Add ML get model snapshots API {pull}35487[#35487] (issue: {issue}29827[#29827]) +* Add "_has_privileges" API to Security Client {pull}35479[#35479] (issue: {issue}29827[#29827]) +* Add Delete Privileges API to HLRC {pull}35454[#35454] (issue: {issue}29827[#29827]) +* Add support for CCR Put Follow API {pull}35409[#35409] +* Add ML delete filter action {pull}35382[#35382] (issue: {issue}29827[#29827]) +* Add delete user action {pull}35294[#35294] (issue: {issue}29827[#29827]) +* HLRC for _mtermvectors {pull}35266[#35266] (issues: {issue}27205[#27205], {issue}33447[#33447]) +* Reindex API with wait_for_completion false {pull}35202[#35202] (issue: {issue}27205[#27205]) +* Add watcher stats API {pull}35185[#35185] (issue: {issue}29827[#29827]) +* Add ML API PUT filter {pull}35175[#35175] (issue: {issue}29827[#29827]) +* HLRC support for getTask {pull}35166[#35166] (issue: {issue}27205[#27205]) +* Add support for the clear realm cache API {pull}35163[#35163] (issue: {issue}29827[#29827]) +* Add InvalidateToken security API {pull}35114[#35114] (issue: {issue}29827[#29827]) +* Add GetRollupIndexCaps API {pull}35102[#35102] (issue: {issue}29827[#29827]) +* Migration api - upgrade {pull}34898[#34898] (issue: {issue}29827[#29827]) +* Add Update datafeed API {pull}34882[#34882] (issue: {issue}29827[#29827]) +* Add stop rollup job support to HL REST Client {pull}34702[#34702] (issue: {issue}29827[#29827]) +* Bulk Api support for global parameters {pull}34528[#34528] (issue: {issue}26026[#26026]) +* Add support for source exists API {pull}34519[#34519] (issue: {issue}27205[#27205]) +* Add document _count API support to Rest High Level Client. {pull}34267[#34267] (issue: {issue}27205[#27205]) +* Add delete rollup job support to HL REST Client {pull}34066[#34066] (issue: {issue}29827[#29827]) +* HLRC API for _termvectors {pull}33447[#33447] (issue: {issue}27205[#27205]) +* Add support for get license basic/trial status API {pull}33176[#33176] (issue: {issue}29827[#29827]) +* Small corrections to HLRC doc for _termvectors {pull}35221[#35221] (issue: {issue}33447[#33447]) + +Java Low Level REST Client:: +* On retry timeout add root exception {pull}25576[#25576] + + Machine Learning:: * Create the {ml} annotations index {pull}36731[#36731] (issue: {issue}33376[#33376]) -* Add cluster setting to enable/disable config migration {pull}36700[#36700] -(issue: {issue}32905[#32905]) -* Enable the use of endpoints starting with `_ml` instead of `_xpack/ml` -{pull}36373[#36373] (issue: {issue}36315[#36315]) -* Add audits when deprecation warnings occur while datafeeds start -{pull}36233[#36233] +* Add cluster setting to enable/disable config migration {pull}36700[#36700] (issue: {issue}32905[#32905]) +* Enable the use of endpoints starting with `_ml` instead of `_xpack/ml` {pull}36373[#36373] (issue: {issue}36315[#36315]) +* Add audits when deprecation warnings occur while datafeeds start {pull}36233[#36233] * Add lazy parsing for DatafeedConfig:Aggs,Query {pull}36117[#36117] * Add support for rollup indexes in datafeeds {pull}34654[#34654] +Monitoring:: +* Make Exporters Async {pull}35765[#35765] (issue: {issue}35743[#35743]) +* Add cluster metadata to cluster_stats docs (#33860) {pull}34023[#34023] (issues: {issue}33860[#33860], {issue}34040[#34040]) + +Network:: +* Unify transport settings naming {pull}36623[#36623] +* Move compression config to ConnectionProfile {pull}35357[#35357] (issue: {issue}34483[#34483]) +* Simplify Transport Compression Setting {pull}34959[#34959] (issue: {issue}33844[#33844]) +* Allow to enable pings for specific remote clusters {pull}34753[#34753] (issues: {issue}30247[#30247], {issue}34405[#34405]) + +Packaging:: +* Introduce Docker images build {pull}36246[#36246] +* Move creation of temporary directory to Java {pull}36002[#36002] (issue: {issue}31003[#31003]) +* Update procrun executables to version 1.1.0 {pull}35147[#35147] + +Plugins:: +* Plugin install: don't print download progress in batch mode {pull}36361[#36361] + +Recovery:: +* Exposed engine must include all operations below global checkpoint during rollback {pull}36159[#36159] (issue: {issue}32867[#32867]) +* Use soft-deleted docs to resolve strategy for engine operation {pull}35230[#35230] (issues: {issue}0[#0], {issue}1[#1], {issue}33656[#33656], {issue}34474[#34474]) +* Put a fake allocation id on allocate stale primary command {pull}34140[#34140] (issue: {issue}33432[#33432]) + +Rollup:: +* Add non-X-Pack centric rollup endpoints {pull}36383[#36383] (issues: {issue}35958[#35958], {issue}35962[#35962]) +* Add more diagnostic stats to job {pull}35471[#35471] +* Add default fields to job configs {pull}34831[#34831] +* Add `wait_for_completion` option to StopRollupJob API {pull}34811[#34811] (issue: {issue}34574[#34574]) + +Scripting:: +* Update joda compat methods to use compat class {pull}36654[#36654] +* [Painless] Add boxed type to boxed type casts for method/return {pull}36571[#36571] +* [Painless] Add def to boxed type casts {pull}36506[#36506] +* [Scripting] Make Max Script Length Setting Dynamic {pull}35184[#35184] (issue: {issue}23209[#23209]) +* [Painless] Add instance bindings {pull}34410[#34410] + +Search:: +* Use SearchRequest copy constructor in ExpandSearchPhase {pull}36772[#36772] (issue: {issue}36641[#36641]) +* Add copy constructor to SearchRequest {pull}36641[#36641] (issue: {issue}32125[#32125]) +* Add raw sort values to SearchSortValues transport serialization {pull}36617[#36617] (issue: {issue}32125[#32125]) +* Warn when using `use_dis_max` in `multi_match` {pull}36614[#36614] (issue: {issue}36488[#36488]) +* Add sort and collapse info to SearchHits transport serialization {pull}36555[#36555] (issue: {issue}32125[#32125]) +* Add default methods to DocValueFormat {pull}36480[#36480] +* Extend field caps API to mark meta fields {pull}36309[#36309] +* Respect indices options on _msearch {pull}35887[#35887] +* Allow efficient can_match phases on frozen indices {pull}35431[#35431] (issues: {issue}34352[#34352], {issue}34357[#34357]) +* Apply `ignore_throttled` also to concrete indices {pull}35335[#35335] (issue: {issue}34354[#34354]) +* Adapt field limit deprecation warning {pull}35302[#35302] (issue: {issue}35284[#35284]) +* Upgrade 6.x to lucene-7.6.0-snapshot-f9598f335b {pull}35225[#35225] +* Prevent throttled indices to be searched through wildcards by default {pull}34354[#34354] (issues: {issue}33732[#33732], {issue}34352[#34352]) +* check for null argument is already done in splitStringByCommaToArray {pull}34268[#34268] +* has_parent builder: exception message/param fix {pull}31182[#31182] + +Security:: +* Option to use endpoints starting with _security {pull}36379[#36379] (issue: {issue}36293[#36293]) +* Make credentials mandatory when launching xpack/migrate {pull}36197[#36197] (issues: {issue}29847[#29847], {issue}33972[#33972]) +* Make credentials mandatory when launching x-pack/migrate {pull}33972[#33972] (issue: {issue}29847[#29847]) + +Snapshot/Restore:: +* Allow Parallel Restore Operations {pull}36397[#36397] +* Repo creation out of ClusterStateTask {pull}36157[#36157] (issue: {issue}9488[#9488]) +* Add read-only repository verification {pull}35731[#35731] (issue: {issue}35703[#35703]) + +SQL:: +* Make `FULL` non-reserved keyword in the grammar {pull}37377[#37377] (issue: {issue}37376[#37376]) +* Extend the ODBC metric by differentiating between 32 and 64bit platforms {pull}36753[#36753] (issue: {issue}36740[#36740]) +* Fix wrong appliance of StackOverflow limit for IN {pull}36724[#36724] (issue: {issue}36592[#36592]) +* Introduce NOW/CURRENT_TIMESTAMP function {pull}36562[#36562] (issue: {issue}36534[#36534]) +* Move requests' parameters to requests JSON body {pull}36149[#36149] (issue: {issue}35992[#35992]) +* Make INTERVAL millis optional {pull}36043[#36043] (issue: {issue}36032[#36032]) +* Implement data type verification for conditionals {pull}35916[#35916] (issue: {issue}35907[#35907]) +* Implement GREATEST and LEAST functions {pull}35879[#35879] (issue: {issue}35878[#35878]) +* Implement null safe equality operator `<=>` {pull}35873[#35873] (issue: {issue}35871[#35871]) +* SYS COLUMNS returns ODBC specific schema {pull}35870[#35870] (issue: {issue}35376[#35376]) +* Polish grammar for intervals {pull}35853[#35853] +* Add filtering to SYS TYPES {pull}35852[#35852] (issue: {issue}35342[#35342]) +* Implement NULLIF(expr1, expr2) function {pull}35826[#35826] (issue: {issue}35818[#35818]) +* Lock down JDBC driver {pull}35798[#35798] (issue: {issue}35437[#35437]) +* Implement NVL(expr1, expr2) {pull}35794[#35794] (issue: {issue}35782[#35782]) +* Implement ISNULL(expr1, expr2) {pull}35793[#35793] (issue: {issue}35781[#35781]) +* Implement IFNULL variant of COALESCE {pull}35762[#35762] (issue: {issue}35749[#35749]) +* XPack FeatureSet functionality {pull}35725[#35725] (issue: {issue}34821[#34821]) +* Perform lazy evaluation of mismatched mappings {pull}35676[#35676] (issues: {issue}35659[#35659], {issue}35675[#35675]) +* Improve validation of unsupported fields {pull}35675[#35675] (issue: {issue}35673[#35673]) +* Move internals from Joda to java.time {pull}35649[#35649] (issue: {issue}35633[#35633]) +* Improve CircuitBreaker logic for SqlParser {pull}35300[#35300] (issue: {issue}35299[#35299]) +* Upgrade jline to version 3.8.2 {pull}35288[#35288] +* New SQL CLI logo {pull}35261[#35261] +* Introduce Coalesce function {pull}35253[#35253] (issue: {issue}35060[#35060]) +* Optimizer rule for folding nullable expressions {pull}35080[#35080] (issue: {issue}34826[#34826]) +* Improve painless script generated from `IN` {pull}35055[#35055] (issue: {issue}34750[#34750]) +* Implement CAST between STRING and IP {pull}34949[#34949] (issue: {issue}34799[#34799]) +* Fix function args verification and error msgs {pull}34926[#34926] (issues: {issue}33469[#33469], {issue}34752[#34752]) +* Handle X-Pack or X-Pack SQL not being available in a more graceful way {pull}34736[#34736] (issue: {issue}30009[#30009]) + +Stats:: +* Handle OS pretty name on old OS without OS release {pull}35453[#35453] (issue: {issue}35440[#35440]) +* Add more detailed OS name on Linux {pull}35352[#35352] + +Task Management:: +* Periodically try to reassign unassigned persistent tasks {pull}36069[#36069] (issue: {issue}35792[#35792]) +* Only require task permissions {pull}35667[#35667] (issue: {issue}35573[#35573]) +* Retry if task can't be written {pull}35054[#35054] (issue: {issue}33764[#33764]) + + [[bug-6.6.0]] [float] === Bug fixes +Aggregations:: +* Fix MultiValuesSourceFieldConfig toXContent {pull}36525[#36525] (issue: {issue}36474[#36474]) +* Cache the score of the parent document in the nested agg {pull}36019[#36019] (issues: {issue}34555[#34555], {issue}35985[#35985]) +* Correct implemented interface of ParsedReverseNested {pull}35455[#35455] (issue: {issue}35449[#35449]) +* Handle IndexOrDocValuesQuery in composite aggregation {pull}35392[#35392] +* Preserve `format` when aggregation contains unmapped date fields {pull}35254[#35254] (issue: {issue}31760[#31760]) +* Check self references in metric agg after last doc collection (#33593) {pull}34001[#34001] + +Audit:: +* Fix origin.type for connection_* events {pull}36410[#36410] +* Fix deprecation of audit log settings {pull}36175[#36175] (issue: {issue}36162[#36162]) +* Fix IndexAuditTrail rolling restart on rollover edge {pull}35988[#35988] (issue: {issue}33867[#33867]) + +Authentication:: +* Fix NPE in CachingUsernamePasswordRealm {pull}36953[#36953] (issue: {issue}36951[#36951]) +* Add support for Kerberos V5 Oid {pull}35764[#35764] (issue: {issue}34763[#34763]) + +Build:: +* Use explicit deps on test tasks for check {pull}36325[#36325] +* Fix jdbc jar pom to not include deps {pull}36036[#36036] (issue: {issue}32014[#32014]) +* Fix official plugins list {pull}35661[#35661] (issue: {issue}35623[#35623]) + +Circuit Breakers:: +* Modify `BigArrays` to take name of circuit breaker {pull}36461[#36461] (issue: {issue}31435[#31435]) + +Core:: +* Revert back to joda's multi date formatters {pull}36814[#36814] (issues: {issue}36447[#36447], {issue}36602[#36602]) +* Fix CompositeBytesReference#slice to not throw AIOOBE with legal offsets. {pull}35955[#35955] (issue: {issue}35950[#35950]) +* Suppress CachedTimeThread in hot threads output {pull}35558[#35558] (issue: {issue}23175[#23175]) +* Upgrade to Joda 2.10.1 {pull}35410[#35410] (issue: {issue}33749[#33749]) +* XContent: Check for bad parsers {pull}34561[#34561] (issue: {issue}34351[#34351]) + +Cross-cluster replication:: +* Add fatal_exception field for ccr stats in monitoring mapping {pull}37563[#37563] +* When removing an AutoFollower also mark it as removed. {pull}37402[#37402] (issue: {issue}36761[#36761]) +* Make shard follow tasks more resilient for restarts {pull}37239[#37239] (issue: {issue}37231[#37231]) +* Resume follow Api should not require a request body {pull}37217[#37217] (issue: {issue}37022[#37022]) +* Report error if auto follower tries auto follow a leader index with soft deletes disabled {pull}36886[#36886] (issue: {issue}33007[#33007]) +* Remote cluster license checker and no license info. {pull}36837[#36837] (issue: {issue}36815[#36815]) +* Make CCR resilient against missing remote cluster connections {pull}36682[#36682] (issues: {issue}36255[#36255], {issue}36667[#36667]) +* Fix follow stats API's follower index filtering feature {pull}36647[#36647] +* AutoFollowCoordinator and follower index already created {pull}36540[#36540] (issue: {issue}33007[#33007]) +* AutoFollowCoordinator should tolerate that auto follow patterns may be removed {pull}35945[#35945] (issue: {issue}35937[#35937]) +* Only auto follow indices when all primary shards have started {pull}35814[#35814] (issue: {issue}35480[#35480]) +* Avoid NPE in follower stats when no tasks metadata {pull}35802[#35802] +* Fix the names of CCR stats endpoints in usage API {pull}35438[#35438] + +CRUD:: +* Synchronize WriteReplicaResult callbacks {pull}36770[#36770] +* Fix DeleteRequest validation for nullable or empty id/type {pull}35314[#35314] (issue: {issue}35297[#35297]) +* Fix UpdateRequest.fromXContent {pull}35257[#35257] (issues: {issue}29293[#29293], {issue}34069[#34069]) + +Distributed:: +* Combine the execution of an exclusive replica operation with primary term update {pull}36116[#36116] (issue: {issue}35850[#35850]) +* ActiveShardCount should not fail when closing the index {pull}35936[#35936] + +Engine:: +* Wrap can_match reader with ElasticsearchDirectoryReader {pull}35857[#35857] +* Copy checkpoint atomically when rolling generation {pull}35407[#35407] + +Features:: +* Handle Null in FetchSourceContext#fetchSource {pull}36839[#36839] (issue: {issue}29293[#29293]) + +Geo:: +* More robust handling of ignore_malformed in geoshape parsing {pull}35603[#35603] (issues: {issue}34047[#34047], {issue}34498[#34498]) +* Better handling of malformed geo_points {pull}35554[#35554] (issue: {issue}35419[#35419]) +* Enables coerce support in WKT polygon parser {pull}35414[#35414] (issue: {issue}35059[#35059]) +* Further improve robustness of geo shape parser for malformed shapes {pull}34498[#34498] (issues: {issue}31449[#31449], {issue}34047[#34047]) + +Index lifecycle management:: +* Remove `indexing_complete` when removing policy {pull}36620[#36620] + +Index APIs:: +* Fix duplicate phrase in shrink/split error message {pull}36734[#36734] (issue: {issue}36729[#36729]) +* Make XContentBuilder in AliasActions build `is_write_index` field {pull}35071[#35071] +* Raise a 404 exception when document source is not found (#33384) {pull}34083[#34083] (issue: {issue}33384[#33384]) + +Ingest:: +* Fix on_failure with Drop processor {pull}36686[#36686] (issue: {issue}36151[#36151]) +* Support default pipelines + bulk upserts {pull}36618[#36618] (issue: {issue}36219[#36219]) +* Support default pipeline through an alias {pull}36231[#36231] (issue: {issue}35817[#35817]) +* Dot_expander_processor prevent null add/append to source document {pull}35106[#35106] + Machine Learning:: * Fix cause of "Sample out of bounds" error message. {ml-pull}335[#335] -* Fix hang when closing a job or creating a forecast. This problem occurs if you -created a forecast for a large job and temporary storage was not cleaned up. -{ml-pull}352[#352] (issue: {ml-issue}350[#350]) -* Wait for autodetect to be ready in the datafeed {pull}37349[#37349] -(issues: {issue}36810[#36810], {issue}37227[#37227]) -* Stop datafeeds when their jobs are stale {pull}37227[#37227] -(issue: {issue}36810[#36810]) -* Order get job stats API response by job id {pull}36841[#36841] -(issue: {issue}36683[#36683]) \ No newline at end of file +* Fix hang when closing a job or creating a forecast. This problem occurs if you created a forecast for a large job and temporary storage was not cleaned up. {ml-pull}352[#352] (issue: {ml-issue}350[#350]) +* Wait for autodetect to be ready in the datafeed {pull}37349[#37349] (issues: {issue}36810[#36810], {issue}37227[#37227]) +* Stop datafeeds when their jobs are stale {pull}37227[#37227] (issue: {issue}36810[#36810]) +* Order get job stats API response by job id {pull}36841[#36841] (issue: {issue}36683[#36683]) + +Mapping:: +* Make sure to accept empty unnested mappings in create index requests. {pull}37089[#37089] + +Monitoring:: +* Add missing error type mapping for apm-server {pull}36178[#36178] (issue: {issue}1614[#1614]) + +Network:: +* Do not resolve addresses in remote connection info {pull}36671[#36671] (issue: {issue}35658[#35658]) +* Always compress based on the settings {pull}36522[#36522] (issue: {issue}36399[#36399]) +* http.publish_host should contain CNAME {pull}32806[#32806] (issue: {issue}22029[#22029]) + +Packaging:: +* Fix error message when package install fails due to missing Java {pull}36077[#36077] (issue: {issue}31845[#31845]) +* Add missing entries to conffiles {pull}35810[#35810] (issue: {issue}35691[#35691]) + +Ranking:: +* QueryRescorer should keep the window size when rewriting {pull}36836[#36836] +* Fix a bug in function_score queries where we use the wrong boost_mode. {pull}35148[#35148] (issue: {issue}35123[#35123]) + +Recovery:: +* Register ResyncTask.Status as a NamedWriteable {pull}36610[#36610] + +Rollup:: +* Fix Rollup's metadata parser {pull}36791[#36791] (issue: {issue}36726[#36726]) +* Fix rollup search statistics {pull}36674[#36674] +* Improve handling of failures on first search {pull}35269[#35269] +* Proactively resolve index patterns in RollupSearch endoint {pull}34930[#34930] (issue: {issue}34828[#34828]) + +Scripting:: +* Properly support no-offset date formatting {pull}36316[#36316] (issue: {issue}36306[#36306]) +* [Painless] Generate Bridge Methods {pull}36097[#36097] +* Fix serialization bug in painless execute api request {pull}36075[#36075] (issue: {issue}36050[#36050]) +* Actually add joda time back to whitelist {pull}35965[#35965] (issue: {issue}35915[#35915]) +* Add back joda to whitelist {pull}35915[#35915] (issue: {issue}35913[#35913]) +* [Painless] Partially fixes def boxed types casting {pull}35563[#35563] (issue: {issue}35351[#35351]) +* Add back lookup vars in score script {pull}34833[#34833] + +Search:: +* Inner hits fail to propagate doc-value format. (#36310) {pull}36355[#36355] (issue: {issue}36310[#36310]) +* Fix custom AUTO issue with Fuzziness#toXContent {pull}35807[#35807] (issue: {issue}33462[#33462]) +* Fix analyzed prefix query in query_string {pull}35756[#35756] (issue: {issue}31702[#31702]) +* Fix problem with MatchNoDocsQuery in disjunction queries {pull}35726[#35726] (issue: {issue}34708[#34708]) +* Fix phrase_slop in query_string query {pull}35533[#35533] (issue: {issue}35125[#35125]) +* Add a More Like This query routing requirement check (#29678) {pull}33974[#33974] + +Security:: +* Remove license state listeners on closables {pull}36308[#36308] (issues: {issue}33328[#33328], {issue}35627[#35627], {issue}35628[#35628]) + +Settings:: +* Fix setting by time unit {pull}37192[#37192] +* Fix handling of fractional byte size value settings {pull}37172[#37172] +* Fix handling of fractional time value settings {pull}37171[#37171] +* Correctly Identify Noop Updates {pull}36560[#36560] (issue: {issue}36496[#36496]) + +Snapshot/Restore:: +* Improve Resilience SnapshotShardService {pull}36113[#36113] (issue: {issue}32265[#32265]) +* Register Azure max_retries setting {pull}35286[#35286] +* Restore Should Check Min. Version {pull}34676[#34676] (issue: {issue}34264[#34264]) + +SQL:: +* Fix issue with field names containing "." {pull}37364[#37364] (issue: {issue}37128[#37128]) +* Proper handling of COUNT(field_name) and COUNT(DISTINCT field_name) {pull}37254[#37254] (issue: {issue}30285[#30285]) +* Fix COUNT DISTINCT filtering {pull}37176[#37176] (issue: {issue}37086[#37086]) +* Fix issue with wrong NULL optimization {pull}37124[#37124] (issue: {issue}35872[#35872]) +* Count distinct doesn't recognize that a string field has a keyword version {pull}37176[#37176] (issue: {issue}37087[#37087]) +* Handle the bwc Joda ZonedDateTime scripting class in Painless {pull}37024[#37024] (issue: {issue}37023[#37023]) +* Fix bug regarding histograms usage in scripting {pull}36866[#36866] +* Fix issue with always false filter involving functions {pull}36830[#36830] (issue: {issue}35980[#35980]) +* Protocol returns ISO 8601 String formatted dates instead of Long for JDBC/ODBC requests {pull}36800[#36800] (issue: {issue}36756[#36756]) +* Fix translation of LIKE/RLIKE keywords {pull}36672[#36672] (issues: {issue}36039[#36039], {issue}36584[#36584]) +* Scripting support for casting functions CAST and CONVERT {pull}36640[#36640] (issue: {issue}36061[#36061]) +* Fix translation to painless for conditionals {pull}36636[#36636] (issue: {issue}36631[#36631]) +* Concat should be always not nullable {pull}36601[#36601] (issue: {issue}36169[#36169]) +* Fix MOD() for long and integer arguments {pull}36599[#36599] (issue: {issue}36364[#36364]) +* Fix issue with complex HAVING and GROUP BY ordinal {pull}36594[#36594] (issue: {issue}36059[#36059]) +* Be lenient for tests involving comparison to H2 but strict for csv spec tests {pull}36498[#36498] (issue: {issue}36483[#36483]) +* Non ISO 8601 versions of DAY_OF_WEEK and WEEK_OF_YEAR functions {pull}36358[#36358] (issue: {issue}36263[#36263]) +* Do not ignore all fields whose names start with underscore {pull}36214[#36214] (issue: {issue}36206[#36206]) +* SUM() and LIKE condition doesn't work anymore {pull}36672[#36672] (issue: {issue}36161[#36161]) +* Fix issue with wrong data type for scripted Grouping keys {pull}35969[#35969] (issue: {issue}35662[#35662]) +* Fix translation of math functions to painless {pull}35910[#35910] (issue: {issue}35654[#35654]) +* Build: Fix jdbc jar to include deps {pull}35602[#35602] +* Fix query translation for scripted queries {pull}35408[#35408] (issue: {issue}35232[#35232]) +* Clear the cursor if nested inner hits are enough to fulfill the query required limits {pull}35398[#35398] (issue: {issue}35176[#35176]) +* Fix null handling for AND and OR in SELECT {pull}35277[#35277] (issue: {issue}35240[#35240]) +* Handle null literal for AND and OR in `WHERE` {pull}35236[#35236] (issue: {issue}35088[#35088]) +* Introduce NotEquals node to simplify expressions {pull}35234[#35234] (issues: {issue}35210[#35210], {issue}35233[#35233]) +* Introduce IsNull node to simplify expressions {pull}35206[#35206] (issues: {issue}34876[#34876], {issue}35171[#35171]) +* Handle wildcard expansion on incorrect fields {pull}35134[#35134] (issue: {issue}35092[#35092]) +* Fix null handling for IN => painless script {pull}35124[#35124] (issues: {issue}35108[#35108], {issue}35122[#35122]) +* Register missing processors {pull}35121[#35121] (issue: {issue}35119[#35119]) +* Fix NPE thrown if HAVING filter evals to null {pull}35108[#35108] (issue: {issue}35107[#35107]) +* Proper handling of nested fields at the beginning of the columns list {pull}35068[#35068] (issue: {issue}32951[#32951]) +* Fix incorrect AVG data type {pull}34948[#34948] (issue: {issue}33773[#33773]) +* Add `CAST` and `CONVERT` to `SHOW FUNCTIONS` {pull}34940[#34940] (issue: {issue}34939[#34939]) +* Handle aggregation for null group {pull}34916[#34916] (issue: {issue}34896[#34896]) +* Provide null-safe scripts for Not and Neg {pull}34877[#34877] (issue: {issue}34848[#34848]) +* Return error with ORDER BY on non-grouped. {pull}34855[#34855] (issue: {issue}34590[#34590]) +* Fix negation of equals comparison. {pull}34680[#34680] (issue: {issue}34558[#34558]) +* CAST doesn't work in ORDER BY. {pull}36640[#36640] (issue: {issue}34557[#34557]) +* COUNT(column) takes into account NULLs {pull}37254[#37254] (issue: {issue}34549[#34549]) + +Watcher:: +* Watcher accounts constructed lazily {pull}36656[#36656] +* Only trigger a watch if new or schedule/changed {pull}35908[#35908] +* Fix Watcher NotificationService's secure settings {pull}35610[#35610] (issue: {issue}35378[#35378]) +* Fix integration tests to ensure correct start/stop of Watcher {pull}35271[#35271] (issues: {issue}29877[#29877], {issue}30705[#30705], {issue}33291[#33291], {issue}34448[#34448], {issue}34462[#34462]) + + +[[regression-6.6.0]] +[float] +=== Regressions + +Scripting:: +* Use Number as a return value for BucketAggregationScript {pull}35653[#35653] (issue: {issue}35351[#35351]) + + +[[upgrade-6.6.0]] +[float] +=== Upgrades + +Network:: +* Upgrade Netty 4.3.32.Final {pull}36102[#36102] (issue: {issue}35360[#35360]) + From 515379449a0b90fd8171d8d4c71d31b7cc6669a1 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 23 Jan 2019 14:23:05 -0800 Subject: [PATCH 075/121] [DOCS] Breaking change for Watcher metric stats names (#37728) --- docs/reference/migration/migrate_6_6.asciidoc | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/docs/reference/migration/migrate_6_6.asciidoc b/docs/reference/migration/migrate_6_6.asciidoc index 95e34b70a3777..33870f0139618 100644 --- a/docs/reference/migration/migrate_6_6.asciidoc +++ b/docs/reference/migration/migrate_6_6.asciidoc @@ -7,13 +7,35 @@ This section discusses the changes that you need to be aware of when migrating your application to Elasticsearch 6.6. -* <> +* <> * <> * <> * <> See also <> and <>. +[float] +[[breaking_66_api_changes]] +=== API changes + +[float] +==== Machine learning API changes + +The get jobs API and get job stats API can retrieve a maximum of 10,000 jobs. +Likewise, the get datafeeds API and get datafeed stats API can retrieve a +maximum of 10,000 datafeeds. Prior to version 6.6, there were no limits on the +results from these APIs. + +[float] +==== {watcher} API changes + +If you used the `metric` parameter with the +{ref}/watcher-api-stats.html[stats API], the response contained incorrect labels, +which are fixed in 6.6 and later releases. If you choose to retrieve +`queued_watches` metrics, it now returns a `queued_watches` list instead of a `current_watches` list. Likewise, if you retrieve `pending_watches` metrics, it +returns a `current_watches` list instead of a `queued_watches` list. The +`pending_watches` metric is deprecated; use `current_watches` instead. + [float] [[breaking_66_search_changes]] === Search changes @@ -142,12 +164,3 @@ previously created indexes. The following type parameters are deprecated for the `geo_shape` field type: `tree`, `precision`, `tree_levels`, `distance_error_pct`, `points_only`, and `strategy`. They will be removed in a future version. - -[float] -[[breaking_66_ml_changes]] -=== Machine learning changes - -The get jobs API and get job stats API can retrieve a maximum of 10,000 jobs. -Likewise, the get datafeeds API and get datafeed stats API can retrieve a -maximum of 10,000 datafeeds. Prior to version 6.6, there were no limits on the -results from these APIs. From a9861f45c9857d45a4e452b954a6b08989c37d2c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 23 Jan 2019 17:39:13 -0800 Subject: [PATCH 076/121] Use explicit version for build-tools in example plugin integ tests (#37792) The example plugins are currently built within the build-tools integ tests as a means to ensure the gradle plugin works for external plugin builds. These tests generate a dummy build.gradle, and a dummy local maven repository to find the local builds dependencies in. Currently that build-tools dependency uses "+" as the version. However, this allows gradle to find the "latest" version, and unfortunately gradle has its own plugin repository which is apparently connected to jcenter. This recently triggered a flood of CI failures when jcenter suddenly pulled alpha2, and all builds started trying to use that instead of the locally built build-tools. This commit uses the explicit version of build-tools that was build locally, which will cause resolution to stop when the local repo is first checked. --- .../org/elasticsearch/gradle/BuildExamplePluginsIT.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/test/java/org/elasticsearch/gradle/BuildExamplePluginsIT.java b/buildSrc/src/test/java/org/elasticsearch/gradle/BuildExamplePluginsIT.java index 2a2304182c80e..c21adf22bcbe9 100644 --- a/buildSrc/src/test/java/org/elasticsearch/gradle/BuildExamplePluginsIT.java +++ b/buildSrc/src/test/java/org/elasticsearch/gradle/BuildExamplePluginsIT.java @@ -40,13 +40,15 @@ public class BuildExamplePluginsIT extends GradleIntegrationTestCase { - private static List EXAMPLE_PLUGINS = Collections.unmodifiableList( + private static final List EXAMPLE_PLUGINS = Collections.unmodifiableList( Arrays.stream( Objects.requireNonNull(System.getProperty("test.build-tools.plugin.examples")) .split(File.pathSeparator) ).map(File::new).collect(Collectors.toList()) ); + private static final String BUILD_TOOLS_VERSION = Objects.requireNonNull(System.getProperty("test.version_under_test")); + @Rule public TemporaryFolder tmpDir = new TemporaryFolder(); @@ -96,7 +98,8 @@ public void testCurrentExamplePlugin() throws IOException { private void adaptBuildScriptForTest() throws IOException { // Add the local repo as a build script URL so we can pull in build-tools and apply the plugin under test - // + is ok because we have no other repo and just want to pick up latest + // we need to specify the exact version of build-tools because gradle automatically adds its plugin portal + // which appears to mirror jcenter, opening us up to pulling a "later" version of build-tools writeBuildScript( "buildscript {\n" + " repositories {\n" + @@ -105,7 +108,7 @@ private void adaptBuildScriptForTest() throws IOException { " }\n" + " }\n" + " dependencies {\n" + - " classpath \"org.elasticsearch.gradle:build-tools:+\"\n" + + " classpath \"org.elasticsearch.gradle:build-tools:" + BUILD_TOOLS_VERSION + "\"\n" + " }\n" + "}\n" ); From 529f3ec52f9bc3720e3bedb1b3b6177b41e2a71d Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Thu, 24 Jan 2019 15:03:49 +0200 Subject: [PATCH 077/121] SQL: Improve handling of invalid args for PERCENTILE/PERCENTILE_RANK (#37803) Improve the Exception and the error message returned when 2nd argument of PERCENTILE and PERCENTILE_RANK is not a constant. --- .../expression/function/aggregate/Percentile.java | 6 ++++++ .../function/aggregate/PercentileRank.java | 6 ++++++ .../analyzer/VerifierErrorMessagesTests.java | 12 +++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java index 6e644fb4f751c..c0118e96833f2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java @@ -16,6 +16,7 @@ import java.util.List; import static java.util.Collections.singletonList; +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; public class Percentile extends NumericAggregate implements EnclosedAgg { @@ -41,6 +42,11 @@ public Percentile replaceChildren(List newChildren) { @Override protected TypeResolution resolveType() { + if (!percent.foldable()) { + return new TypeResolution(format(null, "2nd argument of PERCENTILE must be a constant, received [{}]", + Expressions.name(percent))); + } + TypeResolution resolution = super.resolveType(); if (TypeResolution.TYPE_RESOLVED.equals(resolution)) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java index f01dad8800ccf..41a3d51e50e6e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java @@ -16,6 +16,7 @@ import java.util.List; import static java.util.Collections.singletonList; +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; public class PercentileRank extends AggregateFunction implements EnclosedAgg { @@ -41,6 +42,11 @@ public Expression replaceChildren(List newChildren) { @Override protected TypeResolution resolveType() { + if (!value.foldable()) { + return new TypeResolution(format(null, "2nd argument of PERCENTILE_RANK must be a constant, received [{}]", + Expressions.name(value))); + } + TypeResolution resolution = super.resolveType(); if (resolution.unresolved()) { return resolution; diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index 886060a96a623..03d2285836614 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -529,4 +529,14 @@ public void testAggsInHistogram() { assertEquals("1:47: Cannot use an aggregate [MAX] for grouping", error("SELECT MAX(date) FROM test GROUP BY HISTOGRAM(MAX(int), 1)")); } -} \ No newline at end of file + + public void testErrorMessageForPercentileWithSecondArgBasedOnAField() { + assertEquals("1:8: 2nd argument of PERCENTILE must be a constant, received [ABS(int)]", + error("SELECT PERCENTILE(int, ABS(int)) FROM test")); + } + + public void testErrorMessageForPercentileRankWithSecondArgBasedOnAField() { + assertEquals("1:8: 2nd argument of PERCENTILE_RANK must be a constant, received [ABS(int)]", + error("SELECT PERCENTILE_RANK(int, ABS(int)) FROM test")); + } +} From 997ebfb96bf487812275af7ed90baf9cec5ac529 Mon Sep 17 00:00:00 2001 From: niloct Date: Thu, 24 Jan 2019 13:24:42 -0200 Subject: [PATCH 078/121] Update update-by-query.asciidoc (#37555) Similar fix as #37370. --- docs/reference/docs/update-by-query.asciidoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/reference/docs/update-by-query.asciidoc b/docs/reference/docs/update-by-query.asciidoc index b30ba75c25bf5..764be89ec6f03 100644 --- a/docs/reference/docs/update-by-query.asciidoc +++ b/docs/reference/docs/update-by-query.asciidoc @@ -444,8 +444,9 @@ POST _tasks/r1A2WoRbTwKZ516z6NEs5A:36619/_cancel The task ID can be found using the <>. -Cancellation should happen quickly but might take a few seconds. The task status -API above will continue to list the task until it is wakes to cancel itself. +Cancellation should happen quickly but might take a few seconds. The task status +API above will continue to list the update by query task until this task checks +that it has been cancelled and terminates itself. [float] From 0edaf2b6c1c541d61587edb1fe6fae3b50a7126d Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Thu, 24 Jan 2019 18:40:20 +0200 Subject: [PATCH 079/121] SQL: Fix issue with complex expression as args of PERCENTILE/_RANK (#37102) When the arguements of PERCENTILE and PERCENTILE_RANK can be folded, the `ConstantFolding` rule kicks in and calls the `replaceChildren()` method on `InnerAggregate` which is created from the aggregation rules of the `Optimizerz. `InnerAggregate` in turn, cannot implement the method as the logic of creating a new `InnerAggregate` instance from a list of `Expression`s resides in the Optimizer. So, instead, `ConstantFolding` should be applied before any of the aggregations related rules. Fixes: #37099 --- .../sql/qa/src/main/resources/agg.csv-spec | 4 ++-- .../xpack/sql/optimizer/Optimizer.java | 2 +- .../xpack/sql/planner/QueryFolderTests.java | 21 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec index bdb94321b76d5..d1b1a0e0e8866 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec @@ -3,7 +3,7 @@ // singlePercentileWithoutComma -SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_emp GROUP BY gender; +SELECT gender, PERCENTILE(emp_no, 90 + 7) p1 FROM test_emp GROUP BY gender; gender:s | p1:d null |10019.0 @@ -48,7 +48,7 @@ M |10084.349 |10093.502 ; percentileRank -SELECT gender, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp GROUP BY gender; +SELECT gender, PERCENTILE_RANK(emp_no, 10000 + 25) rank FROM test_emp GROUP BY gender; gender:s | rank:d null |100.0 diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java index 706fb4853626d..aeaca9ea4ddd6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java @@ -159,7 +159,7 @@ protected Iterable.Batch> batches() { Batch label = new Batch("Set as Optimized", Limiter.ONCE, new SetAsOptimized()); - return Arrays.asList(aggregate, operators, local, label); + return Arrays.asList(operators, aggregate, local, label); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryFolderTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryFolderTests.java index c20f4e9d632af..16416d2965e4a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryFolderTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryFolderTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.sql.analysis.index.EsIndex; import org.elasticsearch.xpack.sql.analysis.index.IndexResolution; import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry; +import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute; import org.elasticsearch.xpack.sql.optimizer.Optimizer; import org.elasticsearch.xpack.sql.parser.SqlParser; import org.elasticsearch.xpack.sql.plan.physical.EsQueryExec; @@ -316,4 +317,24 @@ public void testConcatIsNotFoldedForNull() { assertEquals(1, ee.output().size()); assertThat(ee.output().get(0).toString(), startsWith("keyword{f}#")); } + + public void testFoldingOfPercentileSecondArgument() { + PhysicalPlan p = plan("SELECT PERCENTILE(int, 1 + 2) FROM test"); + assertEquals(EsQueryExec.class, p.getClass()); + EsQueryExec ee = (EsQueryExec) p; + assertEquals(1, ee.output().size()); + assertEquals(AggregateFunctionAttribute.class, ee.output().get(0).getClass()); + AggregateFunctionAttribute afa = (AggregateFunctionAttribute) ee.output().get(0); + assertThat(afa.propertyPath(), endsWith("[3.0]")); + } + + public void testFoldingOfPercentileRankSecondArgument() { + PhysicalPlan p = plan("SELECT PERCENTILE_RANK(int, 1 + 2) FROM test"); + assertEquals(EsQueryExec.class, p.getClass()); + EsQueryExec ee = (EsQueryExec) p; + assertEquals(1, ee.output().size()); + assertEquals(AggregateFunctionAttribute.class, ee.output().get(0).getClass()); + AggregateFunctionAttribute afa = (AggregateFunctionAttribute) ee.output().get(0); + assertThat(afa.propertyPath(), endsWith("[3.0]")); + } } From f96bc419b2846cd2d27970ff861ab05936320aae Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Fri, 25 Jan 2019 17:37:23 +0100 Subject: [PATCH 080/121] Remove statement about search and optimistic concurrency control from 6.6 docs (#37876) This turned out to not be true and is tackled by #37639 . That PR will be part of 6.7 --- docs/reference/docs/concurrency-control.asciidoc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/reference/docs/concurrency-control.asciidoc b/docs/reference/docs/concurrency-control.asciidoc index e695e6b5127c9..fe55303eb88b5 100644 --- a/docs/reference/docs/concurrency-control.asciidoc +++ b/docs/reference/docs/concurrency-control.asciidoc @@ -85,10 +85,6 @@ returns: -------------------------------------------------- // TESTRESPONSE[s/"_seq_no" : \d+/"_seq_no" : $body._seq_no/ s/"_primary_term" : 2/"_primary_term" : $body._primary_term/] - -Note: The <> can return the `_seq_no` and `_primary_term` -for each search hit by requesting the `_seq_no` and `_primary_term` <>. - The sequence number and the primary term uniquely identify a change. By noting down the sequence number and primary term returned, you can make sure to only change the document if no other change was made to it since you retrieved it. This From 9364954d8a425424d5efd5874722368cc3b54124 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 16 Jan 2019 13:17:10 -0500 Subject: [PATCH 081/121] Increase timeout for testAddNewReplicas We flush quite often in testAddNewReplicas to create the safe index commit with gaps in sequence numbers. This test is failing recently because CI is too slow to complete 5 small flushes in 10 seconds. This commit increases timeout for this test and also ensures to always terminate the background indexing. The latter is to eliminate unrelated failures if this test fails again. Closes #37183 --- .../RecoveryDuringReplicationTests.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java b/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java index 272ffebee7fe7..abe0f6b6198b9 100644 --- a/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java +++ b/server/src/test/java/org/elasticsearch/index/replication/RecoveryDuringReplicationTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; @@ -65,6 +66,7 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -692,24 +694,37 @@ public void testTransferMaxSeenAutoIdTimestampOnResync() throws Exception { } public void testAddNewReplicas() throws Exception { - try (ReplicationGroup shards = createGroup(between(0, 1))) { + AtomicBoolean stopped = new AtomicBoolean(); + List threads = new ArrayList<>(); + Runnable stopIndexing = () -> { + try { + stopped.set(true); + for (Thread thread : threads) { + thread.join(); + } + } catch (Exception e) { + throw new AssertionError(e); + } + }; + try (ReplicationGroup shards = createGroup(between(0, 1)); + Releasable ignored = stopIndexing::run) { shards.startAll(); - Thread[] threads = new Thread[between(1, 3)]; - AtomicBoolean isStopped = new AtomicBoolean(); boolean appendOnly = randomBoolean(); AtomicInteger docId = new AtomicInteger(); - for (int i = 0; i < threads.length; i++) { - threads[i] = new Thread(() -> { - while (isStopped.get() == false) { + int numThreads = between(1, 3); + for (int i = 0; i < numThreads; i++) { + Thread thread = new Thread(() -> { + while (stopped.get() == false) { try { + int nextId = docId.incrementAndGet(); if (appendOnly) { - String id = randomBoolean() ? Integer.toString(docId.incrementAndGet()) : null; + String id = randomBoolean() ? Integer.toString(nextId) : null; shards.index(new IndexRequest(index.getName(), "type", id).source("{}", XContentType.JSON)); } else if (frequently()) { - String id = Integer.toString(frequently() ? docId.incrementAndGet() : between(0, 10)); + String id = Integer.toString(frequently() ? nextId : between(0, nextId)); shards.index(new IndexRequest(index.getName(), "type", id).source("{}", XContentType.JSON)); } else { - String id = Integer.toString(between(0, docId.get())); + String id = Integer.toString(between(0, nextId)); shards.delete(new DeleteRequest(index.getName(), "type", id)); } if (randomInt(100) < 10) { @@ -720,17 +735,15 @@ public void testAddNewReplicas() throws Exception { } } }); - threads[i].start(); + threads.add(thread); + thread.start(); } - assertBusy(() -> assertThat(docId.get(), greaterThanOrEqualTo(50))); + assertBusy(() -> assertThat(docId.get(), greaterThanOrEqualTo(50)), 60, TimeUnit.SECONDS); // we flush quite often shards.getPrimary().sync(); IndexShard newReplica = shards.addReplica(); shards.recoverReplica(newReplica); - assertBusy(() -> assertThat(docId.get(), greaterThanOrEqualTo(100))); - isStopped.set(true); - for (Thread thread : threads) { - thread.join(); - } + assertBusy(() -> assertThat(docId.get(), greaterThanOrEqualTo(100)), 60, TimeUnit.SECONDS); // we flush quite often + stopIndexing.run(); assertBusy(() -> assertThat(getDocIdAndSeqNos(newReplica), equalTo(getDocIdAndSeqNos(shards.getPrimary())))); } } From 7b33cc1da8299da8ac9436fd044e53a838985475 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Thu, 24 Jan 2019 13:09:43 -0500 Subject: [PATCH 082/121] Adjust minRetainedSeqNo asssertion in CombinedDeletionPolicyTests In these tests, we initialize the retained_seq_no with NO_OPS_PERFORMED, thus we should verify that the min of the retained_seq_no is at least NO_OPS_PERFORMED not 0. Closes #35994 --- .../engine/CombinedDeletionPolicyTests.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/engine/CombinedDeletionPolicyTests.java b/server/src/test/java/org/elasticsearch/index/engine/CombinedDeletionPolicyTests.java index 76c71240bdb7e..a21ab03eaf74e 100644 --- a/server/src/test/java/org/elasticsearch/index/engine/CombinedDeletionPolicyTests.java +++ b/server/src/test/java/org/elasticsearch/index/engine/CombinedDeletionPolicyTests.java @@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicLong; import static java.util.Collections.singletonList; +import static org.elasticsearch.index.seqno.SequenceNumbers.NO_OPS_PERFORMED; import static org.elasticsearch.index.translog.TranslogDeletionPolicies.createTranslogDeletionPolicy; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.doAnswer; @@ -53,7 +54,7 @@ public class CombinedDeletionPolicyTests extends ESTestCase { public void testKeepCommitsAfterGlobalCheckpoint() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(); final int extraRetainedOps = between(0, 100); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, extraRetainedOps); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, extraRetainedOps); TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); CombinedDeletionPolicy indexPolicy = new CombinedDeletionPolicy(logger, translogPolicy, softDeletesPolicy, globalCheckpoint::get); @@ -90,14 +91,15 @@ public void testKeepCommitsAfterGlobalCheckpoint() throws Exception { } assertThat(translogPolicy.getMinTranslogGenerationForRecovery(), equalTo(translogGenList.get(keptIndex))); assertThat(translogPolicy.getTranslogGenerationOfLastCommit(), equalTo(lastTranslogGen)); - assertThat(softDeletesPolicy.getMinRetainedSeqNo(), equalTo( - Math.max(0, Math.min(getLocalCheckpoint(commitList.get(keptIndex)) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); + assertThat(softDeletesPolicy.getMinRetainedSeqNo(), + equalTo(Math.max(NO_OPS_PERFORMED, + Math.min(getLocalCheckpoint(commitList.get(keptIndex)) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); } public void testAcquireIndexCommit() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(); final int extraRetainedOps = between(0, 100); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, extraRetainedOps); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, extraRetainedOps); final UUID translogUUID = UUID.randomUUID(); TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); CombinedDeletionPolicy indexPolicy = new CombinedDeletionPolicy(logger, translogPolicy, softDeletesPolicy, globalCheckpoint::get); @@ -127,7 +129,7 @@ public void testAcquireIndexCommit() throws Exception { indexPolicy.onCommit(commitList); IndexCommit safeCommit = CombinedDeletionPolicy.findSafeCommitPoint(commitList, globalCheckpoint.get()); assertThat(softDeletesPolicy.getMinRetainedSeqNo(), equalTo( - Math.max(0, Math.min(getLocalCheckpoint(safeCommit) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); + Math.max(NO_OPS_PERFORMED, Math.min(getLocalCheckpoint(safeCommit) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); // Captures and releases some commits int captures = between(0, 5); for (int n = 0; n < captures; n++) { @@ -158,7 +160,8 @@ public void testAcquireIndexCommit() throws Exception { assertThat(translogPolicy.getTranslogGenerationOfLastCommit(), equalTo(Long.parseLong(commitList.get(commitList.size() - 1).getUserData().get(Translog.TRANSLOG_GENERATION_KEY)))); assertThat(softDeletesPolicy.getMinRetainedSeqNo(), equalTo( - Math.max(0, Math.min(getLocalCheckpoint(commitList.get(safeIndex)) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); + Math.max(NO_OPS_PERFORMED, + Math.min(getLocalCheckpoint(commitList.get(safeIndex)) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); } snapshottingCommits.forEach(indexPolicy::releaseCommit); globalCheckpoint.set(randomLongBetween(lastMaxSeqNo, Long.MAX_VALUE)); @@ -172,12 +175,12 @@ public void testAcquireIndexCommit() throws Exception { assertThat(translogPolicy.getTranslogGenerationOfLastCommit(), equalTo(lastTranslogGen)); IndexCommit safeCommit = CombinedDeletionPolicy.findSafeCommitPoint(commitList, globalCheckpoint.get()); assertThat(softDeletesPolicy.getMinRetainedSeqNo(), equalTo( - Math.max(0, Math.min(getLocalCheckpoint(safeCommit) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); + Math.max(NO_OPS_PERFORMED, Math.min(getLocalCheckpoint(safeCommit) + 1, globalCheckpoint.get() + 1 - extraRetainedOps)))); } public void testLegacyIndex() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, 0); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, 0); final UUID translogUUID = UUID.randomUUID(); TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); @@ -213,7 +216,7 @@ public void testLegacyIndex() throws Exception { public void testKeepSingleNoOpsCommits() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(randomLong()); final UUID translogUUID = UUID.randomUUID(); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, 0); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, 0); TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); CombinedDeletionPolicy indexPolicy = new CombinedDeletionPolicy(logger, translogPolicy, softDeletesPolicy, globalCheckpoint::get); @@ -264,7 +267,7 @@ public void testKeepSingleNoOpsCommits() throws Exception { public void testDeleteInvalidCommits() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(randomNonNegativeLong()); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, 0); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, 0); TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); CombinedDeletionPolicy indexPolicy = new CombinedDeletionPolicy(logger, translogPolicy, softDeletesPolicy, globalCheckpoint::get); @@ -298,7 +301,7 @@ public void testDeleteInvalidCommits() throws Exception { public void testCheckUnreferencedCommits() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.UNASSIGNED_SEQ_NO); - final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, -1, 0); + final SoftDeletesPolicy softDeletesPolicy = new SoftDeletesPolicy(globalCheckpoint::get, NO_OPS_PERFORMED, 0); final UUID translogUUID = UUID.randomUUID(); final TranslogDeletionPolicy translogPolicy = createTranslogDeletionPolicy(); CombinedDeletionPolicy indexPolicy = new CombinedDeletionPolicy(logger, translogPolicy, softDeletesPolicy, globalCheckpoint::get); From 5813f6cd319541d1f3b8144e50085127124b5991 Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Fri, 25 Jan 2019 23:29:10 +0200 Subject: [PATCH 083/121] SQL: Fix casting from date to numeric type to use millis (#37869) Previously casting from a DATE[TIME] type to a numeric (DOUBLE, LONG, INT, etc. used seconds instead of the epoch millis. Fixes: #37655 --- .../qa/src/main/resources/datetime.csv-spec | 42 ++--- .../xpack/sql/type/DataTypeConversion.java | 4 +- .../elasticsearch/xpack/sql/TestUtils.java | 17 ++ .../sql/type/DataTypeConversionTests.java | 157 +++++++++++++----- 4 files changed, 152 insertions(+), 68 deletions(-) diff --git a/x-pack/plugin/sql/qa/src/main/resources/datetime.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/datetime.csv-spec index 5e51ae69bf396..3f461e994a060 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/datetime.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/datetime.csv-spec @@ -131,32 +131,32 @@ SELECT CONVERT(birth_date, DOUBLE) AS date FROM test_emp GROUP BY date ORDER BY date:d --------------- null --5.631552E8 --5.586624E8 --5.56416E8 --5.539104E8 --5.517504E8 --5.492448E8 --5.406912E8 --5.371488E8 --5.359392E8 +-5.631552E11 +-5.586624E11 +-5.56416E11 +-5.539104E11 +-5.517504E11 +-5.492448E11 +-5.406912E11 +-5.371488E11 +-5.359392E11 ; castedDateTimeWithGroupBy2 -SELECT CAST(hire_date AS INTEGER) AS date FROM test_emp GROUP BY date ORDER BY date LIMIT 10; +SELECT CAST(hire_date AS LONG) AS date FROM test_emp GROUP BY date ORDER BY date LIMIT 10; - date:i + date:l --------------- -477532800 -478051200 -484790400 -489715200 -495763200 -498096000 -498614400 -501206400 -501292800 -501379200 +477532800000 +478051200000 +484790400000 +489715200000 +495763200000 +498096000000 +498614400000 +501206400000 +501292800000 +501379200000 ; dateTimeAggByIsoDayOfWeekWithFilter diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 3cfb5d5ddf804..e0b779551ae88 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -470,9 +470,9 @@ private static Function fromString(Function conv private static Function fromBool(Function converter) { return (Object l) -> converter.apply(((Boolean) l)); } - + private static Function fromDate(Function converter) { - return l -> ((ZonedDateTime) l).toEpochSecond(); + return l -> converter.apply(((ZonedDateTime) l).toInstant().toEpochMilli()); } private static Function toDate(Conversion conversion) { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/TestUtils.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/TestUtils.java index cd6fa79cb552c..cd4f261ce960f 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/TestUtils.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/TestUtils.java @@ -11,6 +11,10 @@ import org.elasticsearch.xpack.sql.session.Configuration; import org.elasticsearch.xpack.sql.util.DateUtils; +import java.time.Clock; +import java.time.Duration; +import java.time.ZonedDateTime; + public class TestUtils { private TestUtils() {} @@ -18,4 +22,17 @@ private TestUtils() {} public static final Configuration TEST_CFG = new Configuration(DateUtils.UTC, Protocol.FETCH_SIZE, Protocol.REQUEST_TIMEOUT, Protocol.PAGE_TIMEOUT, null, Mode.PLAIN, null, null); + /** + * Returns the current UTC date-time with milliseconds precision. + * In Java 9+ (as opposed to Java 8) the {@code Clock} implementation uses system's best clock implementation (which could mean + * that the precision of the clock can be milliseconds, microseconds or nanoseconds), whereas in Java 8 + * {@code System.currentTimeMillis()} is always used. To account for these differences, this method defines a new {@code Clock} + * which will offer a value for {@code ZonedDateTime.now()} set to always have milliseconds precision. + * + * @return {@link ZonedDateTime} instance for the current date-time with milliseconds precision in UTC + */ + public static final ZonedDateTime now() { + return ZonedDateTime.now(Clock.tick(Clock.system(DateUtils.UTC), Duration.ofMillis(1))); + } + } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index ffe68e1765f1c..20976475e1895 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -7,9 +7,9 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.TestUtils; import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.type.DataTypeConversion.Conversion; -import org.elasticsearch.xpack.sql.util.DateUtils; import java.time.ZonedDateTime; @@ -33,16 +33,21 @@ import static org.elasticsearch.xpack.sql.type.DataTypeConversion.commonType; import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor; - public class DataTypeConversionTests extends ESTestCase { - public void testConversionToString() { - Conversion conversion = conversionFor(DOUBLE, KEYWORD); - assertNull(conversion.convert(null)); - assertEquals("10.0", conversion.convert(10.0)); - conversion = conversionFor(DATE, KEYWORD); - assertNull(conversion.convert(null)); - assertEquals("1970-01-01T00:00:00.000Z", conversion.convert(dateTime(0))); + public void testConversionToString() { + DataType to = KEYWORD; + { + Conversion conversion = conversionFor(DOUBLE, to); + assertNull(conversion.convert(null)); + assertEquals("10.0", conversion.convert(10.0)); + } + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals("1973-11-29T21:33:09.101Z", conversion.convert(dateTime(123456789101L))); + assertEquals("1966-02-02T02:26:50.899Z", conversion.convert(dateTime(-123456789101L))); + } } /** @@ -71,12 +76,20 @@ public void testConversionToLong() { assertEquals(1L, conversion.convert(true)); assertEquals(0L, conversion.convert(false)); } - Conversion conversion = conversionFor(KEYWORD, to); - assertNull(conversion.convert(null)); - assertEquals(1L, conversion.convert("1")); - assertEquals(0L, conversion.convert("-0")); - Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); - assertEquals("cannot cast [0xff] to [Long]", e.getMessage()); + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals(123456789101L, conversion.convert(dateTime(123456789101L))); + assertEquals(-123456789101L, conversion.convert(dateTime(-123456789101L))); + } + { + Conversion conversion = conversionFor(KEYWORD, to); + assertNull(conversion.convert(null)); + assertEquals(1L, conversion.convert("1")); + assertEquals(0L, conversion.convert("-0")); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); + assertEquals("cannot cast [0xff] to [Long]", e.getMessage()); + } } @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35683") @@ -89,7 +102,7 @@ public void testConversionToDate() { assertEquals(dateTime(10L), conversion.convert(10.1)); assertEquals(dateTime(11L), conversion.convert(10.6)); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); - assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); + assertEquals("[" + Double.MAX_VALUE + "] out of [long] range", e.getMessage()); } { Conversion conversion = conversionFor(INTEGER, to); @@ -103,44 +116,60 @@ public void testConversionToDate() { assertEquals(dateTime(1), conversion.convert(true)); assertEquals(dateTime(0), conversion.convert(false)); } - Conversion conversion = conversionFor(KEYWORD, to); - assertNull(conversion.convert(null)); + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals(dateTime(123379200000L), conversion.convert(dateTime(123456789101L))); + assertEquals(dateTime(-123465600000L), conversion.convert(dateTime(-123456789101L))); + } + { + Conversion conversion = conversionFor(KEYWORD, to); + assertNull(conversion.convert(null)); + + assertEquals(dateTime(1000L), conversion.convert("1970-01-01T00:00:01Z")); + assertEquals(dateTime(1483228800000L), conversion.convert("2017-01-01T00:00:00Z")); + assertEquals(dateTime(1483228800000L), conversion.convert("2017-01-01T00:00:00Z")); + assertEquals(dateTime(18000000L), conversion.convert("1970-01-01T00:00:00-05:00")); - assertEquals(dateTime(1000L), conversion.convert("1970-01-01T00:00:01Z")); - assertEquals(dateTime(1483228800000L), conversion.convert("2017-01-01T00:00:00Z")); - assertEquals(dateTime(18000000L), conversion.convert("1970-01-01T00:00:00-05:00")); - - // double check back and forth conversion - ZonedDateTime dt = ZonedDateTime.now(DateUtils.UTC); - Conversion forward = conversionFor(DATE, KEYWORD); - Conversion back = conversionFor(KEYWORD, DATE); - assertEquals(dt, back.convert(forward.convert(dt))); - Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); - assertEquals("cannot cast [0xff] to [Date]:Invalid format: \"0xff\" is malformed at \"xff\"", e.getMessage()); + // double check back and forth conversion + ZonedDateTime dt = TestUtils.now(); + Conversion forward = conversionFor(DATE, KEYWORD); + Conversion back = conversionFor(KEYWORD, DATE); + assertEquals(dt, back.convert(forward.convert(dt))); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); + assertEquals("cannot cast [0xff] to [Datetime]:Invalid format: \"0xff\" is malformed at \"xff\"", e.getMessage()); + } } public void testConversionToDouble() { + DataType to = DOUBLE; { - Conversion conversion = conversionFor(FLOAT, DOUBLE); + Conversion conversion = conversionFor(FLOAT, to); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10.0f), 0.00001); assertEquals(10.1, (double) conversion.convert(10.1f), 0.00001); assertEquals(10.6, (double) conversion.convert(10.6f), 0.00001); } { - Conversion conversion = conversionFor(INTEGER, DOUBLE); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10), 0.00001); assertEquals(-134.0, (double) conversion.convert(-134), 0.00001); } { - Conversion conversion = conversionFor(BOOLEAN, DOUBLE); + Conversion conversion = conversionFor(BOOLEAN, to); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert(true), 0); assertEquals(0.0, (double) conversion.convert(false), 0); } { - Conversion conversion = conversionFor(KEYWORD, DOUBLE); + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals(1.23456789101E11, (double) conversion.convert(dateTime(123456789101L)), 0); + assertEquals(-1.23456789101E11, (double) conversion.convert(dateTime(-123456789101L)), 0); + } + { + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert("1"), 0); assertEquals(0.0, (double) conversion.convert("-0"), 0); @@ -151,36 +180,44 @@ public void testConversionToDouble() { } public void testConversionToBoolean() { + DataType to = BOOLEAN; { - Conversion conversion = conversionFor(FLOAT, BOOLEAN); + Conversion conversion = conversionFor(FLOAT, to); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0f)); assertEquals(true, conversion.convert(-10.0f)); assertEquals(false, conversion.convert(0.0f)); } { - Conversion conversion = conversionFor(INTEGER, BOOLEAN); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10)); assertEquals(true, conversion.convert(-10)); assertEquals(false, conversion.convert(0)); } { - Conversion conversion = conversionFor(LONG, BOOLEAN); + Conversion conversion = conversionFor(LONG, to); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10L)); assertEquals(true, conversion.convert(-10L)); assertEquals(false, conversion.convert(0L)); } { - Conversion conversion = conversionFor(DOUBLE, BOOLEAN); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0d)); assertEquals(true, conversion.convert(-10.0d)); assertEquals(false, conversion.convert(0.0d)); } { - Conversion conversion = conversionFor(KEYWORD, BOOLEAN); + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals(true, conversion.convert(dateTime(123456789101L))); + assertEquals(true, conversion.convert(dateTime(-123456789101L))); + assertEquals(false, conversion.convert(dateTime(0L))); + } + { + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); // We only handled upper and lower case true and false assertEquals(true, conversion.convert("true")); @@ -204,8 +241,9 @@ public void testConversionToBoolean() { } public void testConversionToInt() { + DataType to = INTEGER; { - Conversion conversion = conversionFor(DOUBLE, INTEGER); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(10, conversion.convert(10.0)); assertEquals(10, conversion.convert(10.1)); @@ -213,11 +251,21 @@ public void testConversionToInt() { Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Long.MAX_VALUE)); assertEquals("[" + Long.MAX_VALUE + "] out of [Int] range", e.getMessage()); } + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals(12345678, conversion.convert(dateTime(12345678L))); + assertEquals(223456789, conversion.convert(dateTime(223456789L))); + assertEquals(-123456789, conversion.convert(dateTime(-123456789L))); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(dateTime(Long.MAX_VALUE))); + assertEquals("[" + Long.MAX_VALUE + "] out of [Int] range", e.getMessage()); + } } public void testConversionToShort() { + DataType to = SHORT; { - Conversion conversion = conversionFor(DOUBLE, SHORT); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals((short) 10, conversion.convert(10.0)); assertEquals((short) 10, conversion.convert(10.1)); @@ -225,11 +273,21 @@ public void testConversionToShort() { Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Integer.MAX_VALUE)); assertEquals("[" + Integer.MAX_VALUE + "] out of [Short] range", e.getMessage()); } + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals((short) 12345, conversion.convert(dateTime(12345L))); + assertEquals((short) -12345, conversion.convert(dateTime(-12345L))); + Exception e = expectThrows(SqlIllegalArgumentException.class, + () -> conversion.convert(dateTime(Integer.MAX_VALUE))); + assertEquals("[" + Integer.MAX_VALUE + "] out of [Short] range", e.getMessage()); + } } public void testConversionToByte() { + DataType to = BYTE; { - Conversion conversion = conversionFor(DOUBLE, BYTE); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals((byte) 10, conversion.convert(10.0)); assertEquals((byte) 10, conversion.convert(10.1)); @@ -237,6 +295,15 @@ public void testConversionToByte() { Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert(Short.MAX_VALUE)); assertEquals("[" + Short.MAX_VALUE + "] out of [Byte] range", e.getMessage()); } + { + Conversion conversion = conversionFor(DATE, to); + assertNull(conversion.convert(null)); + assertEquals((byte) 123, conversion.convert(dateTime(123L))); + assertEquals((byte) -123, conversion.convert(dateTime(-123L))); + Exception e = expectThrows(SqlIllegalArgumentException.class, + () -> conversion.convert(dateTime(Integer.MAX_VALUE))); + assertEquals("[" + Integer.MAX_VALUE + "] out of [Byte] range", e.getMessage()); + } } public void testConversionToNull() { @@ -264,7 +331,7 @@ public void testCommonType() { assertEquals(NULL, commonType(NULL, NULL)); assertEquals(INTEGER, commonType(INTEGER, KEYWORD)); assertEquals(LONG, commonType(TEXT, LONG)); - assertEquals(null, commonType(TEXT, KEYWORD)); + assertNull(commonType(TEXT, KEYWORD)); assertEquals(SHORT, commonType(SHORT, BYTE)); assertEquals(FLOAT, commonType(BYTE, FLOAT)); assertEquals(FLOAT, commonType(FLOAT, INTEGER)); @@ -278,9 +345,9 @@ public void testEsDataTypes() { } public void testConversionToUnsupported() { - Exception e = expectThrows(SqlIllegalArgumentException.class, - () -> conversionFor(INTEGER, UNSUPPORTED)); - assertEquals("cannot convert from [INTEGER] to [UNSUPPORTED]", e.getMessage()); + Exception e = expectThrows(SqlIllegalArgumentException.class, + () -> conversionFor(INTEGER, UNSUPPORTED)); + assertEquals("cannot convert from [INTEGER] to [UNSUPPORTED]", e.getMessage()); } public void testStringToIp() { From ec243f2ba6a6d78117a4eeee9084c60918b0e507 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 23 Jan 2019 10:24:51 -0500 Subject: [PATCH 084/121] Always return metadata version if metadata is requested (#37674) If the indices of a ClusterStateRequest are specified, we fail to include the cluster state metadata version in the response. Relates #37633 --- .../state/TransportClusterStateAction.java | 1 + .../cluster/SimpleClusterStateIT.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java index 47747c0196c2e..97d524e7437ba 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -154,6 +154,7 @@ private void buildResponse(final ClusterStateRequest request, if (request.metaData()) { if (request.indices().length > 0) { + mdBuilder.version(currentState.metaData().version()); String[] indices = indexNameExpressionResolver.concreteIndexNames(currentState, request); for (String filteredIndex : indices) { IndexMetaData indexMetaData = currentState.metaData().index(filteredIndex); diff --git a/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java index 52f64b923440b..b939d9a6848f7 100644 --- a/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java +++ b/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java @@ -59,6 +59,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateExists; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; @@ -111,6 +112,23 @@ public void testMetadata() throws Exception { assertThat(clusterStateResponse.getState().metaData().indices().size(), is(0)); } + public void testMetadataVersion() { + createIndex("index-1"); + createIndex("index-2"); + long metadataVersion = client().admin().cluster().prepareState().get().getState().metaData().version(); + assertThat(metadataVersion, greaterThan(0L)); + assertThat(client().admin().cluster().prepareState().setIndices("index-1").get().getState().metaData().version(), + equalTo(metadataVersion)); + assertThat(client().admin().cluster().prepareState().setIndices("index-2").get().getState().metaData().version(), + equalTo(metadataVersion)); + assertThat(client().admin().cluster().prepareState().setIndices("*").get().getState().metaData().version(), + equalTo(metadataVersion)); + assertThat(client().admin().cluster().prepareState().setIndices("not-found").get().getState().metaData().version(), + equalTo(metadataVersion)); + assertThat(client().admin().cluster().prepareState().clear().setMetaData(false).get().getState().metaData().version(), + equalTo(0L)); + } + public void testIndexTemplates() throws Exception { client().admin().indices().preparePutTemplate("foo_template") .setPatterns(Collections.singletonList("te*")) From 1f6a8b35ea1815a0d92819f0b73ea4748d477962 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 25 Jan 2019 14:54:13 -0500 Subject: [PATCH 085/121] Relax cluster metadata version check (#37834) If the in_sync_allocations of index-1 or index-2 is changed, the metadata version will be increased. This leads to the failure in the metadata version checks. We need to relax them. Closes #37820 --- .../elasticsearch/cluster/SimpleClusterStateIT.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java index b939d9a6848f7..a7f7904a026e2 100644 --- a/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java +++ b/server/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java @@ -115,16 +115,16 @@ public void testMetadata() throws Exception { public void testMetadataVersion() { createIndex("index-1"); createIndex("index-2"); - long metadataVersion = client().admin().cluster().prepareState().get().getState().metaData().version(); - assertThat(metadataVersion, greaterThan(0L)); + long baselineVersion = client().admin().cluster().prepareState().get().getState().metaData().version(); + assertThat(baselineVersion, greaterThan(0L)); assertThat(client().admin().cluster().prepareState().setIndices("index-1").get().getState().metaData().version(), - equalTo(metadataVersion)); + greaterThanOrEqualTo(baselineVersion)); assertThat(client().admin().cluster().prepareState().setIndices("index-2").get().getState().metaData().version(), - equalTo(metadataVersion)); + greaterThanOrEqualTo(baselineVersion)); assertThat(client().admin().cluster().prepareState().setIndices("*").get().getState().metaData().version(), - equalTo(metadataVersion)); + greaterThanOrEqualTo(baselineVersion)); assertThat(client().admin().cluster().prepareState().setIndices("not-found").get().getState().metaData().version(), - equalTo(metadataVersion)); + greaterThanOrEqualTo(baselineVersion)); assertThat(client().admin().cluster().prepareState().clear().setMetaData(false).get().getState().metaData().version(), equalTo(0L)); } From 8794fc5156b71104e6978912196179cb8d5a8425 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 28 Jan 2019 10:00:11 +0000 Subject: [PATCH 086/121] Document that auto_create_index is dynamic (#37903) We changed the `action.auto_create_index` setting to be a dynamic cluster-level setting in #20274 but today the reference manual indicates that it is still a static node-level setting. This commit addresses this, and clarifies the semantics of patterns that may both permit and forbid the creation of certain indices. Relates #7513, #27026 --- docs/reference/docs/index_.asciidoc | 70 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/docs/reference/docs/index_.asciidoc b/docs/reference/docs/index_.asciidoc index 678e9c69c6ec1..8e586bcff402b 100644 --- a/docs/reference/docs/index_.asciidoc +++ b/docs/reference/docs/index_.asciidoc @@ -56,27 +56,55 @@ NOTE: Replica shards may not all be started when an indexing operation success [[index-creation]] === Automatic Index Creation -The index operation automatically creates an index if it has not been -created before (check out the -<> for manually -creating an index), and also automatically creates a -dynamic type mapping for the specific type if one has not yet been -created (check out the <> -API for manually creating a type mapping). - -The mapping itself is very flexible and is schema-free. New fields and -objects will automatically be added to the mapping definition of the -type specified. Check out the <> -section for more information on mapping definitions. - -Automatic index creation can be disabled by setting -`action.auto_create_index` to `false` in the config file of all nodes. -Automatic mapping creation can be disabled by setting -`index.mapper.dynamic` to `false` per-index as an index setting. - -Automatic index creation can include a pattern based white/black list, -for example, set `action.auto_create_index` to `+aaa*,-bbb*,+ccc*,-*` (+ -meaning allowed, and - meaning disallowed). +The index operation automatically creates an index if it does not already +exist, and applies any <> that are +configured. The index operation also creates a dynamic type mapping for the +specified type if one does not already exist. By default, new fields and +objects will automatically be added to the mapping definition for the specified +type if needed. Check out the <> section for more information +on mapping definitions, and the the <> API for +information about updating type mappings manually. + +Automatic index creation is controlled by the `action.auto_create_index` +setting. This setting defaults to `true`, meaning that indices are always +automatically created. Automatic index creation can be permitted only for +indices matching certain patterns by changing the value of this setting to a +comma-separated list of these patterns. It can also be explicitly permitted and +forbidden by prefixing patterns in the list with a `+` or `-`. Finally it can +be completely disabled by changing this setting to `false`. + +[source,js] +-------------------------------------------------- +PUT _cluster/settings +{ + "persistent": { + "action.auto_create_index": "twitter,index10,-index1*,+ind*" <1> + } +} + +PUT _cluster/settings +{ + "persistent": { + "action.auto_create_index": "false" <2> + } +} + +PUT _cluster/settings +{ + "persistent": { + "action.auto_create_index": "true" <3> + } +} +-------------------------------------------------- +// CONSOLE + +<1> Permit only the auto-creation of indices called `twitter`, `index10`, no +other index matching `index1*`, and any other index matching `ind*`. The +patterns are matched in the order in which they are given. + +<2> Completely disable the auto-creation of indices. + +<3> Permit the auto-creation of indices with any name. This is the default. [float] [[operation-type]] From 92694d4e4fabdcfcdd1f218dba392d8ad4477d42 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 28 Jan 2019 13:04:38 -0800 Subject: [PATCH 087/121] [DOCS] Delayed data annotations (#37939) --- .../ml/delayed-data-detection.asciidoc | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/docs/reference/ml/delayed-data-detection.asciidoc b/docs/reference/ml/delayed-data-detection.asciidoc index 2c2179205c554..872a45d724893 100644 --- a/docs/reference/ml/delayed-data-detection.asciidoc +++ b/docs/reference/ml/delayed-data-detection.asciidoc @@ -5,38 +5,49 @@ Delayed data are documents that are indexed late. That is to say, it is data related to a time that the {dfeed} has already processed. -When you create a datafeed, you can specify a {ref}/ml-datafeed-resource.html[`query_delay`] setting. -This setting enables the datafeed to wait for some time past real-time, which means any "late" data in this period -is fully indexed before the datafeed tries to gather it. However, if the setting is set too low, the datafeed may query -for data before it has been indexed and consequently miss that document. Conversely, if it is set too high, -analysis drifts farther away from real-time. The balance that is struck depends upon each use case and -the environmental factors of the cluster. +When you create a datafeed, you can specify a +{ref}/ml-datafeed-resource.html[`query_delay`] setting. This setting enables the +datafeed to wait for some time past real-time, which means any "late" data in +this period is fully indexed before the datafeed tries to gather it. However, if +the setting is set too low, the datafeed may query for data before it has been +indexed and consequently miss that document. Conversely, if it is set too high, +analysis drifts farther away from real-time. The balance that is struck depends +upon each use case and the environmental factors of the cluster. ==== Why worry about delayed data? -This is a particularly prescient question. If data are delayed randomly (and consequently missing from analysis), -the results of certain types of functions are not really affected. It all comes out ok in the end -as the delayed data is distributed randomly. An example would be a `mean` metric for a field in a large collection of data. -In this case, checking for delayed data may not provide much benefit. If data are consistently delayed, however, jobs with a `low_count` function may -provide false positives. In this situation, it would be useful to see if data -comes in after an anomaly is recorded so that you can determine a next course of action. +This is a particularly prescient question. If data are delayed randomly (and +consequently are missing from analysis), the results of certain types of +functions are not really affected. In these situations, it all comes out okay in +the end as the delayed data is distributed randomly. An example would be a `mean` +metric for a field in a large collection of data. In this case, checking for +delayed data may not provide much benefit. If data are consistently delayed, +however, jobs with a `low_count` function may provide false positives. In this +situation, it would be useful to see if data comes in after an anomaly is +recorded so that you can determine a next course of action. ==== How do we detect delayed data? In addition to the `query_delay` field, there is a -{ref}/ml-datafeed-resource.html#ml-datafeed-delayed-data-check-config[delayed data check config], which enables you to -configure the datafeed to look in the past for delayed data. Every 15 minutes or every `check_window`, -whichever is smaller, the datafeed triggers a document search over the configured indices. This search looks over a -time span with a length of `check_window` ending with the latest finalized bucket. That time span is partitioned into buckets, -whose length equals the bucket span of the associated job. The `doc_count` of those buckets are then compared with the -job's finalized analysis buckets to see whether any data has arrived since the analysis. If there is indeed missing data -due to their ingest delay, the end user is notified. +{ref}/ml-datafeed-resource.html#ml-datafeed-delayed-data-check-config[delayed data check config], +which enables you to configure the datafeed to look in the past for delayed data. +Every 15 minutes or every `check_window`, whichever is smaller, the datafeed +triggers a document search over the configured indices. This search looks over a +time span with a length of `check_window` ending with the latest finalized bucket. +That time span is partitioned into buckets, whose length equals the bucket span +of the associated job. The `doc_count` of those buckets are then compared with +the job's finalized analysis buckets to see whether any data has arrived since +the analysis. If there is indeed missing data due to their ingest delay, the end +user is notified. For example, you can see annotations in {kib} for the periods +where these delays occur. ==== What to do about delayed data? -The most common course of action is to simply to do nothing. For many functions and situations ignoring the data is -acceptable. However, if the amount of delayed data is too great or the situation calls for it, the next course -of action to consider is to increase the `query_delay` of the datafeed. This increased delay allows more time for data to be -indexed. If you have real-time constraints, however, an increased delay might not be desirable. -In which case, you would have to {ref}/tune-for-indexing-speed.html[tune for better indexing speed.] +The most common course of action is to simply to do nothing. For many functions +and situations, ignoring the data is acceptable. However, if the amount of +delayed data is too great or the situation calls for it, the next course of +action to consider is to increase the `query_delay` of the datafeed. This +increased delay allows more time for data to be indexed. If you have real-time +constraints, however, an increased delay might not be desirable. In which case, +you would have to {ref}/tune-for-indexing-speed.html[tune for better indexing speed]. From 01b83109661620839ca2e28e216a50f6acd0f724 Mon Sep 17 00:00:00 2001 From: lcawl Date: Mon, 28 Jan 2019 13:36:06 -0800 Subject: [PATCH 088/121] [DOCS] Removes coming 6.6.0 and 6.5.2 tags --- docs/reference/release-notes/6.5.asciidoc | 2 -- docs/reference/release-notes/6.6.asciidoc | 1 - 2 files changed, 3 deletions(-) diff --git a/docs/reference/release-notes/6.5.asciidoc b/docs/reference/release-notes/6.5.asciidoc index ff6de87f6ab21..5380571b36d8c 100644 --- a/docs/reference/release-notes/6.5.asciidoc +++ b/docs/reference/release-notes/6.5.asciidoc @@ -120,8 +120,6 @@ Snapshot/Restore:: [[release-notes-6.5.2]] == {es} version 6.5.2 -coming[6.5.2] - Also see <>. diff --git a/docs/reference/release-notes/6.6.asciidoc b/docs/reference/release-notes/6.6.asciidoc index 9cd54f2ee69bb..d2992dde697a7 100644 --- a/docs/reference/release-notes/6.6.asciidoc +++ b/docs/reference/release-notes/6.6.asciidoc @@ -2,7 +2,6 @@ [[release-notes-6.6.0]] == {es} version 6.6.0 -coming[6.6.0] Also see <>. [[breaking-6.6.0]] From 0cb696aab9cbb3ad08be2fb3b3e0f61569669670 Mon Sep 17 00:00:00 2001 From: Paul Sanwald Date: Mon, 28 Jan 2019 22:14:50 -0500 Subject: [PATCH 089/121] add es release highlights (#37945) Add elasticsearch release highlights. --- .../release-notes/highlights-6.6.0.asciidoc | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/reference/release-notes/highlights-6.6.0.asciidoc b/docs/reference/release-notes/highlights-6.6.0.asciidoc index 38eb2ae8aebe2..58144ab8b81dc 100644 --- a/docs/reference/release-notes/highlights-6.6.0.asciidoc +++ b/docs/reference/release-notes/highlights-6.6.0.asciidoc @@ -5,3 +5,33 @@ ++++ See also <>. + +[float] +=== Index lifecycle management (Beta) + +The index lifecycle management feature breaks the lifecycle of an index into four phases: hot, warm, cold, and delete phase. +You can define an index lifecycle policy which enables you to: + +* Have one primary shard on each hot node to maximize indexing throughput. +* Replace the hot index with a new empty index as soon as the existing index is “full” or after a time period. +* Move the old index to warm nodes, where it can be shrunk to a single shard and force-merged down to a single segment for optimized storage and querying. +* Later, move the index to cold nodes for cheaper storage. + +See <>. +[float] +=== Frozen indices + +<> allow for a much higher ratio of disk storage to heap memory, at the expense of search latency. When an index is frozen, it takes up no heap +memory, allowing a single node to easily manage thousands of indices with very low overhead. When a search targets frozen indices, the query will fully open, +search, and then close each index sequentially. Frozen indices are replicated, unlike closed indexes. +Frozen indices provide a new set of choices for how to optimize your cluster cost and performance around your needs. + +[float] +=== BKD-backed geoshapes + +In 6.0, we introduced Bkd-backed geopoints, which resulted in significant +storage, memory and performance improvements for querying geopoints. With 6.6.0, +we bring the same Bkd-based benefits to geoshapes. Indexing is faster, it will take up less space on disk, and will use less memory. + +In combination with this work, we are introducing a new experimental field, +`geo`, that combine the `geo_point` and `geo_hash` field. The `geo` field is also backed by BKD trees. From f405c2c4f305eab781333b44de2a205419df09a8 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 29 Jan 2019 09:51:40 +0000 Subject: [PATCH 090/121] [ML] Update ML results mappings on process start (#37758) This change moves the update to the results index mappings from the open job action to the code that starts the autodetect process. When a rolling upgrade is performed we need to update the mappings for already-open jobs that are reassigned from an old version node to a new version node, but the open job action is not called in this case. Closes #37607 --- .../persistence/ElasticsearchMappings.java | 114 ++++++++++++++++ .../ElasticsearchMappingsTests.java | 114 ++++++++++++++++ .../ml/action/TransportOpenJobAction.java | 125 ++--------------- .../autodetect/AutodetectProcessManager.java | 112 +++++++++------- .../action/TransportOpenJobActionTests.java | 114 ---------------- .../AutodetectProcessManagerTests.java | 51 ++++--- .../upgrades/MlMappingsUpgradeIT.java | 126 ++++++++++++++++++ 7 files changed, 455 insertions(+), 301 deletions(-) create mode 100644 x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappings.java index 1b314a4a2f3cb..ad80e2d116761 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappings.java @@ -5,8 +5,24 @@ */ package org.elasticsearch.xpack.core.ml.job.persistence; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; +import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.AliasOrIndex; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.MappingMetaData; +import org.elasticsearch.common.CheckedSupplier; +import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.Index; +import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.xpack.core.ml.datafeed.ChunkingConfig; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import org.elasticsearch.xpack.core.ml.datafeed.DelayedDataCheckConfig; @@ -38,10 +54,16 @@ import org.elasticsearch.xpack.core.ml.notifications.AuditMessage; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.xpack.core.ClientHelper.ML_ORIGIN; +import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; /** * Static methods to create Elasticsearch index mappings for the autodetect @@ -107,6 +129,8 @@ public class ElasticsearchMappings { static final String RAW = "raw"; + private static final Logger logger = LogManager.getLogger(ElasticsearchMappings.class); + private ElasticsearchMappings() { } @@ -968,4 +992,94 @@ public static XContentBuilder auditMessageMapping() throws IOException { .endObject() .endObject(); } + + static String[] mappingRequiresUpdate(ClusterState state, String[] concreteIndices, Version minVersion) throws IOException { + List indicesToUpdate = new ArrayList<>(); + + ImmutableOpenMap> currentMapping = state.metaData().findMappings(concreteIndices, + new String[] {DOC_TYPE}, MapperPlugin.NOOP_FIELD_FILTER); + + for (String index : concreteIndices) { + ImmutableOpenMap innerMap = currentMapping.get(index); + if (innerMap != null) { + MappingMetaData metaData = innerMap.get(DOC_TYPE); + try { + @SuppressWarnings("unchecked") + Map meta = (Map) metaData.sourceAsMap().get("_meta"); + if (meta != null) { + String versionString = (String) meta.get("version"); + if (versionString == null) { + logger.info("Version of mappings for [{}] not found, recreating", index); + indicesToUpdate.add(index); + continue; + } + + Version mappingVersion = Version.fromString(versionString); + + if (mappingVersion.onOrAfter(minVersion)) { + continue; + } else { + logger.info("Mappings for [{}] are outdated [{}], updating it[{}].", index, mappingVersion, Version.CURRENT); + indicesToUpdate.add(index); + continue; + } + } else { + logger.info("Version of mappings for [{}] not found, recreating", index); + indicesToUpdate.add(index); + continue; + } + } catch (Exception e) { + logger.error(new ParameterizedMessage("Failed to retrieve mapping version for [{}], recreating", index), e); + indicesToUpdate.add(index); + continue; + } + } else { + logger.info("No mappings found for [{}], recreating", index); + indicesToUpdate.add(index); + } + } + return indicesToUpdate.toArray(new String[indicesToUpdate.size()]); + } + + public static void addDocMappingIfMissing(String alias, CheckedSupplier mappingSupplier, + Client client, ClusterState state, ActionListener listener) { + AliasOrIndex aliasOrIndex = state.metaData().getAliasAndIndexLookup().get(alias); + if (aliasOrIndex == null) { + // The index has never been created yet + listener.onResponse(true); + return; + } + String[] concreteIndices = aliasOrIndex.getIndices().stream().map(IndexMetaData::getIndex).map(Index::getName) + .toArray(String[]::new); + + String[] indicesThatRequireAnUpdate; + try { + indicesThatRequireAnUpdate = mappingRequiresUpdate(state, concreteIndices, Version.CURRENT); + } catch (IOException e) { + listener.onFailure(e); + return; + } + + if (indicesThatRequireAnUpdate.length > 0) { + try (XContentBuilder mapping = mappingSupplier.get()) { + PutMappingRequest putMappingRequest = new PutMappingRequest(indicesThatRequireAnUpdate); + putMappingRequest.type(DOC_TYPE); + putMappingRequest.source(mapping); + executeAsyncWithOrigin(client, ML_ORIGIN, PutMappingAction.INSTANCE, putMappingRequest, + ActionListener.wrap(response -> { + if (response.isAcknowledged()) { + listener.onResponse(true); + } else { + listener.onFailure(new ElasticsearchException("Attempt to put missing mapping in indices " + + Arrays.toString(indicesThatRequireAnUpdate) + " was not acknowledged")); + } + }, listener::onFailure)); + } catch (IOException e) { + listener.onFailure(e); + } + } else { + logger.trace("Mappings are up to date."); + listener.onResponse(true); + } + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java index e4ce536a3ccf6..b6fec1cb28d1b 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java @@ -9,10 +9,18 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.MappingMetaData; +import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.VersionUtils; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.core.ml.job.config.ModelPlotConfig; @@ -30,6 +38,8 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -128,6 +138,110 @@ public void testTermFieldMapping() throws IOException { assertNull(instanceMapping); } + + public void testMappingRequiresUpdateNoMapping() throws IOException { + ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name")); + ClusterState cs = csBuilder.build(); + String[] indices = new String[] { "no_index" }; + + assertArrayEquals(new String[] { "no_index" }, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateNullMapping() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("null_mapping", null)); + String[] indices = new String[] { "null_index" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateNoVersion() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("no_version_field", "NO_VERSION_FIELD")); + String[] indices = new String[] { "no_version_field" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateRecentMappingVersion() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_current", Version.CURRENT.toString())); + String[] indices = new String[] { "version_current" }; + assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateMaliciousMappingVersion() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData( + Collections.singletonMap("version_current", Collections.singletonMap("nested", "1.0"))); + String[] indices = new String[] { "version_nested" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateBogusMappingVersion() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_bogus", "0.0")); + String[] indices = new String[] { "version_bogus" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + public void testMappingRequiresUpdateNewerMappingVersion() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_newer", Version.CURRENT)); + String[] indices = new String[] { "version_newer" }; + assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, VersionUtils.getPreviousVersion())); + } + + public void testMappingRequiresUpdateNewerMappingVersionMinor() throws IOException { + ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_newer_minor", Version.CURRENT)); + String[] indices = new String[] { "version_newer_minor" }; + assertArrayEquals(new String[] {}, + ElasticsearchMappings.mappingRequiresUpdate(cs, indices, VersionUtils.getPreviousMinorVersion())); + } + + public void testMappingRequiresUpdateSomeVersionMix() throws IOException { + Map versionMix = new HashMap<>(); + versionMix.put("version_54", Version.V_5_4_0); + versionMix.put("version_current", Version.CURRENT); + versionMix.put("version_null", null); + versionMix.put("version_current2", Version.CURRENT); + versionMix.put("version_bogus", "0.0.0"); + versionMix.put("version_current3", Version.CURRENT); + versionMix.put("version_bogus2", "0.0.0"); + + ClusterState cs = getClusterStateWithMappingsWithMetaData(versionMix); + String[] indices = new String[] { "version_54", "version_null", "version_bogus", "version_bogus2" }; + assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, Version.CURRENT)); + } + + private ClusterState getClusterStateWithMappingsWithMetaData(Map namesAndVersions) throws IOException { + MetaData.Builder metaDataBuilder = MetaData.builder(); + + for (Map.Entry entry : namesAndVersions.entrySet()) { + + String indexName = entry.getKey(); + Object version = entry.getValue(); + + IndexMetaData.Builder indexMetaData = IndexMetaData.builder(indexName); + indexMetaData.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); + + Map mapping = new HashMap<>(); + Map properties = new HashMap<>(); + for (int i = 0; i < 10; i++) { + properties.put("field" + i, Collections.singletonMap("type", "string")); + } + mapping.put("properties", properties); + + Map meta = new HashMap<>(); + if (version != null && version.equals("NO_VERSION_FIELD") == false) { + meta.put("version", version); + } + mapping.put("_meta", meta); + + indexMetaData.putMapping(new MappingMetaData(ElasticsearchMappings.DOC_TYPE, mapping)); + + metaDataBuilder.put(indexMetaData); + } + MetaData metaData = metaDataBuilder.build(); + + ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name")); + csBuilder.metaData(metaData); + return csBuilder.build(); + } + private Set collectResultsDocFieldNames() throws IOException { // Only the mappings for the results index should be added below. Do NOT add mappings for other indexes here. return collectFieldNames(ElasticsearchMappings.resultsMapping()); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java index 3b8a7d97c25dd..c7096471023c3 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportOpenJobAction.java @@ -7,14 +7,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ResourceAlreadyExistsException; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; -import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.support.master.TransportMasterNodeAction; @@ -23,23 +20,16 @@ import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; -import org.elasticsearch.cluster.metadata.AliasOrIndex; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.Index; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.persistent.AllocatedPersistentTask; @@ -47,7 +37,6 @@ import org.elasticsearch.persistent.PersistentTasksCustomMetaData; import org.elasticsearch.persistent.PersistentTasksExecutor; import org.elasticsearch.persistent.PersistentTasksService; -import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.threadpool.ThreadPool; @@ -78,9 +67,7 @@ import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcessManager; import org.elasticsearch.xpack.ml.process.MlMemoryTracker; -import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -433,55 +420,6 @@ private static boolean jobHasRules(Job job) { return job.getAnalysisConfig().getDetectors().stream().anyMatch(d -> d.getRules().isEmpty() == false); } - public static String[] mappingRequiresUpdate(ClusterState state, String[] concreteIndices, Version minVersion, Logger logger) - throws IOException { - - List indicesToUpdate = new ArrayList<>(); - - ImmutableOpenMap> currentMapping = state.metaData().findMappings(concreteIndices, - new String[] { ElasticsearchMappings.DOC_TYPE }, MapperPlugin.NOOP_FIELD_FILTER); - - for (String index : concreteIndices) { - ImmutableOpenMap innerMap = currentMapping.get(index); - if (innerMap != null) { - MappingMetaData metaData = innerMap.get(ElasticsearchMappings.DOC_TYPE); - try { - Map meta = (Map) metaData.sourceAsMap().get("_meta"); - if (meta != null) { - String versionString = (String) meta.get("version"); - if (versionString == null) { - logger.info("Version of mappings for [{}] not found, recreating", index); - indicesToUpdate.add(index); - continue; - } - - Version mappingVersion = Version.fromString(versionString); - - if (mappingVersion.onOrAfter(minVersion)) { - continue; - } else { - logger.info("Mappings for [{}] are outdated [{}], updating it[{}].", index, mappingVersion, Version.CURRENT); - indicesToUpdate.add(index); - continue; - } - } else { - logger.info("Version of mappings for [{}] not found, recreating", index); - indicesToUpdate.add(index); - continue; - } - } catch (Exception e) { - logger.error(new ParameterizedMessage("Failed to retrieve mapping version for [{}], recreating", index), e); - indicesToUpdate.add(index); - continue; - } - } else { - logger.info("No mappings found for [{}], recreating", index); - indicesToUpdate.add(index); - } - } - return indicesToUpdate.toArray(new String[indicesToUpdate.size()]); - } - @Override protected String executor() { // This api doesn't do heavy or blocking operations (just delegates PersistentTasksService), @@ -613,25 +551,18 @@ public void onFailure(Exception e) { ); // Try adding state doc mapping - ActionListener resultsPutMappingHandler = ActionListener.wrap( + ActionListener getJobHandler = ActionListener.wrap( response -> { - addDocMappingIfMissing(AnomalyDetectorsIndex.jobStateIndexName(), ElasticsearchMappings::stateMapping, - state, missingMappingsListener); + ElasticsearchMappings.addDocMappingIfMissing(AnomalyDetectorsIndex.jobStateIndexName(), + ElasticsearchMappings::stateMapping, client, state, missingMappingsListener); }, listener::onFailure ); // Get the job config jobManager.getJob(jobParams.getJobId(), ActionListener.wrap( job -> { - try { - jobParams.setJob(job); - - // Try adding results doc mapping - addDocMappingIfMissing(AnomalyDetectorsIndex.jobResultsAliasedName(jobParams.getJobId()), - ElasticsearchMappings::resultsMapping, state, resultsPutMappingHandler); - } catch (Exception e) { - listener.onFailure(e); - } + jobParams.setJob(job); + getJobHandler.onResponse(null); }, listener::onFailure )); @@ -738,48 +669,6 @@ public void onFailure(Exception e) { ); } - private void addDocMappingIfMissing(String alias, CheckedSupplier mappingSupplier, ClusterState state, - ActionListener listener) { - AliasOrIndex aliasOrIndex = state.metaData().getAliasAndIndexLookup().get(alias); - if (aliasOrIndex == null) { - // The index has never been created yet - listener.onResponse(true); - return; - } - String[] concreteIndices = aliasOrIndex.getIndices().stream().map(IndexMetaData::getIndex).map(Index::getName) - .toArray(String[]::new); - - String[] indicesThatRequireAnUpdate; - try { - indicesThatRequireAnUpdate = mappingRequiresUpdate(state, concreteIndices, Version.CURRENT, logger); - } catch (IOException e) { - listener.onFailure(e); - return; - } - - if (indicesThatRequireAnUpdate.length > 0) { - try (XContentBuilder mapping = mappingSupplier.get()) { - PutMappingRequest putMappingRequest = new PutMappingRequest(indicesThatRequireAnUpdate); - putMappingRequest.type(ElasticsearchMappings.DOC_TYPE); - putMappingRequest.source(mapping); - executeAsyncWithOrigin(client, ML_ORIGIN, PutMappingAction.INSTANCE, putMappingRequest, - ActionListener.wrap(response -> { - if (response.isAcknowledged()) { - listener.onResponse(true); - } else { - listener.onFailure(new ElasticsearchException("Attempt to put missing mapping in indices " - + Arrays.toString(indicesThatRequireAnUpdate) + " was not acknowledged")); - } - }, listener::onFailure)); - } catch (IOException e) { - listener.onFailure(e); - } - } else { - logger.trace("Mappings are uptodate."); - listener.onResponse(true); - } - } - public static class OpenJobPersistentTasksExecutor extends PersistentTasksExecutor { private static final Logger logger = LogManager.getLogger(OpenJobPersistentTasksExecutor.class); @@ -798,6 +687,7 @@ public static class OpenJobPersistentTasksExecutor extends PersistentTasksExecut private volatile int maxConcurrentJobAllocations; private volatile int maxMachineMemoryPercent; private volatile int maxLazyMLNodes; + private volatile ClusterState clusterState; public OpenJobPersistentTasksExecutor(Settings settings, ClusterService clusterService, AutodetectProcessManager autodetectProcessManager, MlMemoryTracker memoryTracker, @@ -815,6 +705,7 @@ public OpenJobPersistentTasksExecutor(Settings settings, ClusterService clusterS clusterService.getClusterSettings() .addSettingsUpdateConsumer(MachineLearning.MAX_MACHINE_MEMORY_PERCENT, this::setMaxMachineMemoryPercent); clusterService.getClusterSettings().addSettingsUpdateConsumer(MachineLearning.MAX_LAZY_ML_NODES, this::setMaxLazyMLNodes); + clusterService.addListener(event -> clusterState = event.state()); } @Override @@ -876,7 +767,7 @@ protected void nodeOperation(AllocatedPersistentTask task, OpenJobAction.JobPara } String jobId = jobTask.getJobId(); - autodetectProcessManager.openJob(jobTask, e2 -> { + autodetectProcessManager.openJob(jobTask, clusterState, e2 -> { if (e2 == null) { FinalizeJobExecutionAction.Request finalizeRequest = new FinalizeJobExecutionAction.Request(new String[]{jobId}); executeAsyncWithOrigin(client, ML_ORIGIN, FinalizeJobExecutionAction.INSTANCE, finalizeRequest, diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java index 470efeebdf32a..cc6dcafaa1f20 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java @@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.component.AbstractComponent; @@ -35,6 +36,8 @@ import org.elasticsearch.xpack.core.ml.job.config.JobState; import org.elasticsearch.xpack.core.ml.job.config.JobTaskState; import org.elasticsearch.xpack.core.ml.job.config.MlFilter; +import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex; +import org.elasticsearch.xpack.core.ml.job.persistence.ElasticsearchMappings; import org.elasticsearch.xpack.core.ml.job.process.autodetect.output.FlushAcknowledgement; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.DataCounts; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSizeStats; @@ -413,68 +416,75 @@ public void onFailure(Exception e) { } } - public void openJob(JobTask jobTask, Consumer closeHandler) { + public void openJob(JobTask jobTask, ClusterState clusterState, Consumer closeHandler) { String jobId = jobTask.getJobId(); logger.info("Opening job [{}]", jobId); - jobManager.getJob(jobId, ActionListener.wrap( - job -> { - if (job.getJobVersion() == null) { - closeHandler.accept(ExceptionsHelper.badRequestException("Cannot open job [" + jobId + // Start the process + ActionListener resultsMappingUpdateHandler = ActionListener.wrap( + r -> { + jobManager.getJob(jobId, ActionListener.wrap( + job -> { + if (job.getJobVersion() == null) { + closeHandler.accept(ExceptionsHelper.badRequestException("Cannot open job [" + jobId + "] because jobs created prior to version 5.5 are not supported")); - return; - } - - - processByAllocation.putIfAbsent(jobTask.getAllocationId(), new ProcessContext(jobTask)); - jobResultsProvider.getAutodetectParams(job, params -> { - // We need to fork, otherwise we restore model state from a network thread (several GET api calls): - threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(new AbstractRunnable() { - @Override - public void onFailure(Exception e) { - closeHandler.accept(e); - } + return; + } - @Override - protected void doRun() { - ProcessContext processContext = processByAllocation.get(jobTask.getAllocationId()); - if (processContext == null) { - logger.debug("Aborted opening job [{}] as it has been closed", jobId); - return; - } - if (processContext.getState() != ProcessContext.ProcessStateName.NOT_RUNNING) { - logger.debug("Cannot open job [{}] when its state is [{}]", - jobId, processContext.getState().getClass().getName()); - return; + processByAllocation.putIfAbsent(jobTask.getAllocationId(), new ProcessContext(jobTask)); + jobResultsProvider.getAutodetectParams(job, params -> { + // We need to fork, otherwise we restore model state from a network thread (several GET api calls): + threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(new AbstractRunnable() { + @Override + public void onFailure(Exception e) { + closeHandler.accept(e); } - try { - createProcessAndSetRunning(processContext, job, params, closeHandler); - processContext.getAutodetectCommunicator().init(params.modelSnapshot()); - setJobState(jobTask, JobState.OPENED); - } catch (Exception e1) { - // No need to log here as the persistent task framework will log it + @Override + protected void doRun() { + ProcessContext processContext = processByAllocation.get(jobTask.getAllocationId()); + if (processContext == null) { + logger.debug("Aborted opening job [{}] as it has been closed", jobId); + return; + } + if (processContext.getState() != ProcessContext.ProcessStateName.NOT_RUNNING) { + logger.debug("Cannot open job [{}] when its state is [{}]", + jobId, processContext.getState().getClass().getName()); + return; + } + try { - // Don't leave a partially initialised process hanging around - processContext.newKillBuilder() - .setAwaitCompletion(false) - .setFinish(false) - .kill(); - processByAllocation.remove(jobTask.getAllocationId()); - } finally { - setJobState(jobTask, JobState.FAILED, e2 -> closeHandler.accept(e1)); + createProcessAndSetRunning(processContext, job, params, closeHandler); + processContext.getAutodetectCommunicator().init(params.modelSnapshot()); + setJobState(jobTask, JobState.OPENED); + } catch (Exception e1) { + // No need to log here as the persistent task framework will log it + try { + // Don't leave a partially initialised process hanging around + processContext.newKillBuilder() + .setAwaitCompletion(false) + .setFinish(false) + .kill(); + processByAllocation.remove(jobTask.getAllocationId()); + } finally { + setJobState(jobTask, JobState.FAILED, e2 -> closeHandler.accept(e1)); + } } } - } + }); + }, e1 -> { + logger.warn("Failed to gather information required to open job [" + jobId + "]", e1); + setJobState(jobTask, JobState.FAILED, e2 -> closeHandler.accept(e1)); }); - }, e1 -> { - logger.warn("Failed to gather information required to open job [" + jobId + "]", e1); - setJobState(jobTask, JobState.FAILED, e2 -> closeHandler.accept(e1)); - }); - }, - closeHandler - )); - + }, + closeHandler + )); + }, + closeHandler); + + // Try adding the results doc mapping - this updates to the latest version if an old mapping is present + ElasticsearchMappings.addDocMappingIfMissing(AnomalyDetectorsIndex.jobResultsAliasedName(jobId), + ElasticsearchMappings::resultsMapping, client, clusterState, resultsMappingUpdateHandler); } private void createProcessAndSetRunning(ProcessContext processContext, Job job, AutodetectParams params, Consumer handler) { diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java index f6392c629f2ec..8f22673e27d47 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java @@ -11,7 +11,6 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; @@ -32,7 +31,6 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.VersionUtils; import org.elasticsearch.xpack.core.ml.MlMetaIndex; import org.elasticsearch.xpack.core.ml.MlTasks; import org.elasticsearch.xpack.core.ml.action.OpenJobAction; @@ -47,14 +45,12 @@ import org.elasticsearch.xpack.core.ml.job.config.RuleCondition; import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex; import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndexFields; -import org.elasticsearch.xpack.core.ml.job.persistence.ElasticsearchMappings; import org.elasticsearch.xpack.core.ml.notifications.AuditorField; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.ml.process.MlMemoryTracker; import org.elasticsearch.xpack.ml.support.BaseMlIntegTestCase; import org.junit.Before; -import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; @@ -494,80 +490,6 @@ public void testVerifyIndicesPrimaryShardsAreActive() { assertEquals(indexToRemove, result.get(0)); } - public void testMappingRequiresUpdateNoMapping() throws IOException { - ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name")); - ClusterState cs = csBuilder.build(); - String[] indices = new String[] { "no_index" }; - - assertArrayEquals(new String[] { "no_index" }, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateNullMapping() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("null_mapping", null)); - String[] indices = new String[] { "null_index" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateNoVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("no_version_field", "NO_VERSION_FIELD")); - String[] indices = new String[] { "no_version_field" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateRecentMappingVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_current", Version.CURRENT.toString())); - String[] indices = new String[] { "version_current" }; - assertArrayEquals(new String[] {}, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateMaliciousMappingVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData( - Collections.singletonMap("version_current", Collections.singletonMap("nested", "1.0"))); - String[] indices = new String[] { "version_nested" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateOldMappingVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_54", Version.V_5_4_0.toString())); - String[] indices = new String[] { "version_54" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateBogusMappingVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_bogus", "0.0")); - String[] indices = new String[] { "version_bogus" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - - public void testMappingRequiresUpdateNewerMappingVersion() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_newer", Version.CURRENT)); - String[] indices = new String[] { "version_newer" }; - assertArrayEquals(new String[] {}, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, VersionUtils.getPreviousVersion(), - logger)); - } - - public void testMappingRequiresUpdateNewerMappingVersionMinor() throws IOException { - ClusterState cs = getClusterStateWithMappingsWithMetaData(Collections.singletonMap("version_newer_minor", Version.CURRENT)); - String[] indices = new String[] { "version_newer_minor" }; - assertArrayEquals(new String[] {}, - TransportOpenJobAction.mappingRequiresUpdate(cs, indices, VersionUtils.getPreviousMinorVersion(), logger)); - } - - public void testMappingRequiresUpdateSomeVersionMix() throws IOException { - Map versionMix = new HashMap<>(); - versionMix.put("version_54", Version.V_5_4_0); - versionMix.put("version_current", Version.CURRENT); - versionMix.put("version_null", null); - versionMix.put("version_current2", Version.CURRENT); - versionMix.put("version_bogus", "0.0.0"); - versionMix.put("version_current3", Version.CURRENT); - versionMix.put("version_bogus2", "0.0.0"); - - ClusterState cs = getClusterStateWithMappingsWithMetaData(versionMix); - String[] indices = new String[] { "version_54", "version_null", "version_bogus", "version_bogus2" }; - assertArrayEquals(indices, TransportOpenJobAction.mappingRequiresUpdate(cs, indices, Version.CURRENT, logger)); - } - public void testNodeNameAndVersion() { TransportAddress ta = new TransportAddress(InetAddress.getLoopbackAddress(), 9300); Map attributes = new HashMap<>(); @@ -652,42 +574,6 @@ private void addIndices(MetaData.Builder metaData, RoutingTable.Builder routingT } } - private ClusterState getClusterStateWithMappingsWithMetaData(Map namesAndVersions) throws IOException { - MetaData.Builder metaDataBuilder = MetaData.builder(); - - for (Map.Entry entry : namesAndVersions.entrySet()) { - - String indexName = entry.getKey(); - Object version = entry.getValue(); - - IndexMetaData.Builder indexMetaData = IndexMetaData.builder(indexName); - indexMetaData.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); - - Map mapping = new HashMap<>(); - Map properties = new HashMap<>(); - for (int i = 0; i < 10; i++) { - properties.put("field" + i, Collections.singletonMap("type", "string")); - } - mapping.put("properties", properties); - - Map meta = new HashMap<>(); - if (version != null && version.equals("NO_VERSION_FIELD") == false) { - meta.put("version", version); - } - mapping.put("_meta", meta); - - indexMetaData.putMapping(new MappingMetaData(ElasticsearchMappings.DOC_TYPE, mapping)); - - metaDataBuilder.put(indexMetaData); - } - MetaData metaData = metaDataBuilder.build(); - - ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name")); - csBuilder.metaData(metaData); - return csBuilder.build(); - } - private static Job jobWithRules(String jobId) { DetectionRule rule = new DetectionRule.Builder(Collections.singletonList( new RuleCondition(RuleCondition.AppliesTo.TYPICAL, Operator.LT, 100.0) diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManagerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManagerTests.java index 32f6d2fa88311..69c9ec3eaaa5c 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManagerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManagerTests.java @@ -8,6 +8,9 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.AliasOrIndex; +import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.EsExecutors; @@ -31,6 +34,7 @@ import org.elasticsearch.xpack.core.ml.job.config.JobUpdate; import org.elasticsearch.xpack.core.ml.job.config.MlFilter; import org.elasticsearch.xpack.core.ml.job.config.ModelPlotConfig; +import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.DataCounts; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSizeStats; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSnapshot; @@ -61,6 +65,8 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -106,6 +112,7 @@ public class AutodetectProcessManagerTests extends ESTestCase { private JobDataCountsPersister jobDataCountsPersister; private NormalizerFactory normalizerFactory; private Auditor auditor; + private ClusterState clusterState; private DataCounts dataCounts = new DataCounts("foo"); private ModelSizeStats modelSizeStats = new ModelSizeStats.Builder("foo").build(); @@ -125,6 +132,12 @@ public void setup() throws Exception { jobDataCountsPersister = mock(JobDataCountsPersister.class); normalizerFactory = mock(NormalizerFactory.class); auditor = mock(Auditor.class); + MetaData metaData = mock(MetaData.class); + SortedMap aliasOrIndexSortedMap = new TreeMap<>(); + aliasOrIndexSortedMap.put(AnomalyDetectorsIndex.jobStateIndexName(), mock(AliasOrIndex.Index.class)); + when(metaData.getAliasAndIndexLookup()).thenReturn(aliasOrIndexSortedMap); + clusterState = mock(ClusterState.class); + when(clusterState.metaData()).thenReturn(metaData); doAnswer(invocationOnMock -> { @SuppressWarnings("unchecked") @@ -186,7 +199,7 @@ public void testOpenJob() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); when(jobTask.getAllocationId()).thenReturn(1L); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); assertEquals(1, manager.numberOfOpenJobs()); assertTrue(manager.jobHasActiveAutodetectProcess(jobTask)); verify(jobTask).updatePersistentTaskState(eq(new JobTaskState(JobState.OPENED, 1L)), any()); @@ -212,7 +225,7 @@ public void testOpenJob_withoutVersion() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn(job.getId()); AtomicReference errorHolder = new AtomicReference<>(); - manager.openJob(jobTask, errorHolder::set); + manager.openJob(jobTask, clusterState, errorHolder::set); Exception error = errorHolder.get(); assertThat(error, is(notNullValue())); assertThat(error.getMessage(), equalTo("Cannot open job [no_version] because jobs created prior to version 5.5 are not supported")); @@ -258,22 +271,22 @@ public void testOpenJob_exceedMaxNumJobs() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("bar"); when(jobTask.getAllocationId()).thenReturn(1L); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("baz"); when(jobTask.getAllocationId()).thenReturn(2L); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); assertEquals(3, manager.numberOfOpenJobs()); Exception[] holder = new Exception[1]; jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foobar"); when(jobTask.getAllocationId()).thenReturn(3L); - manager.openJob(jobTask, e -> holder[0] = e); + manager.openJob(jobTask, clusterState, e -> holder[0] = e); Exception e = holder[0]; assertEquals("max running job capacity [3] reached", e.getMessage()); @@ -282,7 +295,7 @@ public void testOpenJob_exceedMaxNumJobs() { when(jobTask.getJobId()).thenReturn("baz"); manager.closeJob(jobTask, false, null); assertEquals(2, manager.numberOfOpenJobs()); - manager.openJob(jobTask, e1 -> {}); + manager.openJob(jobTask, clusterState, e1 -> {}); assertEquals(3, manager.numberOfOpenJobs()); } @@ -294,7 +307,7 @@ public void testProcessData() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); DataLoadParams params = new DataLoadParams(TimeRange.builder().build(), Optional.empty()); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), params, (dataCounts1, e) -> {}); assertEquals(1, manager.numberOfOpenJobs()); @@ -317,7 +330,7 @@ public void testProcessDataThrowsElasticsearchStatusException_onIoException() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); Exception[] holder = new Exception[1]; manager.processData(jobTask, analysisRegistry, inputStream, xContentType, params, (dataCounts1, e) -> holder[0] = e); assertNotNull(holder[0]); @@ -330,7 +343,7 @@ public void testCloseJob() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -358,7 +371,7 @@ public void testCanCloseClosingJob() throws Exception { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -406,7 +419,7 @@ public void testCanKillClosingJob() throws Exception { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -435,7 +448,7 @@ public void testBucketResetMessageIsSent() { InputStream inputStream = createInputStream(""); JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, inputStream, xContentType, params, (dataCounts1, e) -> {}); verify(communicator).writeToJob(same(inputStream), same(analysisRegistry), same(xContentType), same(params), any()); } @@ -447,7 +460,7 @@ public void testFlush() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); InputStream inputStream = createInputStream(""); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, inputStream, randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -487,7 +500,7 @@ public void testCloseThrows() { // create a jobtask JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> { }); @@ -527,7 +540,7 @@ public void testJobHasActiveAutodetectProcess() { when(jobTask.getJobId()).thenReturn("foo"); assertFalse(manager.jobHasActiveAutodetectProcess(jobTask)); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -545,7 +558,7 @@ public void testKillKillsAutodetectProcess() throws IOException { when(jobTask.getJobId()).thenReturn("foo"); assertFalse(manager.jobHasActiveAutodetectProcess(jobTask)); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts1, e) -> {}); @@ -579,7 +592,7 @@ public void testProcessData_GivenStateNotOpened() { JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn("foo"); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); InputStream inputStream = createInputStream(""); DataCounts[] dataCounts = new DataCounts[1]; manager.processData(jobTask, analysisRegistry, inputStream, @@ -724,7 +737,7 @@ private AutodetectProcessManager createManagerAndCallProcessData(AutodetectCommu AutodetectProcessManager manager = createManager(communicator); JobTask jobTask = mock(JobTask.class); when(jobTask.getJobId()).thenReturn(jobId); - manager.openJob(jobTask, e -> {}); + manager.openJob(jobTask, clusterState, e -> {}); manager.processData(jobTask, analysisRegistry, createInputStream(""), randomFrom(XContentType.values()), mock(DataLoadParams.class), (dataCounts, e) -> {}); return manager; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java new file mode 100644 index 0000000000000..465db087af283 --- /dev/null +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java @@ -0,0 +1,126 @@ +/* + * 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.upgrades; + +import org.elasticsearch.Version; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ml.job.config.AnalysisConfig; +import org.elasticsearch.client.ml.job.config.DataDescription; +import org.elasticsearch.client.ml.job.config.Detector; +import org.elasticsearch.client.ml.job.config.Job; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex; +import org.elasticsearch.xpack.core.ml.job.persistence.ElasticsearchMappings; +import org.elasticsearch.xpack.test.rest.XPackRestTestHelper; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MlMappingsUpgradeIT extends AbstractUpgradeTestCase { + + private static final String JOB_ID = "ml-mappings-upgrade-job"; + + @Override + protected Collection templatesToWaitFor() { + List templatesToWaitFor = XPackRestTestHelper.ML_POST_V660_TEMPLATES; + + // If upgrading from a version prior to v6.6.0 the set of templates + // to wait for is different + if (CLUSTER_TYPE == ClusterType.OLD) { + if (UPGRADED_FROM_VERSION.before(Version.V_6_6_0)) { + templatesToWaitFor = XPackRestTestHelper.ML_PRE_V660_TEMPLATES; + } + } + + return Stream.concat(templatesToWaitFor.stream(), + super.templatesToWaitFor().stream()).collect(Collectors.toSet()); + } + + /** + * The purpose of this test is to ensure that when a job is open through a rolling upgrade we upgrade the results + * index mappings when it is assigned to an upgraded node even if no other ML endpoint is called after the upgrade + */ + public void testMappingsUpgrade() throws Exception { + + switch (CLUSTER_TYPE) { + case OLD: + createAndOpenTestJob(); + break; + case MIXED: + // We don't know whether the job is on an old or upgraded node, so cannot assert that the mappings have been upgraded + break; + case UPGRADED: + assertUpgradedMappings(); + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + } + + private void createAndOpenTestJob() throws IOException { + + Detector.Builder d = new Detector.Builder("metric", "responsetime"); + d.setByFieldName("airline"); + AnalysisConfig.Builder analysisConfig = new AnalysisConfig.Builder(Collections.singletonList(d.build())); + analysisConfig.setBucketSpan(TimeValue.timeValueMinutes(10)); + Job.Builder job = new Job.Builder(JOB_ID); + job.setAnalysisConfig(analysisConfig); + job.setDataDescription(new DataDescription.Builder()); + // Use a custom index because other rolling upgrade tests meddle with the shared index + job.setResultsIndexName("mappings-upgrade-test"); + + Request putJob = new Request("PUT", "_xpack/ml/anomaly_detectors/" + JOB_ID); + putJob.setJsonEntity(Strings.toString(job.build())); + Response response = client().performRequest(putJob); + assertEquals(200, response.getStatusLine().getStatusCode()); + + Request openJob = new Request("POST", "_xpack/ml/anomaly_detectors/" + JOB_ID + "/_open"); + response = client().performRequest(openJob); + assertEquals(200, response.getStatusLine().getStatusCode()); + } + + @SuppressWarnings("unchecked") + private void assertUpgradedMappings() throws Exception { + + assertBusy(() -> { + Request getMappings = new Request("GET", AnomalyDetectorsIndex.resultsWriteAlias(JOB_ID) + "/_mappings"); + Response response = client().performRequest(getMappings); + + Map responseLevel = entityAsMap(response); + assertNotNull(responseLevel); + Map indexLevel = null; + // The name of the concrete index underlying the results index alias may or may not have been changed + // by the upgrade process (depending on what other tests are being run and the order they're run in), + // so navigating to the next level of the tree must account for both cases + for (Map.Entry entry : responseLevel.entrySet()) { + if (entry.getKey().startsWith(".ml-anomalies-") && entry.getKey().contains("mappings-upgrade-test")) { + indexLevel = (Map) entry.getValue(); + break; + } + } + assertNotNull(indexLevel); + Map mappingsLevel = (Map) indexLevel.get("mappings"); + assertNotNull(mappingsLevel); + Map typeLevel = (Map) mappingsLevel.get(ElasticsearchMappings.DOC_TYPE); + assertNotNull(typeLevel); + Map metaLevel = (Map) typeLevel.get("_meta"); + assertEquals(Collections.singletonMap("version", Version.CURRENT.toString()), metaLevel); + Map propertiesLevel = (Map) typeLevel.get("properties"); + assertNotNull(propertiesLevel); + // TODO: as the years go by, the field we assert on here should be changed + // to the most recent field we've added that is NOT of type "keyword" + Map fieldLevel = (Map) propertiesLevel.get("multi_bucket_impact"); + assertEquals(Collections.singletonMap("type", "double"), fieldLevel); + }); + } +} From 8153d1db7c798c8d50815f8cd2ca60a8fd1ef0f2 Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Tue, 15 Jan 2019 21:20:19 +0200 Subject: [PATCH 091/121] Upgrade to Gradle 5.1.1 (#37410) Fixes mem leak in the daemon. --- buildSrc/src/main/resources/minimumGradleVersion | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/resources/minimumGradleVersion b/buildSrc/src/main/resources/minimumGradleVersion index 6e6366051638f..ac14c3dfaa865 100644 --- a/buildSrc/src/main/resources/minimumGradleVersion +++ b/buildSrc/src/main/resources/minimumGradleVersion @@ -1 +1 @@ -5.0 \ No newline at end of file +5.1.1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5333afa71b6f5..2970024ea74ef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=17847c8e12b2bcfce26a79f425f082c31d4ded822f99a66127eee2d96bf18216 +distributionSha256Sum=53b71812f18cdb2777e9f1b2a0f2038683907c90bdc406bc64d8b400e1fb2c3b From 6f02cd86d245cd537f14e8d2070150780538c542 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 29 Jan 2019 08:18:26 -0800 Subject: [PATCH 092/121] [DOCS] Changes release-state for 6.6 --- docs/Versions.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index 3349e9a943f5d..96ae25531da9a 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -11,7 +11,7 @@ release-state can be: released | prerelease | unreleased ////////// -:release-state: unreleased +:release-state: released :issue: https://github.com/elastic/elasticsearch/issues/ :ml-issue: https://github.com/elastic/ml-cpp/issues/ From 85675df6d5c054cab830293361cd59439ee77f5b Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Tue, 29 Jan 2019 21:20:09 +0200 Subject: [PATCH 093/121] SQL: Make error msg for validation of 2nd arg of PERCENTILE[_RANK] consistent (#37937) Use `first` and `second` instead of `1st` and `2nd`. --- .../xpack/sql/expression/function/aggregate/Percentile.java | 2 +- .../sql/expression/function/aggregate/PercentileRank.java | 2 +- .../sql/analysis/analyzer/VerifierErrorMessagesTests.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java index c0118e96833f2..738209e2a406d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Percentile.java @@ -43,7 +43,7 @@ public Percentile replaceChildren(List newChildren) { @Override protected TypeResolution resolveType() { if (!percent.foldable()) { - return new TypeResolution(format(null, "2nd argument of PERCENTILE must be a constant, received [{}]", + return new TypeResolution(format(null, "Second argument of PERCENTILE must be a constant, received [{}]", Expressions.name(percent))); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java index 41a3d51e50e6e..957ef9d2b9b89 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/PercentileRank.java @@ -43,7 +43,7 @@ public Expression replaceChildren(List newChildren) { @Override protected TypeResolution resolveType() { if (!value.foldable()) { - return new TypeResolution(format(null, "2nd argument of PERCENTILE_RANK must be a constant, received [{}]", + return new TypeResolution(format(null, "Second argument of PERCENTILE_RANK must be a constant, received [{}]", Expressions.name(value))); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index 03d2285836614..ba2924a0eaeca 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -531,12 +531,12 @@ public void testAggsInHistogram() { } public void testErrorMessageForPercentileWithSecondArgBasedOnAField() { - assertEquals("1:8: 2nd argument of PERCENTILE must be a constant, received [ABS(int)]", + assertEquals("1:8: Second argument of PERCENTILE must be a constant, received [ABS(int)]", error("SELECT PERCENTILE(int, ABS(int)) FROM test")); } public void testErrorMessageForPercentileRankWithSecondArgBasedOnAField() { - assertEquals("1:8: 2nd argument of PERCENTILE_RANK must be a constant, received [ABS(int)]", + assertEquals("1:8: Second argument of PERCENTILE_RANK must be a constant, received [ABS(int)]", error("SELECT PERCENTILE_RANK(int, ABS(int)) FROM test")); } } From d0d8b0cbd8b1b3b6b9278c11da457e3e6822dc8f Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Tue, 29 Jan 2019 23:50:09 +0200 Subject: [PATCH 094/121] Bump 6.6 branch to version 6.6.1 (#37975) --- buildSrc/version.properties | 2 +- server/src/main/java/org/elasticsearch/Version.java | 10 +++++----- .../xpack/ml/action/TransportOpenJobActionTests.java | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 6c1923efe31b5..81f9ccf660e07 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -1,4 +1,4 @@ -elasticsearch = 6.6.0 +elasticsearch = 6.6.1 lucene = 7.6.0 # optional dependencies diff --git a/server/src/main/java/org/elasticsearch/Version.java b/server/src/main/java/org/elasticsearch/Version.java index fcc140f88d895..4bc8f39a67f50 100644 --- a/server/src/main/java/org/elasticsearch/Version.java +++ b/server/src/main/java/org/elasticsearch/Version.java @@ -198,12 +198,12 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_6_5_3 = new Version(V_6_5_3_ID, org.apache.lucene.util.Version.LUCENE_7_5_0); public static final int V_6_5_4_ID = 6050499; public static final Version V_6_5_4 = new Version(V_6_5_4_ID, org.apache.lucene.util.Version.LUCENE_7_5_0); - public static final int V_6_5_5_ID = 6050599; - public static final Version V_6_5_5 = new Version(V_6_5_5_ID, org.apache.lucene.util.Version.LUCENE_7_5_0); public static final int V_6_6_0_ID = 6060099; public static final Version V_6_6_0 = new Version(V_6_6_0_ID, org.apache.lucene.util.Version.LUCENE_7_6_0); + public static final int V_6_6_1_ID = 6060199; + public static final Version V_6_6_1 = new Version(V_6_6_1_ID, org.apache.lucene.util.Version.LUCENE_7_6_0); - public static final Version CURRENT = V_6_6_0; + public static final Version CURRENT = V_6_6_1; static { assert CURRENT.luceneVersion.equals(org.apache.lucene.util.Version.LATEST) : "Version must be upgraded to [" @@ -216,10 +216,10 @@ public static Version readVersion(StreamInput in) throws IOException { public static Version fromId(int id) { switch (id) { + case V_6_6_1_ID: + return V_6_6_1; case V_6_6_0_ID: return V_6_6_0; - case V_6_5_5_ID: - return V_6_5_5; case V_6_5_4_ID: return V_6_5_4; case V_6_5_3_ID: diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java index 8f22673e27d47..86042223cf36e 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportOpenJobActionTests.java @@ -441,8 +441,9 @@ public void testSelectLeastLoadedMlNode_indexJobsCannotBeAssignedToPre660Node() Job job = jobWithRules("v660-job"); Assignment result = TransportOpenJobAction.selectLeastLoadedMlNode("v660-job", job, cs.build(), 2, 10, 30, memoryTracker, logger); assertNull(result.getExecutorNode()); - assertEquals("Not opening job [v660-job] on node [_node_name1] version [6.5.0], " + - "because this node does not support jobs of version [6.6.0]", result.getExplanation()); + assertThat(result.getExplanation(), + containsString("Not opening job [v660-job] on node [_node_name1] version [6.5.0], " + + "because this node does not support jobs of version [6.6.")); nodes = DiscoveryNodes.builder() .add(new DiscoveryNode("_node_name1", "_node_id1", new TransportAddress(InetAddress.getLoopbackAddress(), 9300), From 754b70cd35ef0aeb742c93de61dd7a1fef37d6d4 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Wed, 30 Jan 2019 11:34:47 +0200 Subject: [PATCH 095/121] SQL: Skip the nested and object field types in case of an ODBC request (#37948) (cherry picked from commit 908c8def063e529ee4f1aa146d43132b9dafd66e) --- .../plan/logical/command/sys/SysColumns.java | 76 ++++++++++--------- .../logical/command/sys/SysColumnsTests.java | 61 +++++++++++++-- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java index 073dea1f7dae9..e6a1247eade4d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java @@ -133,42 +133,46 @@ static void fillInRows(String clusterName, String indexName, Map> rows = new ArrayList<>(); SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null, true); - assertEquals(16, rows.size()); + assertEquals(14, rows.size()); assertEquals(24, rows.get(0).size()); List row = rows.get(0); @@ -90,6 +90,16 @@ public void testSysColumnsInOdbcMode() { assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + row = rows.get(3); + assertEquals("keyword", name(row)); + assertEquals((short) Types.VARCHAR, sqlType(row)); + assertEquals(null, radix(row)); + assertEquals(Integer.MAX_VALUE, bufferLength(row)); + assertNull(decimalPrecision(row)); + assertEquals(Short.class, nullable(row).getClass()); + assertEquals(Short.class, sqlDataType(row).getClass()); + assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + row = rows.get(4); assertEquals("date", name(row)); assertEquals((short) Types.TIMESTAMP, sqlType(row)); @@ -101,17 +111,58 @@ public void testSysColumnsInOdbcMode() { assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + row = rows.get(5); + assertEquals("unsupported", name(row)); + assertEquals((short) Types.OTHER, sqlType(row)); + assertEquals(null, radix(row)); + assertEquals(0, precision(row)); + assertEquals(0, bufferLength(row)); + assertNull(decimalPrecision(row)); + assertEquals(Short.class, nullable(row).getClass()); + assertEquals(Short.class, sqlDataType(row).getClass()); + assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + + row = rows.get(6); + assertEquals("some.dotted.field", name(row)); + assertEquals((short) Types.VARCHAR, sqlType(row)); + assertEquals(null, radix(row)); + assertEquals(Integer.MAX_VALUE, bufferLength(row)); + assertNull(decimalPrecision(row)); + assertEquals(Short.class, nullable(row).getClass()); + assertEquals(Short.class, sqlDataType(row).getClass()); + assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + row = rows.get(7); - assertEquals("some.dotted", name(row)); - assertEquals((short) Types.STRUCT, sqlType(row)); + assertEquals("some.string", name(row)); + assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals(null, radix(row)); - assertEquals(-1, bufferLength(row)); + assertEquals(Integer.MAX_VALUE, bufferLength(row)); assertNull(decimalPrecision(row)); assertEquals(Short.class, nullable(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass()); - row = rows.get(15); + row = rows.get(8); + assertEquals("some.string.normalized", name(row)); + assertEquals((short) Types.VARCHAR, sqlType(row)); + assertEquals(null, radix(row)); + assertEquals(Integer.MAX_VALUE, bufferLength(row)); + assertNull(decimalPrecision(row)); + assertEquals(Short.class, nullable(row).getClass()); + assertEquals(Short.class, sqlDataType(row).getClass()); + assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + + row = rows.get(9); + assertEquals("some.string.typical", name(row)); + assertEquals((short) Types.VARCHAR, sqlType(row)); + assertEquals(null, radix(row)); + assertEquals(Integer.MAX_VALUE, bufferLength(row)); + assertNull(decimalPrecision(row)); + assertEquals(Short.class, nullable(row).getClass()); + assertEquals(Short.class, sqlDataType(row).getClass()); + assertEquals(Short.class, sqlDataTypeSub(row).getClass()); + + row = rows.get(13); assertEquals("some.ambiguous.normalized", name(row)); assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals(null, radix(row)); From 59ff166eca8af45268dcd7cc28ca545606b0c65e Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 31 Jan 2019 10:52:49 +0200 Subject: [PATCH 096/121] SQL: Added SSL configuration options tests (#37875) * Removed the allow.self.signed option from the documentation since we allow by default self signed certificates as well. (cherry picked from commit 22d32900784600f99b907ef3e81aca94d5c78f9c) --- docs/reference/sql/endpoints/jdbc.asciidoc | 2 - .../sql/jdbc/JdbcConfigurationTests.java | 157 +++++++++++++++++- .../xpack/sql/client/SslConfig.java | 2 +- 3 files changed, 157 insertions(+), 4 deletions(-) diff --git a/docs/reference/sql/endpoints/jdbc.asciidoc b/docs/reference/sql/endpoints/jdbc.asciidoc index e9af3492adcaa..56c68fd34937f 100644 --- a/docs/reference/sql/endpoints/jdbc.asciidoc +++ b/docs/reference/sql/endpoints/jdbc.asciidoc @@ -115,8 +115,6 @@ Query timeout (in seconds). That is the maximum amount of time waiting for a que `ssl.truststore.pass`:: trust store password -`ssl.cert.allow.self.signed` (default `false`):: Whether or not to allow self signed certificates - `ssl.protocol`(default `TLS`):: SSL protocol to be used [float] diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfigurationTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfigurationTests.java index 5f0f523fb009f..dac9dbba61776 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfigurationTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfigurationTests.java @@ -6,9 +6,16 @@ package org.elasticsearch.xpack.sql.jdbc; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.client.SslConfig; +import java.net.URI; +import java.net.URISyntaxException; +import java.sql.DriverManager; import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; import static org.elasticsearch.xpack.sql.client.ConnectionConfiguration.CONNECT_TIMEOUT; import static org.elasticsearch.xpack.sql.client.ConnectionConfiguration.PAGE_TIMEOUT; @@ -130,5 +137,153 @@ public void testTimoutOverride() throws Exception { assertThat(ci.pageTimeout(), equalTo(4L)); } - + public void testSSLPropertiesInUrl() throws Exception { + Map urlPropMap = sslProperties(); + + Properties allProps = new Properties(); + allProps.putAll(urlPropMap); + String sslUrlProps = urlPropMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); + + assertSslConfig(allProps, ci("jdbc:es://test?" + sslUrlProps.toString()).sslConfig()); + } + + public void testSSLPropertiesInUrlAndProperties() throws Exception { + Map urlPropMap = new HashMap<>(4); + urlPropMap.put("ssl", "false"); + urlPropMap.put("ssl.protocol", "SSLv3"); + urlPropMap.put("ssl.keystore.location", "/abc/xyz"); + urlPropMap.put("ssl.keystore.pass", "mypass"); + + Map propMap = new HashMap<>(4); + propMap.put("ssl.keystore.type", "PKCS12"); + propMap.put("ssl.truststore.location", "/foo/bar"); + propMap.put("ssl.truststore.pass", "anotherpass"); + propMap.put("ssl.truststore.type", "jks"); + + Properties props = new Properties(); + props.putAll(propMap); + String sslUrlProps = urlPropMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); + + Properties allProps = new Properties(); + allProps.putAll(urlPropMap); + allProps.putAll(propMap); + assertSslConfig(allProps, JdbcConfiguration.create("jdbc:es://test?" + sslUrlProps.toString(), props, 0).sslConfig()); + } + + public void testSSLPropertiesOverride() throws Exception { + Map urlPropMap = sslProperties(); + Map propMap = new HashMap<>(8); + propMap.put("ssl", "false"); + propMap.put("ssl.protocol", "TLS"); + propMap.put("ssl.keystore.location", "/xyz"); + propMap.put("ssl.keystore.pass", "different_mypass"); + propMap.put("ssl.keystore.type", "JKS"); + propMap.put("ssl.truststore.location", "/baz"); + propMap.put("ssl.truststore.pass", "different_anotherpass"); + propMap.put("ssl.truststore.type", "PKCS11"); + + Properties props = new Properties(); + props.putAll(propMap); + String sslUrlProps = urlPropMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); + assertSslConfig(props, JdbcConfiguration.create("jdbc:es://test?" + sslUrlProps.toString(), props, 0).sslConfig()); + } + + public void testDriverConfigurationWithSSLInURL() { + Map urlPropMap = sslProperties(); + + Properties allProps = new Properties(); + allProps.putAll(urlPropMap); + String sslUrlProps = urlPropMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); + + try { + DriverManager.getDriver("jdbc:es://test?" + sslUrlProps); + } catch (SQLException sqle) { + fail("Driver registration should have been successful. Error: " + sqle); + } + } + + public void testDataSourceConfigurationWithSSLInURL() throws SQLException, URISyntaxException { + Map urlPropMap = sslProperties(); + + Properties allProps = new Properties(); + allProps.putAll(urlPropMap); + String sslUrlProps = urlPropMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&")); + + EsDataSource dataSource = new EsDataSource(); + String address = "jdbc:es://test?" + sslUrlProps; + dataSource.setUrl(address); + JdbcConnection connection = null; + + try { + connection = (JdbcConnection) dataSource.getConnection(); + } catch (SQLException sqle) { + fail("Connection creation should have been successful. Error: " + sqle); + } + + assertEquals(address, connection.getURL()); + assertSslConfig(allProps, connection.cfg.sslConfig()); + } + + public void testTyposInSslConfigInUrl(){ + assertJdbcSqlExceptionFromUrl("ssl.protocl", "ssl.protocol"); + assertJdbcSqlExceptionFromUrl("sssl", "ssl"); + assertJdbcSqlExceptionFromUrl("ssl.keystore.lction", "ssl.keystore.location"); + assertJdbcSqlExceptionFromUrl("ssl.keystore.pss", "ssl.keystore.pass"); + assertJdbcSqlExceptionFromUrl("ssl.keystore.typ", "ssl.keystore.type"); + assertJdbcSqlExceptionFromUrl("ssl.trustsore.location", "ssl.truststore.location"); + assertJdbcSqlExceptionFromUrl("ssl.tuststore.pass", "ssl.truststore.pass"); + assertJdbcSqlExceptionFromUrl("ssl.ruststore.type", "ssl.truststore.type"); + } + + public void testTyposInSslConfigInProperties() { + assertJdbcSqlExceptionFromProperties("ssl.protocl", "ssl.protocol"); + assertJdbcSqlExceptionFromProperties("sssl", "ssl"); + assertJdbcSqlExceptionFromProperties("ssl.keystore.lction", "ssl.keystore.location"); + assertJdbcSqlExceptionFromProperties("ssl.keystore.pss", "ssl.keystore.pass"); + assertJdbcSqlExceptionFromProperties("ssl.keystore.typ", "ssl.keystore.type"); + assertJdbcSqlExceptionFromProperties("ssl.trustsore.location", "ssl.truststore.location"); + assertJdbcSqlExceptionFromProperties("ssl.tuststore.pass", "ssl.truststore.pass"); + assertJdbcSqlExceptionFromProperties("ssl.ruststore.type", "ssl.truststore.type"); + } + + private Map sslProperties() { + Map sslPropertiesMap = new HashMap<>(8); + // always using "false" so that the SSLContext doesn't actually start verifying the keystore and trustore + // locations, as we don't have file permissions to access them. + sslPropertiesMap.put("ssl", "false"); + sslPropertiesMap.put("ssl.protocol", "SSLv3"); + sslPropertiesMap.put("ssl.keystore.location", "/abc/xyz"); + sslPropertiesMap.put("ssl.keystore.pass", "mypass"); + sslPropertiesMap.put("ssl.keystore.type", "PKCS12"); + sslPropertiesMap.put("ssl.truststore.location", "/foo/bar"); + sslPropertiesMap.put("ssl.truststore.pass", "anotherpass"); + sslPropertiesMap.put("ssl.truststore.type", "jks"); + + return sslPropertiesMap; + } + + private void assertSslConfig(Properties allProperties, SslConfig sslConfig) throws URISyntaxException { + // because SslConfig doesn't expose its internal properties (and it shouldn't), + // we compare a newly created SslConfig with the one from the JdbcConfiguration with the equals() method + SslConfig mockSslConfig = new SslConfig(allProperties, new URI("http://test:9200/")); + assertEquals(mockSslConfig, sslConfig); + } + + private void assertJdbcSqlExceptionFromUrl(String wrongSetting, String correctSetting) { + String url = "jdbc:es://test?" + wrongSetting + "=foo"; + assertJdbcSqlException(wrongSetting, correctSetting, url, null); + } + + private void assertJdbcSqlExceptionFromProperties(String wrongSetting, String correctSetting) { + String url = "jdbc:es://test"; + Properties props = new Properties(); + props.put(wrongSetting, correctSetting); + assertJdbcSqlException(wrongSetting, correctSetting, url, props); + } + + private void assertJdbcSqlException(String wrongSetting, String correctSetting, String url, Properties props) { + JdbcSQLException ex = expectThrows(JdbcSQLException.class, + () -> JdbcConfiguration.create(url, props, 0)); + assertEquals("Unknown parameter [" + wrongSetting + "] ; did you mean [" + correctSetting + "]", ex.getMessage()); + } } diff --git a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/SslConfig.java b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/SslConfig.java index 9e89f7b848c47..dd00a8e914a48 100644 --- a/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/SslConfig.java +++ b/x-pack/plugin/sql/sql-client/src/main/java/org/elasticsearch/xpack/sql/client/SslConfig.java @@ -63,7 +63,7 @@ public class SslConfig { private final SSLContext sslContext; - SslConfig(Properties settings, URI baseURI) { + public SslConfig(Properties settings, URI baseURI) { boolean isSchemaPresent = baseURI.getScheme() != null; boolean isSSLPropertyPresent = settings.getProperty(SSL) != null; boolean isHttpsScheme = "https".equals(baseURI.getScheme()); From 4408297aadcd62c0c77d5d5675a9a12509147634 Mon Sep 17 00:00:00 2001 From: David Kyle Date: Thu, 3 Jan 2019 10:23:13 +0000 Subject: [PATCH 097/121] [ML] Only run migration checks against post v6.6.0 clusters (#37077) --- .../xpack/restart/MlMigrationFullClusterRestartIT.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/MlMigrationFullClusterRestartIT.java b/x-pack/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/MlMigrationFullClusterRestartIT.java index 061083d951bce..59c634981d198 100644 --- a/x-pack/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/MlMigrationFullClusterRestartIT.java +++ b/x-pack/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/MlMigrationFullClusterRestartIT.java @@ -205,6 +205,12 @@ private void waitForDatafeedToBeAssigned(String datafeedId) throws Exception { @SuppressWarnings("unchecked") private void waitForMigration(List expectedMigratedJobs, List expectedMigratedDatafeeds, List unMigratedJobs, List unMigratedDatafeeds) throws Exception { + + // After v6.6.0 jobs are created in the index so no migration will take place + if (getOldClusterVersion().onOrAfter(Version.V_6_6_0)) { + return; + } + assertBusy(() -> { // wait for the eligible configs to be moved from the clusterstate Request getClusterState = new Request("GET", "/_cluster/state/metadata"); From e6a238f45ab86d4c1249f0d11a4926d543ab0c85 Mon Sep 17 00:00:00 2001 From: Marc <498834+cawoodm@users.noreply.github.com> Date: Thu, 31 Jan 2019 16:16:17 +0100 Subject: [PATCH 098/121] "_doc" is correct, not "doc" (#38076) --- docs/reference/mapping.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/mapping.asciidoc b/docs/reference/mapping.asciidoc index c4a7d48fb674a..a56d0b7a4a255 100644 --- a/docs/reference/mapping.asciidoc +++ b/docs/reference/mapping.asciidoc @@ -152,7 +152,7 @@ PUT my_index <1> --------------------------------------- // CONSOLE <1> Create an index called `my_index`. -<2> Add a mapping type called `doc`. +<2> Add a mapping type called `_doc`. <3> Specify fields or _properties_. <4> Specify the data `type` and mapping for each field. From 9a090040ca478f2780022474a89267d32771d976 Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Fri, 1 Feb 2019 16:13:51 +0200 Subject: [PATCH 099/121] SQL: [Docs] Add limitation for aggregate functions on scalars (#38186) Currently aggregate functions can operate only directly on fields. They cannot be used on top of scalar functions as painless scripting is currently not supported. --- docs/reference/sql/limitations.asciidoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/reference/sql/limitations.asciidoc b/docs/reference/sql/limitations.asciidoc index 33a0859a7fda1..792091800ffa1 100644 --- a/docs/reference/sql/limitations.asciidoc +++ b/docs/reference/sql/limitations.asciidoc @@ -70,6 +70,12 @@ When doing aggregations (`GROUP BY`) {es-sql} relies on {es}'s `composite` aggre But this type of aggregation does come with a limitation: sorting can only be applied on the key used for the aggregation's buckets. This means that queries like `SELECT * FROM test GROUP BY age ORDER BY COUNT(*)` are not possible. +[float] +=== Using aggregation functions on top of scalar functions + +Aggregation functions like <>, <>, etc. can only be used +directly on fields, and so queries like `SELECT MAX(abs(age)) FROM test` are not possible. + [float] === Using a sub-select @@ -90,3 +96,4 @@ include-tagged::{sql-specs}/docs.csv-spec[limitationSubSelectRewritten] But, if the sub-select would include a `GROUP BY` or `HAVING` or the enclosing `SELECT` would be more complex than `SELECT X FROM (SELECT ...) WHERE [simple_condition]`, this is currently **un-supported**. + From cf6c40a02d0326676bc96c0430abcc1ebaf7afc2 Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Fri, 1 Feb 2019 07:22:21 -0700 Subject: [PATCH 100/121] Fix MasterServiceTests.testClusterStateUpdateLogging (#38142) Backport of #38116 This changes the test to not use a `CountDownlatch`, instead adding an assertion for the final logging message and waiting until the `MockAppender` has seen it before proceeding. Related to df2c06f6f30f7e23a6863a3f72fc3bdb7648885c Resolves #23739 --- .../cluster/service/MasterServiceTests.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java index b8b6b2dbde49d..e65f9a3bd242c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java @@ -325,11 +325,16 @@ public void testClusterStateUpdateLogging() throws Exception { MasterService.class.getCanonicalName(), Level.DEBUG, "*processing [test3]: took [3s] done publishing updated cluster state (version: *, uuid: *)")); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test4", + MasterService.class.getCanonicalName(), + Level.DEBUG, + "*processing [test4]: took [0s] no change in cluster state")); Logger clusterLogger = LogManager.getLogger(MasterService.class); Loggers.addAppender(clusterLogger, mockAppender); try { - final CountDownLatch latch = new CountDownLatch(4); masterService.currentTimeOverride = System.nanoTime(); masterService.submitStateUpdateTask("test1", new ClusterStateUpdateTask() { @Override @@ -339,9 +344,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { } @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { } @Override public void onFailure(String source, Exception e) { @@ -361,9 +364,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } @Override - public void onFailure(String source, Exception e) { - latch.countDown(); - } + public void onFailure(String source, Exception e) { } }); masterService.submitStateUpdateTask("test3", new ClusterStateUpdateTask() { @Override @@ -373,9 +374,7 @@ public ClusterState execute(ClusterState currentState) { } @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { } @Override public void onFailure(String source, Exception e) { @@ -391,21 +390,18 @@ public ClusterState execute(ClusterState currentState) { } @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { } @Override public void onFailure(String source, Exception e) { fail(); } }); - latch.await(); + assertBusy(mockAppender::assertAllExpectationsMatched); } finally { Loggers.removeAppender(clusterLogger, mockAppender); mockAppender.stop(); } - mockAppender.assertAllExpectationsMatched(); } public void testClusterStateBatchedUpdates() throws BrokenBarrierException, InterruptedException { From abb1c564b8f3c25e1e2078638b004dc3d92557e8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 1 Feb 2019 14:09:51 -0800 Subject: [PATCH 101/121] Allow built-in monitoring_user role to call GET _xpack API (#38220) Backport of #38060 to `6.6`. Original description: This PR adds the `monitor/xpack/info` cluster-level privilege to the built-in `monitoring_user` role. This privilege is required for the Monitoring UI to call the `GET _xpack API` on the Monitoring Cluster. It needs to do this in order to determine the license of the Monitoring Cluster, which further determines whether Cluster Alerts are shown to the user or not. Resolves #37970. - Have you signed the [contributor license agreement](https://www.elastic.co/contributor-agreement)? - Have you followed the [contributor guidelines](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md)? - If submitting code, have you built your formula locally prior to submission with `gradle check`? - If submitting code, is your pull request against master? Unless there is a good reason otherwise, we prefer pull requests against master and will backport as needed. - If submitting code, have you checked that your submission is for an [OS that we support](https://www.elastic.co/support/matrix#show_os)? - If you are submitting this code for a class then read our [policy](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#contributing-as-part-of-a-class) for that. --- .../xpack/core/security/authz/store/ReservedRolesStore.java | 2 +- .../core/security/authz/store/ReservedRolesStoreTests.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index 583a060ddbc6d..6b0c7d5bf010d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -58,7 +58,7 @@ private static Map initializeReservedRoles() { null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null)) .put("monitoring_user", new RoleDescriptor("monitoring_user", - new String[] { "cluster:monitor/main" }, + new String[] { "cluster:monitor/main", "cluster:monitor/xpack/info" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() .indices(".monitoring-*").privileges("read", "read_cross_cluster").build() diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index f73c6c52957f8..737f4e7946aaf 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.xpack.core.action.XPackInfoAction; import org.elasticsearch.xpack.core.ml.MlMetaIndex; import org.elasticsearch.xpack.core.ml.action.CloseJobAction; import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction; @@ -337,6 +338,7 @@ public void testMonitoringUserRole() { Role monitoringUserRole = Role.builder(roleDescriptor, null).build(); assertThat(monitoringUserRole.cluster().check(MainAction.NAME, request), is(true)); + assertThat(monitoringUserRole.cluster().check(XPackInfoAction.NAME, request), is(true)); assertThat(monitoringUserRole.cluster().check(ClusterHealthAction.NAME, request), is(false)); assertThat(monitoringUserRole.cluster().check(ClusterStateAction.NAME, request), is(false)); assertThat(monitoringUserRole.cluster().check(ClusterStatsAction.NAME, request), is(false)); From d16c667dfacca8b761a3144399bfd915dc6ef53f Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 1 Feb 2019 16:01:55 -0500 Subject: [PATCH 102/121] Replace awaitBusy with assertBusy in atLeastDocsIndexed (#38190) Unlike assertBusy, awaitBusy does not retry if the code-block throws an AssertionError. A refresh in atLeastDocsIndexed can fail because we call this method while we are closing some node in FollowerFailOverIT. --- .../test/java/org/elasticsearch/xpack/CcrIntegTestCase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java index e6a35349dcce0..2f05b8e816999 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java @@ -477,14 +477,14 @@ private Map> getDocIdAndSeqNos(InternalTestClus return docs; } - protected void atLeastDocsIndexed(Client client, String index, long numDocsReplicated) throws InterruptedException { + protected void atLeastDocsIndexed(Client client, String index, long numDocsReplicated) throws Exception { logger.info("waiting for at least [{}] documents to be indexed into index [{}]", numDocsReplicated, index); - awaitBusy(() -> { + assertBusy(() -> { refresh(client, index); SearchRequest request = new SearchRequest(index); request.source(new SearchSourceBuilder().size(0)); SearchResponse response = client.search(request).actionGet(); - return response.getHits().getTotalHits() >= numDocsReplicated; + assertThat(response.getHits().getTotalHits(), greaterThanOrEqualTo(numDocsReplicated)); }, 60, TimeUnit.SECONDS); } From 920eba2e098afdaea7c9c13d833d66267dc779c7 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 1 Feb 2019 15:44:39 -0500 Subject: [PATCH 103/121] Enable trace log in FollowerFailOverIT (#38148) This suite still fails one per week sometimes with a worrying assertion. Sadly we are still unable to find the actual source. Expected: but: was This change enables trace log in the suite so we will have a better picture if this fails again. Relates #3333 --- .../bulk/TransportBulkShardOperationsAction.java | 10 ++++++++++ .../xpack/ccr/index/engine/FollowingEngine.java | 3 +++ .../elasticsearch/xpack/CcrIntegTestCase.java | 16 +++++++++------- .../xpack/ccr/FollowerFailOverIT.java | 2 ++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/bulk/TransportBulkShardOperationsAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/bulk/TransportBulkShardOperationsAction.java index 7bbc9a1c7d98f..32e0754a47c22 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/bulk/TransportBulkShardOperationsAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/bulk/TransportBulkShardOperationsAction.java @@ -63,6 +63,9 @@ public TransportBulkShardOperationsAction( @Override protected WritePrimaryResult shardOperationOnPrimary( final BulkShardOperationsRequest request, final IndexShard primary) throws Exception { + if (logger.isTraceEnabled()) { + logger.trace("index [{}] on the following primary shard {}", request.getOperations(), primary.routingEntry()); + } return shardOperationOnPrimary(request.shardId(), request.getHistoryUUID(), request.getOperations(), request.getMaxSeqNoOfUpdatesOrDeletes(), primary, logger); } @@ -137,6 +140,10 @@ public static CcrWritePrimaryResult shardOperationOnPrimary( // replicated to replicas but with the existing primary term (not the current primary term) in order // to guarantee the consistency between the primary and replicas, and between translog and Lucene index. final AlreadyProcessedFollowingEngineException failure = (AlreadyProcessedFollowingEngineException) result.getFailure(); + if (logger.isTraceEnabled()) { + logger.trace("operation [{}] was processed before on following primary shard {} with existing term {}", + targetOp, primary.routingEntry(), failure.getExistingPrimaryTerm()); + } assert failure.getSeqNo() == targetOp.seqNo() : targetOp.seqNo() + " != " + failure.getSeqNo(); if (failure.getExistingPrimaryTerm().isPresent()) { appliedOperations.add(rewriteOperationWithPrimaryTerm(sourceOp, failure.getExistingPrimaryTerm().getAsLong())); @@ -159,6 +166,9 @@ public static CcrWritePrimaryResult shardOperationOnPrimary( @Override protected WriteReplicaResult shardOperationOnReplica( final BulkShardOperationsRequest request, final IndexShard replica) throws Exception { + if (logger.isTraceEnabled()) { + logger.trace("index [{}] on the following replica shard {}", request.getOperations(), replica.routingEntry()); + } return shardOperationOnReplica(request, replica, logger); } diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/index/engine/FollowingEngine.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/index/engine/FollowingEngine.java index c8f62b41618e6..041fd1903ad0d 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/index/engine/FollowingEngine.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/index/engine/FollowingEngine.java @@ -74,6 +74,9 @@ protected InternalEngine.IndexingStrategy indexingStrategyForOperation(final Ind final long maxSeqNoOfUpdatesOrDeletes = getMaxSeqNoOfUpdatesOrDeletes(); assert maxSeqNoOfUpdatesOrDeletes != SequenceNumbers.UNASSIGNED_SEQ_NO : "max_seq_no_of_updates is not initialized"; if (hasBeenProcessedBefore(index)) { + if (logger.isTraceEnabled()) { + logger.trace("index operation [id={} seq_no={} origin={}] was processed before", index.id(), index.seqNo(), index.origin()); + } if (index.origin() == Operation.Origin.PRIMARY) { /* * The existing operation in this engine was probably assigned the term of the previous primary shard which is different diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java index 2f05b8e816999..fd26217c742be 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java @@ -434,6 +434,13 @@ public static ResumeFollowAction.Request resumeFollow(String followerIndex) { * on the follower equal the leader's; then verifies the existing pairs of (docId, seqNo) on the follower also equal the leader. */ protected void assertIndexFullyReplicatedToFollower(String leaderIndex, String followerIndex) throws Exception { + logger.info("--> asserting <> between {} and {}", leaderIndex, followerIndex); + assertBusy(() -> { + Map> docsOnFollower = getDocIdAndSeqNos(clusterGroup.followerCluster, followerIndex); + logger.info("--> docs on the follower {}", docsOnFollower); + assertThat(docsOnFollower, equalTo(getDocIdAndSeqNos(clusterGroup.leaderCluster, leaderIndex))); + }, 120, TimeUnit.SECONDS); + logger.info("--> asserting seq_no_stats between {} and {}", leaderIndex, followerIndex); assertBusy(() -> { Map leaderStats = new HashMap<>(); @@ -450,13 +457,8 @@ protected void assertIndexFullyReplicatedToFollower(String leaderIndex, String f } followerStats.put(shardStat.getShardRouting().shardId().id(), shardStat.getSeqNoStats()); } - assertThat(leaderStats, equalTo(followerStats)); - }, 60, TimeUnit.SECONDS); - logger.info("--> asserting <> between {} and {}", leaderIndex, followerIndex); - assertBusy(() -> { - assertThat(getDocIdAndSeqNos(clusterGroup.leaderCluster, leaderIndex), - equalTo(getDocIdAndSeqNos(clusterGroup.followerCluster, followerIndex))); - }, 60, TimeUnit.SECONDS); + assertThat(followerStats, equalTo(leaderStats)); + }, 120, TimeUnit.SECONDS); } private Map> getDocIdAndSeqNos(InternalTestCluster cluster, String index) throws IOException { diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java index d58a2d0a0f18d..c9b5ffbca6140 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.InternalTestCluster; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.xpack.CcrIntegTestCase; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; @@ -33,6 +34,7 @@ import static java.util.Collections.singletonMap; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +@TestLogging("org.elasticsearch.xpack.ccr:TRACE,org.elasticsearch.index.shard:DEBUG") public class FollowerFailOverIT extends CcrIntegTestCase { @Override From de7accb7a133167357edbde9b28c82b35a90fd0e Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Sun, 3 Feb 2019 13:24:12 +0200 Subject: [PATCH 104/121] Fix NPE in Logfile Audit Filter (#38120) (#38273) The culprit in #38097 is an `IndicesRequest` that has no indices, but instead of `request.indices()` returning `null` or `String[0]` it returned `String[] {null}` . This tripped the audit filter. I have addressed this in two ways: 1. `request.indices()` returning `String[] {null}` is treated as `null` or `String[0]`, i.e. no indices 2. `null` values among the roles and indices lists, which are unexpected, will never again stumble the audit filter; `null` values are treated as special values that will not match any policy, i.e. their events will always be printed. Closes #38097 --- .../audit/logfile/LoggingAuditTrail.java | 14 +++- .../logfile/LoggingAuditTrailFilterTests.java | 83 ++++++++++++++++++- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index 3f1c4b7bcec89..ac3845d103f27 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -52,6 +52,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.TreeMap; import java.util.function.Function; @@ -832,6 +833,7 @@ static final class EventFilterPolicy { EventFilterPolicy(String name, Predicate ignorePrincipalsPredicate, Predicate ignoreRealmsPredicate, Predicate ignoreRolesPredicate, Predicate ignoreIndicesPredicate) { this.name = name; + // "null" values are "unexpected" and should not match any ignore policy this.ignorePrincipalsPredicate = ignorePrincipalsPredicate; this.ignoreRealmsPredicate = ignoreRealmsPredicate; this.ignoreRolesPredicate = ignoreRolesPredicate; @@ -881,8 +883,10 @@ private static List emptyStringBuildsEmptyAutomaton(List l) { * predicate of the corresponding field. */ Predicate ignorePredicate() { - return eventInfo -> ignorePrincipalsPredicate.test(eventInfo.principal) && ignoreRealmsPredicate.test(eventInfo.realm) - && eventInfo.roles.get().allMatch(ignoreRolesPredicate) && eventInfo.indices.get().allMatch(ignoreIndicesPredicate); + return eventInfo -> eventInfo.principal != null && ignorePrincipalsPredicate.test(eventInfo.principal) + && eventInfo.realm != null && ignoreRealmsPredicate.test(eventInfo.realm) + && eventInfo.roles.get().allMatch(role -> role != null && ignoreRolesPredicate.test(role)) + && eventInfo.indices.get().allMatch(index -> index != null && ignoreIndicesPredicate.test(index)); } @Override @@ -970,8 +974,10 @@ static final class AuditEventMetaInfo { // conditions on the `principal` and `realm` fields // 2. reusability of the AuditEventMetaInfo instance: in this case Streams have // to be regenerated as they cannot be operated upon twice - this.roles = () -> roles.filter(r -> r.length != 0).map(Arrays::stream).orElse(Stream.of("")); - this.indices = () -> indices.filter(i -> i.length != 0).map(Arrays::stream).orElse(Stream.of("")); + this.roles = () -> roles.filter(r -> r.length > 0).filter(a -> Arrays.stream(a).anyMatch(Objects::nonNull)) + .map(Arrays::stream).orElse(Stream.of("")); + this.indices = () -> indices.filter(i -> i.length > 0).filter(a -> Arrays.stream(a).anyMatch(Objects::nonNull)) + .map(Arrays::stream).orElse(Stream.of("")); } AuditEventMetaInfo(Optional authenticationToken, Optional realm, Optional indices) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java index 8cb3dbf01b247..6d4bb155ba704 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java @@ -82,6 +82,78 @@ public void init() throws Exception { }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); } + public void testPolicyDoesNotMatchNullValuesInEvent() throws Exception { + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO, null); + final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + // filter by username + final List filteredUsernames = randomNonEmptyListOfFilteredNames(); + final List filteredUsers = filteredUsernames.stream().map(u -> { + if (randomBoolean()) { + return new User(u); + } else { + return new User(new User(u), new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4))); + } + }).collect(Collectors.toList()); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.userPolicy.users", filteredUsernames); + // filter by realms + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.realmsPolicy.realms", filteredRealms); + // filter by roles + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.rolesPolicy.roles", filteredRoles); + // filter by indices + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.indicesPolicy.indices", filteredIndices); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + + // user field matches + assertTrue("Matches the user filter predicate.", auditTrail.eventFilterPolicyRegistry.ignorePredicate().test( + new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.empty(), Optional.empty(), Optional.empty()))); + final User unfilteredUser; + if (randomBoolean()) { + unfilteredUser = new User(null); + } else { + unfilteredUser = new User(new User(null), new User(randomFrom(filteredUsers).principal())); + } + // null user field does NOT match + assertFalse("Does not match the user filter predicate because of null username.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.of(unfilteredUser), Optional.empty(), Optional.empty(), Optional.empty()))); + // realm field matches + assertTrue("Matches the realm filter predicate.", auditTrail.eventFilterPolicyRegistry.ignorePredicate().test( + new AuditEventMetaInfo(Optional.empty(), Optional.of(randomFrom(filteredRealms)), Optional.empty(), Optional.empty()))); + // null realm field does NOT match + assertFalse("Does not match the realm filter predicate because of null realm.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.empty(), Optional.ofNullable(null), Optional.empty(), Optional.empty()))); + // role field matches + assertTrue("Matches the role filter predicate.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.empty()))); + final List unfilteredRoles = new ArrayList<>(); + unfilteredRoles.add(null); + unfilteredRoles.addAll(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles)); + // null role among roles field does NOT match + assertFalse("Does not match the role filter predicate because of null role.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate().test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), + Optional.of(unfilteredRoles.toArray(new String[0])), Optional.empty()))); + // indices field matches + assertTrue("Matches the index filter predicate.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate().test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), + Optional.empty(), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final List unfilteredIndices = new ArrayList<>(); + unfilteredIndices.add(null); + unfilteredIndices.addAll(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices)); + // null index among indices field does NOT match + assertFalse("Does not match the indices filter predicate because of null index.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate().test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), + Optional.empty(), Optional.of(unfilteredIndices.toArray(new String[0]))))); + } + public void testSingleCompletePolicyPredicate() throws Exception { final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO, null); final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); @@ -265,11 +337,18 @@ public void testSingleCompleteWithEmptyFieldPolicyPredicate() throws Exception { .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), Optional.of(someIndicesDoNotMatch.toArray(new String[0]))))); - final Optional emptyIndices = randomBoolean() ? Optional.empty() : Optional.of(new String[0]); assertTrue("Matches the filter predicate because of the empty indices.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), - emptyIndices))); + Optional.empty()))); + assertTrue("Matches the filter predicate because of the empty indices.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(new String[0])))); + assertTrue("Matches the filter predicate because of the empty indices.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(new String[] { null })))); } public void testTwoPolicyPredicatesWithMissingFields() throws Exception { From d05c24c2df6cb1b3e7aa11b906af991d1edfd601 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Sat, 2 Feb 2019 22:05:47 +0200 Subject: [PATCH 105/121] SQL: Generate relevant error message when grouping functions are not used in GROUP BY (#38017) * Add checks for Grouping functions restriction to be placed inside GROUP BY * Fixed bug where GROUP BY HISTOGRAM (not using alias) wasn't recognized properly in the Verifier due to functions equality not working correctly. (cherry picked from commit 6968f0925b3304a66516916cf69d4bcb9749ea30) --- .../sql/qa/src/main/resources/agg.csv-spec | 23 +++++++++++ .../xpack/sql/analysis/analyzer/Verifier.java | 20 +++++++++ .../function/grouping/GroupingFunction.java | 10 ----- .../function/grouping/Histogram.java | 17 +++++--- .../analyzer/VerifierErrorMessagesTests.java | 41 +++++++++++++++++-- 5 files changed, 92 insertions(+), 19 deletions(-) diff --git a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec index d1b1a0e0e8866..4e6d0d0d79fa4 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/agg.csv-spec @@ -311,6 +311,29 @@ SELECT HISTOGRAM(emp_no % 100, 10) AS h, COUNT(*) as c FROM test_emp GROUP BY h 0 |10 ; +histogramGroupByWithoutAlias +schema::h:ts|c:l +SELECT HISTOGRAM(birth_date, INTERVAL 1 YEAR) AS h, COUNT(*) as c FROM test_emp GROUP BY HISTOGRAM(birth_date, INTERVAL 1 YEAR) ORDER BY h DESC; + + h | c +--------------------+--------------- +1964-02-02T00:00:00Z|5 +1963-02-07T00:00:00Z|7 +1962-02-12T00:00:00Z|6 +1961-02-17T00:00:00Z|8 +1960-02-23T00:00:00Z|7 +1959-02-28T00:00:00Z|9 +1958-03-05T00:00:00Z|6 +1957-03-10T00:00:00Z|6 +1956-03-15T00:00:00Z|4 +1955-03-21T00:00:00Z|4 +1954-03-26T00:00:00Z|7 +1953-03-31T00:00:00Z|10 +1952-04-05T00:00:00Z|10 +1951-04-11T00:00:00Z|1 +null |10 +; + countAll schema::all_names:l|c:l SELECT COUNT(ALL first_name) all_names, COUNT(*) c FROM test_emp; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java index 189509e95114c..22d6eb86d8a69 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java @@ -19,6 +19,7 @@ import org.elasticsearch.xpack.sql.expression.function.Functions; import org.elasticsearch.xpack.sql.expression.function.Score; import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute; +import org.elasticsearch.xpack.sql.expression.function.grouping.GroupingFunction; import org.elasticsearch.xpack.sql.expression.function.grouping.GroupingFunctionAttribute; import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction; import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalFunction; @@ -225,6 +226,7 @@ Collection verify(LogicalPlan plan) { validateInExpression(p, localFailures); validateConditional(p, localFailures); + checkGroupingFunctionInGroupBy(p, localFailures); checkFilterOnAggs(p, localFailures); checkFilterOnGrouping(p, localFailures); @@ -560,6 +562,24 @@ private static boolean checkGroupMatch(Expression e, Node source, List localFailures) { + // check if the query has a grouping function (Histogram) but no GROUP BY + if (p instanceof Project) { + Project proj = (Project) p; + proj.projections().forEach(e -> e.forEachDown(f -> + localFailures.add(fail(f, "[%s] needs to be part of the grouping", Expressions.name(f))), GroupingFunction.class)); + } else if (p instanceof Aggregate) { + // if it does have a GROUP BY, check if the groupings contain the grouping functions (Histograms) + Aggregate a = (Aggregate) p; + a.aggregates().forEach(agg -> agg.forEachDown(e -> { + if (a.groupings().size() == 0 + || Expressions.anyMatch(a.groupings(), g -> g instanceof Function && e.functionEquals((Function) g)) == false) { + localFailures.add(fail(e, "[%s] needs to be part of the grouping", Expressions.name(e))); + } + }, GroupingFunction.class)); + } + } private static void checkFilterOnAggs(LogicalPlan p, Set localFailures) { if (p instanceof Filter) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java index a86b4e38e8863..eb7f26222aa99 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/GroupingFunction.java @@ -57,16 +57,6 @@ public GroupingFunctionAttribute toAttribute() { return lazyAttribute; } - @Override - public final GroupingFunction replaceChildren(List newChildren) { - if (newChildren.size() != 1) { - throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); - } - return replaceChild(newChildren.get(0)); - } - - protected abstract GroupingFunction replaceChild(Expression newChild); - @Override protected Pipe makePipe() { // unresolved AggNameInput (should always get replaced by the folder) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/Histogram.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/Histogram.java index 4c1b761b1a00c..b425e736a3951 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/Histogram.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/grouping/Histogram.java @@ -10,12 +10,14 @@ import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal; import org.elasticsearch.xpack.sql.expression.Literal; -import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; +import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; import java.time.ZoneId; +import java.util.Collections; +import java.util.List; import java.util.Objects; public class Histogram extends GroupingFunction { @@ -24,8 +26,8 @@ public class Histogram extends GroupingFunction { private final ZoneId zoneId; public Histogram(Location location, Expression field, Expression interval, ZoneId zoneId) { - super(location, field); - this.interval = (Literal) interval; + super(location, field, Collections.singletonList(interval)); + this.interval = Literal.of(interval); this.zoneId = zoneId; } @@ -51,10 +53,13 @@ protected TypeResolution resolveType() { return resolution; } - + @Override - protected GroupingFunction replaceChild(Expression newChild) { - return new Histogram(location(), newChild, interval, zoneId); + public final GroupingFunction replaceChildren(List newChildren) { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); + } + return new Histogram(location(), newChildren.get(0), newChildren.get(1), zoneId); } @Override diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index ba2924a0eaeca..ec1236f4c44c8 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -506,7 +506,7 @@ public void testAggsInWhere() { } public void testHistogramInFilter() { - assertEquals("1:63: Cannot filter on grouping function [HISTOGRAM(date)], use its argument instead", + assertEquals("1:63: Cannot filter on grouping function [HISTOGRAM(date,INTERVAL 1 MONTH)], use its argument instead", error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) AS h FROM test WHERE " + "HISTOGRAM(date, INTERVAL 1 MONTH) > CAST('2000-01-01' AS DATE) GROUP BY h")); } @@ -520,8 +520,8 @@ public void testHistogramInHaving() { public void testGroupByScalarOnTopOfGrouping() { assertEquals( - "1:14: Cannot combine [HISTOGRAM(date)] grouping function inside GROUP BY, " - + "found [MONTH_OF_YEAR(HISTOGRAM(date) [Z])]; consider moving the expression inside the histogram", + "1:14: Cannot combine [HISTOGRAM(date,INTERVAL 1 MONTH)] grouping function inside GROUP BY, " + + "found [MONTH_OF_YEAR(HISTOGRAM(date,INTERVAL 1 MONTH) [Z])]; consider moving the expression inside the histogram", error("SELECT MONTH(HISTOGRAM(date, INTERVAL 1 MONTH)) AS h FROM test GROUP BY h")); } @@ -529,6 +529,41 @@ public void testAggsInHistogram() { assertEquals("1:47: Cannot use an aggregate [MAX] for grouping", error("SELECT MAX(date) FROM test GROUP BY HISTOGRAM(MAX(int), 1)")); } + + public void testHistogramNotInGrouping() { + assertEquals("1:8: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) AS h FROM test")); + } + + public void testHistogramNotInGroupingWithCount() { + assertEquals("1:8: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) AS h, COUNT(*) FROM test")); + } + + public void testHistogramNotInGroupingWithMaxFirst() { + assertEquals("1:19: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT MAX(date), HISTOGRAM(date, INTERVAL 1 MONTH) AS h FROM test")); + } + + public void testHistogramWithoutAliasNotInGrouping() { + assertEquals("1:8: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) FROM test")); + } + + public void testTwoHistogramsNotInGrouping() { + assertEquals("1:48: [HISTOGRAM(date,INTERVAL 1 DAY)] needs to be part of the grouping", + error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) AS h, HISTOGRAM(date, INTERVAL 1 DAY) FROM test GROUP BY h")); + } + + public void testHistogramNotInGrouping_WithGroupByField() { + assertEquals("1:8: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) FROM test GROUP BY date")); + } + + public void testScalarOfHistogramNotInGrouping() { + assertEquals("1:14: [HISTOGRAM(date,INTERVAL 1 MONTH)] needs to be part of the grouping", + error("SELECT MONTH(HISTOGRAM(date, INTERVAL 1 MONTH)) FROM test")); + } public void testErrorMessageForPercentileWithSecondArgBasedOnAField() { assertEquals("1:8: Second argument of PERCENTILE must be a constant, received [ABS(int)]", From f5d1f04f2f54b9cc314f798cc14ed21565cb3f39 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Mon, 4 Feb 2019 07:03:10 -0600 Subject: [PATCH 106/121] ML: Fix error race condition on stop _all datafeeds and close _all jobs (#38113) (#38211) (#38222) * ML: Ignore when task is not found for _all * Addressing PR comments * Update TransportStopDatafeedAction.java --- .../xpack/ml/action/TransportCloseJobAction.java | 14 ++++++++++++-- .../ml/action/TransportStopDatafeedAction.java | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java index 63b8f90a114d0..4f9fb89509cb7 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportCloseJobAction.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.ml.action; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListenerResponseHandler; import org.elasticsearch.action.FailedNodeException; @@ -17,6 +18,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; @@ -291,7 +293,12 @@ protected void taskOperation(CloseJobAction.Request request, TransportOpenJobAct threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(new AbstractRunnable() { @Override public void onFailure(Exception e) { - listener.onFailure(e); + if (e instanceof ResourceNotFoundException && Strings.isAllOrWildcard(new String[]{request.getJobId()})) { + jobTask.closeJob("close job (api)"); + listener.onResponse(new CloseJobAction.Response(true)); + } else { + listener.onFailure(e); + } } @Override @@ -356,7 +363,10 @@ public void onResponse(PersistentTasksCustomMetaData.PersistentTask task) { @Override public void onFailure(Exception e) { final int slot = counter.incrementAndGet(); - failures.set(slot - 1, e); + if ((e instanceof ResourceNotFoundException && + Strings.isAllOrWildcard(new String[]{request.getJobId()})) == false) { + failures.set(slot - 1, e); + } if (slot == numberOfJobs) { sendResponseOrFailure(request.getJobId(), listener, failures); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java index 77910f21f67d1..5d9d900aaa8dd 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStopDatafeedAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; @@ -193,7 +194,10 @@ public void onResponse(PersistentTasksCustomMetaData.PersistentTask persisten @Override public void onFailure(Exception e) { final int slot = counter.incrementAndGet(); - failures.set(slot - 1, e); + if ((e instanceof ResourceNotFoundException && + Strings.isAllOrWildcard(new String[]{request.getDatafeedId()})) == false) { + failures.set(slot - 1, e); + } if (slot == startedDatafeeds.size()) { sendResponseOrFailure(request.getDatafeedId(), listener, failures); } @@ -221,7 +225,13 @@ protected void taskOperation(StopDatafeedAction.Request request, TransportStartD threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(new AbstractRunnable() { @Override public void onFailure(Exception e) { - listener.onFailure(e); + if ((e instanceof ResourceNotFoundException && + Strings.isAllOrWildcard(new String[]{request.getDatafeedId()}))) { + datafeedTask.stop("stop_datafeed (api)", request.getStopTimeout()); + listener.onResponse(new StopDatafeedAction.Response(true)); + } else { + listener.onFailure(e); + } } @Override From f107ac61a759475097cd935d1662d778fa31c659 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Mon, 4 Feb 2019 16:56:34 +0000 Subject: [PATCH 107/121] [ML] Add explanation so far to file structure finder exceptions (#38337) The explanation so far can be invaluable for troubleshooting as incorrect decisions made early on in the structure analysis can result in seemingly crazy decisions or timeouts later on. Relates elastic/kibana#29821 --- .../filestructurefinder/FileStructureFinderManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/filestructurefinder/FileStructureFinderManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/filestructurefinder/FileStructureFinderManager.java index 935af8d35ed75..5332f18e9f05a 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/filestructurefinder/FileStructureFinderManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/filestructurefinder/FileStructureFinderManager.java @@ -7,6 +7,7 @@ import com.ibm.icu.text.CharsetDetector; import com.ibm.icu.text.CharsetMatch; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.unit.TimeValue; @@ -148,6 +149,14 @@ public FileStructureFinder findFileStructure(List explanation, int ideal Math.max(MIN_SAMPLE_LINE_COUNT, idealSampleLineCount), timeoutChecker); return makeBestStructureFinder(explanation, sampleInfo.v1(), charsetName, sampleInfo.v2(), overrides, timeoutChecker); + } catch (Exception e) { + // Add a dummy exception containing the explanation so far - this can be invaluable for troubleshooting as incorrect + // decisions made early on in the structure analysis can result in seemingly crazy decisions or timeouts later on + if (explanation.isEmpty() == false) { + e.addSuppressed( + new ElasticsearchException(explanation.stream().collect(Collectors.joining("]\n[", "Explanation so far:\n[", "]\n")))); + } + throw e; } } From cb901bb8dae55f0611f1187ab0d7a07c987f1e1d Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Mon, 4 Feb 2019 09:53:03 -0800 Subject: [PATCH 108/121] Preserve ILM operation mode when creating new lifecycles (#38134) (#38230) There was a bug where creating a new policy would start the ILM service, even if it was stopped. This change ensures that there is no change to the existing operation mode Backport of #38134. --- .../action/TransportPutLifecycleAction.java | 3 +- .../IndexLifecycleInitialisationTests.java | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportPutLifecycleAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportPutLifecycleAction.java index 14ca9d6b7b203..3b981727a9b73 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportPutLifecycleAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/indexlifecycle/action/TransportPutLifecycleAction.java @@ -18,7 +18,6 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.xpack.core.indexlifecycle.OperationMode; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.ClientHelper; @@ -89,7 +88,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { LifecyclePolicyMetadata lifecyclePolicyMetadata = new LifecyclePolicyMetadata(request.getPolicy(), filteredHeaders, nextVersion, Instant.now().toEpochMilli()); newPolicies.put(lifecyclePolicyMetadata.getName(), lifecyclePolicyMetadata); - IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies, OperationMode.RUNNING); + IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies, currentMetadata.getOperationMode()); newState.metaData(MetaData.builder(currentState.getMetaData()) .putCustom(IndexLifecycleMetadata.TYPE, newMetadata).build()); return newState.build(); diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationTests.java index a041232d8a7e7..a1a37beb1d129 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleInitialisationTests.java @@ -37,13 +37,17 @@ import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType; import org.elasticsearch.xpack.core.indexlifecycle.MockAction; +import org.elasticsearch.xpack.core.indexlifecycle.OperationMode; import org.elasticsearch.xpack.core.indexlifecycle.Phase; import org.elasticsearch.xpack.core.indexlifecycle.PhaseExecutionInfo; import org.elasticsearch.xpack.core.indexlifecycle.Step; +import org.elasticsearch.xpack.core.indexlifecycle.StopILMRequest; import org.elasticsearch.xpack.core.indexlifecycle.TerminalPolicyStep; import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.GetStatusAction; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.StopILMAction; import org.junit.Before; import java.io.IOException; @@ -364,6 +368,39 @@ public void testMasterFailover() throws Exception { }); } + public void testCreatePolicyWhenStopped() throws Exception { + // start master node + logger.info("Starting server1"); + final String server_1 = internalCluster().startNode(); + final String node1 = getLocalNodeId(server_1); + + assertAcked(client().execute(StopILMAction.INSTANCE, new StopILMRequest()).get()); + assertBusy(() -> assertThat( + client().execute(GetStatusAction.INSTANCE, new GetStatusAction.Request()).get().getMode(), + equalTo(OperationMode.STOPPED))); + + logger.info("Creating lifecycle [test_lifecycle]"); + PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy); + long lowerBoundModifiedDate = Instant.now().toEpochMilli(); + PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get(); + assertAcked(putLifecycleResponse); + long upperBoundModifiedDate = Instant.now().toEpochMilli(); + + // assert version and modified_date + GetLifecycleAction.Response getLifecycleResponse = client().execute(GetLifecycleAction.INSTANCE, + new GetLifecycleAction.Request()).get(); + assertThat(getLifecycleResponse.getPolicies().size(), equalTo(1)); + GetLifecycleAction.LifecyclePolicyResponseItem responseItem = getLifecycleResponse.getPolicies().get(0); + assertThat(responseItem.getLifecyclePolicy(), equalTo(lifecyclePolicy)); + assertThat(responseItem.getVersion(), equalTo(1L)); + long actualModifiedDate = Instant.parse(responseItem.getModifiedDate()).toEpochMilli(); + assertThat(actualModifiedDate, + is(both(greaterThanOrEqualTo(lowerBoundModifiedDate)).and(lessThanOrEqualTo(upperBoundModifiedDate)))); + // assert ILM is still stopped + GetStatusAction.Response statusResponse = client().execute(GetStatusAction.INSTANCE, new GetStatusAction.Request()).get(); + assertThat(statusResponse.getMode(), equalTo(OperationMode.STOPPED)); + } + public void testPollIntervalUpdate() throws Exception { TimeValue pollInterval = TimeValue.timeValueSeconds(randomLongBetween(1, 5)); final String server_1 = internalCluster().startMasterOnlyNode( From 70e3ce52cde312ec5080bfbccb35a424de2edf62 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 4 Feb 2019 15:31:58 -0500 Subject: [PATCH 109/121] Docs: Drop inline callout from scroll example (#38340) (#38365) Coalesces two calls into one in a scroll example so all callouts are at the end of the line. This is the only sort of callouts that are supported by asciidoctor and we'd like to start building our docs with asciidoctor. At present we don't have any mechanism to stop folks adding more inline callouts but we ought to be able to have one in a few weeks. For now, though, removing these inline callouts is a step in the right direction. Relates to #38335 --- docs/reference/search/request/scroll.asciidoc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/reference/search/request/scroll.asciidoc b/docs/reference/search/request/scroll.asciidoc index ad46aabd3b416..13b501275ecf2 100644 --- a/docs/reference/search/request/scroll.asciidoc +++ b/docs/reference/search/request/scroll.asciidoc @@ -57,21 +57,20 @@ results. [source,js] -------------------------------------------------- -POST <1> /_search/scroll <2> +POST /_search/scroll <1> { - "scroll" : "1m", <3> - "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" <4> + "scroll" : "1m", <2> + "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" <3> } -------------------------------------------------- // CONSOLE // TEST[continued s/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==/$body._scroll_id/] -<1> `GET` or `POST` can be used. -<2> The URL should not include the `index` name -- this - is specified in the original `search` request instead. -<3> The `scroll` parameter tells Elasticsearch to keep the search context open +<1> `GET` or `POST` can be used and the URL should not include the `index` + name -- this is specified in the original `search` request instead. +<2> The `scroll` parameter tells Elasticsearch to keep the search context open for another `1m`. -<4> The `scroll_id` parameter +<3> The `scroll_id` parameter The `size` parameter allows you to configure the maximum number of hits to be returned with each batch of results. Each call to the `scroll` API returns the From cd1687aa4b8e668196709b818ff92b99a95534dd Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Mon, 4 Feb 2019 14:21:24 -0800 Subject: [PATCH 110/121] [ILM][TEST] increase assertBusy timeout (#36864) (#38354) the testFullPolicy and testMoveToRolloverStep tests are very important tests, but they sometimes timeout beyond the default 10sec wait for shrink to occur. This commit increases one of the assertBusys to 20 seconds --- .../xpack/indexlifecycle/TimeSeriesLifecycleActionsIT.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/indexlifecycle/TimeSeriesLifecycleActionsIT.java b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/indexlifecycle/TimeSeriesLifecycleActionsIT.java index 85597be1f1f6a..58300f36c2eb2 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/indexlifecycle/TimeSeriesLifecycleActionsIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/indexlifecycle/TimeSeriesLifecycleActionsIT.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import static java.util.Collections.singletonMap; @@ -99,7 +100,7 @@ public void testFullPolicy() throws Exception { // asserts that rollover was called assertBusy(() -> assertTrue(indexExists(secondIndex))); // asserts that shrink deleted the original index - assertBusy(() -> assertFalse(indexExists(originalIndex))); + assertBusy(() -> assertFalse(indexExists(originalIndex)), 20, TimeUnit.SECONDS); // asserts that the delete phase completed for the managed shrunken index assertBusy(() -> assertFalse(indexExists(shrunkenOriginalIndex))); } @@ -177,7 +178,7 @@ public void testMoveToRolloverStep() throws Exception { // asserts that rollover was called assertBusy(() -> assertTrue(indexExists(secondIndex))); // asserts that shrink deleted the original index - assertBusy(() -> assertFalse(indexExists(originalIndex))); + assertBusy(() -> assertFalse(indexExists(originalIndex)), 20, TimeUnit.SECONDS); // asserts that the delete phase completed for the managed shrunken index assertBusy(() -> assertFalse(indexExists(shrunkenOriginalIndex))); } From d0aaf14b2686a26e9ffb18c59c578bd5700410bd Mon Sep 17 00:00:00 2001 From: Yogesh Gaikwad <902768+bizybot@users.noreply.github.com> Date: Tue, 5 Feb 2019 15:01:02 +1100 Subject: [PATCH 111/121] Skip unsupported languages for tests (#38328) (#38385) Skip the languages in tests for which SimpleKdcServer does not handle generalized time correctly. Closes#38320 --- .../xpack/security/authc/kerberos/KerberosTestCase.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/qa/evil-tests/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosTestCase.java b/x-pack/qa/evil-tests/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosTestCase.java index e2a34f8ed0919..407266e76e7d2 100644 --- a/x-pack/qa/evil-tests/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosTestCase.java +++ b/x-pack/qa/evil-tests/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosTestCase.java @@ -77,6 +77,8 @@ public abstract class KerberosTestCase extends ESTestCase { unsupportedLocaleLanguages.add("ps"); unsupportedLocaleLanguages.add("ur"); unsupportedLocaleLanguages.add("pa"); + unsupportedLocaleLanguages.add("ig"); + unsupportedLocaleLanguages.add("sd"); } @BeforeClass From 293715123acf056ca8de8841e2d37331185f145a Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Tue, 5 Feb 2019 15:42:42 +1100 Subject: [PATCH 112/121] Cleanup construction of interceptors (#38388) It would be beneficial to apply some of the request interceptors even when features are disabled. This change reworks the way we build that list so that the interceptors we always want to use are constructed outside of the settings check. Backport of: #38294 --- .../elasticsearch/xpack/security/Security.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 240cf766e33bd..0c2acbcea85ad 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -509,17 +509,17 @@ Collection createComponents(Client client, ThreadPool threadPool, Cluste securityInterceptor.set(new SecurityServerTransportInterceptor(settings, threadPool, authcService.get(), authzService, getLicenseState(), getSslService(), securityContext.get(), destructiveOperations, clusterService)); - final Set requestInterceptors; + Set requestInterceptors = Sets.newHashSet( + new ResizeRequestInterceptor(threadPool, getLicenseState(), auditTrailService), + new IndicesAliasesRequestInterceptor(threadPool.getThreadContext(), getLicenseState(), auditTrailService)); if (XPackSettings.DLS_FLS_ENABLED.get(settings)) { - requestInterceptors = Collections.unmodifiableSet(Sets.newHashSet( - new SearchRequestInterceptor(threadPool, getLicenseState()), - new UpdateRequestInterceptor(threadPool, getLicenseState()), - new BulkShardRequestInterceptor(threadPool, getLicenseState()), - new ResizeRequestInterceptor(threadPool, getLicenseState(), auditTrailService), - new IndicesAliasesRequestInterceptor(threadPool.getThreadContext(), getLicenseState(), auditTrailService))); - } else { - requestInterceptors = Collections.emptySet(); + requestInterceptors.addAll(Arrays.asList( + new SearchRequestInterceptor(threadPool, getLicenseState()), + new UpdateRequestInterceptor(threadPool, getLicenseState()), + new BulkShardRequestInterceptor(threadPool, getLicenseState()) + )); } + requestInterceptors = Collections.unmodifiableSet(requestInterceptors); securityActionFilter.set(new SecurityActionFilter(authcService.get(), authzService, getLicenseState(), requestInterceptors, threadPool, securityContext.get(), destructiveOperations)); From f1aac27ea74ffddbfe8b0e611d2e81229b1d7809 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 5 Feb 2019 10:51:18 +0200 Subject: [PATCH 113/121] Fix IndexAuditTrail rolling upgrade on rollover edge 2 (#38286) (#38381) Fixes a race during the rolling upgrade with the index audit output enabled. The race is that after the upgraded node is restarted, it installs the audit template and updates the mapping of the "current" (from his perspective) audit index. But the template might be installed after a new daily rolled-over index has been created by the other old nodes, using the old templates. However, the new node, even if it installs the template after the rollover edge, can accumulate audit events before the edge, and will correctly try to update the mapping of the audit index before the edge. But this way, the mapping of the index after the edge remains un-updated, because only the master node does the mapping updates. The fix keeps the design of only allowing the master to update the mapping, but the master will try, on a best effort policy, to also possibly update the mapping of the next rollover audit index. --- .../security/audit/index/IndexAuditTrail.java | 73 +++++++++++++------ .../audit/index/IndexNameResolver.java | 22 ++++-- x-pack/qa/rolling-upgrade/build.gradle | 2 + .../upgrades/IndexAuditUpgradeIT.java | 4 +- 4 files changed, 70 insertions(+), 31 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java index a524ab1bcb6b0..78e37a96aea24 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java @@ -109,6 +109,7 @@ import static org.elasticsearch.xpack.security.audit.AuditUtil.indices; import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent; import static org.elasticsearch.xpack.security.audit.index.IndexNameResolver.resolve; +import static org.elasticsearch.xpack.security.audit.index.IndexNameResolver.resolveNext; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_VERSION_STRING; /** @@ -308,6 +309,17 @@ private String getIndexName() { return index; } + private String getNextIndexName() { + final Message first = peek(); + final String index; + if (first == null) { + index = resolveNext(IndexAuditTrailField.INDEX_NAME_PREFIX, DateTime.now(DateTimeZone.UTC), rollover); + } else { + index = resolveNext(IndexAuditTrailField.INDEX_NAME_PREFIX, first.timestamp, rollover); + } + return index; + } + private boolean hasStaleMessage() { final Message first = peek(); if (first == null) { @@ -337,7 +349,7 @@ public void onResponse(ClusterStateResponse clusterStateResponse) { updateCurrentIndexMappingsIfNecessary(clusterStateResponse.getState()); } else if (TemplateUtils.checkTemplateExistsAndVersionMatches(INDEX_TEMPLATE_NAME, SECURITY_VERSION_STRING, clusterStateResponse.getState(), logger, - Version.CURRENT::onOrAfter) == false) { + Version.CURRENT::onOrBefore) == false) { putTemplate(customAuditIndexSettings(settings, logger), e -> { logger.error("failed to put audit trail template", e); @@ -377,6 +389,7 @@ public void onFailure(Exception e) { // pkg private for tests void updateCurrentIndexMappingsIfNecessary(ClusterState state) { + final String nextIndex = getNextIndexName(); final String index = getIndexName(); AliasOrIndex aliasOrIndex = state.getMetaData().getAliasAndIndexLookup().get(index); @@ -391,32 +404,37 @@ void updateCurrentIndexMappingsIfNecessary(ClusterState state) { MappingMetaData docMapping = indexMetaData.mapping("doc"); if (docMapping == null) { if (indexToRemoteCluster || state.nodes().isLocalNodeElectedMaster() || hasStaleMessage()) { - putAuditIndexMappingsAndStart(index); + putAuditIndexMappingsAndStart(index, nextIndex); } else { - logger.trace("audit index [{}] is missing mapping for type [{}]", index, DOC_TYPE); + logger.debug("audit index [{}] is missing mapping for type [{}]", index, DOC_TYPE); transitionStartingToInitialized(); } } else { @SuppressWarnings("unchecked") Map meta = (Map) docMapping.sourceAsMap().get("_meta"); if (meta == null) { - logger.info("Missing _meta field in mapping [{}] of index [{}]", docMapping.type(), index); - throw new IllegalStateException("Cannot read security-version string in index " + index); - } - - final String versionString = (String) meta.get(SECURITY_VERSION_STRING); - if (versionString != null && Version.fromString(versionString).onOrAfter(Version.CURRENT)) { - innerStart(); - } else { + logger.warn("Missing _meta field in mapping [{}] of index [{}]", docMapping.type(), index); if (indexToRemoteCluster || state.nodes().isLocalNodeElectedMaster() || hasStaleMessage()) { - putAuditIndexMappingsAndStart(index); - } else if (versionString == null) { - logger.trace("audit index [{}] mapping is missing meta field [{}]", index, SECURITY_VERSION_STRING); - transitionStartingToInitialized(); + putAuditIndexMappingsAndStart(index, nextIndex); } else { - logger.trace("audit index [{}] has the incorrect version [{}]", index, versionString); + logger.debug("audit index [{}] is missing _meta for type [{}]", index, DOC_TYPE); transitionStartingToInitialized(); } + } else { + final String versionString = (String) meta.get(SECURITY_VERSION_STRING); + if (versionString != null && Version.fromString(versionString).onOrAfter(Version.CURRENT)) { + innerStart(); + } else { + if (indexToRemoteCluster || state.nodes().isLocalNodeElectedMaster() || hasStaleMessage()) { + putAuditIndexMappingsAndStart(index, nextIndex); + } else if (versionString == null) { + logger.debug("audit index [{}] mapping is missing meta field [{}]", index, SECURITY_VERSION_STRING); + transitionStartingToInitialized(); + } else { + logger.debug("audit index [{}] has the incorrect version [{}]", index, versionString); + transitionStartingToInitialized(); + } + } } } } else { @@ -424,15 +442,22 @@ void updateCurrentIndexMappingsIfNecessary(ClusterState state) { } } - private void putAuditIndexMappingsAndStart(String index) { - putAuditIndexMappings(index, getPutIndexTemplateRequest(Settings.EMPTY).mappings().get(DOC_TYPE), - ActionListener.wrap(ignore -> { - logger.trace("updated mappings on audit index [{}]", index); + private void putAuditIndexMappingsAndStart(String index, String nextIndex) { + final String docMapping = getPutIndexTemplateRequest(Settings.EMPTY).mappings().get(DOC_TYPE); + putAuditIndexMappings(index, docMapping, ActionListener.wrap(ignore -> { + logger.debug("updated mappings on audit index [{}]", index); + putAuditIndexMappings(nextIndex, docMapping, ActionListener.wrap(ignoreToo -> { + logger.debug("updated mappings on next audit index [{}]", nextIndex); + innerStart(); + }, e2 -> { + // best effort only + logger.debug("Failed to update mappings on next audit index [{}]", nextIndex); innerStart(); - }, e -> { - logger.error(new ParameterizedMessage("failed to update mappings on audit index [{}]", index), e); - transitionStartingToInitialized(); // reset to initialized so we can retry })); + }, e -> { + logger.error(new ParameterizedMessage("failed to update mappings on audit index [{}]", index), e); + transitionStartingToInitialized(); // reset to initialized so we can retry + })); } private void transitionStartingToInitialized() { @@ -451,7 +476,7 @@ void innerStart() { assert false : message; logger.error(message); } else { - logger.trace("successful state transition from starting to started, current value: [{}]", state.get()); + logger.debug("successful state transition from starting to started, current value: [{}]", state.get()); } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexNameResolver.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexNameResolver.java index 5a65bf813054a..0c422b6f3d06b 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexNameResolver.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexNameResolver.java @@ -9,23 +9,31 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import java.util.function.Function; + public class IndexNameResolver { public enum Rollover { - HOURLY ("-yyyy.MM.dd.HH"), - DAILY ("-yyyy.MM.dd"), - WEEKLY ("-yyyy.w"), - MONTHLY ("-yyyy.MM"); + HOURLY ("-yyyy.MM.dd.HH", d -> d.plusHours(1)), + DAILY ("-yyyy.MM.dd", d -> d.plusDays(1)), + WEEKLY ("-yyyy.w", d -> d.plusWeeks(1)), + MONTHLY ("-yyyy.MM", d -> d.plusMonths(1)); private final DateTimeFormatter formatter; + private final Function next; - Rollover(String format) { + Rollover(String format, Function next) { this.formatter = DateTimeFormat.forPattern(format); + this.next = next; } DateTimeFormatter formatter() { return formatter; } + + Function getNext() { + return next; + } } private IndexNameResolver() {} @@ -34,6 +42,10 @@ public static String resolve(DateTime timestamp, Rollover rollover) { return rollover.formatter().print(timestamp); } + public static String resolveNext(String indexNamePrefix, DateTime timestamp, Rollover rollover) { + return resolve(indexNamePrefix, rollover.getNext().apply(timestamp), rollover); + } + public static String resolve(String indexNamePrefix, DateTime timestamp, Rollover rollover) { return indexNamePrefix + resolve(timestamp, rollover); } diff --git a/x-pack/qa/rolling-upgrade/build.gradle b/x-pack/qa/rolling-upgrade/build.gradle index 92a7c61db7e04..1954f064fc013 100644 --- a/x-pack/qa/rolling-upgrade/build.gradle +++ b/x-pack/qa/rolling-upgrade/build.gradle @@ -154,6 +154,7 @@ subprojects { setting 'xpack.security.audit.outputs', 'index' setting 'xpack.ssl.keystore.path', 'testnode.jks' setting 'xpack.ssl.keystore.password', 'testnode' + setting 'logger.org.elasticsearch.xpack.security.audit.index', 'DEBUG' if (version.onOrAfter('6.0.0') == false) { // this is needed since in 5.6 we don't bootstrap the token service if there is no explicit initial password keystoreSetting 'xpack.security.authc.token.passphrase', 'xpack_token_passphrase' @@ -211,6 +212,7 @@ subprojects { setting 'xpack.security.transport.ssl.enabled', 'true' setting 'xpack.ssl.keystore.path', 'testnode.jks' keystoreSetting 'xpack.ssl.keystore.secure_password', 'testnode' + setting 'logger.org.elasticsearch.xpack.security.audit.index', 'DEBUG' if (version.onOrAfter('6.0.0') == false) { // this is needed since in 5.6 we don't bootstrap the token service if there is no explicit initial password keystoreSetting 'xpack.security.authc.token.passphrase', 'xpack_token_passphrase' diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java index 83f39ea97e79b..e5f8976fd4109 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java @@ -17,6 +17,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.hasSize; @@ -62,12 +63,11 @@ public void findMinVersionInCluster() throws IOException { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/33867") public void testAuditLogs() throws Exception { assertBusy(() -> { assertAuditDocsExist(); assertNumUniqueNodeNameBuckets(expectedNumUniqueNodeNameBuckets()); - }); + }, 30, TimeUnit.SECONDS); } private int expectedNumUniqueNodeNameBuckets() throws IOException { From 3131f599eb78a029a5b506a1de4d86e5dc8efa7a Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 5 Feb 2019 13:37:35 +0200 Subject: [PATCH 114/121] SecuritySettingsSource license.self_generated: trial (#38233) (#38398) Authn is enabled only if `license_type` is non `basic`, but `basic` is what the `LicenseService` generates implicitly. This commit explicitly sets license type to `trial`, which allows for authn, in the `SecuritySettingsSource` which is the settings configuration parameter for `InternalTestCluster`s. The real problem, that had created tests failures like #31028 and #32685, is that the check `licenseState.isAuthAllowed()` can change sporadically. If it were to return `true` or `false` during the whole test there would be no problem. The problem manifests when it turns from `true` to `false` right before `Realms.asList()`. There are other license checks before this one (request filter, token service, etc) that would not cause a problem if they would suddenly see the check as `false`. But switching to `false` before `Realms.asList()` makes it appear that no installed realms could have handled the authn token which is an authentication error, as can be seen in the failing tests. Closes #31028 #32685 --- .../java/org/elasticsearch/test/SecuritySettingsSource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java index 216d8e44148f7..a7051540c2af2 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java @@ -136,7 +136,8 @@ public Settings nodeSettings(int nodeOrdinal) { .put("xpack.security.authc.realms.file.type", FileRealmSettings.TYPE) .put("xpack.security.authc.realms.file.order", 0) .put("xpack.security.authc.realms.index.type", NativeRealmSettings.TYPE) - .put("xpack.security.authc.realms.index.order", "1"); + .put("xpack.security.authc.realms.index.order", "1") + .put("xpack.license.self_generated.type", "trial"); addNodeSSLSettings(builder); return builder.build(); } From ffd3aa19e3c2e9a54a7a3310042560382b6620f1 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 5 Feb 2019 12:00:49 +0200 Subject: [PATCH 115/121] Change the milliseconds precision to 3 digits for intervals. (#38297) (cherry picked from commit cea81b199dcb92bf80fb42a87fb4817a4f63d890) --- .../xpack/sql/expression/literal/Intervals.java | 2 +- .../xpack/sql/expression/literal/IntervalsTests.java | 12 ++++++------ .../xpack/sql/parser/ExpressionTests.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java index 0d194be105f6d..319d193f619d2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java @@ -332,7 +332,7 @@ public static TemporalAmount negate(TemporalAmount interval) { int MAX_HOUR = 23; int MAX_MINUTE = 59; int MAX_SECOND = 59; - int MAX_MILLI = 999999999; + int MAX_MILLI = 999; char DOT = '.'; char SPACE = ' '; diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java index bc84f3837ecca..a6bea515cd698 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java @@ -83,7 +83,7 @@ public void testMinuteInterval() throws Exception { public void testSecondInterval() throws Exception { int randomSeconds = randomNonNegativeInt(); - int randomMillis = randomBoolean() ? (randomBoolean() ? 0 : 999999999) : randomInt(999999999); + int randomMillis = randomBoolean() ? (randomBoolean() ? 0 : 999) : randomInt(999); String value = format(Locale.ROOT, "%s%d.%d", sign, randomSeconds, randomMillis); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_SECOND); assertEquals(maybeNegate(sign, Duration.ofSeconds(randomSeconds).plusMillis(randomMillis)), amount); @@ -128,7 +128,7 @@ public void testDayToSecond() throws Exception { int randomSecond = randomInt(59); boolean withMillis = randomBoolean(); - int randomMilli = withMillis ? randomInt(999999999) : 0; + int randomMilli = withMillis ? randomInt(999) : 0; String millisString = withMillis ? "." + randomMilli : ""; String value = format(Locale.ROOT, "%s%d %d:%d:%d%s", sign, randomDay, randomHour, randomMinute, randomSecond, millisString); @@ -151,7 +151,7 @@ public void testHourToSecond() throws Exception { int randomSecond = randomInt(59); boolean withMillis = randomBoolean(); - int randomMilli = withMillis ? randomInt(999999999) : 0; + int randomMilli = withMillis ? randomInt(999) : 0; String millisString = withMillis ? "." + randomMilli : ""; String value = format(Locale.ROOT, "%s%d:%d:%d%s", sign, randomHour, randomMinute, randomSecond, millisString); @@ -165,7 +165,7 @@ public void testMinuteToSecond() throws Exception { int randomSecond = randomInt(59); boolean withMillis = randomBoolean(); - int randomMilli = withMillis ? randomInt(999999999) : 0; + int randomMilli = withMillis ? randomInt(999) : 0; String millisString = withMillis ? "." + randomMilli : ""; String value = format(Locale.ROOT, "%s%d:%d%s", sign, randomMinute, randomSecond, millisString); @@ -186,11 +186,11 @@ public void testYearToMonthTooBig() throws Exception { public void testMillisTooBig() throws Exception { int randomSeconds = randomNonNegativeInt(); - int millisTooLarge = 1234567890; + int millisTooLarge = 1234; String value = format(Locale.ROOT, "%s%d.%d", sign, randomSeconds, millisTooLarge); ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_SECOND)); assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [" + value + "]: [MILLISECOND] unit has illegal value [" + millisTooLarge - + "], expected a positive number up to [999999999]", pe.getMessage()); + + "], expected a positive number up to [999]", pe.getMessage()); } public void testDayToMinuteTooBig() throws Exception { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java index a85add8b110a0..0da350726c69a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java @@ -151,7 +151,7 @@ public void testStringInterval() throws Exception { int randomHour = randomInt(23); int randomMinute = randomInt(59); int randomSecond = randomInt(59); - int randomMilli = randomInt(999999999); + int randomMilli = randomInt(999); String value = format(Locale.ROOT, "INTERVAL '%d %d:%d:%d.%d' DAY TO SECOND", randomDay, randomHour, randomMinute, randomSecond, randomMilli); @@ -164,7 +164,7 @@ public void testNegativeStringInterval() throws Exception { int randomHour = randomInt(23); int randomMinute = randomInt(59); int randomSecond = randomInt(59); - int randomMilli = randomInt(999999999); + int randomMilli = randomInt(999); String value = format(Locale.ROOT, "INTERVAL -'%d %d:%d:%d.%d' DAY TO SECOND", randomDay, randomHour, randomMinute, randomSecond, randomMilli); From b280850d0710954b5fe8e1ec9c9bc85db6930da5 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Tue, 5 Feb 2019 17:18:39 +0100 Subject: [PATCH 116/121] Backport changes to the release notes script. (#38347) Backport of #37967 and #38307. --- dev-tools/es_release_notes.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-tools/es_release_notes.pl b/dev-tools/es_release_notes.pl index cf19a1cd9ddf5..16a00d4eff2ae 100755 --- a/dev-tools/es_release_notes.pl +++ b/dev-tools/es_release_notes.pl @@ -32,7 +32,7 @@ ">enhancement", ">bug", ">regression", ">upgrade" ); my %Ignore = map { $_ => 1 } - ( ">non-issue", ">refactoring", ">docs", ">test", ">test-failure", ":Core/Build", "backport" ); + ( ">non-issue", ">refactoring", ">docs", ">test", ">test-failure", ">test-mute", ":Core/Infra/Build", "backport" ); my %Group_Labels = ( '>breaking' => 'Breaking changes', @@ -48,7 +48,7 @@ my %Area_Overrides = ( ':ml' => 'Machine Learning', - ':beats' => 'Beats Plugin', + ':Beats' => 'Beats Plugin', ':Docs' => 'Docs Infrastructure' ); From 3529bba890889c678b0274cca358bbde042e7895 Mon Sep 17 00:00:00 2001 From: Cathy Wong <45601140+cathwong@users.noreply.github.com> Date: Tue, 5 Feb 2019 16:00:47 -0500 Subject: [PATCH 117/121] Update ilm-api.asciidoc, point to REMOVE policy (#38235) --- docs/reference/ilm/apis/ilm-api.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/ilm/apis/ilm-api.asciidoc b/docs/reference/ilm/apis/ilm-api.asciidoc index 457ca3daf5d74..c40447149b12a 100644 --- a/docs/reference/ilm/apis/ilm-api.asciidoc +++ b/docs/reference/ilm/apis/ilm-api.asciidoc @@ -21,6 +21,7 @@ about Index Lifecycle Management. * <> * <> +* <> [float] [[ilm-api-management-endpoint]] From 841eb714a054d2327e4bb38193162768a17faa72 Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Wed, 6 Feb 2019 16:19:21 +0200 Subject: [PATCH 118/121] SQL: Fix issue with IN not resolving to underlying keyword field (#38440) - Add resolution to the exact keyword field (if exists) for text fields. - Add proper verification and error message if underlying keyword doesn'texist. - Move check for field attribute in the comparison list to the `resolveType()` method of `IN`. Fixes: #38424 --- .../predicate/operator/comparison/In.java | 24 +++++++++++++++++ .../xpack/sql/planner/QueryTranslator.java | 15 +++-------- .../analyzer/VerifierErrorMessagesTests.java | 27 +++++++++++++------ .../sql/planner/QueryTranslatorTests.java | 19 +++++++------ 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/comparison/In.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/comparison/In.java index dd3b1ce19f14c..1a79bc130bdf6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/comparison/In.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/comparison/In.java @@ -5,8 +5,10 @@ */ package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison; +import org.elasticsearch.xpack.sql.analysis.index.MappingException; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expressions; +import org.elasticsearch.xpack.sql.expression.FieldAttribute; import org.elasticsearch.xpack.sql.expression.Foldables; import org.elasticsearch.xpack.sql.expression.Nullability; import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction; @@ -25,6 +27,7 @@ import java.util.StringJoiner; import java.util.stream.Collectors; +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder; public class In extends ScalarFunction { @@ -113,6 +116,27 @@ protected Pipe makePipe() { return new InPipe(location(), this, children().stream().map(Expressions::pipe).collect(Collectors.toList())); } + @Override + protected TypeResolution resolveType() { + if (value instanceof FieldAttribute) { + try { + ((FieldAttribute) value).exactAttribute(); + } catch (MappingException e) { + return new TypeResolution(format(null, "[{}] cannot operate on field of data type [{}]: {}", + functionName(), value().dataType().esType, e.getMessage())); + } + } + + for (Expression ex : list) { + if (ex.foldable() == false) { + return new TypeResolution(format(null, "Comparisons against variables are not (currently) supported; offender [{}] in [{}]", + Expressions.name(ex), + name())); + } + } + return super.resolveType(); + } + @Override public int hashCode() { return Objects.hash(value, list); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java index a5046aaa6bb56..f58ee7959f012 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryTranslator.java @@ -100,7 +100,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.function.Supplier; import static java.util.Collections.singletonList; @@ -682,16 +681,6 @@ static class InComparisons extends ExpressionTranslator { @Override protected QueryTranslation asQuery(In in, boolean onAggs) { - Optional firstNotFoldable = in.list().stream().filter(expression -> !expression.foldable()).findFirst(); - - if (firstNotFoldable.isPresent()) { - throw new SqlIllegalArgumentException( - "Line {}:{}: Comparisons against variables are not (currently) supported; offender [{}] in [{}]", - firstNotFoldable.get().location().getLineNumber(), - firstNotFoldable.get().location().getColumnNumber(), - Expressions.name(firstNotFoldable.get()), - in.name()); - } if (in.value() instanceof NamedExpression) { NamedExpression ne = (NamedExpression) in.value(); @@ -709,7 +698,9 @@ protected QueryTranslation asQuery(In in, boolean onAggs) { else { Query q = null; if (in.value() instanceof FieldAttribute) { - q = new TermsQuery(in.location(), ne.name(), in.list()); + FieldAttribute fa = (FieldAttribute) in.value(); + // equality should always be against an exact match (which is important for strings) + q = new TermsQuery(in.location(), fa.isInexact() ? fa.exactAttribute().name() : fa.name(), in.list()); } else { q = new ScriptQuery(in.location(), in.asScript()); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index ec1236f4c44c8..4e15955284409 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -351,23 +351,34 @@ public void testInNestedWithDifferentDataTypesFromLeftValue_SelectClause() { } public void testInWithDifferentDataTypes_WhereClause() { - assertEquals("1:49: expected data type [TEXT], value provided is of type [INTEGER]", - error("SELECT * FROM test WHERE text IN ('foo', 'bar', 4)")); + assertEquals("1:52: expected data type [KEYWORD], value provided is of type [INTEGER]", + error("SELECT * FROM test WHERE keyword IN ('foo', 'bar', 4)")); } public void testInNestedWithDifferentDataTypes_WhereClause() { - assertEquals("1:60: expected data type [TEXT], value provided is of type [INTEGER]", - error("SELECT * FROM test WHERE int = 1 OR text IN ('foo', 'bar', 2)")); + assertEquals("1:63: expected data type [KEYWORD], value provided is of type [INTEGER]", + error("SELECT * FROM test WHERE int = 1 OR keyword IN ('foo', 'bar', 2)")); } public void testInWithDifferentDataTypesFromLeftValue_WhereClause() { - assertEquals("1:35: expected data type [TEXT], value provided is of type [INTEGER]", - error("SELECT * FROM test WHERE text IN (1, 2)")); + assertEquals("1:38: expected data type [KEYWORD], value provided is of type [INTEGER]", + error("SELECT * FROM test WHERE keyword IN (1, 2)")); } public void testInNestedWithDifferentDataTypesFromLeftValue_WhereClause() { - assertEquals("1:46: expected data type [TEXT], value provided is of type [INTEGER]", - error("SELECT * FROM test WHERE int = 1 OR text IN (1, 2)")); + assertEquals("1:49: expected data type [KEYWORD], value provided is of type [INTEGER]", + error("SELECT * FROM test WHERE int = 1 OR keyword IN (1, 2)")); + } + + public void testInWithFieldInListOfValues() { + assertEquals("1:30: Comparisons against variables are not (currently) supported; offender [int] in [int IN (1, int)]", + error("SELECT * FROM test WHERE int IN (1, int)")); + } + + public void testInOnFieldTextWithNoKeyword() { + assertEquals("1:31: [IN] cannot operate on field of data type [text]: " + + "No keyword/multi-field defined exact matches for [text]; define one or use MATCH/QUERY instead", + error("SELECT * FROM test WHERE text IN ('foo', 'bar')")); } public void testNotSupportedAggregateOnDate() { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java index 4b3c74f57fadb..2cc75119e989a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java @@ -307,8 +307,8 @@ public void testTranslateInExpression_WhereClause() { tq.asBuilder().toString().replaceAll("\\s", "")); } - public void testTranslateInExpression_WhereClauseAndNullHandling() { - LogicalPlan p = plan("SELECT * FROM test WHERE keyword IN ('foo', null, 'lala', null, 'foo', concat('la', 'la'))"); + public void testTranslateInExpression_WhereClause_TextFieldWithKeyword() { + LogicalPlan p = plan("SELECT * FROM test WHERE some.string IN ('foo', 'bar', 'lala', 'foo', concat('la', 'la'))"); assertTrue(p instanceof Project); assertTrue(p.children().get(0) instanceof Filter); Expression condition = ((Filter) p.children().get(0)).condition(); @@ -317,19 +317,22 @@ public void testTranslateInExpression_WhereClauseAndNullHandling() { Query query = translation.query; assertTrue(query instanceof TermsQuery); TermsQuery tq = (TermsQuery) query; - assertEquals("{\"terms\":{\"keyword\":[\"foo\",\"lala\"],\"boost\":1.0}}", + assertEquals("{\"terms\":{\"some.string.typical\":[\"foo\",\"bar\",\"lala\"],\"boost\":1.0}}", tq.asBuilder().toString().replaceAll("\\s", "")); } - public void testTranslateInExpressionInvalidValues_WhereClause() { - LogicalPlan p = plan("SELECT * FROM test WHERE keyword IN ('foo', 'bar', keyword)"); + public void testTranslateInExpression_WhereClauseAndNullHandling() { + LogicalPlan p = plan("SELECT * FROM test WHERE keyword IN ('foo', null, 'lala', null, 'foo', concat('la', 'la'))"); assertTrue(p instanceof Project); assertTrue(p.children().get(0) instanceof Filter); Expression condition = ((Filter) p.children().get(0)).condition(); assertFalse(condition.foldable()); - SqlIllegalArgumentException ex = expectThrows(SqlIllegalArgumentException.class, () -> QueryTranslator.toQuery(condition, false)); - assertEquals("Line 1:52: Comparisons against variables are not (currently) supported; " + - "offender [keyword] in [keyword IN (foo, bar, keyword)]", ex.getMessage()); + QueryTranslation translation = QueryTranslator.toQuery(condition, false); + Query query = translation.query; + assertTrue(query instanceof TermsQuery); + TermsQuery tq = (TermsQuery) query; + assertEquals("{\"terms\":{\"keyword\":[\"foo\",\"lala\"],\"boost\":1.0}}", + tq.asBuilder().toString().replaceAll("\\s", "")); } public void testTranslateInExpression_WhereClause_Painless() { From bc61eaa5c6d8b230ff4d91a2bfb3222b84afce45 Mon Sep 17 00:00:00 2001 From: Guilherme Ferreira Date: Wed, 6 Feb 2019 17:48:19 +0100 Subject: [PATCH 119/121] bad formatted JSON object (#38515) It just need to replace the wrong " , " to " : " --- docs/reference/ingest/ingest-node.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/ingest/ingest-node.asciidoc b/docs/reference/ingest/ingest-node.asciidoc index 2d0e222741929..cae84ddb912db 100644 --- a/docs/reference/ingest/ingest-node.asciidoc +++ b/docs/reference/ingest/ingest-node.asciidoc @@ -2142,8 +2142,8 @@ block to send the document to the 'failure_index' index for later inspection: "on_failure" : [ { "set" : { - "field", "_index", - "value", "failure_index" + "field": "_index", + "value": "failure_index" } } ] From 8408797dc335f2061cdc41d93bacf7eede4d065a Mon Sep 17 00:00:00 2001 From: Guilherme Ferreira Date: Wed, 6 Feb 2019 19:36:41 +0100 Subject: [PATCH 120/121] fix dissect doc "ip" --> "clientip" (#38512) --- docs/reference/ingest/ingest-node.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ingest/ingest-node.asciidoc b/docs/reference/ingest/ingest-node.asciidoc index cae84ddb912db..862e7f1d041bb 100644 --- a/docs/reference/ingest/ingest-node.asciidoc +++ b/docs/reference/ingest/ingest-node.asciidoc @@ -1810,7 +1810,7 @@ Named skip key modifier example | *Pattern* | `%{clientip} %{?ident} %{?auth} [%{@timestamp}]` | *Input* | 1.2.3.4 - - [30/Apr/1998:22:00:52 +0000] | *Result* a| -* ip = 1.2.3.4 +* clientip = 1.2.3.4 * @timestamp = 30/Apr/1998:22:00:52 +0000 |====== From 0b0af8c92a38844569c0ad1f031a16d8b59f5168 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 8 Feb 2019 11:36:27 +0000 Subject: [PATCH 121/121] [DOCS] Add warning about bypassing ML PUT APIs (#38608) Now that ML configurations are stored in the .ml-config index rather than in cluster state there is a possibility that some users may try to add configurations directly to the index. Allowing this creates a variety of problems including possible data exflitration attacks (depending on how security is set up), so this commit adds warnings against allowing writes to the .ml-config index other than via the ML APIs. Backport of #38509 --- docs/reference/ml/apis/put-datafeed.asciidoc | 5 +++++ docs/reference/ml/apis/put-job.asciidoc | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/reference/ml/apis/put-datafeed.asciidoc b/docs/reference/ml/apis/put-datafeed.asciidoc index d31ce14b1ab8a..21c03e90c8964 100644 --- a/docs/reference/ml/apis/put-datafeed.asciidoc +++ b/docs/reference/ml/apis/put-datafeed.asciidoc @@ -19,6 +19,11 @@ Instantiates a {dfeed}. You must create a job before you create a {dfeed}. You can associate only one {dfeed} to each job. +IMPORTANT: You must use {kib} or this API to create a {dfeed}. Do not put a {dfeed} + directly to the `.ml-config` index using the Elasticsearch index API. + If {es} {security-features} are enabled, do not give users `write` + privileges on the `.ml-config` index. + ==== Path Parameters diff --git a/docs/reference/ml/apis/put-job.asciidoc b/docs/reference/ml/apis/put-job.asciidoc index bb5550ae77519..f14367d9718ec 100644 --- a/docs/reference/ml/apis/put-job.asciidoc +++ b/docs/reference/ml/apis/put-job.asciidoc @@ -12,7 +12,13 @@ Instantiates a job. `PUT _xpack/ml/anomaly_detectors/` -//===== Description +===== Description + +IMPORTANT: You must use {kib} or this API to create a {ml} job. Do not put a job + directly to the `.ml-config` index using the Elasticsearch index API. + If {es} {security-features} are enabled, do not give users `write` + privileges on the `.ml-config` index. + ==== Path Parameters