github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/tables/txnentries/mergeobjects.go (about)

     1  // Copyright 2021 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 txnentries
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/container/types"
    24  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    25  	"github.com/matrixorigin/matrixone/pkg/logutil"
    26  	"github.com/matrixorigin/matrixone/pkg/objectio"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    28  	v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    31  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils"
    32  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle"
    33  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    34  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model"
    35  )
    36  
    37  type mergeObjectsEntry struct {
    38  	sync.RWMutex
    39  	txn           txnif.AsyncTxn
    40  	relation      handle.Relation
    41  	droppedObjs   []*catalog.ObjectEntry
    42  	createdObjs   []*catalog.ObjectEntry
    43  	transMappings *api.BlkTransferBooking
    44  	skipTransfer  bool
    45  
    46  	rt                   *dbutils.Runtime
    47  	pageIds              []*common.ID
    48  	delTbls              [][]*model.TransDels
    49  	collectTs            types.TS
    50  	transCntBeforeCommit int
    51  	nextRoundDirties     map[*catalog.ObjectEntry]struct{}
    52  }
    53  
    54  func NewMergeObjectsEntry(
    55  	txn txnif.AsyncTxn,
    56  	relation handle.Relation,
    57  	droppedObjs, createdObjs []*catalog.ObjectEntry,
    58  	transMappings *api.BlkTransferBooking,
    59  	rt *dbutils.Runtime,
    60  ) (*mergeObjectsEntry, error) {
    61  	totalCreatedBlkCnt := 0
    62  	for _, obj := range createdObjs {
    63  		totalCreatedBlkCnt += obj.BlockCnt()
    64  	}
    65  	entry := &mergeObjectsEntry{
    66  		txn:           txn,
    67  		relation:      relation,
    68  		createdObjs:   createdObjs,
    69  		droppedObjs:   droppedObjs,
    70  		transMappings: transMappings,
    71  		skipTransfer:  transMappings == nil,
    72  		rt:            rt,
    73  	}
    74  
    75  	if !entry.skipTransfer && totalCreatedBlkCnt > 0 {
    76  		entry.delTbls = make([][]*model.TransDels, len(createdObjs))
    77  		for i := 0; i < len(createdObjs); i++ {
    78  			entry.delTbls[i] = make([]*model.TransDels, createdObjs[i].BlockCnt())
    79  		}
    80  		entry.nextRoundDirties = make(map[*catalog.ObjectEntry]struct{})
    81  		entry.collectTs = rt.Now()
    82  		var err error
    83  		// phase 1 transfer
    84  		entry.transCntBeforeCommit, _, err = entry.collectDelsAndTransfer(entry.txn.GetStartTS(), entry.collectTs)
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		entry.prepareTransferPage()
    89  	}
    90  	return entry, nil
    91  }
    92  
    93  func (entry *mergeObjectsEntry) prepareTransferPage() {
    94  	k := 0
    95  	for _, obj := range entry.droppedObjs {
    96  		for j := 0; j < obj.BlockCnt(); j++ {
    97  			if len(entry.transMappings.Mappings[k].M) == 0 {
    98  				k++
    99  				continue
   100  			}
   101  			mapping := entry.transMappings.Mappings[k].M
   102  			if len(mapping) == 0 {
   103  				panic("cannot tranfer empty block")
   104  			}
   105  			tblEntry := obj.GetTable()
   106  			isTransient := !tblEntry.GetLastestSchema().HasPK()
   107  			id := obj.AsCommonID()
   108  			id.SetBlockOffset(uint16(j))
   109  			page := model.NewTransferHashPage(id, time.Now(), isTransient)
   110  			for srcRow, dst := range mapping {
   111  				objID := entry.createdObjs[dst.ObjIdx].ID
   112  				blkID := objectio.NewBlockidWithObjectID(&objID, uint16(dst.BlkIdx))
   113  				page.Train(uint32(srcRow), *objectio.NewRowid(blkID, uint32(dst.RowIdx)))
   114  			}
   115  			entry.pageIds = append(entry.pageIds, id)
   116  			_ = entry.rt.TransferTable.AddPage(page)
   117  			k++
   118  		}
   119  	}
   120  	if k != len(entry.transMappings.Mappings) {
   121  		panic(fmt.Sprintf("k %v, mapping %v", k, len(entry.transMappings.Mappings)))
   122  	}
   123  }
   124  
   125  func (entry *mergeObjectsEntry) PrepareRollback() (err error) {
   126  	for _, id := range entry.pageIds {
   127  		_ = entry.rt.TransferTable.DeletePage(id)
   128  	}
   129  	entry.pageIds = nil
   130  	return
   131  }
   132  
   133  func (entry *mergeObjectsEntry) ApplyRollback() (err error) {
   134  	//TODO::?
   135  	return
   136  }
   137  
   138  func (entry *mergeObjectsEntry) ApplyCommit() (err error) {
   139  	return
   140  }
   141  
   142  func (entry *mergeObjectsEntry) MakeCommand(csn uint32) (cmd txnif.TxnCmd, err error) {
   143  	droppedObjs := make([]*common.ID, 0)
   144  	for _, blk := range entry.droppedObjs {
   145  		id := blk.AsCommonID()
   146  		droppedObjs = append(droppedObjs, id)
   147  	}
   148  	createdObjs := make([]*common.ID, 0)
   149  	for _, blk := range entry.createdObjs {
   150  		id := blk.AsCommonID()
   151  		createdObjs = append(createdObjs, id)
   152  	}
   153  	cmd = newMergeBlocksCmd(
   154  		entry.relation.ID(),
   155  		droppedObjs,
   156  		createdObjs,
   157  		entry.txn,
   158  		csn)
   159  	return
   160  }
   161  
   162  func (entry *mergeObjectsEntry) Set1PC()     {}
   163  func (entry *mergeObjectsEntry) Is1PC() bool { return false }
   164  
   165  // ATTENTION !!! (from, to] !!!
   166  func (entry *mergeObjectsEntry) transferObjectDeletes(
   167  	dropped *catalog.ObjectEntry,
   168  	from, to types.TS,
   169  	blkOffsetBase int) (transCnt int, collect, transfer time.Duration, err error) {
   170  
   171  	dataBlock := dropped.GetObjectData()
   172  
   173  	inst := time.Now()
   174  	bat, _, err := dataBlock.CollectDeleteInRange(
   175  		entry.txn.GetContext(),
   176  		from.Next(),
   177  		to,
   178  		false,
   179  		common.MergeAllocator,
   180  	)
   181  	if err != nil {
   182  		return
   183  	}
   184  	collect = time.Since(inst)
   185  	if bat == nil || bat.Length() == 0 {
   186  		return
   187  	}
   188  	inst = time.Now()
   189  	defer func() { transfer = time.Since(inst) }()
   190  
   191  	entry.nextRoundDirties[dropped] = struct{}{}
   192  
   193  	rowid := vector.MustFixedCol[types.Rowid](bat.GetVectorByName(catalog.PhyAddrColumnName).GetDownstreamVector())
   194  	ts := vector.MustFixedCol[types.TS](bat.GetVectorByName(catalog.AttrCommitTs).GetDownstreamVector())
   195  
   196  	count := len(rowid)
   197  	transCnt += count
   198  	for i := 0; i < count; i++ {
   199  		row := rowid[i].GetRowOffset()
   200  		blkOffsetInObj := int(rowid[i].GetBlockOffset())
   201  		blkOffset := blkOffsetBase + blkOffsetInObj
   202  		mapping := entry.transMappings.Mappings[blkOffset].M
   203  		if len(mapping) == 0 {
   204  			// this block had been all deleted, skip
   205  			// Note: it is possible that the block is empty, but not the object
   206  			continue
   207  		}
   208  		destpos, ok := mapping[int32(row)]
   209  		if !ok {
   210  			_min, _max := int32(math.MaxInt32), int32(0)
   211  			for k := range mapping {
   212  				if k < _min {
   213  					_min = k
   214  				}
   215  				if k > _max {
   216  					_max = k
   217  				}
   218  			}
   219  			panic(fmt.Sprintf(
   220  				"%s-%d find no transfer mapping for row %d, mapping range (%d, %d)",
   221  				dropped.ID.String(), blkOffsetInObj, row, _min, _max))
   222  		}
   223  		if entry.delTbls[destpos.ObjIdx][destpos.BlkIdx] == nil {
   224  			entry.delTbls[destpos.ObjIdx][destpos.BlkIdx] = model.NewTransDels(entry.txn.GetPrepareTS())
   225  		}
   226  		entry.delTbls[destpos.ObjIdx][destpos.BlkIdx].Mapping[int(destpos.RowIdx)] = ts[i]
   227  		var targetObj handle.Object
   228  		targetObj, err = entry.relation.GetObject(&entry.createdObjs[destpos.ObjIdx].ID)
   229  		if err != nil {
   230  			return
   231  		}
   232  		if err = targetObj.RangeDelete(
   233  			uint16(destpos.BlkIdx), uint32(destpos.RowIdx), uint32(destpos.RowIdx), handle.DT_MergeCompact, common.MergeAllocator,
   234  		); err != nil {
   235  			return
   236  		}
   237  	}
   238  	return
   239  }
   240  
   241  type tempStat struct {
   242  	transObj                           int
   243  	pcost, ccost, tcost, mpt, mct, mtt time.Duration
   244  }
   245  
   246  func (s *tempStat) String() string {
   247  	return fmt.Sprintf("transObj %d, pcost %v, ccost %v, tcost %v, mpt %v, mct %v, mtt %v",
   248  		s.transObj, s.pcost, s.ccost, s.tcost, s.mpt, s.mct, s.mtt)
   249  }
   250  
   251  // ATTENTION !!! (from, to] !!!
   252  func (entry *mergeObjectsEntry) collectDelsAndTransfer(from, to types.TS) (transCnt int, stat tempStat, err error) {
   253  	if len(entry.createdObjs) == 0 {
   254  		return
   255  	}
   256  
   257  	blksOffsetBase := 0
   258  	var pcost, ccost, tcost time.Duration
   259  	var mpt, mct, mtt time.Duration
   260  	transobj := 0
   261  	for _, dropped := range entry.droppedObjs {
   262  		inst := time.Now()
   263  		// handle object transfer
   264  		hasMappingInThisObj := false
   265  		blkCnt := dropped.BlockCnt()
   266  		for iblk := 0; iblk < blkCnt; iblk++ {
   267  			if len(entry.transMappings.Mappings[blksOffsetBase+iblk].M) != 0 {
   268  				hasMappingInThisObj = true
   269  				break
   270  			}
   271  		}
   272  		if !hasMappingInThisObj {
   273  			// this object had been all deleted, skip
   274  			blksOffsetBase += blkCnt
   275  			continue
   276  		}
   277  		pcost += time.Since(inst)
   278  		if pcost > mpt {
   279  			mpt = pcost
   280  		}
   281  		cnt := 0
   282  
   283  		var ct, tt time.Duration
   284  		cnt, ct, tt, err = entry.transferObjectDeletes(dropped, from, to, blksOffsetBase)
   285  		if err != nil {
   286  			return
   287  		}
   288  		if ct > mct {
   289  			mct = ct
   290  		}
   291  		if tt > mtt {
   292  			mtt = tt
   293  		}
   294  		ccost += ct
   295  		tcost += tt
   296  		transCnt += cnt
   297  		transobj++
   298  		blksOffsetBase += blkCnt
   299  	}
   300  	stat = tempStat{
   301  		transObj: transobj,
   302  		pcost:    pcost,
   303  		ccost:    ccost,
   304  		tcost:    tcost,
   305  		mpt:      mpt,
   306  		mct:      mct,
   307  		mtt:      mtt,
   308  	}
   309  	return
   310  }
   311  
   312  func (entry *mergeObjectsEntry) PrepareCommit() (err error) {
   313  	inst := time.Now()
   314  	defer func() {
   315  		v2.TaskCommitMergeObjectsDurationHistogram.Observe(time.Since(inst).Seconds())
   316  	}()
   317  	if len(entry.createdObjs) == 0 || entry.skipTransfer {
   318  		logutil.Infof("mergeblocks commit %v, [%v,%v], no transfer",
   319  			entry.relation.ID(),
   320  			entry.txn.GetStartTS().ToString(),
   321  			entry.txn.GetCommitTS().ToString())
   322  		return
   323  	}
   324  
   325  	// phase 2 transfer
   326  	transCnt, stat, err := entry.collectDelsAndTransfer(entry.collectTs, entry.txn.GetPrepareTS())
   327  	if err != nil {
   328  		return nil
   329  	}
   330  
   331  	inst1 := time.Now()
   332  	tblEntry := entry.droppedObjs[0].GetTable()
   333  	tblEntry.Stats.Lock()
   334  	for dropped := range entry.nextRoundDirties {
   335  		tblEntry.DeletedDirties = append(tblEntry.DeletedDirties, dropped)
   336  	}
   337  	tblEntry.Stats.Unlock()
   338  
   339  	for objIdx := range entry.delTbls {
   340  		for blkIdx, delTbl := range entry.delTbls[objIdx] {
   341  			if delTbl != nil {
   342  				destId := objectio.NewBlockidWithObjectID(&entry.createdObjs[objIdx].ID, uint16(blkIdx))
   343  				entry.rt.TransferDelsMap.SetDelsForBlk(*destId, delTbl)
   344  			}
   345  		}
   346  	}
   347  	rest := time.Since(inst1)
   348  	logutil.Infof("mergeblocks commit %v, [%v,%v], trans %d on %d objects, %d in commit queue",
   349  		entry.relation.ID(),
   350  		entry.txn.GetStartTS().ToString(),
   351  		entry.txn.GetCommitTS().ToString(),
   352  		entry.transCntBeforeCommit+transCnt,
   353  		len(entry.nextRoundDirties),
   354  		transCnt,
   355  	)
   356  	if total := time.Since(inst); total > 300*time.Millisecond {
   357  		logutil.Infof("mergeblocks slow commit total %v, transfer: %v, rest %v", total, stat.String(), rest)
   358  	}
   359  
   360  	return
   361  }