vitess.io/vitess@v0.16.2/go/vt/vtorc/inst/analysis_dao_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 inst
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"vitess.io/vitess/go/vt/external/golib/sqlutils"
    25  
    26  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    27  	"vitess.io/vitess/go/vt/vtorc/db"
    28  	"vitess.io/vitess/go/vt/vtorc/test"
    29  )
    30  
    31  // TestGetReplicationAnalysisDecision tests the code of GetReplicationAnalysis decision-making. It doesn't check the SQL query
    32  // run by it. It only checks the analysis part after the rows have been read. This tests fakes the db and explicitly returns the
    33  // rows that are specified in the test.
    34  func TestGetReplicationAnalysisDecision(t *testing.T) {
    35  	tests := []struct {
    36  		name           string
    37  		info           []*test.InfoForRecoveryAnalysis
    38  		codeWanted     AnalysisCode
    39  		shardWanted    string
    40  		keyspaceWanted string
    41  		wantErr        string
    42  	}{
    43  		{
    44  			name: "ClusterHasNoPrimary",
    45  			info: []*test.InfoForRecoveryAnalysis{{
    46  				TabletInfo: &topodatapb.Tablet{
    47  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
    48  					Hostname:      "localhost",
    49  					Keyspace:      "ks",
    50  					Shard:         "0",
    51  					Type:          topodatapb.TabletType_REPLICA,
    52  					MysqlHostname: "localhost",
    53  					MysqlPort:     6709,
    54  				},
    55  				DurabilityPolicy: "none",
    56  				LastCheckValid:   1,
    57  			}},
    58  			keyspaceWanted: "ks",
    59  			shardWanted:    "0",
    60  			codeWanted:     ClusterHasNoPrimary,
    61  		}, {
    62  			name: "DeadPrimary",
    63  			info: []*test.InfoForRecoveryAnalysis{{
    64  				TabletInfo: &topodatapb.Tablet{
    65  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
    66  					Hostname:      "localhost",
    67  					Keyspace:      "ks",
    68  					Shard:         "0",
    69  					Type:          topodatapb.TabletType_PRIMARY,
    70  					MysqlHostname: "localhost",
    71  					MysqlPort:     6709,
    72  				},
    73  				DurabilityPolicy:              "none",
    74  				LastCheckValid:                0,
    75  				CountReplicas:                 4,
    76  				CountValidReplicas:            4,
    77  				CountValidReplicatingReplicas: 0,
    78  				IsPrimary:                     1,
    79  			}},
    80  			keyspaceWanted: "ks",
    81  			shardWanted:    "0",
    82  			codeWanted:     DeadPrimary,
    83  		}, {
    84  			name: "DeadPrimaryWithoutReplicas",
    85  			info: []*test.InfoForRecoveryAnalysis{{
    86  				TabletInfo: &topodatapb.Tablet{
    87  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
    88  					Hostname:      "localhost",
    89  					Keyspace:      "ks",
    90  					Shard:         "0",
    91  					Type:          topodatapb.TabletType_PRIMARY,
    92  					MysqlHostname: "localhost",
    93  					MysqlPort:     6709,
    94  				},
    95  				DurabilityPolicy: "none",
    96  				LastCheckValid:   0,
    97  				CountReplicas:    0,
    98  				IsPrimary:        1,
    99  			}},
   100  			keyspaceWanted: "ks",
   101  			shardWanted:    "0",
   102  			codeWanted:     DeadPrimaryWithoutReplicas,
   103  		}, {
   104  			name: "DeadPrimaryAndReplicas",
   105  			info: []*test.InfoForRecoveryAnalysis{{
   106  				TabletInfo: &topodatapb.Tablet{
   107  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   108  					Hostname:      "localhost",
   109  					Keyspace:      "ks",
   110  					Shard:         "0",
   111  					Type:          topodatapb.TabletType_PRIMARY,
   112  					MysqlHostname: "localhost",
   113  					MysqlPort:     6709,
   114  				},
   115  				DurabilityPolicy: "none",
   116  				LastCheckValid:   0,
   117  				CountReplicas:    3,
   118  				IsPrimary:        1,
   119  			}},
   120  			keyspaceWanted: "ks",
   121  			shardWanted:    "0",
   122  			codeWanted:     DeadPrimaryAndReplicas,
   123  		}, {
   124  			name: "DeadPrimaryAndSomeReplicas",
   125  			info: []*test.InfoForRecoveryAnalysis{{
   126  				TabletInfo: &topodatapb.Tablet{
   127  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   128  					Hostname:      "localhost",
   129  					Keyspace:      "ks",
   130  					Shard:         "0",
   131  					Type:          topodatapb.TabletType_PRIMARY,
   132  					MysqlHostname: "localhost",
   133  					MysqlPort:     6709,
   134  				},
   135  				DurabilityPolicy:              "none",
   136  				LastCheckValid:                0,
   137  				CountReplicas:                 4,
   138  				CountValidReplicas:            2,
   139  				CountValidReplicatingReplicas: 0,
   140  				IsPrimary:                     1,
   141  			}},
   142  			keyspaceWanted: "ks",
   143  			shardWanted:    "0",
   144  			codeWanted:     DeadPrimaryAndSomeReplicas,
   145  		}, {
   146  			name: "PrimaryHasPrimary",
   147  			info: []*test.InfoForRecoveryAnalysis{{
   148  				TabletInfo: &topodatapb.Tablet{
   149  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   150  					Hostname:      "localhost",
   151  					Keyspace:      "ks",
   152  					Shard:         "0",
   153  					Type:          topodatapb.TabletType_PRIMARY,
   154  					MysqlHostname: "localhost",
   155  					MysqlPort:     6709,
   156  				},
   157  				DurabilityPolicy:   "none",
   158  				LastCheckValid:     1,
   159  				CountReplicas:      4,
   160  				CountValidReplicas: 4,
   161  				IsPrimary:          0,
   162  			}},
   163  			keyspaceWanted: "ks",
   164  			shardWanted:    "0",
   165  			codeWanted:     PrimaryHasPrimary,
   166  		}, {
   167  			name: "PrimaryIsReadOnly",
   168  			info: []*test.InfoForRecoveryAnalysis{{
   169  				TabletInfo: &topodatapb.Tablet{
   170  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   171  					Hostname:      "localhost",
   172  					Keyspace:      "ks",
   173  					Shard:         "0",
   174  					Type:          topodatapb.TabletType_PRIMARY,
   175  					MysqlHostname: "localhost",
   176  					MysqlPort:     6709,
   177  				},
   178  				DurabilityPolicy:   "none",
   179  				LastCheckValid:     1,
   180  				CountReplicas:      4,
   181  				CountValidReplicas: 4,
   182  				IsPrimary:          1,
   183  				ReadOnly:           1,
   184  			}},
   185  			keyspaceWanted: "ks",
   186  			shardWanted:    "0",
   187  			codeWanted:     PrimaryIsReadOnly,
   188  		}, {
   189  			name: "PrimarySemiSyncMustNotBeSet",
   190  			info: []*test.InfoForRecoveryAnalysis{{
   191  				TabletInfo: &topodatapb.Tablet{
   192  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   193  					Hostname:      "localhost",
   194  					Keyspace:      "ks",
   195  					Shard:         "0",
   196  					Type:          topodatapb.TabletType_PRIMARY,
   197  					MysqlHostname: "localhost",
   198  					MysqlPort:     6709,
   199  				},
   200  				DurabilityPolicy:       "none",
   201  				LastCheckValid:         1,
   202  				CountReplicas:          4,
   203  				CountValidReplicas:     4,
   204  				IsPrimary:              1,
   205  				SemiSyncPrimaryEnabled: 1,
   206  			}},
   207  			keyspaceWanted: "ks",
   208  			shardWanted:    "0",
   209  			codeWanted:     PrimarySemiSyncMustNotBeSet,
   210  		}, {
   211  			name: "PrimarySemiSyncMustBeSet",
   212  			info: []*test.InfoForRecoveryAnalysis{{
   213  				TabletInfo: &topodatapb.Tablet{
   214  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   215  					Hostname:      "localhost",
   216  					Keyspace:      "ks",
   217  					Shard:         "0",
   218  					Type:          topodatapb.TabletType_PRIMARY,
   219  					MysqlHostname: "localhost",
   220  					MysqlPort:     6709,
   221  				},
   222  				DurabilityPolicy:       "semi_sync",
   223  				LastCheckValid:         1,
   224  				CountReplicas:          4,
   225  				CountValidReplicas:     4,
   226  				IsPrimary:              1,
   227  				SemiSyncPrimaryEnabled: 0,
   228  			}},
   229  			keyspaceWanted: "ks",
   230  			shardWanted:    "0",
   231  			codeWanted:     PrimarySemiSyncMustBeSet,
   232  		}, {
   233  			name: "NotConnectedToPrimary",
   234  			info: []*test.InfoForRecoveryAnalysis{{
   235  				TabletInfo: &topodatapb.Tablet{
   236  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   237  					Hostname:      "localhost",
   238  					Keyspace:      "ks",
   239  					Shard:         "0",
   240  					Type:          topodatapb.TabletType_PRIMARY,
   241  					MysqlHostname: "localhost",
   242  					MysqlPort:     6708,
   243  				},
   244  				DurabilityPolicy:              "none",
   245  				LastCheckValid:                1,
   246  				CountReplicas:                 4,
   247  				CountValidReplicas:            4,
   248  				CountValidReplicatingReplicas: 3,
   249  				CountValidOracleGTIDReplicas:  4,
   250  				CountLoggingReplicas:          2,
   251  				IsPrimary:                     1,
   252  			}, {
   253  				TabletInfo: &topodatapb.Tablet{
   254  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   255  					Hostname:      "localhost",
   256  					Keyspace:      "ks",
   257  					Shard:         "0",
   258  					Type:          topodatapb.TabletType_REPLICA,
   259  					MysqlHostname: "localhost",
   260  					MysqlPort:     6709,
   261  				},
   262  				LastCheckValid: 1,
   263  				ReadOnly:       1,
   264  				IsPrimary:      1,
   265  			}},
   266  			keyspaceWanted: "ks",
   267  			shardWanted:    "0",
   268  			codeWanted:     NotConnectedToPrimary,
   269  		}, {
   270  			name: "ReplicaIsWritable",
   271  			info: []*test.InfoForRecoveryAnalysis{{
   272  				TabletInfo: &topodatapb.Tablet{
   273  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   274  					Hostname:      "localhost",
   275  					Keyspace:      "ks",
   276  					Shard:         "0",
   277  					Type:          topodatapb.TabletType_PRIMARY,
   278  					MysqlHostname: "localhost",
   279  					MysqlPort:     6708,
   280  				},
   281  				DurabilityPolicy:              "none",
   282  				LastCheckValid:                1,
   283  				CountReplicas:                 4,
   284  				CountValidReplicas:            4,
   285  				CountValidReplicatingReplicas: 3,
   286  				CountValidOracleGTIDReplicas:  4,
   287  				CountLoggingReplicas:          2,
   288  				IsPrimary:                     1,
   289  			}, {
   290  				TabletInfo: &topodatapb.Tablet{
   291  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   292  					Hostname:      "localhost",
   293  					Keyspace:      "ks",
   294  					Shard:         "0",
   295  					Type:          topodatapb.TabletType_REPLICA,
   296  					MysqlHostname: "localhost",
   297  					MysqlPort:     6709,
   298  				},
   299  				DurabilityPolicy: "none",
   300  				SourceHost:       "localhost",
   301  				SourcePort:       6708,
   302  				LastCheckValid:   1,
   303  				ReadOnly:         0,
   304  			}},
   305  			keyspaceWanted: "ks",
   306  			shardWanted:    "0",
   307  			codeWanted:     ReplicaIsWritable,
   308  		}, {
   309  			name: "ConnectedToWrongPrimary",
   310  			info: []*test.InfoForRecoveryAnalysis{{
   311  				TabletInfo: &topodatapb.Tablet{
   312  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   313  					Hostname:      "localhost",
   314  					Keyspace:      "ks",
   315  					Shard:         "0",
   316  					Type:          topodatapb.TabletType_PRIMARY,
   317  					MysqlHostname: "localhost",
   318  					MysqlPort:     6708,
   319  				},
   320  				DurabilityPolicy:              "none",
   321  				LastCheckValid:                1,
   322  				CountReplicas:                 4,
   323  				CountValidReplicas:            4,
   324  				CountValidReplicatingReplicas: 3,
   325  				CountValidOracleGTIDReplicas:  4,
   326  				CountLoggingReplicas:          2,
   327  				IsPrimary:                     1,
   328  			}, {
   329  				TabletInfo: &topodatapb.Tablet{
   330  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   331  					Hostname:      "localhost",
   332  					Keyspace:      "ks",
   333  					Shard:         "0",
   334  					Type:          topodatapb.TabletType_REPLICA,
   335  					MysqlHostname: "localhost",
   336  					MysqlPort:     6709,
   337  				},
   338  				DurabilityPolicy: "none",
   339  				SourceHost:       "localhost",
   340  				SourcePort:       6706,
   341  				LastCheckValid:   1,
   342  				ReadOnly:         1,
   343  			}},
   344  			keyspaceWanted: "ks",
   345  			shardWanted:    "0",
   346  			codeWanted:     ConnectedToWrongPrimary,
   347  		}, {
   348  			name: "ReplicationStopped",
   349  			info: []*test.InfoForRecoveryAnalysis{{
   350  				TabletInfo: &topodatapb.Tablet{
   351  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   352  					Hostname:      "localhost",
   353  					Keyspace:      "ks",
   354  					Shard:         "0",
   355  					Type:          topodatapb.TabletType_PRIMARY,
   356  					MysqlHostname: "localhost",
   357  					MysqlPort:     6708,
   358  				},
   359  				DurabilityPolicy:              "none",
   360  				LastCheckValid:                1,
   361  				CountReplicas:                 4,
   362  				CountValidReplicas:            4,
   363  				CountValidReplicatingReplicas: 3,
   364  				CountValidOracleGTIDReplicas:  4,
   365  				CountLoggingReplicas:          2,
   366  				IsPrimary:                     1,
   367  			}, {
   368  				TabletInfo: &topodatapb.Tablet{
   369  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   370  					Hostname:      "localhost",
   371  					Keyspace:      "ks",
   372  					Shard:         "0",
   373  					Type:          topodatapb.TabletType_REPLICA,
   374  					MysqlHostname: "localhost",
   375  					MysqlPort:     6709,
   376  				},
   377  				DurabilityPolicy:   "none",
   378  				SourceHost:         "localhost",
   379  				SourcePort:         6708,
   380  				LastCheckValid:     1,
   381  				ReadOnly:           1,
   382  				ReplicationStopped: 1,
   383  			}},
   384  			keyspaceWanted: "ks",
   385  			shardWanted:    "0",
   386  			codeWanted:     ReplicationStopped,
   387  		},
   388  		{
   389  			name: "ReplicaSemiSyncMustBeSet",
   390  			info: []*test.InfoForRecoveryAnalysis{{
   391  				TabletInfo: &topodatapb.Tablet{
   392  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   393  					Hostname:      "localhost",
   394  					Keyspace:      "ks",
   395  					Shard:         "0",
   396  					Type:          topodatapb.TabletType_PRIMARY,
   397  					MysqlHostname: "localhost",
   398  					MysqlPort:     6708,
   399  				},
   400  				DurabilityPolicy:              "semi_sync",
   401  				LastCheckValid:                1,
   402  				CountReplicas:                 4,
   403  				CountValidReplicas:            4,
   404  				CountValidReplicatingReplicas: 3,
   405  				CountValidOracleGTIDReplicas:  4,
   406  				CountLoggingReplicas:          2,
   407  				IsPrimary:                     1,
   408  				SemiSyncPrimaryEnabled:        1,
   409  			}, {
   410  				TabletInfo: &topodatapb.Tablet{
   411  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   412  					Hostname:      "localhost",
   413  					Keyspace:      "ks",
   414  					Shard:         "0",
   415  					Type:          topodatapb.TabletType_REPLICA,
   416  					MysqlHostname: "localhost",
   417  					MysqlPort:     6709,
   418  				},
   419  				PrimaryTabletInfo: &topodatapb.Tablet{
   420  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   421  					Hostname:      "localhost",
   422  					Keyspace:      "ks",
   423  					Shard:         "0",
   424  					Type:          topodatapb.TabletType_PRIMARY,
   425  					MysqlHostname: "localhost",
   426  					MysqlPort:     6708,
   427  				},
   428  				DurabilityPolicy:       "semi_sync",
   429  				SourceHost:             "localhost",
   430  				SourcePort:             6708,
   431  				LastCheckValid:         1,
   432  				ReadOnly:               1,
   433  				SemiSyncReplicaEnabled: 0,
   434  			}},
   435  			keyspaceWanted: "ks",
   436  			shardWanted:    "0",
   437  			codeWanted:     ReplicaSemiSyncMustBeSet,
   438  		}, {
   439  			name: "ReplicaSemiSyncMustNotBeSet",
   440  			info: []*test.InfoForRecoveryAnalysis{{
   441  				TabletInfo: &topodatapb.Tablet{
   442  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   443  					Hostname:      "localhost",
   444  					Keyspace:      "ks",
   445  					Shard:         "0",
   446  					Type:          topodatapb.TabletType_PRIMARY,
   447  					MysqlHostname: "localhost",
   448  					MysqlPort:     6708,
   449  				},
   450  				DurabilityPolicy:              "none",
   451  				LastCheckValid:                1,
   452  				CountReplicas:                 4,
   453  				CountValidReplicas:            4,
   454  				CountValidReplicatingReplicas: 3,
   455  				CountValidOracleGTIDReplicas:  4,
   456  				CountLoggingReplicas:          2,
   457  				IsPrimary:                     1,
   458  			}, {
   459  				TabletInfo: &topodatapb.Tablet{
   460  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   461  					Hostname:      "localhost",
   462  					Keyspace:      "ks",
   463  					Shard:         "0",
   464  					Type:          topodatapb.TabletType_REPLICA,
   465  					MysqlHostname: "localhost",
   466  					MysqlPort:     6709,
   467  				},
   468  				PrimaryTabletInfo: &topodatapb.Tablet{
   469  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   470  					Hostname:      "localhost",
   471  					Keyspace:      "ks",
   472  					Shard:         "0",
   473  					Type:          topodatapb.TabletType_PRIMARY,
   474  					MysqlHostname: "localhost",
   475  					MysqlPort:     6708,
   476  				},
   477  				DurabilityPolicy:       "none",
   478  				SourceHost:             "localhost",
   479  				SourcePort:             6708,
   480  				LastCheckValid:         1,
   481  				ReadOnly:               1,
   482  				SemiSyncReplicaEnabled: 1,
   483  			}},
   484  			keyspaceWanted: "ks",
   485  			shardWanted:    "0",
   486  			codeWanted:     ReplicaSemiSyncMustNotBeSet,
   487  		}, {
   488  			name: "SnapshotKeyspace",
   489  			info: []*test.InfoForRecoveryAnalysis{{
   490  				TabletInfo: &topodatapb.Tablet{
   491  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   492  					Hostname:      "localhost",
   493  					Keyspace:      "ks",
   494  					Shard:         "0",
   495  					Type:          topodatapb.TabletType_REPLICA,
   496  					MysqlHostname: "localhost",
   497  					MysqlPort:     6709,
   498  				},
   499  				// Snapshot Keyspace
   500  				KeyspaceType:     1,
   501  				DurabilityPolicy: "none",
   502  				LastCheckValid:   1,
   503  			}},
   504  			keyspaceWanted: "ks",
   505  			shardWanted:    "0",
   506  			codeWanted:     NoProblem,
   507  		}, {
   508  			name: "EmptyDurabilityPolicy",
   509  			info: []*test.InfoForRecoveryAnalysis{{
   510  				TabletInfo: &topodatapb.Tablet{
   511  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   512  					Hostname:      "localhost",
   513  					Keyspace:      "ks",
   514  					Shard:         "0",
   515  					Type:          topodatapb.TabletType_REPLICA,
   516  					MysqlHostname: "localhost",
   517  					MysqlPort:     6709,
   518  				},
   519  				LastCheckValid: 1,
   520  			}},
   521  			// We will ignore these keyspaces too until the durability policy is set in the topo server
   522  			keyspaceWanted: "ks",
   523  			shardWanted:    "0",
   524  			codeWanted:     NoProblem,
   525  		}, {
   526  			// If the database_instance table for a tablet is empty (discovery of MySQL information hasn't happened yet or failed)
   527  			// then we shouldn't run a failure fix on it until the discovery succeeds
   528  			name: "Empty database_instance table",
   529  			info: []*test.InfoForRecoveryAnalysis{{
   530  				TabletInfo: &topodatapb.Tablet{
   531  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 101},
   532  					Hostname:      "localhost",
   533  					Keyspace:      "ks",
   534  					Shard:         "0",
   535  					Type:          topodatapb.TabletType_PRIMARY,
   536  					MysqlHostname: "localhost",
   537  					MysqlPort:     6708,
   538  				},
   539  				DurabilityPolicy:              "semi_sync",
   540  				LastCheckValid:                1,
   541  				CountReplicas:                 4,
   542  				CountValidReplicas:            4,
   543  				CountValidReplicatingReplicas: 3,
   544  				CountValidOracleGTIDReplicas:  4,
   545  				CountLoggingReplicas:          2,
   546  				IsPrimary:                     1,
   547  				SemiSyncPrimaryEnabled:        1,
   548  			}, {
   549  				TabletInfo: &topodatapb.Tablet{
   550  					Alias:         &topodatapb.TabletAlias{Cell: "zon1", Uid: 100},
   551  					Hostname:      "localhost",
   552  					Keyspace:      "ks",
   553  					Shard:         "0",
   554  					Type:          topodatapb.TabletType_REPLICA,
   555  					MysqlHostname: "localhost",
   556  					MysqlPort:     6709,
   557  				},
   558  				IsInvalid:        1,
   559  				DurabilityPolicy: "semi_sync",
   560  			}},
   561  			keyspaceWanted: "ks",
   562  			shardWanted:    "0",
   563  			codeWanted:     NoProblem,
   564  		},
   565  	}
   566  	for _, tt := range tests {
   567  		t.Run(tt.name, func(t *testing.T) {
   568  			oldDB := db.Db
   569  			defer func() {
   570  				db.Db = oldDB
   571  			}()
   572  
   573  			var rowMaps []sqlutils.RowMap
   574  			for _, analysis := range tt.info {
   575  				analysis.SetValuesFromTabletInfo()
   576  				rowMaps = append(rowMaps, analysis.ConvertToRowMap())
   577  			}
   578  			db.Db = test.NewTestDB([][]sqlutils.RowMap{rowMaps})
   579  
   580  			got, err := GetReplicationAnalysis("", "", &ReplicationAnalysisHints{})
   581  			if tt.wantErr != "" {
   582  				require.EqualError(t, err, tt.wantErr)
   583  				return
   584  			}
   585  			require.NoError(t, err)
   586  			if tt.codeWanted == NoProblem {
   587  				require.Len(t, got, 0)
   588  				return
   589  			}
   590  			require.Len(t, got, 1)
   591  			require.Equal(t, tt.codeWanted, got[0].Analysis)
   592  			require.Equal(t, tt.keyspaceWanted, got[0].AnalyzedKeyspace)
   593  			require.Equal(t, tt.shardWanted, got[0].AnalyzedShard)
   594  		})
   595  	}
   596  }
   597  
   598  // TestGetReplicationAnalysis tests the entire GetReplicationAnalysis. It inserts data into the database and runs the function.
   599  // The database is not faked. This is intended to give more test coverage. This test is more comprehensive but more expensive than TestGetReplicationAnalysisDecision.
   600  // This test is somewhere between a unit test, and an end-to-end test. It is specifically useful for testing situations which are hard to come by in end-to-end test, but require
   601  // real-world data to test specifically.
   602  func TestGetReplicationAnalysis(t *testing.T) {
   603  	// The initialSQL is a set of insert commands copied from a dump of an actual running VTOrc instances. The relevant insert commands are here.
   604  	// This is a dump taken from a test running 4 tablets, zone1-101 is the primary, zone1-100 is a replica, zone1-112 is a rdonly and zone2-200 is a cross-cell replica.
   605  	initialSQL := []string{
   606  		`INSERT INTO database_instance VALUES('localhost',6747,'2022-12-28 07:26:04','2022-12-28 07:26:04',213696377,'8.0.31','ROW',1,1,'vt-0000000112-bin.000001',15963,'localhost',6714,1,1,'vt-0000000101-bin.000001',15583,'vt-0000000101-bin.000001',15583,0,0,1,'','',1,0,'vt-0000000112-relay-bin.000002',15815,0,1,0,'zone1','',0,0,0,1,'729a4cc4-8680-11ed-a104-47706090afbd:1-54','729a5138-8680-11ed-9240-92a06c3be3c2','2022-12-28 07:26:04','',1,0,0,'zone1-0000000112','Homebrew','8.0','FULL',10816929,0,0,'ON',1,'729a4cc4-8680-11ed-a104-47706090afbd','','729a4cc4-8680-11ed-a104-47706090afbd,729a5138-8680-11ed-9240-92a06c3be3c2',1,1,'',1000000000000000000,1,0,0,0,'',0,'','','[]','',0);`,
   607  		`INSERT INTO database_instance VALUES('localhost',6711,'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,1,1,'vt-0000000101-bin.000001',15583,'vt-0000000101-bin.000001',15583,0,0,1,'','',1,0,'vt-0000000100-relay-bin.000002',15815,0,1,0,'zone1','',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,'zone1-0000000100','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,'',0,'','','[]','',0);`,
   608  		`INSERT INTO database_instance VALUES('localhost',6714,'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,NULL,NULL,0,'','',0,0,'',0,0,0,0,'zone1','',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,'zone1-0000000101','Homebrew','8.0','FULL',11366095,1,1,'ON',1,'','','729a4cc4-8680-11ed-a104-47706090afbd',-1,-1,'',1000000000000000000,1,1,0,2,'',0,'','','[]','',0);`,
   609  		`INSERT INTO database_instance VALUES('localhost',6756,'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,1,1,'vt-0000000101-bin.000001',15583,'vt-0000000101-bin.000001',15583,0,0,1,'','',1,0,'vt-0000000200-relay-bin.000002',15815,0,1,0,'zone2','',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,'zone2-0000000200','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,'',0,'','','[]','',0);`,
   610  		`INSERT INTO vitess_tablet VALUES('zone1-0000000100','localhost',6711,'ks','0','zone1',2,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731307d20706f72745f6d61703a7b6b65793a227674222076616c75653a363730397d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363731312064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`,
   611  		`INSERT INTO vitess_tablet VALUES('zone1-0000000101','localhost',6714,'ks','0','zone1',1,'2022-12-28 07:23:25.129898+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3130317d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363731337d20706f72745f6d61703a7b6b65793a227674222076616c75653a363731327d206b657973706163653a226b73222073686172643a22302220747970653a5052494d415259206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a36373134207072696d6172795f7465726d5f73746172745f74696d653a7b7365636f6e64733a31363732323132323035206e616e6f7365636f6e64733a3132393839383030307d2064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`,
   612  		`INSERT INTO vitess_tablet VALUES('zone1-0000000112','localhost',6747,'ks','0','zone1',3,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653122207569643a3131327d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363734367d20706f72745f6d61703a7b6b65793a227674222076616c75653a363734357d206b657973706163653a226b73222073686172643a22302220747970653a52444f4e4c59206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363734372064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`,
   613  		`INSERT INTO vitess_tablet VALUES('zone2-0000000200','localhost',6756,'ks','0','zone2',2,'0001-01-01 00:00:00+00:00',X'616c6961733a7b63656c6c3a227a6f6e653222207569643a3230307d20686f73746e616d653a226c6f63616c686f73742220706f72745f6d61703a7b6b65793a2267727063222076616c75653a363735357d20706f72745f6d61703a7b6b65793a227674222076616c75653a363735347d206b657973706163653a226b73222073686172643a22302220747970653a5245504c494341206d7973716c5f686f73746e616d653a226c6f63616c686f737422206d7973716c5f706f72743a363735362064625f7365727665725f76657273696f6e3a22382e302e3331222064656661756c745f636f6e6e5f636f6c6c6174696f6e3a3435');`,
   614  		`INSERT INTO vitess_keyspace VALUES('ks',0,'semi_sync');`,
   615  	}
   616  
   617  	// The test is intended to be used as follows. The initial data is stored into the database. Following this, some specific queries are run that each individual test specifies to get the desired state.
   618  	tests := []struct {
   619  		name           string
   620  		sql            []string
   621  		codeWanted     AnalysisCode
   622  		shardWanted    string
   623  		keyspaceWanted string
   624  	}{
   625  		{
   626  			name:       "No additions",
   627  			sql:        nil,
   628  			codeWanted: NoProblem,
   629  		}, {
   630  			name: "Removing Primary Tablet's Vitess record",
   631  			sql: []string{
   632  				// This query removes the primary tablet's vitess_tablet record
   633  				`delete from vitess_tablet where port = 6714`,
   634  			},
   635  			codeWanted:     ClusterHasNoPrimary,
   636  			keyspaceWanted: "ks",
   637  			shardWanted:    "0",
   638  		}, {
   639  			name: "Removing Primary Tablet's MySQL record",
   640  			sql: []string{
   641  				// This query removes the primary tablet's database_instance record
   642  				`delete from database_instance where port = 6714`,
   643  			},
   644  			// As long as we have the vitess record stating that this tablet is the primary
   645  			// It would be incorrect to run a PRS.
   646  			// This situation only happens when we haven't been able to read the MySQL information even once for this tablet.
   647  			// So it is likely a new tablet.
   648  			codeWanted: NoProblem,
   649  		}, {
   650  			name: "Removing Replica Tablet's MySQL record",
   651  			sql: []string{
   652  				// This query removes the replica tablet's database_instance record
   653  				`delete from database_instance where port = 6711`,
   654  			},
   655  			// As long as we don't have the MySQL information, we shouldn't do anything.
   656  			// We should wait for the MySQL information to be refreshed once.
   657  			// This situation only happens when we haven't been able to read the MySQL information even once for this tablet.
   658  			// So it is likely a new tablet.
   659  			codeWanted: NoProblem,
   660  		},
   661  	}
   662  
   663  	for _, tt := range tests {
   664  		t.Run(tt.name, func(t *testing.T) {
   665  			// Each test should clear the database. The easiest way to do that is to run all the initialization commands again
   666  			defer func() {
   667  				db.ClearVTOrcDatabase()
   668  			}()
   669  
   670  			for _, query := range append(initialSQL, tt.sql...) {
   671  				_, err := db.ExecVTOrc(query)
   672  				require.NoError(t, err)
   673  			}
   674  
   675  			got, err := GetReplicationAnalysis("", "", &ReplicationAnalysisHints{})
   676  			require.NoError(t, err)
   677  			if tt.codeWanted == NoProblem {
   678  				require.Len(t, got, 0)
   679  				return
   680  			}
   681  			require.Len(t, got, 1)
   682  			require.Equal(t, tt.codeWanted, got[0].Analysis)
   683  			require.Equal(t, tt.keyspaceWanted, got[0].AnalyzedKeyspace)
   684  			require.Equal(t, tt.shardWanted, got[0].AnalyzedShard)
   685  		})
   686  	}
   687  }