github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/query_cancellation_client_test.go (about)

     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  	"context"
    27  	"fmt"
    28  	"strings"
    29  	"sync"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/m3db/m3/src/dbnode/client"
    34  	"github.com/m3db/m3/src/dbnode/namespace"
    35  	"github.com/m3db/m3/src/dbnode/retention"
    36  	"github.com/m3db/m3/src/dbnode/storage/index"
    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  	"github.com/stretchr/testify/require"
    42  	"github.com/uber/tchannel-go/thrift"
    43  	"go.uber.org/zap"
    44  )
    45  
    46  func TestQueryCancellationAndDeadlinesClient(t *testing.T) {
    47  	if testing.Short() {
    48  		t.SkipNow() // Just skip if we're doing a short run
    49  	}
    50  
    51  	var (
    52  		retentionPeriod = 2 * time.Hour
    53  		dataBlockSize   = time.Hour
    54  		indexBlockSize  = 2 * time.Hour
    55  		bufferFuture    = 20 * time.Minute
    56  		bufferPast      = 10 * time.Minute
    57  	)
    58  
    59  	// Test setup
    60  	md, err := namespace.NewMetadata(testNamespaces[0],
    61  		namespace.NewOptions().
    62  			SetRetentionOptions(
    63  				retention.NewOptions().
    64  					SetRetentionPeriod(retentionPeriod).
    65  					SetBufferPast(bufferPast).
    66  					SetBufferFuture(bufferFuture).
    67  					SetBlockSize(dataBlockSize)).
    68  			SetIndexOptions(
    69  				namespace.NewIndexOptions().
    70  					SetBlockSize(indexBlockSize).SetEnabled(true)).
    71  			SetColdWritesEnabled(true))
    72  	require.NoError(t, err)
    73  
    74  	var (
    75  		intercept       func()
    76  		interceptLock   sync.Mutex
    77  		thriftContextFn = client.ThriftContextFn(func(ctx context.Context) thrift.Context {
    78  			interceptLock.Lock()
    79  			if intercept != nil {
    80  				intercept()
    81  			}
    82  			interceptLock.Unlock()
    83  			return thrift.Wrap(ctx)
    84  		})
    85  	)
    86  
    87  	testOpts := NewTestOptions(t).
    88  		SetNamespaces([]namespace.Metadata{md}).
    89  		SetWriteNewSeriesAsync(true).
    90  		SetCustomClientAdminOptions([]client.CustomAdminOption{
    91  			client.CustomAdminOption(func(opts client.AdminOptions) client.AdminOptions {
    92  				return opts.SetThriftContextFn(thriftContextFn).(client.AdminOptions)
    93  			}),
    94  		})
    95  
    96  	testSetup, err := NewTestSetup(t, testOpts, nil)
    97  	require.NoError(t, err)
    98  	defer testSetup.Close()
    99  
   100  	// Start the server
   101  	log := testSetup.StorageOpts().InstrumentOptions().Logger()
   102  	require.NoError(t, testSetup.StartServer())
   103  
   104  	// Stop the server
   105  	defer func() {
   106  		require.NoError(t, testSetup.StopServer())
   107  		log.Debug("server is now down")
   108  	}()
   109  
   110  	var (
   111  		nowFn     = testSetup.StorageOpts().ClockOptions().NowFn()
   112  		end       = xtime.ToUnixNano(nowFn()).Truncate(time.Hour)
   113  		start     = end.Add(-time.Hour)
   114  		query     = index.Query{Query: idx.NewTermQuery([]byte("shared"), []byte("shared"))}
   115  		queryOpts = index.QueryOptions{StartInclusive: start, EndExclusive: end}
   116  	)
   117  	session, err := testSetup.M3DBClient().DefaultSession()
   118  	require.NoError(t, err)
   119  
   120  	for i := 0; i < 2; i++ {
   121  		var (
   122  			metricName = fmt.Sprintf("metric_%v", i)
   123  			tags       = ident.StringTag("shared", "shared")
   124  			timestamp  = xtime.ToUnixNano(nowFn()).Add(-time.Minute * time.Duration(i+1))
   125  		)
   126  		err := session.WriteTagged(md.ID(), ident.StringID(metricName),
   127  			ident.NewTagsIterator(ident.NewTags(tags)), timestamp, 0.0, xtime.Second, nil)
   128  		require.NoError(t, err)
   129  	}
   130  
   131  	log.Info("testing client query semantics")
   132  
   133  	// Test query no deadline.
   134  	_, _, err = session.FetchTagged(context.Background(), md.ID(), query, queryOpts)
   135  	// Expect error since we did not set a deadline.
   136  	require.Error(t, err)
   137  	log.Info("expected deadline not set error from fetch tagged", zap.Error(err))
   138  
   139  	require.True(t, strings.Contains(err.Error(), client.ErrCallWithoutDeadline.Error()),
   140  		fmt.Sprintf("actual error: %s\n", err.Error()))
   141  
   142  	// Test query with cancel.
   143  	var wg sync.WaitGroup
   144  	wg.Add(1)
   145  	ctx, cancel := context.WithCancel(ContextWithDefaultTimeout())
   146  
   147  	var (
   148  		once          sync.Once
   149  		interceptExec = func() {
   150  			defer wg.Done()
   151  			log.Info("host queue worker enqueued, cancelling context")
   152  			cancel()
   153  		}
   154  	)
   155  	interceptLock.Lock()
   156  	intercept = func() {
   157  		once.Do(interceptExec)
   158  	}
   159  	interceptLock.Unlock()
   160  
   161  	_, _, err = session.FetchTagged(ctx, md.ID(), query, queryOpts)
   162  	// Expect error since we cancelled the context.
   163  	require.Error(t, err)
   164  	log.Info("expected cancelled error from fetch tagged", zap.Error(err))
   165  
   166  	require.True(t, strings.Contains(err.Error(), "ErrCodeCancelled"),
   167  		fmt.Sprintf("actual error: %s\n", err.Error()))
   168  }