Data sources: Improve error messages for grpc errors (#102372)

* Data sources: Improve error messages for grpc errors

* Improve code comments

* Fix lint

* Update connection issue message

* Update name

* Update comment

* Update, rename and add test

* Update, remove POC change

* Fix lint
This commit is contained in:
Ivana Huckova
2025-03-20 18:44:47 +01:00
committed by GitHub
parent 3ef583aa05
commit 307974f20d
4 changed files with 62 additions and 17 deletions

View File

@ -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)
}

View File

@ -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())
)

View File

@ -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) {
for _, e := range passthroughErrors {
if errors.Is(err, e) {
return nil, err
}
if errors.Is(err, plugins.ErrPluginUnavailable) {
return nil, err
}
if errors.Is(err, context.Canceled) {

View File

@ -27,22 +27,37 @@ func TestQueryData(t *testing.T) {
tcs := []struct {
err error
expectedError error
shouldPassThrough bool
}{
{
err: plugins.ErrPluginUnavailable,
expectedError: plugins.ErrPluginUnavailable,
shouldPassThrough: true,
},
{
err: plugins.ErrMethodNotImplemented,
expectedError: plugins.ErrMethodNotImplemented,
shouldPassThrough: true,
},
{
err: errors.New("surprise surprise"),
expectedError: plugins.ErrPluginRequestFailureErrorBase,
shouldPassThrough: false,
},
{
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)
if tc.shouldPassThrough {
require.Equal(t, tc.err, err)
} else {
require.ErrorIs(t, err, tc.expectedError)
}
})
}
})