github.com/matrixorigin/matrixone@v1.2.0/pkg/bootstrap/custom_upgrade.go (about) 1 // Copyright 2024 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bootstrap 16 17 import ( 18 "context" 19 "fmt" 20 "time" 21 22 "github.com/matrixorigin/matrixone/pkg/bootstrap/versions" 23 "github.com/matrixorigin/matrixone/pkg/catalog" 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/container/vector" 26 "github.com/matrixorigin/matrixone/pkg/util/executor" 27 "go.uber.org/zap" 28 ) 29 30 func (s *service) UpgradeTenant(ctx context.Context, tenantName string, retryCount uint32, isALLAccount bool) (bool, error) { 31 // Before manually upgrade, check if there are unready upgrade tasks in system upgrade environment 32 err := s.UpgradePreCheck(ctx) 33 if err != nil { 34 return true, err 35 } 36 37 if isALLAccount { 38 return true, s.BootstrapUpgrade(ctx) 39 } else { 40 err = s.CheckAndUpgradeCluster(ctx) 41 if err != nil { 42 return true, err 43 } 44 45 tenantID, err := s.CheckUpgradeAccount(ctx, tenantName) 46 if err != nil { 47 return true, err 48 } 49 50 err = s.stopper.RunNamedRetryTask("UpgradeTenant", tenantID, retryCount, s.UpgradeOneTenant) 51 if err != nil { 52 return true, err 53 } 54 //if err = s.UpgradeOneTenant(ctx, tenantID); err != nil { 55 // return true, err 56 //} 57 } 58 return true, nil 59 } 60 61 // CheckAndUpgradeCluster Before manually upgrade, it is necessary to ensure that the cluster upgrade is completed. 62 // When performing the cluster upgrade, the tenant upgrade task will be initialized 63 func (s *service) CheckAndUpgradeCluster(ctx context.Context) error { 64 s.adjustUpgrade() 65 66 if err := retryRun(ctx, "doCheckUpgrade", s.doCheckUpgrade); err != nil { 67 getUpgradeLogger().Error("check upgrade failed", zap.Error(err)) 68 return err 69 } 70 if err := s.stopper.RunTask(s.asyncUpgradeTask); err != nil { 71 return err 72 } 73 return nil 74 } 75 76 // UpgradeOneTenant Perform metadata upgrade for individual tenants 77 func (s *service) UpgradeOneTenant(ctx context.Context, tenantID int32) error { 78 s.mu.RLock() 79 checked := s.mu.tenants[tenantID] 80 s.mu.RUnlock() 81 if checked { 82 return nil 83 } 84 85 ctx, cancel := context.WithTimeout(ctx, time.Hour*12) 86 defer cancel() 87 88 opts := executor.Options{}. 89 WithMinCommittedTS(s.now()). 90 WithWaitCommittedLogApplied(). 91 WithTimeZone(time.Local) 92 err := s.exec.ExecTxn( 93 ctx, 94 func(txn executor.TxnExecutor) error { 95 txn.Use(catalog.MO_CATALOG) 96 97 version, err := versions.GetTenantVersion(tenantID, txn) 98 if err != nil { 99 return err 100 } 101 102 // tenant create at current cn, can work correctly 103 currentCN := s.getFinalVersionHandle().Metadata() 104 if versions.Compare(currentCN.Version, version) < 0 { 105 // tenant create at 1.4.0, current tenant version 1.5.0, it must be cannot work 106 return moerr.NewInvalidInputNoCtx("tenant version %s is greater than current cn version %s", 107 version, currentCN.Version) 108 } 109 110 // arrive here means tenant version <= current cn version, need upgrade. 111 latestVersion, err := versions.GetLatestVersion(txn) 112 if err != nil { 113 return err 114 } 115 if latestVersion.Version != currentCN.Version { 116 panic("BUG: current cn's version(" + 117 currentCN.Version + 118 ") must equal cluster latest version(" + 119 latestVersion.Version + 120 ")") 121 } 122 123 for { 124 // upgrade completed 125 if s.upgrade.finalVersionCompleted.Load() { 126 break 127 } 128 129 upgrades, err := versions.GetUpgradeVersions(latestVersion.Version, latestVersion.VersionOffset, txn, false, true) 130 if err != nil { 131 return err 132 } 133 // latest cluster is already upgrade completed 134 if upgrades[len(upgrades)-1].State == versions.StateUpgradingTenant || 135 upgrades[len(upgrades)-1].State == versions.StateReady { 136 break 137 } 138 139 time.Sleep(time.Second) 140 } 141 142 // upgrade in current goroutine immediately 143 version, err = versions.GetTenantCreateVersionForUpdate(tenantID, txn) 144 if err != nil { 145 return err 146 } 147 from := version 148 for _, v := range s.handles { 149 if versions.Compare(v.Metadata().Version, from) >= 0 && 150 v.Metadata().CanDirectUpgrade(from) { 151 if err := v.HandleTenantUpgrade(ctx, tenantID, txn); err != nil { 152 return err 153 } 154 if err := versions.UpgradeTenantVersion(tenantID, v.Metadata().Version, txn); err != nil { 155 return err 156 } 157 from = v.Metadata().Version 158 } 159 } 160 return nil 161 }, 162 opts) 163 if err != nil { 164 return err 165 } 166 s.mu.Lock() 167 s.mu.tenants[tenantID] = true 168 s.mu.Unlock() 169 return nil 170 } 171 172 // CheckUpgradeAccount Custom upgrade account Check if the tenant name exists and is legal 173 func (s *service) CheckUpgradeAccount(ctx context.Context, accountName string) (int32, error) { 174 var accountId int32 175 176 opts := executor.Options{}. 177 WithDatabase(catalog.MO_CATALOG). 178 WithMinCommittedTS(s.now()). 179 WithWaitCommittedLogApplied(). 180 WithTimeZone(time.Local) 181 err := s.exec.ExecTxn( 182 ctx, 183 func(txn executor.TxnExecutor) error { 184 var err error 185 accountId, err = GetAccountIdByName(accountName, txn) 186 if err != nil { 187 getUpgradeLogger().Error("failed to get accountId by accountName when upgrade account", 188 zap.Error(err)) 189 return err 190 } 191 return nil 192 }, 193 opts) 194 return accountId, err 195 } 196 197 // GetAccountIdByName get accountId by accountName when upgrade account 198 func GetAccountIdByName(accountName string, txn executor.TxnExecutor) (int32, error) { 199 sql := fmt.Sprintf("select account_id, account_name from %s.%s where account_name = '%s'", 200 catalog.MO_CATALOG, catalog.MOAccountTable, accountName) 201 res, err := txn.Exec(sql, executor.StatementOption{}) 202 if err != nil { 203 return -1, err 204 } 205 206 // Check if the group account name exists 207 var accountId int32 = -1 208 res.ReadRows(func(rows int, cols []*vector.Vector) bool { 209 accountId = vector.GetFixedAt[int32](cols[0], 0) 210 return true 211 }) 212 213 if accountId == -1 { 214 return -1, moerr.NewInvalidInputNoCtx("The input account name '%s' is invalid, please check input!", accountName) 215 } 216 return accountId, nil 217 } 218 219 // UpgradePreCheck Manual upgrade environment pre check, check if there are any unready upgrade tasks in upgrade environment. 220 // If there are, the unready upgrade tasks need to be processed first 221 func (s *service) UpgradePreCheck(ctx context.Context) error { 222 opts := executor.Options{}. 223 WithDatabase(catalog.MO_CATALOG). 224 WithMinCommittedTS(s.now()). 225 WithWaitCommittedLogApplied(). 226 WithTimeZone(time.Local) 227 err := s.exec.ExecTxn( 228 ctx, 229 func(txn executor.TxnExecutor) error { 230 created, err := versions.IsFrameworkTablesCreated(txn) 231 if err != nil { 232 getUpgradeLogger().Error("failed to check upgrade framework", 233 zap.Error(err)) 234 return err 235 } 236 if !created { 237 return nil 238 } 239 240 final := s.getFinalVersionHandle().Metadata() 241 unReady, err := checkUpgradePerVersionUnready(txn, final) 242 if err != nil { 243 getUpgradeLogger().Error("failed to check task status in pgrade environment", zap.Error(err)) 244 return err 245 } 246 247 if unReady { 248 getUpgradeLogger().Info("There are unexecuted tenant upgrade tasks in upgrade environment, start asynchronous supplementary execution") 249 s.adjustUpgrade() 250 if err := s.stopper.RunTask(s.asyncUpgradeTask); err != nil { 251 return err 252 } 253 for i := 0; i < s.upgrade.upgradeTenantTasks; i++ { 254 if err := s.stopper.RunTask(s.asyncUpgradeTenantTask); err != nil { 255 return err 256 } 257 } 258 return moerr.NewInternalError(ctx, "There is an untrigged upgrade tasks in the system, execution started, Please try again later") 259 } 260 return nil 261 }, 262 opts) 263 return err 264 } 265 266 // checkUpgradeEnvUnReady Check if the upgrade environment is ready 267 func checkUpgradePerVersionUnready(txn executor.TxnExecutor, final versions.Version) (bool, error) { 268 sql := fmt.Sprintf("select id, from_version, to_version, final_version, final_version_offset from %s.%s "+ 269 "where state = 1 and final_version != '%s' and final_version_offset != %d", 270 catalog.MO_CATALOG, catalog.MOUpgradeTable, final.Version, final.VersionOffset) 271 res, err := txn.Exec(sql, executor.StatementOption{}) 272 if err != nil { 273 return false, err 274 } 275 276 var loaded bool 277 res.ReadRows(func(rows int, cols []*vector.Vector) bool { 278 loaded = true 279 return false 280 }) 281 return loaded, nil 282 }