diff --git a/pkg/plugins/backendplugin/grpcplugin/client_v2.go b/pkg/plugins/backendplugin/grpcplugin/client_v2.go index 7babf11c2df..78d7b0d6dcc 100644 --- a/pkg/plugins/backendplugin/grpcplugin/client_v2.go +++ b/pkg/plugins/backendplugin/grpcplugin/client_v2.go @@ -172,6 +172,14 @@ func (c *ClientV2) QueryData(ctx context.Context, req *backend.QueryDataRequest) return nil, plugins.ErrMethodNotImplemented } + if status.Code(err) == codes.Unavailable { + return nil, plugins.ErrPluginGrpcConnectionUnavailableBase.Errorf("%v", err) + } + + if status.Code(err) == codes.ResourceExhausted { + return nil, plugins.ErrPluginGrpcResourceExhaustedBase.Errorf("%v", err) + } + if errorSource, ok := backend.ErrorSourceFromGrpcStatusError(ctx, err); ok { return nil, handleGrpcStatusError(ctx, errorSource, err) } diff --git a/pkg/plugins/errors.go b/pkg/plugins/errors.go index 1ab785ff7a3..77e35735ee0 100644 --- a/pkg/plugins/errors.go +++ b/pkg/plugins/errors.go @@ -35,4 +35,16 @@ var ( // Exposed as a base error to wrap it with plugin cancelled errors. ErrPluginRequestCanceledErrorBase = errutil.ClientClosedRequest("plugin.requestCanceled", errutil.WithPublicMessage("Plugin request canceled")) + + // ErrPluginGrpcResourceExhaustedBase error returned when a plugin response is larger than the grpc limit. + // Exposed as a base error to wrap it with plugin resource exhausted errors. + ErrPluginGrpcResourceExhaustedBase = errutil.Internal("plugin.resourceExhausted", + errutil.WithPublicMessage("The response is too large. Please try to reduce the time range or narrow down your query to return fewer data points."), + errutil.WithDownstream()) + + // ErrPluginGrpcConnectionUnavailableBase error returned when a plugin connection issue occurs. + // Exposed as a base error to wrap it with plugin connection issue errors. + ErrPluginGrpcConnectionUnavailableBase = errutil.Internal("plugin.connectionUnavailable", + errutil.WithPublicMessage("Data source became unavailable during request. Please try again."), + errutil.WithDownstream()) ) diff --git a/pkg/plugins/manager/client/client.go b/pkg/plugins/manager/client/client.go index 7ab5d6a60ff..4f4d19aa6b0 100644 --- a/pkg/plugins/manager/client/client.go +++ b/pkg/plugins/manager/client/client.go @@ -30,6 +30,14 @@ var ( errNilSender = errors.New("sender cannot be nil") ) +// passthroughErrors contains a list of errors that should be returned directly to the caller without wrapping +var passthroughErrors = []error{ + plugins.ErrPluginUnavailable, + plugins.ErrMethodNotImplemented, + plugins.ErrPluginGrpcResourceExhaustedBase, + plugins.ErrPluginGrpcConnectionUnavailableBase, +} + type Service struct { pluginRegistry registry.Service } @@ -52,12 +60,10 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) resp, err := p.QueryData(ctx, req) if err != nil { - if errors.Is(err, plugins.ErrMethodNotImplemented) { - return nil, err - } - - if errors.Is(err, plugins.ErrPluginUnavailable) { - return nil, err + for _, e := range passthroughErrors { + if errors.Is(err, e) { + return nil, err + } } if errors.Is(err, context.Canceled) { diff --git a/pkg/plugins/manager/client/client_test.go b/pkg/plugins/manager/client/client_test.go index f45761c3649..4964a3b1631 100644 --- a/pkg/plugins/manager/client/client_test.go +++ b/pkg/plugins/manager/client/client_test.go @@ -25,24 +25,39 @@ func TestQueryData(t *testing.T) { t.Run("Non-empty registry", func(t *testing.T) { tcs := []struct { - err error - expectedError error + err error + expectedError error + shouldPassThrough bool }{ { - err: plugins.ErrPluginUnavailable, - expectedError: plugins.ErrPluginUnavailable, + err: plugins.ErrPluginUnavailable, + expectedError: plugins.ErrPluginUnavailable, + shouldPassThrough: true, }, { - err: plugins.ErrMethodNotImplemented, - expectedError: plugins.ErrMethodNotImplemented, + err: plugins.ErrMethodNotImplemented, + expectedError: plugins.ErrMethodNotImplemented, + shouldPassThrough: true, }, { - err: errors.New("surprise surprise"), - expectedError: plugins.ErrPluginRequestFailureErrorBase, + err: errors.New("surprise surprise"), + expectedError: plugins.ErrPluginRequestFailureErrorBase, + shouldPassThrough: false, }, { - err: context.Canceled, - expectedError: plugins.ErrPluginRequestCanceledErrorBase, + err: context.Canceled, + expectedError: plugins.ErrPluginRequestCanceledErrorBase, + shouldPassThrough: false, + }, + { + err: plugins.ErrPluginGrpcConnectionUnavailableBase.Errorf("unavailable"), + expectedError: plugins.ErrPluginGrpcConnectionUnavailableBase.Errorf("unavailable"), + shouldPassThrough: true, + }, + { + err: plugins.ErrPluginGrpcResourceExhaustedBase.Errorf("exhausted"), + expectedError: plugins.ErrPluginGrpcResourceExhaustedBase.Errorf("exhausted"), + shouldPassThrough: true, }, } @@ -69,7 +84,11 @@ func TestQueryData(t *testing.T) { }, }) require.Error(t, err) - require.ErrorIs(t, err, tc.expectedError) + if tc.shouldPassThrough { + require.Equal(t, tc.err, err) + } else { + require.ErrorIs(t, err, tc.expectedError) + } }) } })