diff --git a/go/vt/external/golib/sqlutils/sqlutils.go b/go/vt/external/golib/sqlutils/sqlutils.go index 38b0b0c9e62..61d3debef12 100644 --- a/go/vt/external/golib/sqlutils/sqlutils.go +++ b/go/vt/external/golib/sqlutils/sqlutils.go @@ -33,7 +33,7 @@ import ( "vitess.io/vitess/go/vt/log" ) -const DateTimeFormat = "2006-01-02 15:04:05.999999" +const DateTimeFormat = "2006-01-02 15:04:05.999999 +0000 UTC" // RowMap represents one row in a result set. Its objective is to allow // for easy, typed getters by column name. diff --git a/go/vt/external/golib/sqlutils/sqlutils_test.go b/go/vt/external/golib/sqlutils/sqlutils_test.go index a7ac8680072..4091afabfbe 100644 --- a/go/vt/external/golib/sqlutils/sqlutils_test.go +++ b/go/vt/external/golib/sqlutils/sqlutils_test.go @@ -25,6 +25,7 @@ import ( ) func TestRowMap(t *testing.T) { + getTimeExpected := time.Date(2024, time.January, 24, 12, 34, 56, 789000000, time.UTC) tt := []struct { name string rowMap RowMap @@ -82,8 +83,8 @@ func TestRowMap(t *testing.T) { }, { "GetTime", - RowMap{"key": CellData{String: "2024-01-24 12:34:56.789"}}, - time.Date(2024, time.January, 24, 12, 34, 56, 789000000, time.UTC), + RowMap{"key": CellData{String: getTimeExpected.UTC().String()}}, + getTimeExpected, }, { "GetTime Error", diff --git a/go/vt/vtorc/inst/analysis.go b/go/vt/vtorc/inst/analysis.go index c02adbdba1c..7ad66265c0c 100644 --- a/go/vt/vtorc/inst/analysis.go +++ b/go/vt/vtorc/inst/analysis.go @@ -94,7 +94,7 @@ type ReplicationAnalysis struct { AnalyzedKeyspace string AnalyzedShard string // ShardPrimaryTermTimestamp is the primary term start time stored in the shard record. - ShardPrimaryTermTimestamp string + ShardPrimaryTermTimestamp time.Time AnalyzedInstanceBinlogCoordinates BinlogCoordinates IsPrimary bool IsClusterPrimary bool diff --git a/go/vt/vtorc/inst/analysis_dao.go b/go/vt/vtorc/inst/analysis_dao.go index 59c76c0b2aa..36e7aa370fe 100644 --- a/go/vt/vtorc/inst/analysis_dao.go +++ b/go/vt/vtorc/inst/analysis_dao.go @@ -313,7 +313,7 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna return nil } - a.ShardPrimaryTermTimestamp = m.GetString("shard_primary_term_timestamp") + a.ShardPrimaryTermTimestamp = m.GetTime("shard_primary_term_timestamp") a.IsPrimary = m.GetBool("is_primary") a.AnalyzedInstanceAlias = topoproto.TabletAliasString(tablet.Alias) a.AnalyzedInstancePrimaryAlias = topoproto.TabletAliasString(primaryTablet.Alias) @@ -456,12 +456,12 @@ func GetReplicationAnalysis(keyspace string, shard string, hints *ReplicationAna } else if topo.IsReplicaType(a.TabletType) && a.ErrantGTID != "" { a.Analysis = ErrantGTIDDetected a.Description = "Tablet has errant GTIDs" - } else if topo.IsReplicaType(a.TabletType) && ca.primaryAlias == "" && a.ShardPrimaryTermTimestamp == "" { + } else if topo.IsReplicaType(a.TabletType) && ca.primaryAlias == "" && a.ShardPrimaryTermTimestamp.IsZero() { // ClusterHasNoPrimary should only be detected when the shard record doesn't have any primary term start time specified either. a.Analysis = ClusterHasNoPrimary a.Description = "Cluster has no primary" ca.hasShardWideAction = true - } else if topo.IsReplicaType(a.TabletType) && ca.primaryAlias == "" && a.ShardPrimaryTermTimestamp != "" { + } else if topo.IsReplicaType(a.TabletType) && ca.primaryAlias == "" && !a.ShardPrimaryTermTimestamp.IsZero() { // If there are no primary tablets, but the shard primary start time isn't empty, then we know // the primary tablet was deleted. a.Analysis = PrimaryTabletDeleted diff --git a/go/vt/vtorc/inst/analysis_dao_test.go b/go/vt/vtorc/inst/analysis_dao_test.go index 4a996766e77..d866bc92847 100644 --- a/go/vt/vtorc/inst/analysis_dao_test.go +++ b/go/vt/vtorc/inst/analysis_dao_test.go @@ -38,11 +38,11 @@ var ( `INSERT INTO database_instance VALUES('zone1-0000000100','localhost',6711,2,'zone1','2022-12-28 07:26:04','2022-12-28 07:26:04',1094500338,'8.0.31','ROW',1,1,'vt-0000000100-bin.000001',15963,'localhost',6714,8,4.0,1,1,'vt-0000000101-bin.000001',15583,'vt-0000000101-bin.000001',15583,0,0,1,'','',1,'vt-0000000100-relay-bin.000002',15815,1,0,0,0,0,1,'729a4cc4-8680-11ed-a104-47706090afbd:1-54','729a5138-8680-11ed-acf8-d6b0ef9f4eaa','2022-12-28 07:26:04','',1,0,0,'Homebrew','8.0','FULL',10103920,0,1,'ON',1,'729a4cc4-8680-11ed-a104-47706090afbd','','729a4cc4-8680-11ed-a104-47706090afbd,729a5138-8680-11ed-acf8-d6b0ef9f4eaa',1,1,1000000000000000000,1,0,1,0,false,false);`, `INSERT INTO database_instance VALUES('zone1-0000000101','localhost',6714,1,'zone1','2022-12-28 07:26:04','2022-12-28 07:26:04',390954723,'8.0.31','ROW',1,1,'vt-0000000101-bin.000001',15583,'',0,0,0,0,0,'',0,'',0,NULL,NULL,0,'','',0,'',0,0,0,0,0,0,1,'729a4cc4-8680-11ed-a104-47706090afbd:1-54','729a4cc4-8680-11ed-a104-47706090afbd','2022-12-28 07:26:04','',0,0,0,'Homebrew','8.0','FULL',11366095,1,1,'ON',1,'','','729a4cc4-8680-11ed-a104-47706090afbd',-1,-1,1000000000000000000,1,1,0,2,false,false);`, `INSERT INTO database_instance VALUES('zone2-0000000200','localhost',6756,2,'zone2','2022-12-28 07:26:05','2022-12-28 07:26:05',444286571,'8.0.31','ROW',1,1,'vt-0000000200-bin.000001',15963,'localhost',6714,8,4.0,1,1,'vt-0000000101-bin.000001',15583,'vt-0000000101-bin.000001',15583,0,0,1,'','',1,'vt-0000000200-relay-bin.000002',15815,1,0,0,0,0,1,'729a4cc4-8680-11ed-a104-47706090afbd:1-54','729a497c-8680-11ed-8ad4-3f51d747db75','2022-12-28 07:26:05','',1,0,0,'Homebrew','8.0','FULL',10443112,0,1,'ON',1,'729a4cc4-8680-11ed-a104-47706090afbd','','729a4cc4-8680-11ed-a104-47706090afbd,729a497c-8680-11ed-8ad4-3f51d747db75',1,1,1000000000000000000,1,0,1,0,false,false);`, - `INSERT INTO vitess_tablet VALUES('zone1-0000000100','localhost',6711,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731307d20706f72745f6d61703a7b6b65793a227674222076616c75653a363730397d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363731312064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, - `INSERT INTO vitess_tablet VALUES('zone1-0000000101','localhost',6714,'ks','0','zone1',1,'2022-12-28 07:23:25.129898+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130317d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731337d20706f72745f6d61703a7b6b65793a227674222076616c75653a363731327d206b657973706163653a226b73222073686172643a22302220747970653a5052494d415259206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a36373134207072696d6172795f7465726d5f73746172745f74696d653a7b7365636f6e64733a31363732323132323035206e616e6f7365636f6e64733a3132393839383030307d2064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, - `INSERT INTO vitess_tablet VALUES('zone1-0000000112','localhost',6747,'ks','0','zone1',3,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3131327d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363734367d20706f72745f6d61703a7b6b65793a227674222076616c75653a363734357d206b657973706163653a226b73222073686172643a22302220747970653a52444f4e4c59206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363734372064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, - `INSERT INTO vitess_tablet VALUES('zone2-0000000200','localhost',6756,'ks','0','zone2',2,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653222207569643a3230307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363735357d20706f72745f6d61703a7b6b65793a227674222076616c75653a363735347d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363735362064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, - `INSERT INTO vitess_shard VALUES('ks','0','zone1-0000000101','2022-12-28 07:23:25.129898+00:00');`, + `INSERT INTO vitess_tablet VALUES('zone1-0000000100','localhost',6711,'ks','0','zone1',2,'0001-01-01 00:00:00 +0000 UTC',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731307d20706f72745f6d61703a7b6b65793a227674222076616c75653a363730397d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363731312064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, + `INSERT INTO vitess_tablet VALUES('zone1-0000000101','localhost',6714,'ks','0','zone1',1,'2022-12-28 07:23:25.129898 +0000 UTC',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130317d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731337d20706f72745f6d61703a7b6b65793a227674222076616c75653a363731327d206b657973706163653a226b73222073686172643a22302220747970653a5052494d415259206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a36373134207072696d6172795f7465726d5f73746172745f74696d653a7b7365636f6e64733a31363732323132323035206e616e6f7365636f6e64733a3132393839383030307d2064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, + `INSERT INTO vitess_tablet VALUES('zone1-0000000112','localhost',6747,'ks','0','zone1',3,'0001-01-01 00:00:00 +0000 UTC',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3131327d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363734367d20706f72745f6d61703a7b6b65793a227674222076616c75653a363734357d206b657973706163653a226b73222073686172643a22302220747970653a52444f4e4c59206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363734372064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, + `INSERT INTO vitess_tablet VALUES('zone2-0000000200','localhost',6756,'ks','0','zone2',2,'0001-01-01 00:00:00 +0000 UTC',X'616c6961733a7b63656c6c3a227a6f6e653222207569643a3230307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363735357d20706f72745f6d61703a7b6b65793a227674222076616c75653a363735347d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363735362064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`, + `INSERT INTO vitess_shard VALUES('ks','0','zone1-0000000101','2025-06-25 23:48:57.306096 +0000 UTC');`, `INSERT INTO vitess_keyspace VALUES('ks',0,'semi_sync');`, } ) @@ -89,7 +89,7 @@ func TestGetReplicationAnalysisDecision(t *testing.T) { MysqlHostname: "localhost", MysqlPort: 6709, }, - ShardPrimaryTermTimestamp: "2022-12-28 07:23:25.129898+00:00", + ShardPrimaryTermTimestamp: "2022-12-28 07:23:25.129898 +0000 UTC", DurabilityPolicy: policy.DurabilityNone, LastCheckValid: 1, }}, diff --git a/go/vt/vtorc/inst/shard_dao.go b/go/vt/vtorc/inst/shard_dao.go index 7fe8df110f9..73eb435e010 100644 --- a/go/vt/vtorc/inst/shard_dao.go +++ b/go/vt/vtorc/inst/shard_dao.go @@ -18,6 +18,7 @@ package inst import ( "errors" + "time" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/vt/external/golib/sqlutils" @@ -42,7 +43,7 @@ func ReadShardNames(keyspaceName string) (shardNames []string, err error) { } // ReadShardPrimaryInformation reads the vitess shard record and gets the shard primary alias and timestamp. -func ReadShardPrimaryInformation(keyspaceName, shardName string) (primaryAlias string, primaryTimestamp string, err error) { +func ReadShardPrimaryInformation(keyspaceName, shardName string) (primaryAlias string, primaryTimestamp time.Time, err error) { if err = topo.ValidateKeyspaceName(keyspaceName); err != nil { return } @@ -50,28 +51,28 @@ func ReadShardPrimaryInformation(keyspaceName, shardName string) (primaryAlias s return } - query := ` - select + query := `SELECT primary_alias, primary_timestamp - from + FROM vitess_shard - where keyspace=? and shard=? - ` + WHERE + keyspace = ? + AND shard = ?` args := sqlutils.Args(keyspaceName, shardName) shardFound := false err = db.QueryVTOrc(query, args, func(row sqlutils.RowMap) error { shardFound = true primaryAlias = row.GetString("primary_alias") - primaryTimestamp = row.GetString("primary_timestamp") + primaryTimestamp = row.GetTime("primary_timestamp") return nil }) if err != nil { return } if !shardFound { - return "", "", ErrShardNotFound + err = ErrShardNotFound } - return primaryAlias, primaryTimestamp, nil + return primaryAlias, primaryTimestamp, err } // SaveShard saves the shard record against the shard name. @@ -87,7 +88,7 @@ func SaveShard(shard *topo.ShardInfo) error { shard.Keyspace(), shard.ShardName(), getShardPrimaryAliasString(shard), - getShardPrimaryTermStartTimeString(shard), + getShardPrimaryTermStartTime(shard), ) return err } @@ -100,12 +101,12 @@ func getShardPrimaryAliasString(shard *topo.ShardInfo) string { return topoproto.TabletAliasString(shard.PrimaryAlias) } -// getShardPrimaryAliasString gets the shard primary term start time to be stored as a string in the database. -func getShardPrimaryTermStartTimeString(shard *topo.ShardInfo) string { +// getShardPrimaryTermStartTime gets the shard primary term start time to be stored as a string in the database. +func getShardPrimaryTermStartTime(shard *topo.ShardInfo) time.Time { if shard.PrimaryTermStartTime == nil { - return "" + return time.Time{} } - return protoutil.TimeFromProto(shard.PrimaryTermStartTime).UTC().String() + return protoutil.TimeFromProto(shard.PrimaryTermStartTime).UTC() } // DeleteShard deletes a shard using a keyspace and shard name. diff --git a/go/vt/vtorc/inst/shard_dao_test.go b/go/vt/vtorc/inst/shard_dao_test.go index 0077b3d64af..04bb6b430f7 100644 --- a/go/vt/vtorc/inst/shard_dao_test.go +++ b/go/vt/vtorc/inst/shard_dao_test.go @@ -40,7 +40,7 @@ func TestSaveReadAndDeleteShard(t *testing.T) { shardName string shard *topodatapb.Shard primaryAliasWanted string - primaryTimestampWanted string + primaryTimestampWanted time.Time err string }{ { @@ -54,7 +54,7 @@ func TestSaveReadAndDeleteShard(t *testing.T) { }, PrimaryTermStartTime: protoutil.TimeToProto(timeToUse.Add(1 * time.Hour)), }, - primaryTimestampWanted: "2023-07-24 06:00:05.000001 +0000 UTC", + primaryTimestampWanted: timeToUse.Add(1 * time.Hour).UTC(), primaryAliasWanted: "zone1-0000000301", }, { name: "Success with empty primary alias", @@ -63,7 +63,7 @@ func TestSaveReadAndDeleteShard(t *testing.T) { shard: &topodatapb.Shard{ PrimaryTermStartTime: protoutil.TimeToProto(timeToUse), }, - primaryTimestampWanted: "2023-07-24 05:00:05.000001 +0000 UTC", + primaryTimestampWanted: timeToUse.UTC(), primaryAliasWanted: "", }, { name: "Success with empty primary term start time", @@ -75,7 +75,7 @@ func TestSaveReadAndDeleteShard(t *testing.T) { Uid: 301, }, }, - primaryTimestampWanted: "", + primaryTimestampWanted: time.Time{}, primaryAliasWanted: "zone1-0000000301", }, {