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  }