Skip to content

Conversation

@martin-gaievski
Copy link
Member

@martin-gaievski martin-gaievski commented Feb 12, 2026

Description

ProfileScorer.getChildren() currently delegates directly to the wrapped scorer's getChildren(),
which skips the ProfileScorer level in the scorer tree. This makes the wrapped scorer invisible to
any code traversing the scorer hierarchy via the standard Lucene Scorable.getChildren() API.

This change makes ProfileScorer expose the wrapped scorer as a child with the "PROFILED"
relationship label, allowing plugins and custom collectors to discover wrapped scorers through
profiling wrappers using standard tree traversal — without requiring reflection.

Changes

  • ProfileScorer.java: Changed getChildren() from return scorer.getChildren() to
    return Collections.singletonList(new ChildScorable(scorer, "PROFILED"))
  • ProfileScorerTests.java: Added testGetChildren_exposesWrappedScorerAsChild() to verify
    the new behavior

Related Issues

Resolves #20548

PR #20549 added getWrappedScorer() — a public method on ProfileScorer that returns the wrapped scorer. This was intended to let neural-search unwrap ProfileScorer to find HybridQueryScorer.

When implementing the neural-search side, we discovered that getWrappedScorer() alone doesn't solve the problem because ProfileScorer is a package-private final class. Even though getWrappedScorer() is a public method, Java's access control system blocks Method.invoke() from external modules (plugins) on methods of non-public classes. The plugin code gets:

IllegalAccessException: class LeafCollector cannot access a member of class 
org.opensearch.search.profile.query.ProfileScorer with modifiers "public"

This means the neural-search plugin had to use method.setAccessible(true) + @SuppressForbidden to call getWrappedScorer() via reflection — defeating the purpose of having a clean public API.

Check List

  • Functionality includes testing.
  • API changes companion pull request created, if applicable.
  • Public documentation issue/PR created, if applicable.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

This PR modifies ProfileScorer to expose the wrapped scorer as a child through the getChildren() method, which now returns a singleton list containing a ChildScorable with "PROFILED" relationship instead of delegating to the wrapped scorer's children. Includes corresponding test coverage and changelog documentation.

Changes

Cohort / File(s) Summary
ProfileScorer Implementation
server/src/main/java/org/opensearch/search/profile/query/ProfileScorer.java
Added java.util.Collections import. Modified getChildren() to return a singleton list wrapping the scorer in a ChildScorable with "PROFILED" relationship, exposing the wrapped scorer for plugin access instead of delegating to the wrapped scorer's children.
Test Coverage
server/src/test/java/org/opensearch/search/profile/query/ProfileScorerTests.java
Added testGetChildren_exposesWrappedScorerAsChild() test method verifying that getChildren() returns exactly one child (the wrapped scorer) with PROFILED relationship. Updated imports to include java.util.Collection.
Documentation
CHANGELOG.md
Added Fixed entry under Unreleased 3.x describing that ProfileScorer.getChildren() now exposes the wrapped scorer for plugin compatibility.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • opensearch-project/OpenSearch#20549: Implements an alternative approach (getWrappedScorer() accessor method) to expose the wrapped scorer in ProfileScorer for the same plugin compatibility use case.
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing ProfileScorer.getChildren() to expose the wrapped scorer in the scorer tree.
Linked Issues check ✅ Passed The PR implementation aligns with issue #20548's objective of exposing the wrapped scorer, though via getChildren() rather than a dedicated getWrappedScorer() method.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue: ProfileScorer.getChildren() modification, import addition, and corresponding test coverage.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering problem statement, solution, changes, related issues, and checklist items as required by the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added enhancement Enhancement or improvement to existing feature or request Search:Query Capabilities labels Feb 12, 2026
@github-actions
Copy link
Contributor

❌ Gradle check result for 6fddaa4: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@martin-gaievski
Copy link
Member Author

Looks like rarely failing test, not related to this change, passes in my local environment

Task :server:test

REPRODUCE WITH: ./gradlew ':server:test' --tests 'org.opensearch.index.shard.IndexShardTests.testRestoreSearchOnlyShardFromStoreOnNewNode' -Dtests.seed=B3BDACD5418087E4 -Dtests.security.manager=true -Dtests.jvm.argline="-XX:TieredStopAtLevel=1 -XX:ReservedCodeCacheSize=64m" -Dtests.locale=ha-NE -Dtests.timezone=Asia/Makassar -Druntime.java=25

IndexShardTests > testRestoreSearchOnlyShardFromStoreOnNewNode FAILED
    [index][[index][0]] IndexShardRecoveryException[Failed to recover from gateway]; nested: CorruptIndexException[misplaced codec footer (file truncated?): length=0 but footerLength==16 (resource=metadata__9223372036854775805__9223372036854775804__9223372036854775802__9223372036854775805__-1039764442__9223370265996427162__2)];
        at __randomizedtesting.SeedInfo.seed([B3BDACD5418087E4:7369770EE6AE81DC]:0)
        at app//org.opensearch.index.shard.StoreRecovery.internalRecoverFromStoreForSearchReplica(StoreRecovery.java:815)
        at app//org.opensearch.index.shard.StoreRecovery.lambda$recoverFromStore$0(StoreRecovery.java:127)
        at app//org.opensearch.core.action.ActionListener.completeWith(ActionListener.java:344)
        at app//org.opensearch.index.shard.StoreRecovery.recoverFromStore(StoreRecovery.java:124)
        at app//org.opensearch.index.shard.IndexShard.recoverFromStore(IndexShard.java:3198)
        at app//org.opensearch.index.shard.IndexShardTestCase.recoverFromStore(IndexShardTestCase.java:1479)
        at app//org.opensearch.index.shard.IndexShardTestCase.recoverShardFromStore(IndexShardTestCase.java:1085)
        at app//org.opensearch.index.shard.IndexShardTests.testRestoreSearchOnlyShardFromStoreOnNewNode(IndexShardTests.java:3092)

        Caused by:
        org.apache.lucene.index.CorruptIndexException: misplaced codec footer (file truncated?): length=0 but footerLength==16 (resource=metadata__9223372036854775805__9223372036854775804__9223372036854775802__9223372036854775805__-1039764442__9223370265996427162__2)
            at app//org.apache.lucene.codecs.CodecUtil.checksumEntireFile(CodecUtil.java:616)
            at app//org.opensearch.common.io.VersionedCodecStreamWrapper.readStream(VersionedCodecStreamWrapper.java:63)
            at app//org.opensearch.index.store.RemoteSegmentStoreDirectory.readMetadataFile(RemoteSegmentStoreDirectory.java:282)
            at app//org.opensearch.index.store.RemoteSegmentStoreDirectory.readLatestMetadataFile(RemoteSegmentStoreDirectory.java:271)
            at app//org.opensearch.index.store.RemoteSegmentStoreDirectory.init(RemoteSegmentStoreDirectory.java:178)
            at app//org.opensearch.index.shard.IndexShard.syncSegmentsFromRemoteSegmentStore(IndexShard.java:5497)
            at app//org.opensearch.index.shard.IndexShard.syncSegmentsFromRemoteSegmentStore(IndexShard.java:5480)
            at app//org.opensearch.index.shard.IndexShard.innerOpenEngineAndTranslog(IndexShard.java:2871)
            at app//org.opensearch.index.shard.IndexShard.openEngineAndSkipTranslogRecovery(IndexShard.java:2838)
            at app//org.opensearch.index.shard.IndexShard.openEngineAndSkipTranslogRecoveryFromSnapshot(IndexShard.java:2832)
            at app//org.opensearch.index.shard.StoreRecovery.completeRecovery(StoreRecovery.java:843)
            at app//org.opensearch.index.shard.StoreRecovery.internalRecoverFromStoreForSearchReplica(StoreRecovery.java:813)
            ... 7 more

> Task :plugins:mapper-murmur3:bundlePlugin

@github-actions
Copy link
Contributor

❌ Gradle check result for 6fddaa4: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Enhancement or improvement to existing feature or request lucene Search:Query Capabilities

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add getWrappedScorer() method to ProfileScorer for consistency with ProfileCollector

1 participant