Skip to content

Scoped value#20987

Draft
sruti1312 wants to merge 6 commits intoopensearch-project:mainfrom
prudhvigodithi:scoped_value
Draft

Scoped value#20987
sruti1312 wants to merge 6 commits intoopensearch-project:mainfrom
prudhvigodithi:scoped_value

Conversation

@sruti1312
Copy link
Copy Markdown
Contributor

Description

[Describe what this change achieves]

Related Issues

Resolves #[Issue number to be closed when this PR is merged]

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.

Signed-off-by: Prudhvi Godithi <pgodithi@amazon.com>
Signed-off-by: Prudhvi Godithi <pgodithi@amazon.com>
Signed-off-by: Prudhvi Godithi <pgodithi@amazon.com>
@github-actions
Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🧪 No relevant tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Rejection Handling Bug

When super.execute(command) throws OpenSearchRejectedExecutionException, the code checks if (command instanceof AbstractRunnable) to call onRejected. However, command is now always the wrapped Runnable returned by wrapRunnable(...), not the original AbstractRunnable. This means the rejection callback on the original AbstractRunnable will never be invoked, silently swallowing rejections for tasks that previously handled them.

command = wrapRunnable(() -> {
    final IndexInputScope scope = new IndexInputScope();
    ScopedValue.where(IndexInputScope.SCOPE, scope).run(() -> {
        try {
            inner.run();
        } finally {
            scope.closeAll();
        }
    });
});
try {
    super.execute(command);
} catch (OpenSearchRejectedExecutionException ex) {
    if (command instanceof AbstractRunnable) {
Thread Safety

IndexInputScope.inputs uses a plain ArrayList with no synchronization. If any code path registers inputs from a thread other than the one that created the scope (e.g., async callbacks or child tasks that inherit the scoped value), concurrent modifications will cause ConcurrentModificationException or data corruption.

private final List<Closeable> inputs = new ArrayList<>();

/**
 * Register an IndexInput (clone or slice) for cleanup when this scope ends.
 */
public void register(Closeable input) {
    inputs.add(input);
}

@github-actions
Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Ensure cleanup runs even on scoped value failure

The scope.closeAll() is called inside the ScopedValue carrier's run() lambda, but if
ScopedValue.where(...).run(...) itself throws an unchecked exception before or after
the inner lambda, closeAll() may not be called. The finally block should be placed
at the outer level, wrapping the entire ScopedValue.where(...).run(...) call to
ensure cleanup always happens.

server/src/main/java/org/opensearch/common/util/concurrent/OpenSearchThreadPoolExecutor.java [132-141]

 command = wrapRunnable(() -> {
     final IndexInputScope scope = new IndexInputScope();
-    ScopedValue.where(IndexInputScope.SCOPE, scope).run(() -> {
-        try {
-            inner.run();
-        } finally {
-            scope.closeAll();
-        }
-    });
+    try {
+        ScopedValue.where(IndexInputScope.SCOPE, scope).run(inner::run);
+    } finally {
+        scope.closeAll();
+    }
 });
Suggestion importance[1-10]: 6

__

Why: Moving the finally block outside the ScopedValue.where(...).run(...) call ensures scope.closeAll() is always invoked even if the ScopedValue machinery itself throws. The improved code is cleaner and more robust, correctly separating the scoped binding from the cleanup responsibility.

Low
Add thread safety to registration method

The register method is not thread-safe. If multiple threads share or access the same
IndexInputScope instance, concurrent calls to register and closeAll could cause
ConcurrentModificationException or data corruption. Consider using a thread-safe
collection like CopyOnWriteArrayList or adding synchronization.

server/src/main/java/org/opensearch/common/util/concurrent/IndexInputScope.java [40-42]

-public void register(Closeable input) {
+public synchronized void register(Closeable input) {
     inputs.add(input);
 }
Suggestion importance[1-10]: 3

__

Why: The IndexInputScope is bound per-task via ScopedValue, meaning each task gets its own instance. Since a single task runs on a single thread, concurrent access to the same IndexInputScope instance is unlikely in normal usage. The suggestion is technically valid but has low practical impact given the design.

Low

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 7960bdd: 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?

Signed-off-by: Prudhvi Godithi <pgodithi@amazon.com>
@github-actions
Copy link
Copy Markdown
Contributor

Failed to generate code suggestions for PR

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for a84d355: 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?

Signed-off-by: Prudhvi Godithi <pgodithi@amazon.com>
@github-actions
Copy link
Copy Markdown
Contributor

PR Code Analyzer ❗

AI-powered 'Code-Diff-Analyzer' found issues on commit 9daa123.

PathLineSeverityDescription
test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java332mediumTwo critical test-framework initialization calls are commented out without explanation: BootstrapForTesting.ensureInitialized() (which handles security manager and plugin initialization for tests) and TransportService.ensureClassloaded() (which ensures server streamables are registered). The PR's stated purpose is ScopedValue/preview feature support, making these suppressions unrelated to the change. Disabling bootstrap initialization in the test framework could cause security checks to be skipped during test runs, potentially allowing unsafe code to pass CI. This warrants investigation to confirm these were intentionally disabled for a legitimate reason rather than to bypass test-time security enforcement.

The table above displays the top 10 most important findings.

Total: 1 | Critical: 0 | High: 0 | Medium: 1 | Low: 0


Pull Requests Author(s): Please update your Pull Request according to the report above.

Repository Maintainer(s): You can bypass diff analyzer by adding label skip-diff-analyzer after reviewing the changes carefully, then re-run failed actions. To re-enable the analyzer, remove the label, then re-run all actions.


⚠️ Note: The Code-Diff-Analyzer helps protect against potentially harmful code patterns. Please ensure you have thoroughly reviewed the changes beforehand.

Thanks.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants