Skip to content

Commit

Permalink
[dbnode] Add a few tests for query limit exceeded error (#3030)
Browse files Browse the repository at this point in the history
  • Loading branch information
vpranckaitis authored Dec 21, 2020
1 parent cc3718f commit e8996ba
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 1 deletion.
109 changes: 109 additions & 0 deletions src/dbnode/integration/query_limit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// +build integration

// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package integration

import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/m3db/m3/src/dbnode/client"
"github.com/m3db/m3/src/dbnode/namespace"
"github.com/m3db/m3/src/dbnode/storage"
"github.com/m3db/m3/src/dbnode/storage/index"
"github.com/m3db/m3/src/dbnode/storage/limits"
"github.com/m3db/m3/src/m3ninx/idx"
"github.com/m3db/m3/src/x/ident"
xtime "github.com/m3db/m3/src/x/time"
)

func TestQueryLimitExceededError(t *testing.T) {
testOpts, ns := newTestOptionsWithIndexedNamespace(t)
testSetup := newTestSetupWithQueryLimits(t, testOpts)
defer testSetup.Close()

require.NoError(t, testSetup.StartServer())
defer func() {
require.NoError(t, testSetup.StopServer())
}()

var (
nowFn = testSetup.StorageOpts().ClockOptions().NowFn()
end = nowFn().Truncate(time.Hour)
start = end.Add(-time.Hour)
query = index.Query{Query: idx.NewTermQuery([]byte("tag"), []byte("value"))}
queryOpts = index.QueryOptions{StartInclusive: start, EndExclusive: end}
)

session, err := testSetup.M3DBClient().DefaultSession()
require.NoError(t, err)

for i := 0; i < 2; i++ {
var (
metricName = fmt.Sprintf("metric_%v", i)
tags = ident.StringTag("tag", "value")
timestamp = nowFn().Add(-time.Minute * time.Duration(i+1))
)
session.WriteTagged(ns.ID(), ident.StringID(metricName),
ident.NewTagsIterator(ident.NewTags(tags)), timestamp, 0.0, xtime.Second, nil)
}

_, _, err = session.FetchTagged(ns.ID(), query, queryOpts)
require.True(t, client.IsResourceExhaustedError(err),
"expected resource exhausted error, got: %v", err)
}

func newTestOptionsWithIndexedNamespace(t *testing.T) (TestOptions, namespace.Metadata) {
idxOpts := namespace.NewIndexOptions().SetEnabled(true)
nsOpts := namespace.NewOptions().SetIndexOptions(idxOpts)
ns, err := namespace.NewMetadata(testNamespaces[0], nsOpts)
require.NoError(t, err)

testOpts := NewTestOptions(t).SetNamespaces([]namespace.Metadata{ns})
return testOpts, ns
}

func newTestSetupWithQueryLimits(t *testing.T, opts TestOptions) TestSetup {
storageLimitsFn := func(storageOpts storage.Options) storage.Options {
queryLookback := limits.DefaultLookbackLimitOptions()
queryLookback.Limit = 1
queryLookback.Lookback = time.Hour

limitOpts := limits.NewOptions().
SetBytesReadLimitOpts(queryLookback).
SetDocsLimitOpts(queryLookback).
SetInstrumentOptions(storageOpts.InstrumentOptions())
queryLimits, err := limits.NewQueryLimits(limitOpts)
require.NoError(t, err)

indexOpts := storageOpts.IndexOptions().SetQueryLimits(queryLimits)
return storageOpts.SetIndexOptions(indexOpts)
}

setup, err := NewTestSetup(t, opts, nil, storageLimitsFn)
require.NoError(t, err)

return setup
}
10 changes: 9 additions & 1 deletion src/dbnode/storage/limits/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ func (err *queryLimitExceededError) Error() string {

// IsQueryLimitExceededError returns true if the error is a query limits exceeded error.
func IsQueryLimitExceededError(err error) bool {
//nolint:errorlint
for err != nil {
if _, ok := err.(*queryLimitExceededError); ok { //nolint:errorlint
if _, ok := err.(*queryLimitExceededError); ok {
return true
}
if multiErr, ok := err.(xerrors.MultiError); ok {
for _, e := range multiErr.Errors() {
if IsQueryLimitExceededError(e) {
return true
}
}
}
err = xerrors.InnerError(err)
}
return false
Expand Down
96 changes: 96 additions & 0 deletions src/dbnode/storage/limits/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package limits

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"

xerrors "github.com/m3db/m3/src/x/errors"
)

func TestIsQueryLimitExceededError(t *testing.T) {
randomErr := xerrors.NewNonRetryableError(errors.New("random error"))
limitExceededErr := NewQueryLimitExceededError("query limit exceeded")

tests := []struct {
name string
err error
expected bool
}{
{
"not query limit exceeded",
randomErr,
false,
},
{
"query limit exceeded",
limitExceededErr,
true,
},
{
"inner non query limit exceeded",
xerrors.NewInvalidParamsError(randomErr),
false,
},
{
"inner query limit exceeded",
xerrors.NewInvalidParamsError(limitExceededErr),
true,
},
{
"empty multi error",
multiError(),
false,
},
{
"multi error without query limit exceeded",
multiError(randomErr),
false,
},
{
"multi error with only query limit exceeded",
multiError(limitExceededErr),
true,
},
{
"multi error with query limit exceeded",
multiError(randomErr, xerrors.NewRetryableError(limitExceededErr)),
true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, IsQueryLimitExceededError(tt.err))
})
}
}

func multiError(errs ...error) error {
multiErr := xerrors.NewMultiError()
for _, e := range errs {
multiErr = multiErr.Add(e)
}
return multiErr.FinalError()
}

0 comments on commit e8996ba

Please sign in to comment.