Repository navigation
Fix three Elasticsearch index-list bugs, and build the dated index names through one policy - #14140
Merged
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Disabled record streams can resolve to the metrics rolling policy and silently miss existing trace data.
1 open finding
What changed in this PR
Centralizes Elasticsearch dated-index selection, extending the earlier metrics fix to a shared policy for reads, writes, and index management.
Changes:
- Introduces physical-table rolling policies and replaces the old naming utilities.
- Fixes Zipkin epoch-range searches, schema-free registration, and multi-index OTLP scrolling.
- Adds regression coverage and migrates existing tests.
| File | Description |
|---|---|
| EsReadIndexSelectionTest.java | Tests index selection across query paths. |
| AggregationQueryEsDAOTest.java | Captures index arrays instead of suppliers. |
| TimeSeriesUtilsTestCase.java | Removes tests migrated to IndexNamesTest. |
| TimeSeriesUtilsTest.java | Removes superseded naming tests. |
| StorageEsInstallerTest.java | Tests registration without schema changes. |
| IndexNamesTest.java | Tests centralized naming and rolling. |
| StorageModuleElasticsearchProvider.java | Configures the shared naming policy. |
| ZipkinQueryEsDAO.java | Uses the alias for epoch-reaching searches. |
| TraceQueryEsDAO.java | Uses shared trace-index selection. |
| TagAutoCompleteQueryDAO.java | Uses shared tag-index selection. |
| OTLPTraceQueryEsDAO.java | Passes dated indices separately to scrolling. |
| MetricsQueryEsDAO.java | Centralizes metric point and range selection. |
| LogQueryEsDAO.java | Uses shared log-index selection. |
| GenAIEvaluationRecordQueryEsDAO.java | Uses shared evaluation-index selection. |
| AIAgentConversationQueryEsDAO.java | Migrates conversation and file searches. |
| AggregationQueryEsDAO.java | Uses shared top-N index selection. |
| TimeSeriesUtils.java | Removes the old naming utility. |
| TimeRangeIndexNameGenerator.java | Removes the supplier-based range generator. |
| StorageEsInstaller.java | Registers schema-free models and uses centralized naming. |
| RecordEsDAO.java | Migrates record write-index naming. |
| NoneStreamEsDAO.java | Migrates non-stream write-index naming. |
| MetricsEsDAO.java | Migrates metric storage-index naming. |
| IndexRolling.java | Defines model-based rolling policies. |
| IndexNames.java | Centralizes dated-index construction. |
| IndexFamily.java | Resolves physical tables and rolling policies. |
| IndexController.java | Adds concurrent catalogs and rolling validation. |
| HistoryDeleteEsDAO.java | Uses shared naming and date extraction. |
| EsDAO.java | Accepts index arrays for debug-enabled searches. |
| ElasticSearchIT.java | Tests scrolling across existing and missing indices. |
| ElasticSearchScroller.java | Supports index arrays. |
| ElasticSearchClient.java | Namespaces each index and preserves scroll parameters. |
| docs/en/changes/changes.md | Documents fixes and naming consolidation. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
wankai123
previously approved these changes
Oct 15, 2026
kezhenxu94
reviewed
Oct 15, 2026
wu-sheng
force-pushed
the
refactor/es-index-name-policy
branch
from
October 15, 2026 07:13
8fc4a45 to
d5e002a
Compare
… three index-list bugs The day step of a physical table is now decided once, from its models: IndexRolling (NONE, DAY_STEP, SUPER_DATASET_DAY_STEP) is recorded per physical table by IndexController.LogicIndicesRegister.registerRelation, which rejects a table registered with two kinds; the catalog maps are concurrent, as runtime rules register models after boot. IndexNames builds every dated index name from an IndexFamily, so a caller no longer picks the step. TimeSeriesUtils and TimeRangeIndexNameGenerator are removed, and ElasticSearchClient.search takes an index array instead of a Supplier. Apart from the fixes below, index names are unchanged. Fixes: - Zipkin getTraces listed one daily index per day since 1970 when the query started at or before the epoch. It now searches the zipkin_span family in that case, as its time filter already did. - A model applied without a schema change skipped the isExists probe, the only place its table was registered, so with logicSharding on a bundled MAL rule restored after its runtime override kept the override's table. StorageEsInstaller now registers in populateLocalCacheOnly. - OTLP queryTraceById joined the day indices into one name, so the namespace went on the first only and a missing day failed the scroll. The scroller now takes the indices as an array, each namespaced, skipping missing days.
wu-sheng
force-pushed
the
refactor/es-index-name-policy
branch
from
October 15, 2026 07:27
d5e002a to
0f2389f
Compare
kezhenxu94
approved these changes
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fix three Elasticsearch index-list bugs, and build the dated index names through one policy
Follows #14139. That PR fixed five metric reads that picked the wrong day step. The root cause was that every caller chose the day step itself, through
TimeSeriesUtilsorTimeRangeIndexNameGenerator. This PR decides the step once per physical table and builds every dated index name in one place, so a caller can no longer pick the wrong one. Alias and management index names still come fromIndexController, as before.The policy
IndexRolling(NONE,DAY_STEP,SUPER_DATASET_DAY_STEP) is decided once from the model.IndexController.LogicIndicesRegister.registerRelationrecords it per physical table, and rejects a table registered with two kinds. The catalog maps become concurrent, since runtime rules register models after boot.IndexFamilyis a physical table plus its rolling, resolved from a model or a logical name. An unregistered name rolls bydayStep: only metric reads pass names that may be unregistered.IndexNamesbuilds every dated name:writeIndex,pointIndex,rangeIndices,rangeIndicesBetweenTimestamps,latestIndex,indexStartDay.TimeSeriesUtilsandTimeRangeIndexNameGeneratorare removed.ElasticSearchClient.searchtakes an index array instead of aSupplier. The newsearch(String[], Search, SearchParams)keeps routing and scroll, and the scroller can take an array.Apart from the three fixes below, index names are unchanged. Super dataset reads now take their step from the registered table, which every OAP registers at boot.
Bugs fixed on the way
lookbackequal toendTs). With the default day step of 1 that is over 20,000 names, which can exceed the request line Elasticsearch accepts. It now searches thezipkin_spanfamily in that case, as its time filter already did.isExistsregistered a model's table, and a node applying a model without a schema change skips it. One example is a peer reinstalling a bundled MAL rule after the runtime rule overriding it is deleted. WithlogicShardingon and the two rules using different functions, the metric's table changes, so reads kept the runtime rule's table.StorageEsInstallernow registers inpopulateLocalCacheOnly. I traced this path in code; I did not reproduce it on a cluster.swby default, went on the first name only, and a missing day failed the scroll withindex_not_found_exception. The day indices now go as an array, each namespaced, and missing days are skipped. No changelog line, since the feature hasn't been released.Verification
EsReadIndexSelectionTestwas written first, against the old code. It pins the indices the migrated reads send. In the cases that list dated indices, one step rolls by 3 and the other by 1, so the names show which step the read used; the rest pin when a read falls back to the whole index family. The top N read is pinned byAggregationQueryEsDAOTest. The same assertions pass after the refactor.IndexNameswithTimeSeriesUtilsmatched all 653,400 names it checked: steps 1 to 11 for both kinds, dates from 2000 to 2031, every downsampling, ranges up to 40 days either way, and latest indices. It is not committed, because the old classes are gone.ElasticSearchIT#scrollOverSeveralIndicesruns on ES 6.3.2 and 7.8.0, with and without a namespace. All 4 runs pass. The old comma-joined form fails all 4 withindex_not_found_exception.storage-elasticsearch-pluginandlibrary-client, checkstyle,javadoc:javadocandlicense-eye header checkpass.cf1c2be22cwith this PR'slibrary-clientandstorage-elasticsearch-pluginjars: Storage ES 8.18.8 (76 passed), Storage ES Sharding 8.18.8 (76 passed, which boots every model with the two-kinds rolling check), and Zipkin ES (7 passed).Test changes
TimeSeriesUtilsTestandTimeSeriesUtilsTestCaseare ported toIndexNamesTest. TheirqueryIndexName(..., Step.DAY, true, true)case, which no production code reached, became apointIndexon a super dataset table at second precision.AggregationQueryEsDAOTestcaptures an array instead of aSupplier.Left for later
TTL deletes a whole index once its first day passes the deadline. When
daySteporsuperDatasetDayStepis greater than 1, that is up to step − 1 days early; the default step of 1 is not affected. The fix is to compare the index's last day. It changes behavior, so it gets its own PR.Reads that receive a duration but search every index of a table (topology, records, alarm, event) could narrow to the range, each with its own rule.
If this pull request closes/resolves/fixes an existing issue, replace the issue number. Closes #.
Update the
CHANGESlog.