github.com/matrixorigin/matrixone@v1.2.0/pkg/incrservice/store_sql.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 incrservice
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    24  	"github.com/matrixorigin/matrixone/pkg/defines"
    25  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    26  	"github.com/matrixorigin/matrixone/pkg/txn/trace"
    27  	"github.com/matrixorigin/matrixone/pkg/util/executor"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  var (
    32  	database      = "mo_catalog"
    33  	incrTableName = "mo_increment_columns"
    34  )
    35  
    36  func (c AutoColumn) getInsertSQL() string {
    37  	return fmt.Sprintf(`insert into %s(table_id, col_name, col_index, offset, step) 
    38  		values(%d, '%s', %d, %d, %d)`,
    39  		incrTableName,
    40  		c.TableID,
    41  		c.ColName,
    42  		c.ColIndex,
    43  		c.Offset,
    44  		c.Step)
    45  }
    46  
    47  type sqlStore struct {
    48  	exec executor.SQLExecutor
    49  }
    50  
    51  func NewSQLStore(exec executor.SQLExecutor) (IncrValueStore, error) {
    52  	return &sqlStore{exec: exec}, nil
    53  }
    54  
    55  func (s *sqlStore) NewTxnOperator(ctx context.Context) client.TxnOperator {
    56  	return s.exec.NewTxnOperator(ctx)
    57  }
    58  
    59  // only use for debug
    60  func (s *sqlStore) SelectAll(
    61  	ctx context.Context,
    62  	tableID uint64,
    63  	txnOp client.TxnOperator) (string, error) {
    64  	fetchSQL := fmt.Sprintf(`select col_name, table_id from %s`, incrTableName)
    65  	opts := executor.Options{}.
    66  		WithDatabase(database).
    67  		WithTxn(txnOp)
    68  	txnInfo := ""
    69  	if txnOp != nil {
    70  		opts = opts.WithDisableIncrStatement()
    71  		txnInfo = txnOp.Txn().DebugString()
    72  	} else {
    73  		opts = opts.WithEnableTrace()
    74  	}
    75  	res, err := s.exec.Exec(ctx, fetchSQL, opts)
    76  	if err != nil {
    77  		return "", err
    78  	}
    79  	defer res.Close()
    80  
    81  	accountId, err := defines.GetAccountId(ctx)
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  	str := fmt.Sprintf("Cannot find tableID %d in table %s, accountid %d, txn: %s", tableID, incrTableName,
    86  		accountId, txnInfo)
    87  	res.ReadRows(func(rows int, cols []*vector.Vector) bool {
    88  		for i := 0; i < rows; i++ {
    89  			str += fmt.Sprintf("\tcol_name: %s, table_id: %d\n",
    90  				executor.GetStringRows(cols[0])[i],
    91  				executor.GetFixedRows[uint64](cols[1])[i])
    92  		}
    93  		return true
    94  	})
    95  	return str, nil
    96  }
    97  
    98  func (s *sqlStore) Create(
    99  	ctx context.Context,
   100  	tableID uint64,
   101  	cols []AutoColumn,
   102  	txnOp client.TxnOperator) error {
   103  	opts := executor.Options{}.
   104  		WithDatabase(database).
   105  		WithTxn(txnOp).
   106  		WithWaitCommittedLogApplied()
   107  	if txnOp != nil {
   108  		opts = opts.WithDisableIncrStatement()
   109  	} else {
   110  		opts = opts.WithEnableTrace()
   111  	}
   112  
   113  	return s.exec.ExecTxn(
   114  		ctx,
   115  		func(te executor.TxnExecutor) error {
   116  			for _, col := range cols {
   117  				res, err := te.Exec(col.getInsertSQL(), executor.StatementOption{})
   118  				if err != nil {
   119  					return err
   120  				}
   121  				res.Close()
   122  			}
   123  			return nil
   124  		},
   125  		opts)
   126  }
   127  
   128  func (s *sqlStore) Allocate(
   129  	ctx context.Context,
   130  	tableID uint64,
   131  	colName string,
   132  	count int,
   133  	txnOp client.TxnOperator) (uint64, uint64, error) {
   134  	var current, next, step uint64
   135  	ok := false
   136  
   137  	fetchSQL := fmt.Sprintf(`select offset, step from %s where table_id = %d and col_name = '%s' for update`,
   138  		incrTableName,
   139  		tableID,
   140  		colName)
   141  	opts := executor.Options{}.
   142  		WithDatabase(database).
   143  		WithTxn(txnOp).
   144  		WithWaitCommittedLogApplied() // make sure the update is visible to the subsequence txn, wait log tail applied
   145  	if txnOp != nil {
   146  		opts = opts.WithDisableIncrStatement()
   147  	} else {
   148  		opts = opts.WithEnableTrace()
   149  	}
   150  
   151  	ctxDone := func() bool {
   152  		select {
   153  		case <-ctx.Done():
   154  			return true
   155  		default:
   156  			return false
   157  		}
   158  	}
   159  	for {
   160  		err := s.exec.ExecTxn(
   161  			ctx,
   162  			func(te executor.TxnExecutor) error {
   163  				txnOp = te.Txn()
   164  				start := time.Now()
   165  				res, err := te.Exec(fetchSQL, executor.StatementOption{})
   166  				if err != nil {
   167  					return err
   168  				}
   169  				rows := 0
   170  				res.ReadRows(func(_ int, cols []*vector.Vector) bool {
   171  					current = executor.GetFixedRows[uint64](cols[0])[0]
   172  					step = executor.GetFixedRows[uint64](cols[1])[0]
   173  					rows++
   174  					return true
   175  				})
   176  				res.Close()
   177  
   178  				if rows != 1 {
   179  					accountId, err := defines.GetAccountId(ctx)
   180  					if err != nil {
   181  						return err
   182  					}
   183  					selectAll, err := s.SelectAll(ctx, tableID, txnOp)
   184  					if err != nil {
   185  						return err
   186  					}
   187  					trace.GetService().Sync()
   188  					getLogger().Fatal("BUG: read incr record invalid",
   189  						zap.String("fetch-sql", fetchSQL),
   190  						zap.Any("account", accountId),
   191  						zap.Uint64("table", tableID),
   192  						zap.String("col", colName),
   193  						zap.Int("rows", rows),
   194  						zap.Duration("cost", time.Since(start)),
   195  						zap.String("select-all", selectAll),
   196  						zap.Bool("ctx-done", ctxDone()))
   197  				}
   198  
   199  				next = getNext(current, count, int(step))
   200  				sql := fmt.Sprintf(`update %s set offset = %d 
   201  				where table_id = %d and col_name = '%s' and offset = %d`,
   202  					incrTableName,
   203  					next,
   204  					tableID,
   205  					colName,
   206  					current)
   207  				start = time.Now()
   208  				res, err = te.Exec(sql, executor.StatementOption{})
   209  				if err != nil {
   210  					return err
   211  				}
   212  
   213  				if res.AffectedRows == 1 {
   214  					ok = true
   215  				} else {
   216  					accountId, err := defines.GetAccountId(ctx)
   217  					if err != nil {
   218  						return err
   219  					}
   220  					selectAll, err := s.SelectAll(ctx, tableID, txnOp)
   221  					if err != nil {
   222  						return err
   223  					}
   224  					trace.GetService().Sync()
   225  					getLogger().Fatal("BUG: update incr record returns invalid affected rows",
   226  						zap.String("update-sql", sql),
   227  						zap.Any("account", accountId),
   228  						zap.Uint64("table", tableID),
   229  						zap.String("col", colName),
   230  						zap.Uint64("affected-rows", res.AffectedRows),
   231  						zap.String("select-all", selectAll),
   232  						zap.Duration("cost", time.Since(start)),
   233  						zap.Bool("ctx-done", ctxDone()))
   234  				}
   235  				res.Close()
   236  				return nil
   237  			},
   238  			opts)
   239  		if err != nil {
   240  			// retry ww conflict if the txn is not pessimistic
   241  			if txnOp != nil && !txnOp.Txn().IsPessimistic() &&
   242  				moerr.IsMoErrCode(err, moerr.ErrTxnWWConflict) {
   243  				continue
   244  			}
   245  
   246  			return 0, 0, err
   247  		}
   248  		if ok {
   249  			break
   250  		}
   251  	}
   252  
   253  	from, to := getNextRange(current, next, int(step))
   254  	return from, to, nil
   255  }
   256  
   257  func (s *sqlStore) UpdateMinValue(
   258  	ctx context.Context,
   259  	tableID uint64,
   260  	col string,
   261  	minValue uint64,
   262  	txnOp client.TxnOperator) error {
   263  	opts := executor.Options{}.
   264  		WithDatabase(database).
   265  		WithTxn(txnOp)
   266  
   267  	// txnOp is nil means the auto increment metadata is already insert into catalog.MOAutoIncrTable and committed.
   268  	// So updateMinValue will use a new txn to update the min value. To avoid w-w conflict, we need to wait this
   269  	// committed log tail applied to ensure subsequence txn must get a snapshot ts which is large than this commit.
   270  	if txnOp == nil {
   271  		opts = opts.WithWaitCommittedLogApplied().
   272  			WithEnableTrace()
   273  	} else {
   274  		opts = opts.WithDisableIncrStatement()
   275  	}
   276  	res, err := s.exec.Exec(
   277  		ctx,
   278  		fmt.Sprintf("update %s set offset = %d where table_id = %d and col_name = '%s' and offset < %d",
   279  			incrTableName,
   280  			minValue,
   281  			tableID,
   282  			col,
   283  			minValue),
   284  		opts)
   285  	if err != nil {
   286  		return err
   287  	}
   288  	defer res.Close()
   289  	return nil
   290  }
   291  
   292  func (s *sqlStore) Delete(
   293  	ctx context.Context,
   294  	tableID uint64) error {
   295  	opts := executor.Options{}.
   296  		WithDatabase(database).
   297  		WithEnableTrace().
   298  		WithWaitCommittedLogApplied()
   299  	res, err := s.exec.Exec(
   300  		ctx,
   301  		fmt.Sprintf("delete from %s where table_id = %d",
   302  			incrTableName, tableID),
   303  		opts)
   304  	if err != nil {
   305  		return err
   306  	}
   307  	defer res.Close()
   308  	return nil
   309  }
   310  
   311  func (s *sqlStore) GetColumns(
   312  	ctx context.Context,
   313  	tableID uint64,
   314  	txnOp client.TxnOperator) ([]AutoColumn, error) {
   315  	fetchSQL := fmt.Sprintf(`select col_name, col_index, offset, step from %s where table_id = %d order by col_index`,
   316  		incrTableName,
   317  		tableID)
   318  	opts := executor.Options{}.
   319  		WithDatabase(database).
   320  		WithTxn(txnOp)
   321  
   322  	if txnOp != nil {
   323  		opts = opts.WithDisableIncrStatement()
   324  	} else {
   325  		opts = opts.WithEnableTrace()
   326  	}
   327  
   328  	res, err := s.exec.Exec(ctx, fetchSQL, opts)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	defer res.Close()
   333  
   334  	var colNames []string
   335  	var indexes []int32
   336  	var offsets []uint64
   337  	var steps []uint64
   338  	res.ReadRows(func(rows int, cols []*vector.Vector) bool {
   339  		colNames = append(colNames, executor.GetStringRows(cols[0])...)
   340  		indexes = append(indexes, executor.GetFixedRows[int32](cols[1])...)
   341  		offsets = append(offsets, executor.GetFixedRows[uint64](cols[2])...)
   342  		steps = append(steps, executor.GetFixedRows[uint64](cols[3])...)
   343  		return true
   344  	})
   345  
   346  	cols := make([]AutoColumn, len(colNames))
   347  	for idx, colName := range colNames {
   348  		cols[idx] = AutoColumn{
   349  			TableID:  tableID,
   350  			ColName:  colName,
   351  			ColIndex: int(indexes[idx]),
   352  			Offset:   offsets[idx],
   353  			Step:     steps[idx],
   354  		}
   355  	}
   356  	return cols, nil
   357  }
   358  
   359  func (s *sqlStore) Close() {
   360  
   361  }