vitess.io/vitess@v0.16.2/go/vt/vttablet/endtoend/connkilling/connkiller_test.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  /*
    18  All tests in this package come with a three second time out for OLTP session
    19  */
    20  package connkilling
    21  
    22  import (
    23  	"context"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"vitess.io/vitess/go/vt/vttablet/endtoend/framework"
    31  )
    32  
    33  func TestTxKillerKillsTransactionsInReservedConnections(t *testing.T) {
    34  	client := framework.NewClient()
    35  	defer client.Release()
    36  
    37  	_, err := client.ReserveBeginExecute("select 42", nil, nil, nil)
    38  	require.NoError(t, err)
    39  
    40  	assertIsKilledWithin(t, client, queryPollOpts{
    41  		StopAfter: 6 * time.Second,
    42  		PollEvery: time.Second,
    43  	})
    44  }
    45  
    46  func TestTxKillerDoesNotKillReservedConnectionsInUse(t *testing.T) {
    47  	client := framework.NewClient()
    48  	defer client.Release()
    49  
    50  	_, err := client.ReserveExecute("select 42", nil, nil)
    51  	require.NoError(t, err)
    52  
    53  	assertIsNotKilledOver5Second(t, client)
    54  }
    55  
    56  func TestTxKillerCountsTimeFromTxStartedNotStatefulConnCreated(t *testing.T) {
    57  	client := framework.NewClient()
    58  	defer client.Release()
    59  
    60  	// reserve connection at 0th second
    61  	_, err := client.ReserveExecute("select 42", nil, nil)
    62  	require.NoError(t, err)
    63  
    64  	// elapsed 2 seconds
    65  	time.Sleep(2 * time.Second)
    66  
    67  	// update the timer on tx start - new tx timer starts
    68  	_, err = client.BeginExecute("select 44", nil, nil)
    69  	require.NoError(t, err)
    70  
    71  	// elapsed 1 second from tx and 3 second from reserved conn.
    72  	time.Sleep(1 * time.Second)
    73  	_, err = client.Execute("select 43", nil)
    74  	require.NoError(t, err)
    75  
    76  	// elapsed 2 second from tx and 4 second from reserved conn. It does not fail.
    77  	time.Sleep(1 * time.Second)
    78  	_, err = client.Execute("select 43", nil)
    79  	require.NoError(t, err)
    80  
    81  	assertIsKilledWithin(t, client, queryPollOpts{
    82  		StopAfter: 6 * time.Second,
    83  		PollEvery: time.Second,
    84  	})
    85  }
    86  
    87  func TestTxKillerKillsTransactionThreeSecondsAfterCreation(t *testing.T) {
    88  	client := framework.NewClient()
    89  	defer client.Release()
    90  
    91  	_, err := client.BeginExecute("select 42", nil, nil)
    92  	require.NoError(t, err)
    93  
    94  	assertIsKilledWithin(t, client, queryPollOpts{
    95  		StopAfter: 6 * time.Second,
    96  		PollEvery: time.Second,
    97  	})
    98  }
    99  
   100  func assertIsNotKilledOver5Second(t *testing.T, client *framework.QueryClient) {
   101  	for i := 0; i < 5; i++ {
   102  		_, err := client.Execute("select 43", nil)
   103  		require.NoError(t, err)
   104  		time.Sleep(1 * time.Second)
   105  	}
   106  }
   107  
   108  type queryPollOpts struct {
   109  	StopAfter time.Duration
   110  	PollEvery time.Duration
   111  }
   112  
   113  func assertIsKilledWithin(t testing.TB, client *framework.QueryClient, opts queryPollOpts) {
   114  	t.Helper()
   115  
   116  	var err error
   117  
   118  	doQuery := func() {
   119  		_, err = client.Execute("select 43", nil)
   120  		if err != nil {
   121  			if strings.Contains(err.Error(), "in use: for tx killer rollback") {
   122  				t.Logf("tx is mid-rollback and not killed yet, ignoring this error: %s", err)
   123  				err = nil
   124  			}
   125  		}
   126  	}
   127  
   128  	ctx, cancel := context.WithTimeout(context.Background(), opts.StopAfter)
   129  	defer cancel()
   130  
   131  	defer func() {
   132  		// Check one last time after we've hit StopAfter
   133  		if err == nil {
   134  			doQuery()
   135  		}
   136  
   137  		// then it should still be killed. transactions are tracked per tx-creation time and not last-used time
   138  		require.Error(t, err)
   139  		require.Contains(t, err.Error(), "exceeded timeout: 3s")
   140  	}()
   141  
   142  	ticker := time.NewTicker(opts.PollEvery)
   143  	defer func() {
   144  		ticker.Stop()
   145  		select {
   146  		case <-ticker.C:
   147  		default:
   148  		}
   149  	}()
   150  
   151  poll:
   152  	for {
   153  		select {
   154  		case <-ctx.Done():
   155  			break poll
   156  		case <-ticker.C:
   157  			doQuery()
   158  			if err != nil {
   159  				break poll
   160  			}
   161  		}
   162  	}
   163  }