github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/tables/txnentries/flushTableTail.go (about) 1 package txnentries 2 3 // Copyright 2021 Matrix Origin 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "time" 23 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/container/vector" 26 "github.com/matrixorigin/matrixone/pkg/logutil" 27 "github.com/matrixorigin/matrixone/pkg/objectio" 28 "github.com/matrixorigin/matrixone/pkg/pb/api" 29 v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 30 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 31 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 32 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" 33 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils" 34 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle" 35 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 36 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model" 37 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks" 38 ) 39 40 type flushTableTailEntry struct { 41 txn txnif.AsyncTxn 42 43 taskID uint64 44 tableEntry *catalog.TableEntry 45 46 transMappings *api.BlkTransferBooking 47 ablksMetas []*catalog.ObjectEntry 48 delSrcMetas []*catalog.ObjectEntry 49 ablksHandles []handle.Object 50 delSrcHandles []handle.Object 51 createdBlkHandles handle.Object 52 createdDeletesFile string 53 createdMergeFile string 54 dirtyLen int 55 rt *dbutils.Runtime 56 dirtyEndTs types.TS 57 58 // use TxnMgr.Now as collectTs to do the first collect deletes, 59 // which is a relief for the second try in the commit queue 60 collectTs types.TS 61 // we have to record the ACTUAL commit time of those deletes that happened 62 // in the flushing process, before packed them into the created object. 63 delTbls []*model.TransDels 64 // some statistics 65 pageIds []*common.ID 66 transCntBeforeCommit int 67 nextRoundDirties map[*catalog.ObjectEntry]struct{} 68 } 69 70 func NewFlushTableTailEntry( 71 txn txnif.AsyncTxn, 72 taskID uint64, 73 mapping *api.BlkTransferBooking, 74 tableEntry *catalog.TableEntry, 75 ablksMetas []*catalog.ObjectEntry, 76 nblksMetas []*catalog.ObjectEntry, 77 ablksHandles []handle.Object, 78 nblksHandles []handle.Object, 79 createdBlkHandles handle.Object, 80 createdDeletesFile string, 81 createdMergeFile string, 82 dirtyLen int, 83 rt *dbutils.Runtime, 84 dirtyEndTs types.TS, 85 ) (*flushTableTailEntry, error) { 86 87 entry := &flushTableTailEntry{ 88 txn: txn, 89 taskID: taskID, 90 transMappings: mapping, 91 tableEntry: tableEntry, 92 ablksMetas: ablksMetas, 93 delSrcMetas: nblksMetas, 94 ablksHandles: ablksHandles, 95 delSrcHandles: nblksHandles, 96 createdBlkHandles: createdBlkHandles, 97 createdDeletesFile: createdDeletesFile, 98 createdMergeFile: createdMergeFile, 99 dirtyLen: dirtyLen, 100 rt: rt, 101 dirtyEndTs: dirtyEndTs, 102 } 103 104 if entry.transMappings != nil { 105 if entry.createdBlkHandles != nil { 106 entry.delTbls = make([]*model.TransDels, entry.createdBlkHandles.GetMeta().(*catalog.ObjectEntry).BlockCnt()) 107 entry.nextRoundDirties = make(map[*catalog.ObjectEntry]struct{}) 108 // collect deletes phase 1 109 entry.collectTs = rt.Now() 110 var err error 111 entry.transCntBeforeCommit, err = entry.collectDelsAndTransfer(entry.txn.GetStartTS(), entry.collectTs) 112 if err != nil { 113 return nil, err 114 } 115 } 116 // prepare transfer pages 117 entry.addTransferPages() 118 } 119 120 return entry, nil 121 } 122 123 // add transfer pages for dropped aobjects 124 func (entry *flushTableTailEntry) addTransferPages() { 125 isTransient := !entry.tableEntry.GetLastestSchemaLocked().HasPK() 126 for i, mcontainer := range entry.transMappings.Mappings { 127 m := mcontainer.M 128 if len(m) == 0 { 129 continue 130 } 131 id := entry.ablksHandles[i].Fingerprint() 132 entry.pageIds = append(entry.pageIds, id) 133 page := model.NewTransferHashPage(id, time.Now(), isTransient) 134 for srcRow, dst := range m { 135 blkid := objectio.NewBlockidWithObjectID(entry.createdBlkHandles.GetID(), uint16(dst.BlkIdx)) 136 page.Train(uint32(srcRow), *objectio.NewRowid(blkid, uint32(dst.RowIdx))) 137 } 138 entry.rt.TransferTable.AddPage(page) 139 } 140 } 141 142 // collectDelsAndTransfer collects deletes in flush process and moves them to the created obj 143 // ATTENTION !!! (from, to] !!! 144 func (entry *flushTableTailEntry) collectDelsAndTransfer(from, to types.TS) (transCnt int, err error) { 145 if len(entry.ablksHandles) == 0 { 146 return 147 } 148 // if created blk handles is nil, all rows in ablks are deleted 149 if entry.createdBlkHandles == nil { 150 return 151 } 152 for i, blk := range entry.ablksMetas { 153 // For ablock, there is only one block in it. 154 // Checking the block mapping once is enough 155 mapping := entry.transMappings.Mappings[i].M 156 if len(mapping) == 0 { 157 // empty frozen aobjects, it can not has any more deletes 158 continue 159 } 160 dataBlock := blk.GetObjectData() 161 var bat *containers.Batch 162 bat, _, err = dataBlock.CollectDeleteInRange( 163 entry.txn.GetContext(), 164 from.Next(), // NOTE HERE 165 to, 166 false, 167 common.MergeAllocator, 168 ) 169 if err != nil { 170 return 171 } 172 if bat == nil || bat.Length() == 0 { 173 continue 174 } 175 rowid := vector.MustFixedCol[types.Rowid](bat.GetVectorByName(catalog.PhyAddrColumnName).GetDownstreamVector()) 176 ts := vector.MustFixedCol[types.TS](bat.GetVectorByName(catalog.AttrCommitTs).GetDownstreamVector()) 177 178 count := len(rowid) 179 transCnt += count 180 for i := 0; i < count; i++ { 181 row := rowid[i].GetRowOffset() 182 destpos, ok := mapping[int32(row)] 183 if !ok { 184 panic(fmt.Sprintf("%s find no transfer mapping for row %d", blk.ID.String(), row)) 185 } 186 if entry.delTbls[destpos.BlkIdx] == nil { 187 entry.delTbls[destpos.BlkIdx] = model.NewTransDels(entry.txn.GetPrepareTS()) 188 } 189 entry.delTbls[destpos.BlkIdx].Mapping[int(destpos.RowIdx)] = ts[i] 190 if err = entry.createdBlkHandles.RangeDelete( 191 uint16(destpos.BlkIdx), uint32(destpos.RowIdx), uint32(destpos.RowIdx), handle.DT_MergeCompact, common.MergeAllocator, 192 ); err != nil { 193 bat.Close() 194 return 195 } 196 } 197 bat.Close() 198 entry.nextRoundDirties[blk] = struct{}{} 199 } 200 return 201 } 202 203 // PrepareCommit check deletes between start ts and commit ts 204 func (entry *flushTableTailEntry) PrepareCommit() error { 205 inst := time.Now() 206 defer func() { 207 v2.TaskCommitTableTailDurationHistogram.Observe(time.Since(inst).Seconds()) 208 }() 209 if entry.transMappings == nil { 210 // no del table, no transfer 211 return nil 212 } 213 trans, err := entry.collectDelsAndTransfer(entry.collectTs, entry.txn.GetPrepareTS()) 214 if err != nil { 215 return err 216 } 217 218 for i, delTbl := range entry.delTbls { 219 if delTbl != nil { 220 destid := objectio.NewBlockidWithObjectID(entry.createdBlkHandles.GetID(), uint16(i)) 221 entry.rt.TransferDelsMap.SetDelsForBlk(*destid, delTbl) 222 } 223 } 224 225 if aconflictCnt, totalTrans := len(entry.nextRoundDirties), trans+entry.transCntBeforeCommit; aconflictCnt > 0 || totalTrans > 0 { 226 logutil.Infof( 227 "[FlushTabletail] task %d ww (%s .. %s): on %d ablk, transfer %v rows, %d in commit queue", 228 entry.taskID, 229 entry.txn.GetStartTS().ToString(), 230 entry.txn.GetPrepareTS().ToString(), 231 aconflictCnt, 232 totalTrans, 233 trans, 234 ) 235 } 236 return nil 237 } 238 239 // PrepareRollback remove transfer page and written files 240 func (entry *flushTableTailEntry) PrepareRollback() (err error) { 241 logutil.Warnf("[FlushTabletail] FT task %d rollback", entry.taskID) 242 // remove transfer page 243 for _, id := range entry.pageIds { 244 _ = entry.rt.TransferTable.DeletePage(id) 245 } 246 247 // why not clean TranDel? 248 // 1. There's little tiny chance for a txn to fail after PrepareCommit 249 // 2. If txn failed, no txn will see the transfered deletes, 250 // so no one will consult the TransferDelsMap about the right commite time. 251 // It's ok to leave the DelsMap fade away naturally. 252 253 // remove written file 254 fs := entry.rt.Fs.Service 255 256 // object for snapshot read of aobjects 257 ablkNames := make([]string, 0, len(entry.ablksMetas)) 258 for _, blk := range entry.ablksMetas { 259 if !blk.HasPersistedData() { 260 logutil.Infof("[FlushTabletail] skip empty ablk %s when rollback", blk.ID.String()) 261 continue 262 } 263 seg := blk.ID.Segment() 264 name := objectio.BuildObjectName(seg, 0).String() 265 ablkNames = append(ablkNames, name) 266 } 267 268 // for io task, dispatch by round robin, scope can be nil 269 entry.rt.Scheduler.ScheduleScopedFn(&tasks.Context{}, tasks.IOTask, nil, func() error { 270 // TODO: variable as timeout 271 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 272 defer cancel() 273 for _, name := range ablkNames { 274 _ = fs.Delete(ctx, name) 275 } 276 if entry.createdDeletesFile != "" { 277 _ = fs.Delete(ctx, entry.createdDeletesFile) 278 } 279 if entry.createdMergeFile != "" { 280 _ = fs.Delete(ctx, entry.createdMergeFile) 281 } 282 return nil 283 }) 284 return 285 } 286 287 // ApplyCommit Gc in memory deletes and update table compact status 288 func (entry *flushTableTailEntry) ApplyCommit() (err error) { 289 for _, blk := range entry.ablksMetas { 290 _ = blk.GetObjectData().TryUpgrade() 291 blk.GetObjectData().UpgradeAllDeleteChain() 292 } 293 294 for _, blk := range entry.delSrcMetas { 295 blk.GetObjectData().UpgradeAllDeleteChain() 296 } 297 298 tbl := entry.tableEntry 299 tbl.Stats.Lock() 300 defer tbl.Stats.Unlock() 301 tbl.Stats.LastFlush = entry.dirtyEndTs 302 // no merge tasks touch the dirties, we are good to clean all 303 if entry.dirtyLen == len(tbl.DeletedDirties) { 304 tbl.DeletedDirties = tbl.DeletedDirties[:0] 305 } else { 306 // some merge tasks touch the dirties, we need to keep those new dirties 307 tbl.DeletedDirties = tbl.DeletedDirties[entry.dirtyLen:] 308 } 309 for k := range entry.nextRoundDirties { 310 tbl.DeletedDirties = append(tbl.DeletedDirties, k) 311 } 312 return 313 } 314 315 func (entry *flushTableTailEntry) ApplyRollback() (err error) { 316 return 317 } 318 319 func (entry *flushTableTailEntry) MakeCommand(csn uint32) (cmd txnif.TxnCmd, err error) { 320 return &flushTableTailCmd{}, nil 321 } 322 func (entry *flushTableTailEntry) IsAborted() bool { return false } 323 func (entry *flushTableTailEntry) Set1PC() {} 324 func (entry *flushTableTailEntry) Is1PC() bool { return false } 325 326 //////////////////////////////////////// 327 // flushTableTailCmd 328 //////////////////////////////////////// 329 330 type flushTableTailCmd struct{} 331 332 func (cmd *flushTableTailCmd) GetType() uint16 { return IOET_WALTxnCommand_Compact } 333 func (cmd *flushTableTailCmd) WriteTo(w io.Writer) (n int64, err error) { 334 typ := IOET_WALTxnCommand_FlushTableTail 335 if _, err = w.Write(types.EncodeUint16(&typ)); err != nil { 336 return 337 } 338 n = 2 339 ver := IOET_WALTxnCommand_FlushTableTail_CurrVer 340 if _, err = w.Write(types.EncodeUint16(&ver)); err != nil { 341 return 342 } 343 n = 2 344 return 345 } 346 func (cmd *flushTableTailCmd) MarshalBinary() (buf []byte, err error) { 347 var bbuf bytes.Buffer 348 if _, err = cmd.WriteTo(&bbuf); err != nil { 349 return 350 } 351 buf = bbuf.Bytes() 352 return 353 } 354 func (cmd *flushTableTailCmd) ReadFrom(r io.Reader) (n int64, err error) { return } 355 func (cmd *flushTableTailCmd) UnmarshalBinary(buf []byte) (err error) { return } 356 func (cmd *flushTableTailCmd) Desc() string { return "CmdName=CPCT" } 357 func (cmd *flushTableTailCmd) String() string { return "CmdName=CPCT" } 358 func (cmd *flushTableTailCmd) VerboseString() string { return "CmdName=CPCT" } 359 func (cmd *flushTableTailCmd) ApplyCommit() {} 360 func (cmd *flushTableTailCmd) ApplyRollback() {} 361 func (cmd *flushTableTailCmd) SetReplayTxn(txnif.AsyncTxn) {} 362 func (cmd *flushTableTailCmd) Close() {}