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  }