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 }