vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/reservedconn/get_lock_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  package reservedconn
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"vitess.io/vitess/go/test/endtoend/utils"
    26  
    27  	"vitess.io/vitess/go/sync2"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  
    32  	"vitess.io/vitess/go/mysql"
    33  )
    34  
    35  func TestLockUnlock(t *testing.T) {
    36  	conn, err := mysql.Connect(context.Background(), &vtParams)
    37  	require.NoError(t, err)
    38  	defer conn.Close()
    39  
    40  	utils.AssertMatches(t, conn, `select release_lock('lock name')`, `[[NULL]]`)
    41  	utils.AssertMatches(t, conn, `select get_lock('lock name', 2)`, `[[INT64(1)]]`)
    42  	utils.AssertMatches(t, conn, `select get_lock('lock name', 2)`, `[[INT64(1)]]`)
    43  	utils.AssertMatches(t, conn, `select is_free_lock('lock name')`, `[[INT64(0)]]`)
    44  	assert.NotEmpty(t,
    45  		utils.Exec(t, conn, `select is_used_lock('lock name')`))
    46  	utils.AssertMatches(t, conn, `select release_lock('lock name')`, `[[INT64(1)]]`)
    47  	utils.AssertMatches(t, conn, `select release_all_locks()`, `[[UINT64(1)]]`)
    48  	utils.AssertMatches(t, conn, `select release_lock('lock name')`, `[[NULL]]`)
    49  }
    50  
    51  func TestLocksDontIntersect(t *testing.T) {
    52  	conn1, err := mysql.Connect(context.Background(), &vtParams)
    53  	require.NoError(t, err)
    54  	defer conn1.Close()
    55  	conn2, err := mysql.Connect(context.Background(), &vtParams)
    56  	require.NoError(t, err)
    57  	defer conn2.Close()
    58  
    59  	utils.AssertMatches(t, conn1, `select get_lock('lock1', 2)`, `[[INT64(1)]]`)
    60  	utils.AssertMatches(t, conn2, `select get_lock('lock2', 2)`, `[[INT64(1)]]`)
    61  	utils.AssertMatches(t, conn1, `select release_lock('lock1')`, `[[INT64(1)]]`)
    62  	utils.AssertMatches(t, conn2, `select release_lock('lock2')`, `[[INT64(1)]]`)
    63  }
    64  
    65  func TestLocksIntersect(t *testing.T) {
    66  	conn1, err := mysql.Connect(context.Background(), &vtParams)
    67  	require.NoError(t, err)
    68  	defer conn1.Close()
    69  	conn2, err := mysql.Connect(context.Background(), &vtParams)
    70  	require.NoError(t, err)
    71  	defer conn2.Close()
    72  
    73  	utils.AssertMatches(t, conn1, `select get_lock('lock1', 100)`, `[[INT64(1)]]`)
    74  	utils.AssertMatches(t, conn2, `select get_lock('lock2', 100)`, `[[INT64(1)]]`)
    75  
    76  	// Locks will not succeed.
    77  	utils.AssertMatches(t, conn1, `select get_lock('lock2', 1)`, `[[INT64(0)]]`)
    78  	utils.AssertMatches(t, conn2, `select get_lock('lock1', 1)`, `[[INT64(0)]]`)
    79  	utils.AssertMatches(t, conn1, `select release_lock('lock2')`, `[[INT64(0)]]`)
    80  	utils.AssertMatches(t, conn2, `select release_lock('lock1')`, `[[INT64(0)]]`)
    81  
    82  	utils.AssertMatches(t, conn1, `select release_lock('lock1')`, `[[INT64(1)]]`)
    83  	utils.AssertMatches(t, conn2, `select release_lock('lock2')`, `[[INT64(1)]]`)
    84  }
    85  
    86  func TestLocksAreExplicitlyReleaseAndRegrab(t *testing.T) {
    87  	conn1, err := mysql.Connect(context.Background(), &vtParams)
    88  	require.NoError(t, err)
    89  	defer conn1.Close()
    90  	conn2, err := mysql.Connect(context.Background(), &vtParams)
    91  	require.NoError(t, err)
    92  	defer conn2.Close()
    93  
    94  	utils.AssertMatches(t, conn1, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
    95  	utils.AssertMatches(t, conn1, `select release_lock('lock')`, `[[INT64(1)]]`)
    96  	utils.AssertMatches(t, conn2, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
    97  }
    98  
    99  func TestLocksAreReleasedWhenConnectionIsClosed(t *testing.T) {
   100  	conn1, err := mysql.Connect(context.Background(), &vtParams)
   101  	require.NoError(t, err)
   102  	defer conn1.Close()
   103  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   104  	require.NoError(t, err)
   105  	defer conn2.Close()
   106  
   107  	utils.AssertMatches(t, conn1, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   108  	conn1.Close()
   109  
   110  	utils.AssertMatches(t, conn2, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   111  }
   112  
   113  func TestLocksBlockEachOther(t *testing.T) {
   114  	conn1, err := mysql.Connect(context.Background(), &vtParams)
   115  	require.NoError(t, err)
   116  	defer conn1.Close()
   117  
   118  	// in the first connection, grab a lock
   119  	utils.AssertMatches(t, conn1, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   120  
   121  	released := sync2.NewAtomicBool(false)
   122  
   123  	go func() {
   124  		conn2, err := mysql.Connect(context.Background(), &vtParams)
   125  		require.NoError(t, err)
   126  		defer conn2.Close()
   127  
   128  		// in the second connection, we try to grab a lock, and should get blocked
   129  		utils.AssertMatches(t, conn2, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   130  		assert.True(t, released.Get(), "was not blocked by get_lock")
   131  		utils.AssertMatches(t, conn2, `select release_lock('lock')`, `[[INT64(1)]]`)
   132  	}()
   133  
   134  	time.Sleep(1 * time.Second)
   135  
   136  	released.Set(true)
   137  	utils.AssertMatches(t, conn1, `select release_lock('lock')`, `[[INT64(1)]]`)
   138  }
   139  
   140  func TestLocksBlocksWithTx(t *testing.T) {
   141  	conn1, err := mysql.Connect(context.Background(), &vtParams)
   142  	require.NoError(t, err)
   143  	defer conn1.Close()
   144  
   145  	// in the first connection, grab a lock
   146  	utils.AssertMatches(t, conn1, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   147  	utils.Exec(t, conn1, "begin")
   148  	utils.Exec(t, conn1, "insert into test(id, val1) values(1,'1')") // -80
   149  	utils.Exec(t, conn1, "commit")
   150  
   151  	released := sync2.NewAtomicBool(false)
   152  
   153  	go func() {
   154  		conn2, err := mysql.Connect(context.Background(), &vtParams)
   155  		require.NoError(t, err)
   156  		defer conn2.Close()
   157  
   158  		// in the second connection, we try to grab a lock, and should get blocked
   159  		utils.AssertMatches(t, conn2, `select get_lock('lock', 2)`, `[[INT64(1)]]`)
   160  		assert.True(t, released.Get(), "was not blocked by get_lock")
   161  		utils.AssertMatches(t, conn2, `select release_lock('lock')`, `[[INT64(1)]]`)
   162  	}()
   163  
   164  	time.Sleep(1 * time.Second)
   165  
   166  	released.Set(true)
   167  	utils.AssertMatches(t, conn1, `select release_lock('lock')`, `[[INT64(1)]]`)
   168  	utils.Exec(t, conn1, "delete from test")
   169  }
   170  
   171  func TestLocksWithTxFailure(t *testing.T) {
   172  	conn1, err := mysql.Connect(context.Background(), &vtParams)
   173  	require.NoError(t, err)
   174  	defer conn1.Close()
   175  
   176  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   177  	require.NoError(t, err)
   178  	defer conn2.Close()
   179  
   180  	// in the first connection, grab a lock for infinite time
   181  	utils.AssertMatches(t, conn1, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   182  
   183  	utils.Exec(t, conn1, "use `ks:80-`")
   184  	utils.Exec(t, conn1, "begin")
   185  	qr := utils.Exec(t, conn1, "select connection_id()")
   186  	utils.Exec(t, conn1, "use ks")
   187  	// kill the mysql connection shard which has transaction open.
   188  	vttablet1 := clusterInstance.Keyspaces[0].Shards[1].PrimaryTablet() // 80-
   189  	vttablet1.VttabletProcess.QueryTablet(fmt.Sprintf("kill %s", qr.Rows[0][0].ToString()), keyspaceName, false)
   190  
   191  	// transaction fails on commit.
   192  	_, err = conn1.ExecuteFetch("commit", 1, true)
   193  	require.Error(t, err)
   194  
   195  	// in the second connection, lock acquisition should fail as first connection still hold the lock though the transaction has failed.
   196  	utils.AssertMatches(t, conn2, `select get_lock('lock', 2)`, `[[INT64(0)]]`)
   197  	utils.AssertMatches(t, conn2, `select release_lock('lock')`, `[[INT64(0)]]`)
   198  }
   199  
   200  func TestLocksWithTxOngoingAndReleaseLock(t *testing.T) {
   201  	conn, err := mysql.Connect(context.Background(), &vtParams)
   202  	require.NoError(t, err)
   203  	defer conn.Close()
   204  
   205  	utils.AssertMatches(t, conn, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   206  	utils.Exec(t, conn, "begin")
   207  	utils.Exec(t, conn, "insert into test(id, val1) values(1,'1')")
   208  	utils.AssertMatches(t, conn, `select release_lock('lock')`, `[[INT64(1)]]`)
   209  	utils.AssertMatches(t, conn, `select id, val1 from test where id = 1`, `[[INT64(1) VARCHAR("1")]]`)
   210  	utils.Exec(t, conn, "rollback")
   211  	assertIsEmpty(t, conn, `select id, val1 from test where id = 1`)
   212  }
   213  
   214  func TestLocksWithTxOngoingAndLockFails(t *testing.T) {
   215  	conn1, err := mysql.Connect(context.Background(), &vtParams)
   216  	require.NoError(t, err)
   217  	defer conn1.Close()
   218  
   219  	conn2, err := mysql.Connect(context.Background(), &vtParams)
   220  	require.NoError(t, err)
   221  	defer conn2.Close()
   222  
   223  	utils.AssertMatches(t, conn2, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   224  
   225  	utils.Exec(t, conn1, "begin")
   226  	utils.Exec(t, conn1, "insert into test(id, val1) values(1,'1')")
   227  	utils.AssertMatches(t, conn1, `select get_lock('lock', 1)`, `[[INT64(0)]]`)
   228  	utils.AssertMatches(t, conn1, `select id, val1 from test where id = 1`, `[[INT64(1) VARCHAR("1")]]`)
   229  	utils.Exec(t, conn1, "rollback")
   230  	assertIsEmpty(t, conn1, `select id, val1 from test where id = 1`)
   231  
   232  	utils.AssertMatches(t, conn2, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   233  }
   234  
   235  func TestLocksKeepLockConnectionActive(t *testing.T) {
   236  	conn, err := mysql.Connect(context.Background(), &vtParams)
   237  	require.NoError(t, err)
   238  	defer conn.Close()
   239  
   240  	utils.AssertMatches(t, conn, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   241  	time.Sleep(3 * time.Second)                                            // lock heartbeat time is 2 seconds.
   242  	utils.AssertMatches(t, conn, `select * from test where id = 42`, `[]`) // this will trigger heartbeat.
   243  	time.Sleep(3 * time.Second)                                            // lock connection will not timeout after 5 seconds.
   244  	utils.AssertMatches(t, conn, `select is_free_lock('lock')`, `[[INT64(0)]]`)
   245  
   246  }
   247  
   248  func TestLocksResetLockOnTimeout(t *testing.T) {
   249  	conn, err := mysql.Connect(context.Background(), &vtParams)
   250  	require.NoError(t, err)
   251  	defer conn.Close()
   252  
   253  	utils.AssertMatches(t, conn, `select get_lock('lock', -1)`, `[[INT64(1)]]`)
   254  	time.Sleep(6 * time.Second) // lock connection timeout is 5 seconds.
   255  	utils.AssertContainsError(t, conn, `select is_free_lock('lock')`, "held locks released")
   256  	utils.AssertMatches(t, conn, `select is_free_lock('lock')`, `[[INT64(1)]]`)
   257  }
   258  
   259  func TestLockWaitOnConnTimeoutWithTxNext(t *testing.T) {
   260  	conn, err := mysql.Connect(context.Background(), &vtParams)
   261  	require.NoError(t, err)
   262  	defer conn.Close()
   263  
   264  	utils.AssertMatches(t, conn, `select get_lock('lock', 5)`, `[[INT64(1)]]`)
   265  	time.Sleep(1 * time.Second)
   266  	utils.AssertMatches(t, conn, `select release_lock('lock')`, `[[INT64(1)]]`)
   267  	time.Sleep(12 * time.Second) // wait for reserved connection timeout of 5 seconds and some buffer
   268  	_ = utils.Exec(t, conn, `begin`)
   269  	_ = utils.Exec(t, conn, `insert into test(id, val1) values (1, 'msg')`)
   270  	time.Sleep(1 * time.Second) // some wait for rollback to kick in (won't happen after fix)
   271  	utils.AssertMatches(t, conn, `select id, val1 from test where val1 = 'msg'`, `[[INT64(1) VARCHAR("msg")]]`)
   272  	_ = utils.Exec(t, conn, `commit`)
   273  }