Skip to content

Conversation

@dnhatn
Copy link
Member

@dnhatn dnhatn commented Aug 14, 2025

Reading keyword fields that are the primary sort in the index can be sped up by skip reading, as identical values are stored together. In this case, we can use values from the doc_values skipper instead of loading values from doc_values. However, the doc_values skipper is not enabled yet. Here, we use two buffers when reading ordinals: one for the beginning of the block and one for the end. If both return the same value, we can skip the middle. There is a follow-up step where we fill the values in the middle until we reach the last value. This optimization should speed up time-series queries.

@dnhatn dnhatn force-pushed the values-reader-with-skipper branch from ce26e27 to b2ae4bf Compare August 14, 2025 21:50
@dnhatn dnhatn changed the title Load constant blocks for keyword fields with index sorts Speed up loading keyword fields with index sorts Aug 14, 2025
@dnhatn dnhatn force-pushed the values-reader-with-skipper branch from b2ae4bf to 503b524 Compare August 14, 2025 22:02
@dnhatn dnhatn force-pushed the values-reader-with-skipper branch from 3081cfa to 8639922 Compare August 14, 2025 22:24
@dnhatn dnhatn requested a review from martijnvg August 15, 2025 00:13
@dnhatn dnhatn force-pushed the values-reader-with-skipper branch from 165df8b to e24a8e0 Compare August 15, 2025 00:44
Copy link
Member

@martijnvg martijnvg left a comment

Choose a reason for hiding this comment

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

Thanks this looks great - LGTM.

try (var reader = DirectoryReader.open(iw)) {
int gaugeIndex = numDocs;
for (var leaf : reader.leaves()) {
var timestampDV = getBulkNumericDocValues(leaf.reader(), timestampField);
Copy link
Member

Choose a reason for hiding this comment

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

Like we talked about we need to adjust these test to also use sorted(set) doc values.

readFields(in, state.fieldInfos);

final var indexSort = state.segmentInfo.getIndexSort();
if (indexSort != null && indexSort.getSort().length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

👍 - good idea to extract this information from segment info, no need to check for index setting.

if (lookaheadBlockIndex + 1 != blockIndex) {
lookaheadData.seek(indexReader.get(blockIndex));
}
if (maxOrd >= 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Currently this is only invoked from getSorted(...) and in that case maxOrd is always >= 0.
I think we should keep this branch, otherwise we can't reuse lookAheadValueAt(...) for numeric doc value.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe replace this with a switch statement:

switch (maxOrdAsInt) {
    case -1:
        decoder.decode(lookaheadData, lookaheadBlock);
        break;
    default:
        decoder.decodeOrdinals(lookaheadData, lookaheadBlock, bitsPerOrd);
}

I think this is a little bit more efficient?

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried but Intellij suggested this 91d32d1

Copy link
Member

Choose a reason for hiding this comment

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

👍 - I suspect that is also easier to be optimized at runtime by jvm.

* to load the requested values, for example due to unsupported underlying data.
* This allows callers to optimistically try optimized loading strategies first, and fall back if necessary.
*/
interface OptionalColumnAtATimeReader {
Copy link
Member

Choose a reason for hiding this comment

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

👍 This is better than previous abstraction (BulkNumericDocValues).

}

@Override
public BytesRefBlock constantBytes(BytesRef value, int count) {
Copy link
Member

Choose a reason for hiding this comment

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

Could this also be used in SingletonOrdinalsBuilder#tryBuildConstantBlock(...)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Great point. Unfortunately, we don't have DelegatingBlockLoaderFactory here. I will move this to BlockFactory in a follow-up.

final class ES819TSDBDocValuesProducer extends DocValuesProducer {
final IntObjectHashMap<NumericEntry> numerics;
private int primarySortFieldNumber = -1;
private boolean primarySortFieldReversed = false;
Copy link
Member

Choose a reason for hiding this comment

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

Note that this field can be converted to a variable.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks, I removed a0ce374


@Override
long lookAheadValueAt(int targetDoc) throws IOException {
return 0L; // Only one ordinal!
Copy link
Member

Choose a reason for hiding this comment

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

easy one :)

Copy link
Member Author

Choose a reason for hiding this comment

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

the test found it :)

@dnhatn dnhatn added :Analytics/ES|QL AKA ESQL :StorageEngine/TSDB You know, for Metrics labels Aug 15, 2025
@dnhatn dnhatn marked this pull request as ready for review August 15, 2025 06:20
@elasticsearchmachine elasticsearchmachine added Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) Team:StorageEngine labels Aug 15, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)

@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-storage-engine (Team:StorageEngine)

@elasticsearchmachine
Copy link
Collaborator

Hi @dnhatn, I've created a changelog YAML for you.

@martijnvg martijnvg merged commit 64f8209 into elastic:main Aug 15, 2025
34 checks passed
szybia added a commit to szybia/elasticsearch that referenced this pull request Aug 15, 2025
* upstream/main: (32 commits)
  Speed up loading keyword fields with index sorts (elastic#132950)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testSyntheticSourceWithTranslogSnapshot elastic#132964
  Simplify EsqlSession (elastic#132848)
  Implement WriteLoadConstraintDecider#canAllocate (elastic#132041)
  Mute org.elasticsearch.test.rest.yaml.CcsCommonYamlTestSuiteIT test {p0=search/400_synthetic_source/_doc_count} elastic#132965
  Switch to PR-based benchmark pipeline defined in ES repo (elastic#132941)
  Breakdown undesired allocations by shard routing role (elastic#132235)
  Implement v_magnitude function (elastic#132765)
  Introduce execution location marker for better handling of remote/local compatibility (elastic#132205)
  Mute org.elasticsearch.cluster.ClusterInfoServiceIT testMaxQueueLatenciesInClusterInfo elastic#132957
  Unmuting simulate index data stream mapping overrides yaml rest test (elastic#132946)
  Remove CrossClusterCancellationIT.createLocalIndex() (elastic#132952)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testFetch elastic#132956
  Fix failing UT by adding a required capability (elastic#132947)
  Precompute the BitsetCacheKey hashCode (elastic#132875)
  Adding simulate ingest effective mapping (elastic#132833)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testFetchMany elastic#132948
  Rename skipping logic to remove hard link to skip_unavailable (elastic#132861)
  Store ignored source in unique stored fields per entry (elastic#132142)
  Add random tests with match_only_text multi-field (elastic#132380)
  ...
joshua-adams-1 pushed a commit to joshua-adams-1/elasticsearch that referenced this pull request Aug 15, 2025
Reading keyword fields that are the primary sort in the index can be sped up by skip reading, as identical values are stored together. In this case, we can use values from the doc_values skipper instead of loading values from doc_values. However, the doc_values skipper is not enabled yet. Here, we use two buffers when reading ordinals: one for the beginning of the block and one for the end. If both return the same value, we can skip the middle. There is a follow-up step where we fill the values in the middle until we reach the last value. This optimization should speed up time-series queries.
szybia added a commit to szybia/elasticsearch that referenced this pull request Aug 15, 2025
…-stats

* upstream/main: (36 commits)
  Fix reproducability of builds against Java EA versions (elastic#132847)
  Speed up loading keyword fields with index sorts (elastic#132950)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testSyntheticSourceWithTranslogSnapshot elastic#132964
  Simplify EsqlSession (elastic#132848)
  Implement WriteLoadConstraintDecider#canAllocate (elastic#132041)
  Mute org.elasticsearch.test.rest.yaml.CcsCommonYamlTestSuiteIT test {p0=search/400_synthetic_source/_doc_count} elastic#132965
  Switch to PR-based benchmark pipeline defined in ES repo (elastic#132941)
  Breakdown undesired allocations by shard routing role (elastic#132235)
  Implement v_magnitude function (elastic#132765)
  Introduce execution location marker for better handling of remote/local compatibility (elastic#132205)
  Mute org.elasticsearch.cluster.ClusterInfoServiceIT testMaxQueueLatenciesInClusterInfo elastic#132957
  Unmuting simulate index data stream mapping overrides yaml rest test (elastic#132946)
  Remove CrossClusterCancellationIT.createLocalIndex() (elastic#132952)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testFetch elastic#132956
  Fix failing UT by adding a required capability (elastic#132947)
  Precompute the BitsetCacheKey hashCode (elastic#132875)
  Adding simulate ingest effective mapping (elastic#132833)
  Mute org.elasticsearch.index.mapper.LongFieldMapperTests testFetchMany elastic#132948
  Rename skipping logic to remove hard link to skip_unavailable (elastic#132861)
  Store ignored source in unique stored fields per entry (elastic#132142)
  ...
@dnhatn dnhatn deleted the values-reader-with-skipper branch September 9, 2025 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL >enhancement :StorageEngine/TSDB You know, for Metrics Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) Team:StorageEngine v9.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants