Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added
- Use Lucene `pack` method for `half_float` and `usigned_long` when using `ApproximatePointRangeQuery`.
- Add a mapper for context aware segments grouping criteria ([#19233](https://github.com/opensearch-project/OpenSearch/pull/19233))
- Return full error for GRPC error response ([#19568](https://github.com/opensearch-project/OpenSearch/pull/19568))

### Changed
- Faster `terms` query creation for `keyword` field with index and docValues enabled ([#19350](https://github.com/opensearch-project/OpenSearch/pull/19350))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void onResponse(SearchResponse response) {

@Override
public void onFailure(Exception e) {
logger.error("SearchRequestActionListener failed to process search request: " + e.getMessage());
logger.debug("SearchRequestActionListener failed to process search request: " + e.getMessage());
StatusRuntimeException grpcError = GrpcErrorHandler.convertToGrpcError(e);
responseObserver.onError(grpcError);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void bulk(org.opensearch.protobufs.BulkRequest request, StreamObserver<or
BulkRequestActionListener listener = new BulkRequestActionListener(responseObserver);
client.bulk(bulkRequest, listener);
} catch (RuntimeException e) {
logger.error("DocumentServiceImpl failed to process bulk request, request=" + request + ", error=" + e.getMessage());
logger.debug("DocumentServiceImpl failed: {} - {}", e.getClass().getSimpleName(), e.getMessage());
StatusRuntimeException grpcError = GrpcErrorHandler.convertToGrpcError(e);
responseObserver.onError(grpcError);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void search(
SearchRequestActionListener listener = new SearchRequestActionListener(responseObserver);
client.search(searchRequest, listener);
} catch (RuntimeException | IOException e) {
logger.error("SearchServiceImpl failed to process search request, request=" + request + ", error=" + e.getMessage());
logger.debug("SearchServiceImpl failed to process search request, request=" + request + ", error=" + e.getMessage());
StatusRuntimeException grpcError = GrpcErrorHandler.convertToGrpcError(e);
responseObserver.onError(grpcError);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
import org.apache.logging.log4j.Logger;
import org.opensearch.ExceptionsHelper;
import org.opensearch.OpenSearchException;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.core.compress.NotCompressedException;
import org.opensearch.core.compress.NotXContentException;
import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
Expand All @@ -26,7 +29,7 @@
import io.grpc.StatusRuntimeException;

/**
* Converts exceptions to a GRPC StatusRuntimeException.
* Converts exceptions to gRPC StatusRuntimeException with enhanced error details.
*/
public class GrpcErrorHandler {
private static final Logger logger = LogManager.getLogger(GrpcErrorHandler.class);
Expand All @@ -36,12 +39,12 @@ private GrpcErrorHandler() {
}

/**
* Converts an exception to an appropriate GRPC StatusRuntimeException.
* Uses shared constants from {@link ExceptionsHelper.ErrorMessages} and {@link ExceptionsHelper#summaryMessage}
* for exact parity with HTTP error handling.
* Converts an exception to an appropriate gRPC StatusRuntimeException.
* Uses comprehensive exception type mapping for granular gRPC status codes,
* with enhanced XContent details for OpenSearchExceptions and full stack traces for debugging.
*
* @param e The exception to convert
* @return StatusRuntimeException with appropriate GRPC status and HTTP-identical error messages
* @return StatusRuntimeException with appropriate gRPC status and enhanced error details
*/
public static StatusRuntimeException convertToGrpcError(Exception e) {
// ========== OpenSearch Business Logic Exceptions ==========
Expand Down Expand Up @@ -95,22 +98,68 @@ else if (e instanceof IllegalArgumentException) {
}

/**
* Handles OpenSearch-specific exceptions by converting their HTTP status to GRPC status.
* Uses {@link ExceptionsHelper#summaryMessage(Throwable)} for exact parity with HTTP error handling.
* Handles OpenSearch-specific exceptions using the exact same logic as HTTP error responses.
*
* Uses {@link ExceptionsHelper#unwrapToOpenSearchException(Throwable)} for shared unwrapping logic
* with HTTP's {@link OpenSearchException#generateFailureXContent}.
* Equivalent to: {@code BytesRestResponse.build()} and {@code OpenSearchException.generateThrowableXContent()}
* in HTTP REST handling.
*
* @param e The {@link OpenSearchException} to convert
* @return StatusRuntimeException with mapped GRPC status and HTTP-identical error message
* @param ose The OpenSearchException to convert
* @return StatusRuntimeException with mapped gRPC status and HTTP-identical error message
*/
private static StatusRuntimeException handleOpenSearchException(OpenSearchException e) {
Status grpcStatus = RestToGrpcStatusConverter.convertRestToGrpcStatus(e.status());
private static StatusRuntimeException handleOpenSearchException(OpenSearchException ose) {
Status grpcStatus = RestToGrpcStatusConverter.convertRestToGrpcStatus(ose.status());

Throwable unwrapped = ExceptionsHelper.unwrapToOpenSearchException(e);
// Use existing HTTP logic but enhance description with metadata from XContent
Throwable unwrapped = ExceptionsHelper.unwrapToOpenSearchException(ose);
String baseDescription = ExceptionsHelper.summaryMessage(unwrapped);

String description = ExceptionsHelper.summaryMessage(unwrapped);
return grpcStatus.withDescription(description).asRuntimeException();
// Extract metadata using the same XContent infrastructure as HTTP
String enhancedDescription = enhanceDescriptionWithXContentMetadata(unwrapped, baseDescription);

return grpcStatus.withDescription(enhancedDescription).asRuntimeException();
}

/**
* Enhances error description using the exact same XContent infrastructure as HTTP responses.
* This ensures perfect consistency by reusing the identical serialization logic.
*
* Equivalent to: {@code OpenSearchException.generateThrowableXContent()} used by HTTP error responses.
*
* @param exception The exception to extract metadata from
* @param baseDescription The base description from ExceptionsHelper.summaryMessage()
* @return Enhanced description with full JSON metadata from XContent
*/
private static String enhanceDescriptionWithXContentMetadata(Throwable exception, String baseDescription) {
try {
// Use the exact same method as HTTP error responses
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
// Wrap in object like HTTP does in BytesRestResponse.build()
builder.startObject();

// Use the same method as HTTP REST responses (BytesRestResponse.build)
// This includes root_cause analysis, just like HTTP
OpenSearchException.generateFailureXContent(builder, ToXContent.EMPTY_PARAMS, (Exception) exception, true);

// Add status field like HTTP does
org.opensearch.core.rest.RestStatus restStatus = ExceptionsHelper.status((Exception) exception);
builder.field("status", restStatus.getStatus());

builder.endObject();

// Get the full JSON structure - identical to HTTP error responses
String jsonString = builder.toString();

// Log the full JSON for debugging at DEBUG level
logger.debug("gRPC error details: {}", jsonString);

// Return the base description with full JSON details
return baseDescription + "; details=" + jsonString;
}

} catch (Exception e) {
// If XContent extraction fails, log and return base description
logger.error("Failed to extract XContent metadata for exception: " + e.getMessage());
return baseDescription;
}
}
}
Loading
Loading