vitess.io/vitess@v0.16.2/go/test/endtoend/tabletmanager/lock_unlock_test.go (about) 1 /* 2 Copyright 2019 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 tabletmanager 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "testing" 24 "time" 25 26 "vitess.io/vitess/go/test/endtoend/utils" 27 28 "github.com/stretchr/testify/require" 29 30 "github.com/stretchr/testify/assert" 31 32 "vitess.io/vitess/go/mysql" 33 "vitess.io/vitess/go/test/endtoend/cluster" 34 ) 35 36 // TestLockAndUnlock tests the lock ability by locking a replica and asserting it does not see changes 37 func TestLockAndUnlock(t *testing.T) { 38 defer cluster.PanicHandler(t) 39 ctx := context.Background() 40 41 conn, err := mysql.Connect(ctx, &primaryTabletParams) 42 require.Nil(t, err) 43 defer conn.Close() 44 45 replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) 46 require.Nil(t, err) 47 defer replicaConn.Close() 48 49 // first make sure that our writes to the primary make it to the replica 50 utils.Exec(t, conn, "delete from t1") 51 utils.Exec(t, conn, "insert into t1(id, value) values(1,'a'), (2,'b')") 52 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) 53 54 // now lock the replica 55 err = tmcLockTables(ctx, replicaTablet.GrpcPort) 56 require.Nil(t, err) 57 // make sure that writing to the primary does not show up on the replica while locked 58 utils.Exec(t, conn, "insert into t1(id, value) values(3,'c')") 59 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) 60 61 // finally, make sure that unlocking the replica leads to the previous write showing up 62 err = tmcUnlockTables(ctx, replicaTablet.GrpcPort) 63 require.Nil(t, err) 64 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")] [VARCHAR("c")]]`) 65 66 // Unlocking when we do not have a valid lock should lead to an exception being raised 67 err = tmcUnlockTables(ctx, replicaTablet.GrpcPort) 68 want := "tables were not locked" 69 if err == nil || !strings.Contains(err.Error(), want) { 70 t.Errorf("Table unlock: %v, must contain %s", err, want) 71 } 72 73 // Clean the table for further testing 74 utils.Exec(t, conn, "delete from t1") 75 } 76 77 // TestStartReplicationUntilAfter tests by writing three rows, noting the gtid after each, and then replaying them one by one 78 func TestStartReplicationUntilAfter(t *testing.T) { 79 defer cluster.PanicHandler(t) 80 ctx := context.Background() 81 82 conn, err := mysql.Connect(ctx, &primaryTabletParams) 83 require.Nil(t, err) 84 defer conn.Close() 85 86 replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) 87 require.Nil(t, err) 88 defer replicaConn.Close() 89 90 //first we stop replication to the replica, so we can move forward step by step. 91 err = tmcStopReplication(ctx, replicaTablet.GrpcPort) 92 require.Nil(t, err) 93 94 utils.Exec(t, conn, "insert into t1(id, value) values(1,'a')") 95 pos1, err := tmcPrimaryPosition(ctx, primaryTablet.GrpcPort) 96 require.Nil(t, err) 97 98 utils.Exec(t, conn, "insert into t1(id, value) values(2,'b')") 99 pos2, err := tmcPrimaryPosition(ctx, primaryTablet.GrpcPort) 100 require.Nil(t, err) 101 102 utils.Exec(t, conn, "insert into t1(id, value) values(3,'c')") 103 pos3, err := tmcPrimaryPosition(ctx, primaryTablet.GrpcPort) 104 require.Nil(t, err) 105 106 // Now, we'll resume stepwise position by position and make sure that we see the expected data 107 checkDataOnReplica(t, replicaConn, `[]`) 108 109 // starts the mysql replication until 110 timeout := 10 * time.Second 111 err = tmcStartReplicationUntilAfter(ctx, replicaTablet.GrpcPort, pos1, timeout) 112 require.Nil(t, err) 113 // first row should be visible 114 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")]]`) 115 116 err = tmcStartReplicationUntilAfter(ctx, replicaTablet.GrpcPort, pos2, timeout) 117 require.Nil(t, err) 118 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) 119 120 err = tmcStartReplicationUntilAfter(ctx, replicaTablet.GrpcPort, pos3, timeout) 121 require.Nil(t, err) 122 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")] [VARCHAR("c")]]`) 123 124 // Strat replication to the replica 125 err = tmcStartReplication(ctx, replicaTablet.GrpcPort) 126 require.Nil(t, err) 127 // Clean the table for further testing 128 utils.Exec(t, conn, "delete from t1") 129 } 130 131 // TestLockAndTimeout tests that the lock times out and updates can be seen after timeout 132 func TestLockAndTimeout(t *testing.T) { 133 defer cluster.PanicHandler(t) 134 ctx := context.Background() 135 136 primaryConn, err := mysql.Connect(ctx, &primaryTabletParams) 137 require.Nil(t, err) 138 defer primaryConn.Close() 139 140 replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) 141 require.Nil(t, err) 142 defer replicaConn.Close() 143 144 // first make sure that our writes to the primary make it to the replica 145 utils.Exec(t, primaryConn, "insert into t1(id, value) values(1,'a')") 146 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")]]`) 147 148 // now lock the replica 149 err = tmcLockTables(ctx, replicaTablet.GrpcPort) 150 require.Nil(t, err) 151 152 // make sure that writing to the primary does not show up on the replica while locked 153 utils.Exec(t, primaryConn, "insert into t1(id, value) values(2,'b')") 154 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")]]`) 155 156 // the tests sets the lock timeout to 5 seconds, so sleeping 8 should be safe 157 time.Sleep(8 * time.Second) 158 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) 159 160 // Clean the table for further testing 161 utils.Exec(t, primaryConn, "delete from t1") 162 } 163 164 func checkDataOnReplica(t *testing.T, replicaConn *mysql.Conn, want string) { 165 startTime := time.Now() 166 for { 167 qr := utils.Exec(t, replicaConn, "select value from t1") 168 got := fmt.Sprintf("%v", qr.Rows) 169 170 if time.Since(startTime) > 3*time.Second /* timeout */ { 171 assert.Equal(t, want, got) 172 break 173 } 174 175 if got == want { 176 assert.Equal(t, want, got) 177 break 178 } else { 179 time.Sleep(300 * time.Millisecond /* interval at which to check again */) 180 } 181 } 182 }