Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add verbose pipeline parameter to output each processor's execution details #16843

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

junweid62
Copy link

@junweid62 junweid62 commented Dec 13, 2024

Description

Related RFC : #16705

This PR introduces enhancements to OpenSearch's search pipeline functionality, focusing on improving the traceability and debugging of search request and response transformations. It addresses the increasing complexity of search pipeline processors by implementing verbose mode support, which provides detailed insights into processor execution.

  1. Adds Verbose Mode for Search Pipelines:

    • Introduced the verbose_pipeline parameter to search requests, default to false.
    • Tracks each processor’s input, output, execution time, and status (success/failure).
    • Provides detailed logs of the data flow through request and response processors.
  2. Improves Pipeline Debugging:

    • Captures step-by-step data transformations applied by each processor.
    • Includes execution metadata (e.g., timestamps and elapsed time) in search responses for better analysis.
  3. Supports All Pipeline Configurations:

    • Works seamlessly with:
      • Default pipelines configured at the index level.
      • Pipelines explicitly specified in the search request.
      • Ad-hoc pipelines defined inline.
  4. Test Framework Enhancements:

    • Added tests for verbose mode to ensure correct functionality and compatibility.

Example output with request processor: filter_query response processor: rename_field and sort

{
    "took": 29,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.9116078,
        "hits": [
            {
                "_index": "my_index",
                "_id": "2",
                "_score": 0.9116078,
                "_source": {
                    "notification": "This is a public message 2",
                    "visibility": "public"
                }
            }
        ]
    },
    "processor_result": [
        {
            "processor_name": "filter_query",
            "duration_millis": 0,
            "input_data": {
                "query": {
                    "bool": {
                        "must": [
                            {
                                "match": {
                                    "message": {
                                        "query": "This is a public message 1",
                                        "operator": "OR",
                                        "prefix_length": 0,
                                        "max_expansions": 50,
                                        "fuzzy_transpositions": true,
                                        "lenient": false,
                                        "zero_terms_query": "NONE",
                                        "auto_generate_synonyms_phrase_query": true,
                                        "boost": 1.0
                                    }
                                }
                            }
                        ],
                        "adjust_pure_negative": true,
                        "boost": 1.0
                    }
                },
                "search_pipeline": "my_pipeline"
            },
            "output_data": {
                "query": {
                    "bool": {
                        "must": [
                            {
                                "bool": {
                                    "must": [
                                        {
                                            "match": {
                                                "message": {
                                                    "query": "This is a public message 1",
                                                    "operator": "OR",
                                                    "prefix_length": 0,
                                                    "max_expansions": 50,
                                                    "fuzzy_transpositions": true,
                                                    "lenient": false,
                                                    "zero_terms_query": "NONE",
                                                    "auto_generate_synonyms_phrase_query": true,
                                                    "boost": 1.0
                                                }
                                            }
                                        }
                                    ],
                                    "adjust_pure_negative": true,
                                    "boost": 1.0
                                }
                            }
                        ],
                        "filter": [
                            {
                                "term": {
                                    "visibility": {
                                        "value": "public",
                                        "boost": 1.0
                                    }
                                }
                            }
                        ],
                        "adjust_pure_negative": true,
                        "boost": 1.0
                    }
                },
                "search_pipeline": "my_pipeline"
            }
        },
        {
            "processor_name": "rename_field",
            "duration_millis": 0,
            "input_data": [
                {
                    "_index": "my_index",
                    "_id": "2",
                    "_score": 0.9116078,
                    "_source": {
                        "message": "This is a public message 2",
                        "visibility": "public"
                    }
                }
            ],
            "output_data": [
                {
                    "_index": "my_index",
                    "_id": "2",
                    "_score": 0.9116078,
                    "_source": {
                        "notification": "This is a public message 2",
                        "visibility": "public"
                    }
                }
            ]
        },
        {
            "processor_name": "sort",
            "duration_millis": 0,
            "input_data": [
                {
                    "_index": "my_index",
                    "_id": "2",
                    "_score": 0.9116078,
                    "_source": {
                        "notification": "This is a public message 2",
                        "visibility": "public"
                    }
                }
            ],
            "output_data": [
                {
                    "_index": "my_index",
                    "_id": "2",
                    "_score": 0.9116078,
                    "_source": {
                        "notification": "This is a public message 2",
                        "visibility": "public"
                    }
                }
            ]
        }
    ]
}

Related Issues

Resolves #14745

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.

@github-actions github-actions bot added enhancement Enhancement or improvement to existing feature or request Other Priority-High Search Search query, autocomplete ...etc v2.19.0 Issues and PRs related to version 2.19.0 labels Dec 13, 2024
@junweid62 junweid62 added the backport 2.x Backport to 2.x branch label Dec 13, 2024
Signed-off-by: Junwei Dai <[email protected]>
@junweid62 junweid62 force-pushed the search-pipline-execution branch from d5a2c4c to d931750 Compare December 13, 2024 00:26
Signed-off-by: Junwei Dai <[email protected]>
@junweid62
Copy link
Author

@joshpalis Hey Josh, can you take a look when you have time? :)

Signed-off-by: Junwei Dai <[email protected]>
Copy link
Contributor

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

Copy link
Contributor

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

@junweid62 junweid62 force-pushed the search-pipline-execution branch from 719fa1c to e4e30f5 Compare December 13, 2024 17:41
Copy link
Member

@joshpalis joshpalis left a comment

Choose a reason for hiding this comment

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

Great work @junweid62 on implementing this feature. A few comments from my initial pass

Copy link
Contributor

❌ Gradle check result for e4e30f5: 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: Junwei Dai <[email protected]>
Copy link
Contributor

❕ Gradle check result for 615a4b6: UNSTABLE

  • TEST FAILURES:
      2 org.opensearch.cluster.MinimumClusterManagerNodesIT.testThreeNodesNoClusterManagerBlock

Please review all flaky tests that succeeded after retry and create an issue if one does not already exist to track the flaky failure.

Copy link

codecov bot commented Dec 17, 2024

Codecov Report

Attention: Patch coverage is 42.45810% with 103 lines in your changes missing coverage. Please review.

Project coverage is 72.04%. Comparing base (b67cdf4) to head (615a4b6).
Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
...arch/search/pipeline/ProcessorExecutionDetail.java 18.44% 84 Missing ⚠️
.../java/org/opensearch/search/pipeline/Pipeline.java 68.18% 6 Missing and 1 partial ⚠️
...a/org/opensearch/action/search/SearchResponse.java 16.66% 4 Missing and 1 partial ⚠️
...opensearch/search/builder/SearchSourceBuilder.java 78.57% 0 Missing and 3 partials ⚠️
...ensearch/action/search/SearchResponseSections.java 77.77% 1 Missing and 1 partial ⚠️
...pensearch/rest/action/search/RestSearchAction.java 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #16843      +/-   ##
============================================
+ Coverage     72.03%   72.04%   +0.01%     
- Complexity    65230    65238       +8     
============================================
  Files          5318     5319       +1     
  Lines        304051   304224     +173     
  Branches      43990    44022      +32     
============================================
+ Hits         219021   219187     +166     
- Misses        67121    67134      +13     
+ Partials      17909    17903       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Signed-off-by: Junwei Dai <[email protected]>
@junweid62 junweid62 force-pushed the search-pipline-execution branch from 820849b to 0ae4d06 Compare December 18, 2024 21:59
Copy link
Contributor

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

@junweid62
Copy link
Author

Hi, @dbwiddis Could you please take a moment to review this PR when you have the time? I’d appreciate any feedback you might have :)

CHANGELOG.md Outdated Show resolved Hide resolved
@@ -302,6 +305,9 @@ public SearchSourceBuilder(StreamInput in) throws IOException {
if (in.getVersion().onOrAfter(Version.V_2_18_0)) {
searchPipeline = in.readOptionalString();
}
if (in.getVersion().onOrAfter(Version.CURRENT)) {
Copy link
Member

Choose a reason for hiding this comment

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

I think you need to have exact version here, CURRENT will change with every next release

Copy link
Author

Choose a reason for hiding this comment

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

I initially used the exact version here, but it caused failures in the BWC integration tests. @dbwiddis suggested switching to CURRENT for now, with the plan to update the version in a separate PR when preparing for the release.

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 correct: once this gets backported to 2.x, then a PR should be submitted to 2.x branch changing it to 2_19_0 and then forward-ported. The problem is that current 2.19.0 code (2.x branch) doesn't have this code but would pass the onOrAfter check if we set it now.

Here's some examples of that workflow in progress: #16086 and #16174

That said, it's useful to include a TODO comment here as a reminder so it doesn't get forgotten, leading to bugs like #16590 :)

Signed-off-by: Junwei Dai <[email protected]>
Copy link
Contributor

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

Copy link
Member

@dbwiddis dbwiddis left a comment

Choose a reason for hiding this comment

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

LGTM generally. Some comments and suggestions

@@ -302,6 +305,9 @@ public SearchSourceBuilder(StreamInput in) throws IOException {
if (in.getVersion().onOrAfter(Version.V_2_18_0)) {
searchPipeline = in.readOptionalString();
}
if (in.getVersion().onOrAfter(Version.CURRENT)) {
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 correct: once this gets backported to 2.x, then a PR should be submitted to 2.x branch changing it to 2_19_0 and then forward-ported. The problem is that current 2.19.0 code (2.x branch) doesn't have this code but would pass the onOrAfter check if we set it now.

Here's some examples of that workflow in progress: #16086 and #16174

That said, it's useful to include a TODO comment here as a reminder so it doesn't get forgotten, leading to bugs like #16590 :)

beforeResponseProcessor(processor);
final long start = relativeTimeSupplier.getAsLong();
processor.processResponseAsync(request, r, requestContext, ActionListener.wrap(rr -> {
long took = TimeUnit.NANOSECONDS.toMillis(relativeTimeSupplier.getAsLong() - start);
afterResponseProcessor(processor, took);
if (request.source().verbosePipeline()) {
detail.addOutput(Arrays.asList(rr.getHits().deepCopy().getHits()));
Copy link
Member

Choose a reason for hiding this comment

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

Can you help me understand why this deepCopy is needed?

@@ -17,6 +20,9 @@
public class PipelineProcessingContext {
private final Map<String, Object> attributes = new HashMap<>();

// Key for processor execution details
private static final String PROCESSOR_EXECUTION_DETAILS_KEY = "processorExecutionDetails";
Copy link
Member

Choose a reason for hiding this comment

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

Something seems off to me about having this private constant in this class. I see it's used in a getter so nobody needs to know what it is, but it's also a key in an attributes map that could have a name collision.

Are other attributes defined (should this constant be somewhere else, and public)? If not, is re-using this map for an undocumented purpose a good idea?

*
* @opensearch.internal
*/
@PublicApi(since = "1.0.0")
Copy link
Member

Choose a reason for hiding this comment

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

Is this new? Should this version be different?

Comment on lines +200 to +211
private static Object parseFieldFromXContent(XContentParser parser) throws IOException {
XContentParser.Token token = parser.currentToken();
if (token == XContentParser.Token.VALUE_NULL) {
return null;
} else if (token == XContentParser.Token.START_ARRAY) {
return parseArrayFromXContent(parser);
} else if (token == XContentParser.Token.START_OBJECT) {
return parser.map();
} else {
return parser.textOrNull();
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I would be surprised if this utility method (and the related array one) doesn't already exist somewhere (like here?). If it doesn't, perhaps this code should be in such a util class?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 2.x Backport to 2.x branch enhancement Enhancement or improvement to existing feature or request Other Priority-High Search Search query, autocomplete ...etc v2.19.0 Issues and PRs related to version 2.19.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Support a verbose/debugging param in search pipelines
4 participants