github.com/matrixorigin/matrixone@v1.2.0/pkg/bootstrap/service.go (about) 1 // Copyright 2023 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 "strings" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "go.uber.org/zap" 26 27 "github.com/matrixorigin/matrixone/pkg/bootstrap/versions" 28 "github.com/matrixorigin/matrixone/pkg/catalog" 29 "github.com/matrixorigin/matrixone/pkg/common/stopper" 30 "github.com/matrixorigin/matrixone/pkg/container/vector" 31 "github.com/matrixorigin/matrixone/pkg/frontend" 32 "github.com/matrixorigin/matrixone/pkg/logutil" 33 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 34 "github.com/matrixorigin/matrixone/pkg/predefine" 35 "github.com/matrixorigin/matrixone/pkg/txn/client" 36 "github.com/matrixorigin/matrixone/pkg/txn/clock" 37 "github.com/matrixorigin/matrixone/pkg/txn/trace" 38 "github.com/matrixorigin/matrixone/pkg/util/executor" 39 "github.com/matrixorigin/matrixone/pkg/util/metric/mometric" 40 "github.com/matrixorigin/matrixone/pkg/util/sysview" 41 "github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace" 42 ) 43 44 var ( 45 _ Service = (*service)(nil) 46 ) 47 48 var ( 49 bootstrapKey = "_mo_bootstrap" 50 ) 51 52 var ( 53 bootstrappedCheckerDB = catalog.MOTaskDB 54 // Note: The following tables belong to data dictionary table, and system tables's creation will depend on 55 // the following system tables. Therefore, when creating tenants, they must be created first 56 step1InitSQLs = []string{ 57 frontend.MoCatalogMoIndexesDDL, 58 frontend.MoCatalogMoTablePartitionsDDL, 59 frontend.MoCatalogMoAutoIncrTableDDL, 60 frontend.MoCatalogMoForeignKeysDDL, 61 } 62 63 step2InitSQLs = []string{ 64 fmt.Sprintf(`create database %s`, catalog.MOTaskDB), 65 frontend.MoTaskSysAsyncTaskDDL, 66 frontend.MoTaskSysCronTaskDDL, 67 frontend.MoTaskSysDaemonTaskDDL, 68 fmt.Sprintf(`create index idx_task_status on %s.sys_async_task(task_status)`, 69 catalog.MOTaskDB), 70 71 fmt.Sprintf(`create index idx_task_runner on %s.sys_async_task(task_runner)`, 72 catalog.MOTaskDB), 73 74 fmt.Sprintf(`create index idx_task_executor on %s.sys_async_task(task_metadata_executor)`, 75 catalog.MOTaskDB), 76 77 fmt.Sprintf(`create index idx_task_epoch on %s.sys_async_task(task_epoch)`, 78 catalog.MOTaskDB), 79 80 fmt.Sprintf(`create index idx_account_id on %s.sys_daemon_task(account_id)`, 81 catalog.MOTaskDB), 82 83 fmt.Sprintf(`create index idx_last_heartbeat on %s.sys_daemon_task(last_heartbeat)`, 84 catalog.MOTaskDB), 85 } 86 87 step3InitSQLs = []string{ 88 frontend.MoCatalogMoVersionDDL, 89 frontend.MoCatalogMoUpgradeDDL, 90 frontend.MoCatalogMoUpgradeTenantDDL, 91 } 92 93 initMoVersionFormat = `insert into %s.%s values ('%s', %d, %d, current_timestamp(), current_timestamp())` 94 95 initSQLs []string 96 ) 97 98 func init() { 99 initSQLs = append(initSQLs, step1InitSQLs...) 100 initSQLs = append(initSQLs, step2InitSQLs...) 101 initSQLs = append(initSQLs, step3InitSQLs...) 102 103 // generate system cron tasks sql 104 sql, err := predefine.GenInitCronTaskSQL() 105 if err != nil { 106 panic(err) 107 } 108 initSQLs = append(initSQLs, sql) 109 110 initSQLs = append(initSQLs, trace.InitSQLs...) 111 } 112 113 type service struct { 114 lock Locker 115 clock clock.Clock 116 client client.TxnClient 117 exec executor.SQLExecutor 118 stopper *stopper.Stopper 119 handles []VersionHandle 120 121 mu struct { 122 sync.RWMutex 123 tenants map[int32]bool 124 } 125 126 upgrade struct { 127 upgradeTenantBatch int 128 checkUpgradeDuration time.Duration 129 checkUpgradeTenantDuration time.Duration 130 upgradeTenantTasks int 131 finalVersionCompleted atomic.Bool 132 } 133 } 134 135 // NewService create service to bootstrap mo database 136 func NewService( 137 lock Locker, 138 clock clock.Clock, 139 client client.TxnClient, 140 exec executor.SQLExecutor, 141 opts ...Option) Service { 142 s := &service{ 143 clock: clock, 144 exec: exec, 145 lock: lock, 146 client: client, 147 stopper: stopper.NewStopper("upgrade", stopper.WithLogger(getLogger().RawLogger())), 148 } 149 s.mu.tenants = make(map[int32]bool) 150 s.initUpgrade() 151 152 for _, opt := range opts { 153 opt(s) 154 } 155 return s 156 } 157 158 func (s *service) Bootstrap(ctx context.Context) error { 159 getLogger().Info("start to check bootstrap state") 160 161 if ok, err := s.checkAlreadyBootstrapped(ctx); ok { 162 getLogger().Info("mo already bootstrapped") 163 return nil 164 } else if err != nil { 165 return err 166 } 167 168 ok, err := s.lock.Get(ctx, bootstrapKey) 169 if err != nil { 170 return err 171 } 172 173 // current node get the bootstrap privilege 174 if ok { 175 // the auto-increment service has already been initialized at current time 176 return s.execBootstrap(ctx) 177 } 178 179 // otherwise, wait bootstrap completed 180 for { 181 select { 182 case <-ctx.Done(): 183 return ctx.Err() 184 case <-time.After(time.Second): 185 } 186 if ok, err := s.checkAlreadyBootstrapped(ctx); ok || err != nil { 187 getLogger().Info("waiting bootstrap completed", 188 zap.Bool("result", ok), 189 zap.Error(err)) 190 return err 191 } 192 } 193 } 194 195 func (s *service) checkAlreadyBootstrapped(ctx context.Context) (bool, error) { 196 res, err := s.exec.Exec(ctx, "show databases", executor.Options{}.WithMinCommittedTS(s.now())) 197 if err != nil { 198 return false, err 199 } 200 defer res.Close() 201 202 var dbs []string 203 res.ReadRows(func(_ int, cols []*vector.Vector) bool { 204 dbs = append(dbs, executor.GetStringRows(cols[0])...) 205 return true 206 }) 207 for _, db := range dbs { 208 if strings.EqualFold(db, bootstrappedCheckerDB) { 209 return true, nil 210 } 211 } 212 return false, nil 213 } 214 215 func (s *service) execBootstrap(ctx context.Context) error { 216 opts := executor.Options{}. 217 WithMinCommittedTS(s.now()). 218 WithDisableTrace(). 219 WithWaitCommittedLogApplied(). 220 WithTimeZone(time.Local). 221 WithAccountID(catalog.System_Account) 222 223 err := s.exec.ExecTxn(ctx, func(txn executor.TxnExecutor) error { 224 if err := initPreprocessSQL(ctx, txn, s.GetFinalVersion(), s.GetFinalVersionOffset()); err != nil { 225 return err 226 } 227 if err := frontend.InitSysTenant(ctx, txn, s.GetFinalVersion()); err != nil { 228 return err 229 } 230 if err := sysview.InitSchema(ctx, txn); err != nil { 231 return err 232 } 233 if err := mometric.InitSchema(ctx, txn); err != nil { 234 return err 235 } 236 if err := motrace.InitSchemaWithTxn(ctx, txn); err != nil { 237 return err 238 } 239 return nil 240 }, opts) 241 242 if err != nil { 243 getLogger().Error("bootstrap system init failed", zap.Error(err)) 244 return err 245 } 246 getLogger().Info("bootstrap system init completed") 247 248 if s.client != nil { 249 getLogger().Info("wait bootstrap logtail applied") 250 251 // if we bootstrapped, in current cn, we must wait logtails to be applied. All subsequence operations need to see the 252 // bootstrap data. 253 s.client.SyncLatestCommitTS(s.now()) 254 } 255 256 getLogger().Info("successfully completed bootstrap") 257 return nil 258 } 259 260 func (s *service) now() timestamp.Timestamp { 261 n, _ := s.clock.Now() 262 return n 263 } 264 265 func (s *service) Close() error { 266 s.stopper.Stop() 267 return nil 268 } 269 270 // initPreprocessSQL Execute preprocessed SQL, which typically must be completed before system tenant initialization 271 func initPreprocessSQL(ctx context.Context, txn executor.TxnExecutor, finalVersion string, finalVersonOffset int32) error { 272 var timeCost time.Duration 273 defer func() { 274 logutil.Debugf("Initialize system pre SQL: create cost %d ms", timeCost.Milliseconds()) 275 }() 276 277 begin := time.Now() 278 var initMoVersion string 279 for _, sql := range initSQLs { 280 if _, err := txn.Exec(sql, executor.StatementOption{}); err != nil { 281 return err 282 } 283 } 284 285 initMoVersion = fmt.Sprintf(initMoVersionFormat, catalog.MO_CATALOG, catalog.MOVersionTable, finalVersion, finalVersonOffset, versions.StateReady) 286 if _, err := txn.Exec(initMoVersion, executor.StatementOption{}); err != nil { 287 return err 288 } 289 290 timeCost = time.Since(begin) 291 return nil 292 }