github.com/matrixorigin/matrixone@v1.2.0/pkg/incrservice/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 incrservice 16 17 import ( 18 "context" 19 "encoding/hex" 20 "fmt" 21 "sync" 22 "time" 23 24 "github.com/matrixorigin/matrixone/pkg/common/log" 25 "github.com/matrixorigin/matrixone/pkg/common/moerr" 26 "github.com/matrixorigin/matrixone/pkg/common/stopper" 27 "github.com/matrixorigin/matrixone/pkg/container/batch" 28 "github.com/matrixorigin/matrixone/pkg/defines" 29 "github.com/matrixorigin/matrixone/pkg/pb/txn" 30 "github.com/matrixorigin/matrixone/pkg/txn/client" 31 "go.uber.org/zap" 32 ) 33 34 var ( 35 lazyDeleteInterval = time.Second * 10 36 ) 37 38 type service struct { 39 uuid string 40 logger *log.MOLogger 41 cfg Config 42 store IncrValueStore 43 allocator valueAllocator 44 stopper *stopper.Stopper 45 46 mu struct { 47 sync.Mutex 48 closed bool 49 destroyed map[uint64]deleteCtx 50 tables map[uint64]incrTableCache 51 creates map[string][]uint64 52 deletes map[string][]deleteCtx 53 } 54 } 55 56 func NewIncrService( 57 uuid string, 58 store IncrValueStore, 59 cfg Config) AutoIncrementService { 60 logger := getLogger() 61 cfg.adjust() 62 s := &service{ 63 uuid: uuid, 64 logger: logger, 65 cfg: cfg, 66 store: store, 67 allocator: newValueAllocator(store), 68 stopper: stopper.NewStopper("incr-service", stopper.WithLogger(getLogger().RawLogger())), 69 } 70 s.mu.destroyed = make(map[uint64]deleteCtx) 71 s.mu.tables = make(map[uint64]incrTableCache, 1024) 72 s.mu.creates = make(map[string][]uint64, 1024) 73 s.mu.deletes = make(map[string][]deleteCtx, 1024) 74 if err := s.stopper.RunTask(s.destroyTables); err != nil { 75 panic(err) 76 } 77 return s 78 } 79 80 func (s *service) UUID() string { 81 return s.uuid 82 } 83 84 func (s *service) Create( 85 ctx context.Context, 86 tableID uint64, 87 cols []AutoColumn, 88 txnOp client.TxnOperator) error { 89 s.logger.Info("create auto increment table", 90 zap.Uint64("table-id", tableID), 91 zap.String("txn", txnOp.Txn().DebugString())) 92 93 txnOp.AppendEventCallback( 94 client.ClosedEvent, 95 s.txnClosed) 96 if err := s.store.Create(ctx, tableID, cols, txnOp); err != nil { 97 s.logger.Error("create auto increment cache failed", 98 zap.Uint64("table-id", tableID), 99 zap.String("txn", hex.EncodeToString(txnOp.Txn().ID)), 100 zap.Error(err)) 101 return err 102 } 103 c, err := newTableCache( 104 ctx, 105 tableID, 106 cols, 107 s.cfg, 108 s.allocator, 109 txnOp, 110 false) 111 if err != nil { 112 return err 113 } 114 115 s.mu.Lock() 116 defer s.mu.Unlock() 117 key := string(txnOp.Txn().ID) 118 s.mu.creates[key] = append(s.mu.creates[key], tableID) 119 return s.doCreateLocked( 120 tableID, 121 c, 122 txnOp.Txn().ID) 123 } 124 125 func (s *service) Reset( 126 ctx context.Context, 127 oldTableID, 128 newTableID uint64, 129 keep bool, 130 txnOp client.TxnOperator) error { 131 s.logger.Info("reset auto increment table", 132 zap.Uint64("table-id", oldTableID), 133 zap.String("txn", txnOp.Txn().DebugString()), 134 zap.Uint64("new-table-id", newTableID)) 135 136 cols, err := s.store.GetColumns(ctx, oldTableID, txnOp) 137 if err != nil { 138 return err 139 } 140 if len(cols) == 0 { 141 rows, err := s.store.SelectAll(ctx, oldTableID, txnOp) 142 if err != nil { 143 return err 144 } 145 s.logger.Info("no columns found", 146 zap.Uint64("table-id", oldTableID), 147 zap.String("txn", txnOp.Txn().DebugString()), 148 zap.String("rows", rows)) 149 } 150 151 if !keep { 152 for idx := range cols { 153 cols[idx].Offset = 0 154 } 155 } else if c := s.getTableCache(oldTableID); c != nil { 156 // reuse ids in cache 157 if err := c.adjust(ctx, cols); err != nil { 158 return err 159 } 160 } 161 162 if err := s.Delete(ctx, oldTableID, txnOp); err != nil { 163 return err 164 } 165 for idx := range cols { 166 cols[idx].TableID = newTableID 167 } 168 return s.Create(ctx, newTableID, cols, txnOp) 169 } 170 171 func (s *service) Delete( 172 ctx context.Context, 173 tableID uint64, 174 txnOp client.TxnOperator) error { 175 s.logger.Info("delete auto increment table", 176 zap.Uint64("table-id", tableID), 177 zap.String("txn", txnOp.Txn().DebugString())) 178 179 txnOp.AppendEventCallback( 180 client.ClosedEvent, 181 s.txnClosed) 182 183 s.mu.Lock() 184 defer s.mu.Unlock() 185 delCtx, err := newDeleteCtx(ctx, tableID) 186 if err != nil { 187 return err 188 } 189 key := string(txnOp.Txn().ID) 190 s.mu.deletes[key] = append(s.mu.deletes[key], delCtx) 191 if s.logger.Enabled(zap.InfoLevel) { 192 s.logger.Info("ready to delete auto increment table cache", 193 zap.Uint64("table-id", tableID), 194 zap.String("txn", hex.EncodeToString(txnOp.Txn().ID))) 195 } 196 return nil 197 } 198 199 func (s *service) InsertValues( 200 ctx context.Context, 201 tableID uint64, 202 bat *batch.Batch, 203 estimate int64, 204 ) (uint64, error) { 205 ts, err := s.getCommittedTableCache( 206 ctx, 207 tableID) 208 if err != nil { 209 return 0, err 210 } 211 return ts.insertAutoValues( 212 ctx, 213 tableID, 214 bat, 215 estimate, 216 ) 217 } 218 219 func (s *service) CurrentValue( 220 ctx context.Context, 221 tableID uint64, 222 col string) (uint64, error) { 223 ts, err := s.getCommittedTableCache( 224 ctx, 225 tableID) 226 if err != nil { 227 return 0, err 228 } 229 return ts.currentValue(ctx, tableID, col) 230 } 231 232 func (s *service) Close() { 233 s.stopper.Stop() 234 235 s.mu.Lock() 236 if s.mu.closed { 237 s.mu.Unlock() 238 return 239 } 240 s.mu.closed = true 241 for _, tc := range s.mu.tables { 242 if err := tc.close(); err != nil { 243 panic(err) 244 } 245 } 246 s.mu.Unlock() 247 248 s.allocator.close() 249 s.store.Close() 250 } 251 252 func (s *service) doCreateLocked( 253 tableID uint64, 254 c incrTableCache, 255 txnID []byte) error { 256 s.mu.tables[tableID] = c 257 if s.logger.Enabled(zap.InfoLevel) { 258 s.logger.Info("auto increment cache created", 259 zap.Uint64("table-id", tableID), 260 zap.String("txn", hex.EncodeToString(txnID))) 261 } 262 return nil 263 } 264 265 func (s *service) getCommittedTableCache( 266 ctx context.Context, 267 tableID uint64) (incrTableCache, error) { 268 s.mu.Lock() 269 defer s.mu.Unlock() 270 c, ok := s.mu.tables[tableID] 271 if ok { 272 return c, nil 273 } 274 275 if _, ok := s.mu.destroyed[tableID]; ok { 276 return nil, moerr.NewNoSuchTableNoCtx("", fmt.Sprintf("%d", tableID)) 277 } 278 279 txnOp := s.store.NewTxnOperator(ctx) 280 if txnOp != nil { 281 defer txnOp.Rollback(ctx) 282 } 283 s.logger.Info("try to get columns", zap.Uint64("tableId", tableID), zap.String("txn", txnOp.Txn().DebugString())) 284 285 cols, err := s.store.GetColumns(ctx, tableID, txnOp) 286 if err != nil { 287 return nil, err 288 } 289 if len(cols) == 0 { 290 table, err := s.store.SelectAll(ctx, tableID, txnOp) 291 if err != nil { 292 return nil, err 293 } 294 return nil, moerr.NewNoSuchTableNoCtx("", table) 295 } 296 297 c, err = newTableCache( 298 ctx, 299 tableID, 300 cols, 301 s.cfg, 302 s.allocator, 303 nil, 304 true) 305 if err != nil { 306 return nil, err 307 } 308 s.doCreateLocked(tableID, c, nil) 309 return c, nil 310 } 311 312 func (s *service) txnClosed(event client.TxnEvent) { 313 s.mu.Lock() 314 defer s.mu.Unlock() 315 316 s.handleCreatesLocked(event.Txn) 317 s.handleDeletesLocked(event.Txn) 318 } 319 320 func (s *service) handleCreatesLocked(txnMeta txn.TxnMeta) { 321 key := string(txnMeta.ID) 322 tables, ok := s.mu.creates[key] 323 if !ok { 324 return 325 } 326 327 for _, id := range tables { 328 if tc, ok := s.mu.tables[id]; ok { 329 if txnMeta.Status == txn.TxnStatus_Committed { 330 tc.commit() 331 } else { 332 _ = tc.close() 333 delete(s.mu.tables, id) 334 s.logger.Info("auto increment cache destroyed with txn aborted", 335 zap.Uint64("table-id", id), 336 zap.String("txn", hex.EncodeToString(txnMeta.ID))) 337 338 } 339 } 340 } 341 342 delete(s.mu.creates, key) 343 } 344 345 func (s *service) handleDeletesLocked(txnMeta txn.TxnMeta) { 346 key := string(txnMeta.ID) 347 tables, ok := s.mu.deletes[key] 348 if !ok { 349 return 350 } 351 352 if txnMeta.Status == txn.TxnStatus_Committed { 353 for _, ctx := range tables { 354 if tc, ok := s.mu.tables[ctx.tableID]; ok { 355 _ = tc.close() 356 delete(s.mu.tables, ctx.tableID) 357 s.mu.destroyed[ctx.tableID] = ctx 358 s.logger.Info("auto increment cache delete", 359 zap.Uint64("table-id", ctx.tableID), 360 zap.String("txn", hex.EncodeToString(txnMeta.ID))) 361 362 } 363 } 364 } 365 delete(s.mu.deletes, key) 366 } 367 368 func (s *service) getTableCache(tableID uint64) incrTableCache { 369 s.mu.Lock() 370 defer s.mu.Unlock() 371 372 return s.mu.tables[tableID] 373 } 374 375 func (s *service) destroyTables(ctx context.Context) { 376 for { 377 select { 378 case <-ctx.Done(): 379 return 380 case <-time.After(lazyDeleteInterval): 381 s.mu.Lock() 382 deletes := make([]deleteCtx, 0, len(s.mu.destroyed)) 383 for _, ctx := range s.mu.destroyed { 384 deletes = append(deletes, ctx) 385 } 386 s.mu.Unlock() 387 388 for _, dc := range deletes { 389 ctx, cancel := context.WithTimeout(defines.AttachAccountId(ctx, dc.accountID), time.Second*30) 390 if err := s.store.Delete(ctx, dc.tableID); err == nil { 391 s.mu.Lock() 392 delete(s.mu.destroyed, dc.tableID) 393 s.mu.Unlock() 394 } 395 cancel() 396 } 397 } 398 } 399 } 400 401 type deleteCtx struct { 402 accountID uint32 403 tableID uint64 404 } 405 406 func newDeleteCtx(ctx context.Context, tableID uint64) (deleteCtx, error) { 407 accountId, err := getAccountID(ctx) 408 if err != nil { 409 return deleteCtx{}, err 410 } 411 return deleteCtx{ 412 tableID: tableID, 413 accountID: accountId, 414 }, nil 415 }