Skip to content

Commit e8996ba

Browse files
authored
[dbnode] Add a few tests for query limit exceeded error (#3030)
1 parent cc3718f commit e8996ba

File tree

3 files changed

+214
-1
lines changed

3 files changed

+214
-1
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// +build integration
2+
3+
// Copyright (c) 2020 Uber Technologies, Inc.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
// THE SOFTWARE.
22+
23+
package integration
24+
25+
import (
26+
"fmt"
27+
"testing"
28+
"time"
29+
30+
"github.com/stretchr/testify/require"
31+
32+
"github.com/m3db/m3/src/dbnode/client"
33+
"github.com/m3db/m3/src/dbnode/namespace"
34+
"github.com/m3db/m3/src/dbnode/storage"
35+
"github.com/m3db/m3/src/dbnode/storage/index"
36+
"github.com/m3db/m3/src/dbnode/storage/limits"
37+
"github.com/m3db/m3/src/m3ninx/idx"
38+
"github.com/m3db/m3/src/x/ident"
39+
xtime "github.com/m3db/m3/src/x/time"
40+
)
41+
42+
func TestQueryLimitExceededError(t *testing.T) {
43+
testOpts, ns := newTestOptionsWithIndexedNamespace(t)
44+
testSetup := newTestSetupWithQueryLimits(t, testOpts)
45+
defer testSetup.Close()
46+
47+
require.NoError(t, testSetup.StartServer())
48+
defer func() {
49+
require.NoError(t, testSetup.StopServer())
50+
}()
51+
52+
var (
53+
nowFn = testSetup.StorageOpts().ClockOptions().NowFn()
54+
end = nowFn().Truncate(time.Hour)
55+
start = end.Add(-time.Hour)
56+
query = index.Query{Query: idx.NewTermQuery([]byte("tag"), []byte("value"))}
57+
queryOpts = index.QueryOptions{StartInclusive: start, EndExclusive: end}
58+
)
59+
60+
session, err := testSetup.M3DBClient().DefaultSession()
61+
require.NoError(t, err)
62+
63+
for i := 0; i < 2; i++ {
64+
var (
65+
metricName = fmt.Sprintf("metric_%v", i)
66+
tags = ident.StringTag("tag", "value")
67+
timestamp = nowFn().Add(-time.Minute * time.Duration(i+1))
68+
)
69+
session.WriteTagged(ns.ID(), ident.StringID(metricName),
70+
ident.NewTagsIterator(ident.NewTags(tags)), timestamp, 0.0, xtime.Second, nil)
71+
}
72+
73+
_, _, err = session.FetchTagged(ns.ID(), query, queryOpts)
74+
require.True(t, client.IsResourceExhaustedError(err),
75+
"expected resource exhausted error, got: %v", err)
76+
}
77+
78+
func newTestOptionsWithIndexedNamespace(t *testing.T) (TestOptions, namespace.Metadata) {
79+
idxOpts := namespace.NewIndexOptions().SetEnabled(true)
80+
nsOpts := namespace.NewOptions().SetIndexOptions(idxOpts)
81+
ns, err := namespace.NewMetadata(testNamespaces[0], nsOpts)
82+
require.NoError(t, err)
83+
84+
testOpts := NewTestOptions(t).SetNamespaces([]namespace.Metadata{ns})
85+
return testOpts, ns
86+
}
87+
88+
func newTestSetupWithQueryLimits(t *testing.T, opts TestOptions) TestSetup {
89+
storageLimitsFn := func(storageOpts storage.Options) storage.Options {
90+
queryLookback := limits.DefaultLookbackLimitOptions()
91+
queryLookback.Limit = 1
92+
queryLookback.Lookback = time.Hour
93+
94+
limitOpts := limits.NewOptions().
95+
SetBytesReadLimitOpts(queryLookback).
96+
SetDocsLimitOpts(queryLookback).
97+
SetInstrumentOptions(storageOpts.InstrumentOptions())
98+
queryLimits, err := limits.NewQueryLimits(limitOpts)
99+
require.NoError(t, err)
100+
101+
indexOpts := storageOpts.IndexOptions().SetQueryLimits(queryLimits)
102+
return storageOpts.SetIndexOptions(indexOpts)
103+
}
104+
105+
setup, err := NewTestSetup(t, opts, nil, storageLimitsFn)
106+
require.NoError(t, err)
107+
108+
return setup
109+
}

src/dbnode/storage/limits/errors.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,18 @@ func (err *queryLimitExceededError) Error() string {
3939

4040
// IsQueryLimitExceededError returns true if the error is a query limits exceeded error.
4141
func IsQueryLimitExceededError(err error) bool {
42+
//nolint:errorlint
4243
for err != nil {
43-
if _, ok := err.(*queryLimitExceededError); ok { //nolint:errorlint
44+
if _, ok := err.(*queryLimitExceededError); ok {
4445
return true
4546
}
47+
if multiErr, ok := err.(xerrors.MultiError); ok {
48+
for _, e := range multiErr.Errors() {
49+
if IsQueryLimitExceededError(e) {
50+
return true
51+
}
52+
}
53+
}
4654
err = xerrors.InnerError(err)
4755
}
4856
return false
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) 2020 Uber Technologies, Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package limits
22+
23+
import (
24+
"errors"
25+
"testing"
26+
27+
"github.com/stretchr/testify/assert"
28+
29+
xerrors "github.com/m3db/m3/src/x/errors"
30+
)
31+
32+
func TestIsQueryLimitExceededError(t *testing.T) {
33+
randomErr := xerrors.NewNonRetryableError(errors.New("random error"))
34+
limitExceededErr := NewQueryLimitExceededError("query limit exceeded")
35+
36+
tests := []struct {
37+
name string
38+
err error
39+
expected bool
40+
}{
41+
{
42+
"not query limit exceeded",
43+
randomErr,
44+
false,
45+
},
46+
{
47+
"query limit exceeded",
48+
limitExceededErr,
49+
true,
50+
},
51+
{
52+
"inner non query limit exceeded",
53+
xerrors.NewInvalidParamsError(randomErr),
54+
false,
55+
},
56+
{
57+
"inner query limit exceeded",
58+
xerrors.NewInvalidParamsError(limitExceededErr),
59+
true,
60+
},
61+
{
62+
"empty multi error",
63+
multiError(),
64+
false,
65+
},
66+
{
67+
"multi error without query limit exceeded",
68+
multiError(randomErr),
69+
false,
70+
},
71+
{
72+
"multi error with only query limit exceeded",
73+
multiError(limitExceededErr),
74+
true,
75+
},
76+
{
77+
"multi error with query limit exceeded",
78+
multiError(randomErr, xerrors.NewRetryableError(limitExceededErr)),
79+
true,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.name, func(t *testing.T) {
85+
assert.Equal(t, tt.expected, IsQueryLimitExceededError(tt.err))
86+
})
87+
}
88+
}
89+
90+
func multiError(errs ...error) error {
91+
multiErr := xerrors.NewMultiError()
92+
for _, e := range errs {
93+
multiErr = multiErr.Add(e)
94+
}
95+
return multiErr.FinalError()
96+
}

0 commit comments

Comments
 (0)