Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
644777c
Enable synthetic_id by default if valid
burqen Feb 13, 2026
fec3891
Remove unnecessary time_series_dimension from field
burqen Mar 11, 2026
f45455b
Fix test failure in TSDBSyntheticIdsIT
burqen Mar 11, 2026
2ad61ba
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 11, 2026
c10e955
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 12, 2026
d299af1
NodeFeature index.time_series_synthetic_id_default
burqen Mar 12, 2026
8358b29
Skip tsdb/25_id... and delete/70_tsdb in yamlRestCompatTest
burqen Mar 3, 2026
e050b55
Ensure green to make sure replicas have caught up
burqen Mar 12, 2026
2934324
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
elasticmachine Mar 12, 2026
64b7f9d
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 13, 2026
299b2e9
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 13, 2026
60a03c7
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 13, 2026
4cfaacf
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 16, 2026
6ca2f5f
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 16, 2026
9214cb4
Remove unused logger from Skip.java
burqen Mar 16, 2026
d5c2caf
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 16, 2026
d830bb7
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 17, 2026
3d3c039
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 17, 2026
0c5c514
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 17, 2026
b2681f7
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 17, 2026
cc8a4bb
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 18, 2026
22c2032
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 18, 2026
c6a5c82
Merge branch 'main' into ap/2026.03.11.synthetic-id-enable-by-default…
burqen Mar 18, 2026
c2cf400
Merge remote-tracking branch 'upstream/main' into ap/2026.03.11.synth…
burqen Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.elasticsearch.gradle.internal.test.rest.transform.RestTestTransformByParentObject;
import org.elasticsearch.gradle.internal.test.rest.transform.RestTestTransformGlobalSetup;
import org.gradle.api.tasks.Input;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;

Expand All @@ -27,6 +29,7 @@
*/
public class Skip implements RestTestTransformGlobalSetup, RestTestTransformByParentObject {

private static final Logger log = LoggerFactory.getLogger(Skip.class);
Comment thread
burqen marked this conversation as resolved.
Outdated
private static JsonNodeFactory jsonNodeFactory = JsonNodeFactory.withExactBigDecimals(false);

private final String skipReason;
Expand Down Expand Up @@ -87,8 +90,14 @@ private void addSkip(ArrayNode skipParent) {
@Override
public void transformTest(ObjectNode parent) {
if (testName.isBlank() == false) {
assert parent.get(testName) instanceof ArrayNode;
addSkip((ArrayNode) parent.get(testName));
JsonNode value = parent.get(testName);
// Only apply skip to test documents where the key is the test name and value is the steps array.
// Do not apply to nested keys with the same name (e.g. "do: get: { ... }" request body).
// This makes it possible to skip tests where the test name is an overloaded term such as
// task.skipTest("tsdb/25_id_generation/delete",...)
if (value instanceof ArrayNode) {
addSkip((ArrayNode) value);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
import org.elasticsearch.action.admin.indices.diskusage.TransportAnalyzeIndexDiskUsageAction;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.ProjectId;
import org.elasticsearch.cluster.metadata.Template;
import org.elasticsearch.cluster.routing.RecoverySource;
Expand All @@ -44,6 +46,8 @@
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.index.codec.CodecService;
import org.elasticsearch.index.codec.storedfields.TSDBStoredFieldsFormat;
import org.elasticsearch.index.codec.tsdb.TSDBSyntheticIdStoredFieldsReader;
Expand Down Expand Up @@ -77,6 +81,7 @@
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentType;
import org.hamcrest.Matchers;

import java.io.IOException;
import java.nio.file.Path;
Expand Down Expand Up @@ -1292,6 +1297,70 @@ public void testMerge() throws Exception {
assertShardsHaveNoIdStoredFieldValuesOnDisk(indices);
}

public void testDefaultSetting() throws Exception {
assumeTrue("Test should only run with feature flag", IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG);

String indexName = randomIndexName();

// Don't set IndexSettings.SYNTHETIC_ID to test default behavior.
// Use default codec so the SYNTHETIC_ID default is true
// (codec will be randomised by ESIntegTestCase.randomIndexTemplate if not explicitly set)
Settings.Builder settingsBuilder = Settings.builder()
.put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES)
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "hostname")
.put(EngineConfig.INDEX_CODEC_SETTING.getKey(), CodecService.DEFAULT_CODEC);
final var mapping = """
{
"properties": {
"@timestamp": {
"type": "date"
},
"hostname": {
"type": "keyword",
"time_series_dimension": true
},
"metric": {
"properties": {
"field": {
"type": "keyword"
},
"value": {
"type": "integer",
"time_series_metric": "counter"
}
}
}
}
}
""";
assertAcked(client().admin().indices().prepareCreate(indexName).setSettings(settingsBuilder).setMapping(mapping).get());

var timestamp = Instant.now();
createDocuments(
indexName,
document(timestamp, "vm-dev01", "cpu-load", 0),
document(timestamp.plus(1, ChronoUnit.SECONDS), "vm-dev02", "cpu-load", 1)
);
ensureGreen(indexName);
flushAndRefresh(indexName);

GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings(TEST_REQUEST_TIMEOUT, indexName).get();
String versionSetting = getSettingsResponse.getSetting(indexName, IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey());
IndexVersion version = IndexVersion.fromId(Integer.parseInt(versionSetting));
assertTrue(version.onOrAfter(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT));
String syntheticIdSetting = getSettingsResponse.getSetting(indexName, IndexSettings.SYNTHETIC_ID.getKey());
assertThat(syntheticIdSetting, Matchers.nullValue());

var diskUsage = diskUsage(indexName);
var diskUsageIdField = AnalyzeIndexDiskUsageTestUtils.getPerFieldDiskUsage(diskUsage, IdFieldMapper.NAME);
assertThat("_id field should not have postings on disk", diskUsageIdField.getInvertedIndexBytes(), equalTo(0L));
assertThat("_id field should have bloom filter usage", diskUsageIdField.getBloomFilterBytes(), greaterThan(0L));

var indices = new HashSet<String>();
indices.add(indexName);
assertShardsHaveNoIdStoredFieldValuesOnDisk(indices);
}

private static long documentCount(String dataStreamName) {
return indicesAdmin().prepareStats(dataStreamName).setDocs(true).get().getTotal().docs.getCount();
}
Expand Down
12 changes: 12 additions & 0 deletions rest-api-spec/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,16 @@ tasks.named("yamlRestCompatTestTransform").configure ({ task ->
"get/100_synthetic_source/fields with ignore_malformed",
"Malformed values are now stored in binary doc values which sort differently than stored fields"
)
task.skipTest("delete/70_tsdb/basic tsdb delete", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/routing_path matches object", "ids have changed after introduction of synthetic id")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we set the setting to false in this test suite?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for the compatibility tests. They copy the old yaml file from the bwc branch and updating all of those is not worth it IMO.

task.skipTest("tsdb/25_id_generation/ids query", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/create operation on top of old document fails over bulk", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/delete over _bulk", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/delete", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/index a new document on top of an old one", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/generates a consistent id", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/create operation on top of old document fails", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/get", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/routing_path matches deep object", "ids have changed after introduction of synthetic id")
task.skipTest("tsdb/25_id_generation/index a new document on top of an old one over bulk", "ids have changed after introduction of synthetic id")
})
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.index.codec.CodecService;
import org.elasticsearch.index.engine.EngineConfig;
import org.elasticsearch.index.mapper.DateFieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.search.QueryParserHelper;
Expand Down Expand Up @@ -2574,15 +2576,7 @@ IndexMetadata build(boolean repair) {
String indexModeString = settings.get(IndexSettings.MODE.getKey());
final IndexMode indexMode = indexModeString != null ? IndexMode.fromString(indexModeString.toLowerCase(Locale.ROOT)) : null;
final boolean isTsdb = indexMode == IndexMode.TIME_SERIES;
boolean useTimeSeriesSyntheticId = false;
if (isTsdb
&& IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG
&& indexCreatedVersion.onOrAfter(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_94)) {
var setting = settings.get(IndexSettings.SYNTHETIC_ID.getKey());
if (setting != null && setting.equalsIgnoreCase(Boolean.TRUE.toString())) {
useTimeSeriesSyntheticId = true;
}
}
boolean useTimeSeriesSyntheticId = shouldUseTimeSeriesSyntheticId(isTsdb, indexCreatedVersion, settings);
final boolean sequenceNumbersDisabled = IndexSettings.DISABLE_SEQUENCE_NUMBERS_FEATURE_FLAG
&& indexCreatedVersion.onOrAfter(IndexVersions.DISABLE_SEQUENCE_NUMBERS)
&& settings.getAsBoolean(IndexSettings.DISABLE_SEQUENCE_NUMBERS.getKey(), false);
Expand Down Expand Up @@ -2642,6 +2636,18 @@ IndexMetadata build(boolean repair) {
);
}

private static boolean shouldUseTimeSeriesSyntheticId(boolean isTsdb, IndexVersion version, Settings settings) {
String codecSetting = settings.get(EngineConfig.INDEX_CODEC_SETTING.getKey());
if (IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG
&& isTsdb
&& version.onOrAfter(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_94)
&& (codecSetting == null || codecSetting.equalsIgnoreCase(CodecService.DEFAULT_CODEC))) {
boolean defaultValue = version.onOrAfter(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT);
return settings.getAsBoolean(IndexSettings.SYNTHETIC_ID.getKey(), defaultValue);
}
return false;
}

@SuppressWarnings("unchecked")
public static void toXContent(IndexMetadata indexMetadata, XContentBuilder builder, ToXContent.Params params) throws IOException {
Metadata.XContentContext context = Metadata.XContentContext.valueOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public Set<NodeFeature> getFeatures() {
public static final NodeFeature LOGSDB_NO_HOST_NAME_FIELD = new NodeFeature("index.logsdb_no_host_name_field");

public static final NodeFeature TIME_SERIES_SYNTHETIC_ID = new NodeFeature("index.time_series_synthetic_id");
public static final NodeFeature TIME_SERIES_SYNTHETIC_ID_DEFAULT = new NodeFeature("index.time_series_synthetic_id_default");

public static final NodeFeature TIME_SERIES_NO_SEQNO = new NodeFeature("index.time_series_no_seqno");

Expand All @@ -51,6 +52,7 @@ public Set<NodeFeature> getTestFeatures() {
return Set.of(
LOGSDB_NO_HOST_NAME_FIELD,
TIME_SERIES_SYNTHETIC_ID,
TIME_SERIES_SYNTHETIC_ID_DEFAULT,
TIME_SERIES_NO_SEQNO,
SYNONYMS_SET_LENIENT_ON_NON_EXISTING,
THROW_EXCEPTION_FOR_UNKNOWN_TOKEN_IN_REST_INDEX_PUT_ALIAS_ACTION,
Expand Down
65 changes: 37 additions & 28 deletions server/src/main/java/org/elasticsearch/index/IndexSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,43 @@ public boolean isES87TSDBCodecEnabled() {
Property.Final
);

/**
* The {@link IndexMode "mode"} of the index.
*/
public static final Setting<IndexMode> MODE = Setting.enumSetting(

@burqen burqen Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to move this to have access in SYNTHETIC_ID, but the code is unchanged

IndexMode.class,
"index.mode",
IndexMode.STANDARD,
new Setting.Validator<>() {
@Override
public void validate(IndexMode value) {}

@Override
public void validate(IndexMode value, Map<Setting<?>, Object> settings) {
value.validateWithOtherSettings(settings);
}

@Override
public Iterator<Setting<?>> settings() {
return IndexMode.VALIDATE_WITH_SETTINGS.iterator();
}
},
Property.IndexScope,
Property.Final,
Property.ServerlessPublic
);

public static final boolean TSDB_SYNTHETIC_ID_FEATURE_FLAG = new FeatureFlag("tsdb_synthetic_id").isEnabled();
public static final Setting<Boolean> SYNTHETIC_ID = Setting.boolSetting("index.mapping.synthetic_id", false, new Setting.Validator<>() {
public static final Setting<Boolean> SYNTHETIC_ID = Setting.boolSetting("index.mapping.synthetic_id", settings -> {
IndexVersion indexVersion = SETTING_INDEX_VERSION_CREATED.get(settings);
IndexMode indexMode = MODE.get(settings);
String codec = INDEX_CODEC_SETTING.get(settings);
boolean onByDefault = indexVersion.onOrAfter(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT);
return TSDB_SYNTHETIC_ID_FEATURE_FLAG
&& IndexMode.TIME_SERIES.equals(indexMode)
&& CodecService.DEFAULT_CODEC.equalsIgnoreCase(codec)
&& onByDefault ? Boolean.TRUE.toString() : Boolean.FALSE.toString();
Comment thread
burqen marked this conversation as resolved.
}, new Setting.Validator<>() {
@Override
public void validate(Boolean enabled) {
if (enabled) {
Expand Down Expand Up @@ -717,7 +752,7 @@ public void validate(Boolean enabled, Map<Setting<?>, Object> settings) {
}

var codecName = (String) settings.get(INDEX_CODEC_SETTING);
if (codecName.equals(CodecService.DEFAULT_CODEC) == false) {
if (codecName.equalsIgnoreCase(CodecService.DEFAULT_CODEC) == false) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
Expand Down Expand Up @@ -757,32 +792,6 @@ public Iterator<Setting<?>> settings() {
}
}, Property.IndexScope, Property.Final);

/**
* The {@link IndexMode "mode"} of the index.
*/
public static final Setting<IndexMode> MODE = Setting.enumSetting(
IndexMode.class,
"index.mode",
IndexMode.STANDARD,
new Setting.Validator<>() {
@Override
public void validate(IndexMode value) {}

@Override
public void validate(IndexMode value, Map<Setting<?>, Object> settings) {
value.validateWithOtherSettings(settings);
}

@Override
public Iterator<Setting<?>> settings() {
return IndexMode.VALIDATE_WITH_SETTINGS.iterator();
}
},
Property.IndexScope,
Property.Final,
Property.ServerlessPublic
);

public static final Setting<Boolean> USE_DOC_VALUES_SKIPPER = Setting.boolSetting("index.mapping.use_doc_values_skipper", s -> {
IndexVersion iv = SETTING_INDEX_VERSION_CREATED.get(s);
if (MODE.get(s) == IndexMode.TIME_SERIES) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ private static Version parseUnchecked(String version) {
public static final IndexVersion DISABLE_SEQUENCE_NUMBERS = def(9_074_0_00, Version.LUCENE_10_3_2);
public static final IndexVersion UPGRADE_TO_LUCENE_10_4_0 = def(9_075_00_0, Version.LUCENE_10_4_0);
public static final IndexVersion UPGRADE_DISKBBQ_ES940 = def(9_076_00_0, Version.LUCENE_10_4_0);
public static final IndexVersion TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT = def(9_077_0_00, Version.LUCENE_10_4_0);

/*
* STOP! READ THIS FIRST! No, really,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,45 @@ public void testSyntheticIdCorrectSettings() {
assertTrue(indexMetadata.useTimeSeriesSyntheticId());
}

public void testSyntheticIdDefaultValueTrue() {
assumeTrue("Test should only run with feature flag", IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG);
IndexVersion version = IndexVersionUtils.randomVersionBetween(
IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT,
IndexVersion.current()
);
IndexMode mode = IndexMode.TIME_SERIES;
String codec = CodecService.DEFAULT_CODEC;

Settings settings = Settings.builder()
.put(EngineConfig.INDEX_CODEC_SETTING.getKey(), codec)
.put(IndexSettings.MODE.getKey(), mode)
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "some-routing")
.build();
IndexMetadata indexMetadata = newIndexMeta("some-index", settings, version);

IndexSettings indexSettings = new IndexSettings(indexMetadata, Settings.EMPTY);
assertTrue(indexSettings.useTimeSeriesSyntheticId());
assertTrue(indexMetadata.useTimeSeriesSyntheticId());
}

public void testSyntheticIdDefaultValueFalse() {
assumeTrue("Test should only run with feature flag", IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG);
IndexVersion version = IndexVersionUtils.getPreviousVersion(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_DEFAULT);
IndexMode mode = IndexMode.TIME_SERIES;
String codec = CodecService.DEFAULT_CODEC;

Settings settings = Settings.builder()
.put(EngineConfig.INDEX_CODEC_SETTING.getKey(), codec)
.put(IndexSettings.MODE.getKey(), mode)
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "some-routing")
.build();
IndexMetadata indexMetadata = newIndexMeta("some-index", settings, version);

IndexSettings indexSettings = new IndexSettings(indexMetadata, Settings.EMPTY);
assertFalse(indexSettings.useTimeSeriesSyntheticId());
assertFalse(indexMetadata.useTimeSeriesSyntheticId());
}

public void testSyntheticIdBadVersion() {
assumeTrue("Test should only run with feature flag", IndexSettings.TSDB_SYNTHETIC_ID_FEATURE_FLAG);
IndexVersion badVersion = IndexVersionUtils.getPreviousVersion(IndexVersions.TIME_SERIES_USE_SYNTHETIC_ID_94);
Expand Down
Loading