Skip to content

Commit c662208

Browse files
authored
Merge branch 'main' into pem-unwanted-test-tsrateit
2 parents e736f1c + fabf32c commit c662208

File tree

38 files changed

+984
-338
lines changed

38 files changed

+984
-338
lines changed

TESTING.asciidoc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -681,12 +681,15 @@ There are multiple base classes for tests:
681681
directly by unit tests.
682682
* **`ESSingleNodeTestCase`**: This test case sets up a cluster that has a
683683
single node.
684-
* **`ESIntegTestCase`**: An integration test case that creates a cluster that
685-
might have multiple nodes.
686-
* **`ESRestTestCase`**: An integration tests that interacts with an external
687-
cluster via the REST API. This is used for Java based REST tests.
688-
* **`ESClientYamlSuiteTestCase` **: A subclass of `ESRestTestCase` used to run
689-
YAML based REST tests.
684+
* **`ESIntegTestCase`**: An internal integration test that starts nodes within the same JVM as the test.
685+
These allow you to test functionality that is not exposed via the REST API, or for verifying a certain internal state.
686+
Additionally, you can easily simulate tricky distributed setups that are difficult to do in REST tests.
687+
If you only need to start one node, use `ESSingleNodeTestCase` instead, which is a much lighter test setup.
688+
* **`ESRestTestCase`**: An integration test that interacts with an external
689+
cluster via the REST API. This is used for Java based REST tests. This should
690+
be the first choice for writing integration tests as these tests run in a much more
691+
realistic setup.
692+
* **`ESClientYamlSuiteTestCase` **: A subclass of `ESRestTestCase` used to run YAML based REST tests.
690693

691694
=== Good practices
692695

docs/changelog/133369.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 133369
2+
summary: Enable `date` `date_nanos` implicit casting
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

docs/reference/query-languages/esql/esql-multi-index.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,62 @@ FROM events_*
135135
| 2023-10-23T12:27:28.948Z | 172.21.2.113 | 2764889 | Connected to 10.1.0.2 |
136136
| 2023-10-23T12:15:03.360Z | 172.21.2.162 | 3450233 | Connected to 10.1.0.3 |
137137

138+
### Date and date_nanos union type [esql-multi-index-date-date-nanos-union]
139+
```{applies_to}
140+
stack: ga 9.2.0
141+
```
142+
When the type of an {{esql}} field is a *union* of `date` and `date_nanos` across different indices, {{esql}} automatically casts all values to the `date_nanos` type during query execution. This implicit casting ensures that all values are handled with nanosecond precision, regardless of their original type. As a result, users can write queries against such fields without needing to perform explicit type conversions, and the query engine will seamlessly align the types for consistent and precise results.
143+
144+
`date_nanos` fields offer higher precision but have a narrower range of valid values compared to `date` fields. This limits their representable dates roughly from 1970 to 2262. This is because dates are stored as a `long` representing nanoseconds since the epoch. When a field is mapped as both `date` and `date_nanos` across different indices, {{esql}} defaults to the more precise `date_nanos` type. This behavior ensures that no precision is lost when querying multiple indices with differing date field types. For dates that fall outside the valid range of `date_nanos` in fields that are mapped to both `date` and `date_nanos` across different indices, {{esql}} returns null by default. However, users can explicitly cast these fields to the `date` type to obtain a valid value, with precision limited to milliseconds.
145+
146+
For example, if the `@timestamp` field is mapped as `date` in one index and `date_nanos` in another, {{esql}} will automatically treat all `@timestamp` values as `date_nanos` during query execution. This allows users to write queries that utilize the `@timestamp` field without encountering type mismatch errors, ensuring accurate time-based operations and comparisons across the combined dataset.
147+
148+
**index: events_date**
149+
150+
```
151+
{
152+
"mappings": {
153+
"properties": {
154+
"@timestamp": { "type": "date" },
155+
"client_ip": { "type": "ip" },
156+
"event_duration": { "type": "long" },
157+
"message": { "type": "keyword" }
158+
}
159+
}
160+
}
161+
```
162+
163+
**index: events_date_nanos**
164+
165+
```
166+
{
167+
"mappings": {
168+
"properties": {
169+
"@timestamp": { "type": "date_nanos" },
170+
"client_ip": { "type": "ip" },
171+
"event_duration": { "type": "long" },
172+
"message": { "type": "keyword" }
173+
}
174+
}
175+
}
176+
```
177+
178+
```esql
179+
FROM events_date*
180+
| EVAL date = @timestamp::date
181+
| KEEP @timestamp, date, client_ip, event_duration, message
182+
| SORT date
183+
```
184+
185+
| @timestamp:date_nanos | date:date | client_ip:ip | event_duration:long | message:keyword |
186+
|--------------------------| --- |--------------|---------| --- |
187+
| null |1969-10-23T13:33:34.937Z| 172.21.0.5 | 1232382 |Disconnected|
188+
| 2023-10-23T12:15:03.360Z |2023-10-23T12:15:03.360Z| 172.21.2.162 | 3450233 |Connected to 10.1.0.3|
189+
| 2023-10-23T12:15:03.360103847Z|2023-10-23T12:15:03.360Z| 172.22.2.162 | 3450233 |Connected to 10.1.0.3|
190+
| 2023-10-23T12:27:28.948Z |2023-10-23T12:27:28.948Z| 172.22.2.113 | 2764889 |Connected to 10.1.0.2|
191+
| 2023-10-23T12:27:28.948Z |2023-10-23T12:27:28.948Z| 172.21.2.113 | 2764889 |Connected to 10.1.0.2|
192+
| 2023-10-23T13:33:34.937193Z |2023-10-23T13:33:34.937Z| 172.22.0.5 | 1232382 |Disconnected|
193+
| null |2263-10-23T13:51:54.732Z| 172.21.3.15 | 725448 |Connection error|
138194

139195
## Index metadata [esql-multi-index-index-metadata]
140196

muted-tests.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,12 +513,6 @@ tests:
513513
- class: org.elasticsearch.xpack.ml.integration.InferenceIT
514514
method: testInferClassificationModel
515515
issue: https://github.com/elastic/elasticsearch/issues/133448
516-
- class: org.elasticsearch.xpack.logsdb.LogsIndexingIT
517-
method: testRouteOnSortFields
518-
issue: https://github.com/elastic/elasticsearch/issues/133993
519-
- class: org.elasticsearch.xpack.logsdb.LogsIndexingIT
520-
method: testShrink
521-
issue: https://github.com/elastic/elasticsearch/issues/133875
522516
- class: org.elasticsearch.xpack.esql.action.CrossClusterQueryWithPartialResultsIT
523517
method: testPartialResults
524518
issue: https://github.com/elastic/elasticsearch/issues/131481
@@ -534,6 +528,9 @@ tests:
534528
- class: org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDeciderIT
535529
method: testShardsAreKeptInPreferredTierUntilTheNextTierIsInItsFinalState
536530
issue: https://github.com/elastic/elasticsearch/issues/134050
531+
- class: org.elasticsearch.cluster.ClusterInfoServiceIT
532+
method: testMaxQueueLatenciesInClusterInfo
533+
issue: https://github.com/elastic/elasticsearch/issues/134088
537534

538535
# Examples:
539536
#

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ static TransportVersion def(int id) {
358358
public static final TransportVersion RESOLVE_INDEX_MODE_FILTER = def(9_149_0_00);
359359
public static final TransportVersion SEMANTIC_QUERY_MULTIPLE_INFERENCE_IDS = def(9_150_0_00);
360360
public static final TransportVersion ESQL_LOOKUP_JOIN_PRE_JOIN_FILTER = def(9_151_0_00);
361+
public static final TransportVersion INFERENCE_API_DISABLE_EIS_RATE_LIMITING = def(9_152_0_00);
361362

362363
/*
363364
* STOP! READ THIS FIRST! No, really,

server/src/main/java/org/elasticsearch/index/codec/PerFieldFormatSupplier.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,37 @@
2727
import org.elasticsearch.index.mapper.Mapper;
2828
import org.elasticsearch.index.mapper.MapperService;
2929
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
30+
import org.elasticsearch.index.mapper.TimeSeriesIdFieldMapper;
31+
import org.elasticsearch.index.mapper.TimeSeriesRoutingHashFieldMapper;
3032
import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper;
3133

34+
import java.util.Collections;
35+
import java.util.HashSet;
36+
import java.util.Set;
37+
3238
/**
3339
* Class that encapsulates the logic of figuring out the most appropriate file format for a given field, across postings, doc values and
3440
* vectors.
3541
*/
3642
public class PerFieldFormatSupplier {
3743

38-
private static final FeatureFlag SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT = new FeatureFlag("seqno_field_use_tsdb_doc_values_format");
44+
static final FeatureFlag SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT = new FeatureFlag("seqno_field_use_tsdb_doc_values_format");
45+
private static final Set<String> INCLUDE_META_FIELDS;
46+
47+
static {
48+
// TODO: should we just allow all fields to use tsdb doc values codec?
49+
// Avoid using tsdb codec for fields like _seq_no, _primary_term.
50+
// But _tsid and _ts_routing_hash should always use the tsdb codec.
51+
Set<String> includeMetaField = new HashSet<>(3);
52+
includeMetaField.add(TimeSeriesIdFieldMapper.NAME);
53+
includeMetaField.add(TimeSeriesRoutingHashFieldMapper.NAME);
54+
if (SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT.isEnabled()) {
55+
includeMetaField.add(SeqNoFieldMapper.NAME);
56+
}
57+
// Don't the include _recovery_source_size and _recovery_source fields, since their values can be trimmed away in
58+
// RecoverySourcePruneMergePolicy, which leads to inconsistencies between merge stats and actual values.
59+
INCLUDE_META_FIELDS = Collections.unmodifiableSet(includeMetaField);
60+
}
3961

4062
private static final DocValuesFormat docValuesFormat = new Lucene90DocValuesFormat();
4163
private static final KnnVectorsFormat knnVectorsFormat = new Lucene99HnswVectorsFormat();
@@ -126,13 +148,7 @@ boolean useTSDBDocValuesFormat(final String field) {
126148
}
127149

128150
private boolean excludeFields(String fieldName) {
129-
// TODO: should we just allow all fields to use tsdb doc values codec?
130-
// Avoid using tsdb codec for fields like _seq_no, _primary_term.
131-
// But _tsid and _ts_routing_hash should always use the tsdb codec.
132-
return fieldName.startsWith("_")
133-
&& fieldName.equals("_tsid") == false
134-
&& fieldName.equals("_ts_routing_hash") == false
135-
&& (SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT.isEnabled() && fieldName.equals(SeqNoFieldMapper.NAME) == false);
151+
return fieldName.startsWith("_") && INCLUDE_META_FIELDS.contains(fieldName) == false;
136152
}
137153

138154
private boolean isTimeSeriesModeIndex() {

server/src/test/java/org/elasticsearch/index/codec/PerFieldMapperCodecTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import org.elasticsearch.index.codec.bloomfilter.ES87BloomFilterPostingsFormat;
2222
import org.elasticsearch.index.codec.postings.ES812PostingsFormat;
2323
import org.elasticsearch.index.mapper.MapperService;
24+
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
25+
import org.elasticsearch.index.mapper.SourceFieldMapper;
26+
import org.elasticsearch.index.mapper.TimeSeriesIdFieldMapper;
27+
import org.elasticsearch.index.mapper.TimeSeriesRoutingHashFieldMapper;
2428
import org.elasticsearch.test.ESTestCase;
2529

2630
import java.io.IOException;
@@ -201,6 +205,33 @@ public void testLogsIndexMode() throws IOException {
201205
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat("response_size")), is(true));
202206
}
203207

208+
public void testMetaFields() throws IOException {
209+
PerFieldFormatSupplier perFieldMapperCodec = createFormatSupplier(true, IndexMode.LOGSDB, MAPPING_3);
210+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(TimeSeriesIdFieldMapper.NAME)), is(true));
211+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(TimeSeriesRoutingHashFieldMapper.NAME)), is(true));
212+
// See: PerFieldFormatSupplier why these fields shouldn't use tsdb codec
213+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(SourceFieldMapper.RECOVERY_SOURCE_NAME)), is(false));
214+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(SourceFieldMapper.RECOVERY_SOURCE_SIZE_NAME)), is(false));
215+
}
216+
217+
public void testSeqnoField() throws IOException {
218+
assumeTrue(
219+
"seqno_field_use_tsdb_doc_values_format should be enabled",
220+
PerFieldFormatSupplier.SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT.isEnabled()
221+
);
222+
PerFieldFormatSupplier perFieldMapperCodec = createFormatSupplier(true, IndexMode.LOGSDB, MAPPING_3);
223+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(SeqNoFieldMapper.NAME)), is(true));
224+
}
225+
226+
public void testSeqnoFieldFeatureFlagDisabled() throws IOException {
227+
assumeTrue(
228+
"seqno_field_use_tsdb_doc_values_format should be disabled",
229+
PerFieldFormatSupplier.SEQNO_FIELD_USE_TSDB_DOC_VALUES_FORMAT.isEnabled() == false
230+
);
231+
PerFieldFormatSupplier perFieldMapperCodec = createFormatSupplier(true, IndexMode.LOGSDB, MAPPING_3);
232+
assertThat((perFieldMapperCodec.useTSDBDocValuesFormat(SeqNoFieldMapper.NAME)), is(false));
233+
}
234+
204235
private PerFieldFormatSupplier createFormatSupplier(boolean enableES87TSDBCodec, IndexMode mode, String mapping) throws IOException {
205236
Settings.Builder settings = Settings.builder();
206237
settings.put(IndexSettings.MODE.getKey(), mode);

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneOperator.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434
import java.io.IOException;
3535
import java.io.UncheckedIOException;
36-
import java.util.Collection;
3736
import java.util.Collections;
3837
import java.util.HashSet;
3938
import java.util.List;
@@ -284,20 +283,14 @@ List<Object> tags() {
284283
public String toString() {
285284
StringBuilder sb = new StringBuilder();
286285
sb.append(this.getClass().getSimpleName()).append("[");
287-
sb.append("shards = ").append(sortedUnion(processedShards, sliceQueue.remainingShardsIdentifiers()));
286+
sb.append("shards = ")
287+
.append(sliceQueue.partitioningStrategies().keySet().stream().sorted().collect(Collectors.joining(",", "[", "]")));
288288
sb.append(", maxPageSize = ").append(maxPageSize);
289289
describe(sb);
290290
sb.append("]");
291291
return sb.toString();
292292
}
293293

294-
private static Set<String> sortedUnion(Collection<String> a, Collection<String> b) {
295-
var result = new TreeSet<String>();
296-
result.addAll(a);
297-
result.addAll(b);
298-
return result;
299-
}
300-
301294
protected abstract void describe(StringBuilder sb);
302295

303296
@Override

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneSliceQueue.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.io.UncheckedIOException;
2424
import java.util.ArrayList;
2525
import java.util.Arrays;
26-
import java.util.Collection;
2726
import java.util.Collections;
2827
import java.util.Comparator;
2928
import java.util.HashMap;
@@ -178,17 +177,6 @@ public Map<String, PartitioningStrategy> partitioningStrategies() {
178177
return partitioningStrategies;
179178
}
180179

181-
public Collection<String> remainingShardsIdentifiers() {
182-
List<String> remaining = new ArrayList<>(slices.length());
183-
for (int i = 0; i < slices.length(); i++) {
184-
LuceneSlice slice = slices.get(i);
185-
if (slice != null) {
186-
remaining.add(slice.shardContext().shardIdentifier());
187-
}
188-
}
189-
return remaining;
190-
}
191-
192180
public static LuceneSliceQueue create(
193181
List<? extends ShardContext> contexts,
194182
Function<ShardContext, List<QueryAndTags>> queryFunction,

x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import static org.elasticsearch.test.ListMatcher.matchesList;
5959
import static org.elasticsearch.test.MapMatcher.assertMap;
6060
import static org.elasticsearch.test.MapMatcher.matchesMap;
61-
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.IMPLICIT_CASTING_DATE_AND_DATE_NANOS;
6261
import static org.elasticsearch.xpack.esql.core.type.DataType.isMillisOrNanos;
6362
import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.Mode.SYNC;
6463
import static org.elasticsearch.xpack.esql.tools.ProfileParser.parseProfile;
@@ -715,9 +714,7 @@ public void testSuggestedCast() throws IOException {
715714
Map<String, Object> results = entityAsMap(resp);
716715
List<?> columns = (List<?>) results.get("columns");
717716
DataType suggestedCast = DataType.suggestedCast(Set.of(listOfTypes.get(i), listOfTypes.get(j)));
718-
if (IMPLICIT_CASTING_DATE_AND_DATE_NANOS.isEnabled()
719-
&& isMillisOrNanos(listOfTypes.get(i))
720-
&& isMillisOrNanos(listOfTypes.get(j))) {
717+
if (isMillisOrNanos(listOfTypes.get(i)) && isMillisOrNanos(listOfTypes.get(j))) {
721718
// datetime and date_nanos are casted to date_nanos implicitly
722719
assertThat(columns, equalTo(List.of(Map.ofEntries(Map.entry("name", "my_field"), Map.entry("type", "date_nanos")))));
723720
} else {

0 commit comments

Comments
 (0)