vitess.io/vitess@v0.16.2/go/test/endtoend/vreplication/time_zone_test.go (about) 1 /* 2 Copyright 2022 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 vreplication 18 19 import ( 20 "fmt" 21 "os" 22 "testing" 23 "time" 24 25 "vitess.io/vitess/go/test/endtoend/cluster" 26 27 "github.com/stretchr/testify/require" 28 29 "vitess.io/vitess/go/vt/log" 30 ) 31 32 // TestMoveTablesTZ tests the conversion of datetime based on the source timezone passed to the MoveTables workflow 33 func TestMoveTablesTZ(t *testing.T) { 34 allCellNames = "zone1" 35 defaultCellName := "zone1" 36 workflow := "tz" 37 sourceKs := "product" 38 targetKs := "customer" 39 shard := "0" 40 ksWorkflow := fmt.Sprintf("%s.%s", targetKs, workflow) 41 ksReverseWorkflow := fmt.Sprintf("%s.%s_reverse", sourceKs, workflow) 42 43 vc = NewVitessCluster(t, "TestCellAliasVreplicationWorkflow", []string{"zone1"}, mainClusterConfig) 44 require.NotNil(t, vc) 45 defaultCell = vc.Cells[defaultCellName] 46 cells := []*Cell{defaultCell} 47 48 defer vc.TearDown(t) 49 50 cell1 := vc.Cells["zone1"] 51 vc.AddKeyspace(t, []*Cell{cell1}, sourceKs, "0", initialProductVSchema, initialProductSchema, 0, 0, 100, sourceKsOpts) 52 53 vtgate = cell1.Vtgates[0] 54 require.NotNil(t, vtgate) 55 err := cluster.WaitForHealthyShard(vc.VtctldClient, sourceKs, shard) 56 require.NoError(t, err) 57 58 vtgateConn = getConnection(t, vc.ClusterConfig.hostname, vc.ClusterConfig.vtgateMySQLPort) 59 defer vtgateConn.Close() 60 verifyClusterHealth(t, vc) 61 62 productTab := vc.Cells[defaultCell.Name].Keyspaces[sourceKs].Shards["0"].Tablets["zone1-100"].Vttablet 63 timeZoneSQLBytes, _ := os.ReadFile("tz.sql") 64 timeZoneSQL := string(timeZoneSQLBytes) 65 66 // it seems to take some time for the mysql server to load time zone info after the tables in mysql db have been populated 67 loadTimeZoneInfo := func(tab *cluster.VttabletProcess, sql, timezone string) { 68 _, err := tab.QueryTabletWithDB(timeZoneSQL, "mysql") 69 require.NoError(t, err) 70 timer := time.NewTimer(1 * time.Minute) 71 for { 72 select { 73 case <-timer.C: 74 require.Fail(t, "could not load time zone info") 75 default: 76 } 77 _, err = tab.QueryTablet(fmt.Sprintf("SET GLOBAL time_zone = '%s';", timezone), "", false) 78 if err == nil { 79 timer.Stop() 80 return 81 } 82 time.Sleep(100 * time.Millisecond) 83 } 84 } 85 loadTimeZoneInfo(productTab, timeZoneSQL, "US/Pacific") 86 87 insertInitialData(t) 88 89 if _, err := vc.AddKeyspace(t, cells, targetKs, "0", customerVSchema, customerSchema, defaultReplicas, defaultRdonly, 200, targetKsOpts); err != nil { 90 t.Fatal(err) 91 } 92 err = cluster.WaitForHealthyShard(vc.VtctldClient, targetKs, shard) 93 require.NoError(t, err) 94 95 defaultCell := vc.Cells["zone1"] 96 custKs := vc.Cells[defaultCell.Name].Keyspaces[targetKs] 97 customerTab := custKs.Shards["0"].Tablets["zone1-200"].Vttablet 98 99 loadTimeZoneInfo(customerTab, timeZoneSQL, "UTC") 100 101 tables := "datze" 102 103 ksErrorWorkflow := fmt.Sprintf("%s.%s", targetKs, "tzerr") 104 output, err := vc.VtctlClient.ExecuteCommandWithOutput("MoveTables", "--", "--source", sourceKs, "--tables", 105 tables, "--source_time_zone", "US/Pacifik", "Create", ksErrorWorkflow) 106 require.Error(t, err, output) 107 require.Contains(t, output, "time zone is invalid") 108 109 output, err = vc.VtctlClient.ExecuteCommandWithOutput("MoveTables", "--", "--source", sourceKs, "--tables", 110 tables, "--source_time_zone", "US/Pacific", "Create", ksWorkflow) 111 require.NoError(t, err, output) 112 113 catchup(t, customerTab, workflow, "MoveTables") 114 115 // inserts to test date conversions in replication (vplayer) mode (insert statements) 116 _, err = vtgateConn.ExecuteFetch("insert into datze(id, dt2) values (11, '2022-01-01 10:20:30')", 1, false) // standard time 117 require.NoError(t, err) 118 _, err = vtgateConn.ExecuteFetch("insert into datze(id, dt2) values (12, '2022-04-01 5:06:07')", 1, false) // dst 119 require.NoError(t, err) 120 121 vdiff1(t, ksWorkflow, "") 122 123 // update to test date conversions in replication (vplayer) mode (update statements) 124 _, err = vtgateConn.ExecuteFetch("update datze set dt2 = '2022-04-01 5:06:07' where id = 11", 1, false) // dst 125 require.NoError(t, err) 126 _, err = vtgateConn.ExecuteFetch("update datze set dt2 = '2022-01-01 10:20:30' where id = 12", 1, false) // standard time 127 require.NoError(t, err) 128 129 vdiff1(t, ksWorkflow, "") 130 131 query := "select * from datze" 132 qrSourceUSPacific, err := productTab.QueryTablet(query, sourceKs, true) 133 require.NoError(t, err) 134 require.NotNil(t, qrSourceUSPacific) 135 136 qrTargetUTC, err := customerTab.QueryTablet(query, targetKs, true) 137 require.NoError(t, err) 138 require.NotNil(t, qrTargetUTC) 139 140 require.Equal(t, len(qrSourceUSPacific.Rows), len(qrTargetUTC.Rows)) 141 142 pacificLocation, err := time.LoadLocation("US/Pacific") 143 require.NoError(t, err) 144 145 // for reference the columns in the test are as follows: 146 // * dt1 datetime default current_timestamp, constant for all rows 147 // * dt2 datetime, different values. First row is in standard time, rest with daylight savings including times around the time zone switch 148 // * ts1 timestamp default current_timestamp, constant for all rows 149 for i, row := range qrSourceUSPacific.Named().Rows { 150 // source and UTC results must differ since source is in US/Pacific 151 require.NotEqual(t, row.AsString("dt1", ""), qrTargetUTC.Named().Rows[i].AsString("dt1", "")) 152 require.NotEqual(t, row.AsString("dt2", ""), qrTargetUTC.Named().Rows[i].AsString("dt2", "")) 153 require.NotEqual(t, row.AsString("ts1", ""), qrTargetUTC.Named().Rows[i].AsString("ts1", "")) 154 155 dtLayout := "2006-01-02 15:04:05" 156 // now compare times b/w source and target (actual). VDiff has already compared, but we want to validate that vdiff1 is right too! 157 dt2a, err := time.Parse(dtLayout, qrTargetUTC.Named().Rows[i].AsString("dt2", "")) 158 require.NoError(t, err) 159 targetUTCTUnix := dt2a.Unix() 160 161 dt2b, err := time.Parse(dtLayout, qrSourceUSPacific.Named().Rows[i].AsString("dt2", "")) 162 require.NoError(t, err) 163 sourceUSPacific := dt2b.Unix() 164 165 dtt := dt2b.In(pacificLocation) 166 zone, _ := dtt.Zone() 167 var hoursBehind int64 168 if zone == "PDT" { // daylight savings is on 169 hoursBehind = 7 170 } else { 171 hoursBehind = 8 172 } 173 // extra logging, so that we can spot any issues in CI test runs 174 log.Infof("times are %s, %s, hours behind %d", dt2a, dt2b, hoursBehind) 175 require.Equal(t, hoursBehind*3600, targetUTCTUnix-sourceUSPacific) 176 } 177 178 // user should be either running this query or have set their location in their driver to map from the time in Vitess/UTC to local 179 query = "select id, convert_tz(dt1, 'UTC', 'US/Pacific') dt1, convert_tz(dt2, 'UTC', 'US/Pacific') dt2, convert_tz(ts1, 'UTC', 'US/Pacific') ts1 from datze" 180 qrTargetUSPacific, err := customerTab.QueryTablet(query, "customer", true) 181 require.NoError(t, err) 182 require.NotNil(t, qrTargetUSPacific) 183 require.Equal(t, len(qrSourceUSPacific.Rows), len(qrTargetUSPacific.Rows)) 184 185 for i, row := range qrSourceUSPacific.Named().Rows { 186 // source and target results must match since source is in US/Pacific and we are converting target columns explicitly to US/Pacific 187 require.Equal(t, row.AsString("dt1", ""), qrTargetUSPacific.Named().Rows[i].AsString("dt1", "")) 188 require.Equal(t, row.AsString("dt2", ""), qrTargetUSPacific.Named().Rows[i].AsString("dt2", "")) 189 require.Equal(t, row.AsString("ts1", ""), qrTargetUSPacific.Named().Rows[i].AsString("ts1", "")) 190 } 191 output, err = vc.VtctlClient.ExecuteCommandWithOutput("MoveTables", "--", "SwitchTraffic", ksWorkflow) 192 require.NoError(t, err, output) 193 194 qr, err := productTab.QueryTablet(fmt.Sprintf("select * from _vt.vreplication where workflow='%s_reverse'", workflow), "", false) 195 if err != nil { 196 return 197 } 198 for _, row := range qr.Named().Rows { 199 bls := row["source"].ToString() 200 require.Contains(t, bls, "source_time_zone:\"UTC\"") 201 require.Contains(t, bls, "target_time_zone:\"US/Pacific\"") 202 } 203 204 // inserts to test date conversions in reverse replication 205 execVtgateQuery(t, vtgateConn, "customer", "insert into datze(id, dt2) values (13, '2022-01-01 18:20:30')") 206 execVtgateQuery(t, vtgateConn, "customer", "insert into datze(id, dt2) values (14, '2022-04-01 12:06:07')") 207 vdiff1(t, ksReverseWorkflow, "") 208 }