github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/logtail/handle.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 logtail 16 17 /* 18 19 an application on logtail mgr: build reponse to SyncLogTailRequest 20 21 */ 22 23 import ( 24 "context" 25 "fmt" 26 "hash/fnv" 27 "strings" 28 29 pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog" 30 "github.com/matrixorigin/matrixone/pkg/common/moerr" 31 "github.com/matrixorigin/matrixone/pkg/container/batch" 32 "github.com/matrixorigin/matrixone/pkg/container/types" 33 "github.com/matrixorigin/matrixone/pkg/fileservice" 34 "github.com/matrixorigin/matrixone/pkg/logutil" 35 "github.com/matrixorigin/matrixone/pkg/pb/api" 36 "github.com/matrixorigin/matrixone/pkg/util/fault" 37 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 38 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 39 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" 40 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/dataio/blockio" 41 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks" 42 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnimpl" 43 "go.uber.org/zap" 44 ) 45 46 const Size90M = 90 * 1024 * 1024 47 48 type CheckpointClient interface { 49 CollectCheckpointsInRange(ctx context.Context, start, end types.TS) (ckpLoc string, lastEnd types.TS, err error) 50 FlushTable(dbID, tableID uint64, ts types.TS) error 51 } 52 53 func DecideTableScope(tableID uint64) Scope { 54 var scope Scope 55 switch tableID { 56 case pkgcatalog.MO_DATABASE_ID: 57 scope = ScopeDatabases 58 case pkgcatalog.MO_TABLES_ID: 59 scope = ScopeTables 60 case pkgcatalog.MO_COLUMNS_ID: 61 scope = ScopeColumns 62 default: 63 scope = ScopeUserTables 64 } 65 return scope 66 } 67 68 func HandleSyncLogTailReq( 69 ctx context.Context, 70 ckpClient CheckpointClient, 71 mgr *Manager, 72 c *catalog.Catalog, 73 req api.SyncLogTailReq, 74 canRetry bool) (resp api.SyncLogTailResp, err error) { 75 logutil.Debugf("[Logtail] begin handle %+v", req) 76 defer func() { 77 logutil.Debugf("[Logtail] end handle %d entries[%q], err %v", len(resp.Commands), resp.CkpLocation, err) 78 }() 79 start := types.BuildTS(req.CnHave.PhysicalTime, req.CnHave.LogicalTime) 80 end := types.BuildTS(req.CnWant.PhysicalTime, req.CnWant.LogicalTime) 81 did, tid := req.Table.DbId, req.Table.TbId 82 dbEntry, err := c.GetDatabaseByID(did) 83 if err != nil { 84 return 85 } 86 tableEntry, err := dbEntry.GetTableEntryByID(tid) 87 if err != nil { 88 return 89 } 90 if start.Less(tableEntry.GetCreatedAt()) { 91 start = tableEntry.GetCreatedAt() 92 } 93 94 ckpLoc, checkpointed, err := ckpClient.CollectCheckpointsInRange(ctx, start, end) 95 if err != nil { 96 return 97 } 98 99 if checkpointed.GreaterEq(end) { 100 return api.SyncLogTailResp{ 101 CkpLocation: ckpLoc, 102 }, err 103 } else if ckpLoc != "" { 104 start = checkpointed.Next() 105 } 106 107 scope := DecideTableScope(tid) 108 109 var visitor RespBuilder 110 111 if scope == ScopeUserTables { 112 visitor = NewTableLogtailRespBuilder(ckpLoc, start, end, tableEntry) 113 } else { 114 visitor = NewCatalogLogtailRespBuilder(scope, ckpLoc, start, end) 115 } 116 defer visitor.Close() 117 118 operator := mgr.GetTableOperator(start, end, c, did, tid, scope, visitor) 119 if err := operator.Run(); err != nil { 120 return api.SyncLogTailResp{}, err 121 } 122 resp, err = visitor.BuildResp() 123 124 if canRetry && scope == ScopeUserTables { // check simple conditions first 125 _, name, forceFlush := fault.TriggerFault("logtail_max_size") 126 if (forceFlush && name == tableEntry.GetSchema().Name) || resp.ProtoSize() > Size90M { 127 _ = ckpClient.FlushTable(did, tid, end) 128 // try again after flushing 129 newResp, err := HandleSyncLogTailReq(ctx, ckpClient, mgr, c, req, false) 130 logutil.Infof("[logtail] flush result: %d -> %d err: %v", resp.ProtoSize(), newResp.ProtoSize(), err) 131 return newResp, err 132 } 133 } 134 return 135 } 136 137 type RespBuilder interface { 138 catalog.Processor 139 BuildResp() (api.SyncLogTailResp, error) 140 Close() 141 } 142 143 // CatalogLogtailRespBuilder knows how to make api-entry from catalog entry 144 // impl catalog.Processor interface, driven by LogtailCollector 145 type CatalogLogtailRespBuilder struct { 146 *catalog.LoopProcessor 147 scope Scope 148 start, end types.TS 149 checkpoint string 150 insBatch *containers.Batch 151 delBatch *containers.Batch 152 } 153 154 func NewCatalogLogtailRespBuilder(scope Scope, ckp string, start, end types.TS) *CatalogLogtailRespBuilder { 155 b := &CatalogLogtailRespBuilder{ 156 LoopProcessor: new(catalog.LoopProcessor), 157 scope: scope, 158 start: start, 159 end: end, 160 checkpoint: ckp, 161 } 162 switch scope { 163 case ScopeDatabases: 164 b.insBatch = makeRespBatchFromSchema(catalog.SystemDBSchema) 165 case ScopeTables: 166 b.insBatch = makeRespBatchFromSchema(catalog.SystemTableSchema) 167 case ScopeColumns: 168 b.insBatch = makeRespBatchFromSchema(catalog.SystemColumnSchema) 169 } 170 b.delBatch = makeRespBatchFromSchema(DelSchema) 171 b.DatabaseFn = b.VisitDB 172 b.TableFn = b.VisitTbl 173 174 return b 175 } 176 177 func (b *CatalogLogtailRespBuilder) Close() { 178 if b.insBatch != nil { 179 b.insBatch.Close() 180 b.insBatch = nil 181 } 182 if b.delBatch != nil { 183 b.delBatch.Close() 184 b.delBatch = nil 185 } 186 } 187 188 func (b *CatalogLogtailRespBuilder) VisitDB(entry *catalog.DBEntry) error { 189 entry.RLock() 190 if shouldIgnoreDBInLogtail(entry.ID) { 191 entry.RUnlock() 192 return nil 193 } 194 mvccNodes := entry.ClonePreparedInRange(b.start, b.end) 195 entry.RUnlock() 196 for _, node := range mvccNodes { 197 if node.IsAborted() { 198 continue 199 } 200 dbNode := node.(*catalog.DBMVCCNode) 201 if dbNode.HasDropCommitted() { 202 // delScehma is empty, it will just fill rowid / commit ts 203 catalogEntry2Batch(b.delBatch, entry, DelSchema, txnimpl.FillDBRow, u64ToRowID(entry.GetID()), dbNode.GetEnd()) 204 } else { 205 catalogEntry2Batch(b.insBatch, entry, catalog.SystemDBSchema, txnimpl.FillDBRow, u64ToRowID(entry.GetID()), dbNode.GetEnd()) 206 } 207 } 208 return nil 209 } 210 211 func (b *CatalogLogtailRespBuilder) VisitTbl(entry *catalog.TableEntry) error { 212 entry.RLock() 213 if shouldIgnoreTblInLogtail(entry.ID) { 214 entry.RUnlock() 215 return nil 216 } 217 mvccNodes := entry.ClonePreparedInRange(b.start, b.end) 218 entry.RUnlock() 219 for _, node := range mvccNodes { 220 if node.IsAborted() { 221 continue 222 } 223 tblNode := node.(*catalog.TableMVCCNode) 224 if b.scope == ScopeColumns { 225 var dstBatch *containers.Batch 226 if !tblNode.HasDropCommitted() { 227 dstBatch = b.insBatch 228 // fill unique syscol fields if inserting 229 for _, syscol := range catalog.SystemColumnSchema.ColDefs { 230 txnimpl.FillColumnRow(entry, syscol.Name, b.insBatch.GetVectorByName(syscol.Name)) 231 } 232 } else { 233 dstBatch = b.delBatch 234 } 235 236 // fill common syscol fields for every user column 237 rowidVec := dstBatch.GetVectorByName(catalog.AttrRowID) 238 commitVec := dstBatch.GetVectorByName(catalog.AttrCommitTs) 239 tableID := entry.GetID() 240 commitTs := tblNode.GetEnd() 241 for _, usercol := range entry.GetSchema().ColDefs { 242 rowidVec.Append(bytesToRowID([]byte(fmt.Sprintf("%d-%s", tableID, usercol.Name)))) 243 commitVec.Append(commitTs) 244 } 245 } else { 246 if tblNode.HasDropCommitted() { 247 catalogEntry2Batch(b.delBatch, entry, DelSchema, txnimpl.FillTableRow, u64ToRowID(entry.GetID()), tblNode.GetEnd()) 248 } else { 249 catalogEntry2Batch(b.insBatch, entry, catalog.SystemTableSchema, txnimpl.FillTableRow, u64ToRowID(entry.GetID()), tblNode.GetEnd()) 250 } 251 } 252 } 253 return nil 254 } 255 256 func (b *CatalogLogtailRespBuilder) BuildResp() (api.SyncLogTailResp, error) { 257 entries := make([]*api.Entry, 0) 258 var tblID uint64 259 var tableName string 260 switch b.scope { 261 case ScopeDatabases: 262 tblID = pkgcatalog.MO_DATABASE_ID 263 tableName = pkgcatalog.MO_DATABASE 264 case ScopeTables: 265 tblID = pkgcatalog.MO_TABLES_ID 266 tableName = pkgcatalog.MO_TABLES 267 case ScopeColumns: 268 tblID = pkgcatalog.MO_COLUMNS_ID 269 tableName = pkgcatalog.MO_COLUMNS 270 } 271 272 if b.insBatch.Length() > 0 { 273 bat, err := containersBatchToProtoBatch(b.insBatch) 274 logutil.Debugf("[logtail] catalog insert to %d-%s, %s", tblID, tableName, 275 DebugBatchToString("catalog", b.insBatch, true, zap.DebugLevel)) 276 if err != nil { 277 return api.SyncLogTailResp{}, err 278 } 279 insEntry := &api.Entry{ 280 EntryType: api.Entry_Insert, 281 TableId: tblID, 282 TableName: tableName, 283 DatabaseId: pkgcatalog.MO_CATALOG_ID, 284 DatabaseName: pkgcatalog.MO_CATALOG, 285 Bat: bat, 286 } 287 entries = append(entries, insEntry) 288 } 289 if b.delBatch.Length() > 0 { 290 bat, err := containersBatchToProtoBatch(b.delBatch) 291 logutil.Debugf("[logtail] catalog delete from %d-%s, %s", tblID, tableName, 292 DebugBatchToString("catalog", b.delBatch, false, zap.DebugLevel)) 293 if err != nil { 294 return api.SyncLogTailResp{}, err 295 } 296 delEntry := &api.Entry{ 297 EntryType: api.Entry_Delete, 298 TableId: tblID, 299 TableName: tableName, 300 DatabaseId: pkgcatalog.MO_CATALOG_ID, 301 DatabaseName: pkgcatalog.MO_CATALOG, 302 Bat: bat, 303 } 304 entries = append(entries, delEntry) 305 } 306 return api.SyncLogTailResp{ 307 CkpLocation: b.checkpoint, 308 Commands: entries, 309 }, nil 310 } 311 312 // this is used to collect ONE ROW of db or table change 313 func catalogEntry2Batch[T *catalog.DBEntry | *catalog.TableEntry]( 314 dstBatch *containers.Batch, 315 e T, 316 schema *catalog.Schema, 317 fillDataRow func(e T, attr string, col containers.Vector, ts types.TS), 318 rowid types.Rowid, 319 commitTs types.TS, 320 ) { 321 for _, col := range schema.ColDefs { 322 fillDataRow(e, col.Name, dstBatch.GetVectorByName(col.Name), commitTs) 323 } 324 dstBatch.GetVectorByName(catalog.AttrRowID).Append(rowid) 325 dstBatch.GetVectorByName(catalog.AttrCommitTs).Append(commitTs) 326 } 327 328 func u64ToRowID(v uint64) types.Rowid { 329 var rowid types.Rowid 330 bs := types.EncodeUint64(&v) 331 copy(rowid[0:], bs) 332 return rowid 333 } 334 335 func bytesToRowID(bs []byte) types.Rowid { 336 var rowid types.Rowid 337 if size := len(bs); size <= types.RowidSize { 338 copy(rowid[:size], bs[:size]) 339 } else { 340 hasher := fnv.New128() 341 hasher.Write(bs) 342 hasher.Sum(rowid[:0]) 343 } 344 return rowid 345 } 346 347 // make batch, append necessary field like commit ts 348 func makeRespBatchFromSchema(schema *catalog.Schema) *containers.Batch { 349 bat := containers.NewBatch() 350 351 bat.AddVector(catalog.AttrRowID, containers.MakeVector(types.T_Rowid.ToType(), false)) 352 bat.AddVector(catalog.AttrCommitTs, containers.MakeVector(types.T_TS.ToType(), false)) 353 // Types() is not used, then empty schema can also be handled here 354 typs := schema.AllTypes() 355 attrs := schema.AllNames() 356 nullables := schema.AllNullables() 357 for i, attr := range attrs { 358 if attr == catalog.PhyAddrColumnName { 359 continue 360 } 361 bat.AddVector(attr, containers.MakeVector(typs[i], nullables[i])) 362 } 363 return bat 364 } 365 366 // consume containers.Batch to construct api batch 367 func containersBatchToProtoBatch(bat *containers.Batch) (*api.Batch, error) { 368 mobat := containers.CopyToMoBatch(bat) 369 return batch.BatchToProtoBatch(mobat) 370 } 371 372 type TableLogtailRespBuilder struct { 373 *catalog.LoopProcessor 374 start, end types.TS 375 did, tid uint64 376 dname, tname string 377 checkpoint string 378 blkMetaInsBatch *containers.Batch 379 blkMetaDelBatch *containers.Batch 380 dataInsBatch *containers.Batch 381 dataDelBatch *containers.Batch 382 } 383 384 func NewTableLogtailRespBuilder(ckp string, start, end types.TS, tbl *catalog.TableEntry) *TableLogtailRespBuilder { 385 b := &TableLogtailRespBuilder{ 386 LoopProcessor: new(catalog.LoopProcessor), 387 start: start, 388 end: end, 389 checkpoint: ckp, 390 } 391 b.BlockFn = b.VisitBlk 392 393 schema := tbl.GetSchema() 394 395 b.did = tbl.GetDB().GetID() 396 b.tid = tbl.ID 397 b.dname = tbl.GetDB().GetName() 398 b.tname = schema.Name 399 400 b.dataInsBatch = makeRespBatchFromSchema(schema) 401 b.dataDelBatch = makeRespBatchFromSchema(DelSchema) 402 b.blkMetaInsBatch = makeRespBatchFromSchema(BlkMetaSchema) 403 b.blkMetaDelBatch = makeRespBatchFromSchema(DelSchema) 404 return b 405 } 406 407 func (b *TableLogtailRespBuilder) Close() { 408 if b.dataInsBatch != nil { 409 b.dataInsBatch.Close() 410 b.dataInsBatch = nil 411 } 412 if b.dataDelBatch != nil { 413 b.dataDelBatch.Close() 414 b.dataDelBatch = nil 415 } 416 if b.blkMetaInsBatch != nil { 417 b.blkMetaInsBatch.Close() 418 b.blkMetaInsBatch = nil 419 } 420 if b.blkMetaDelBatch != nil { 421 b.blkMetaDelBatch.Close() 422 b.blkMetaDelBatch = nil 423 } 424 } 425 426 func (b *TableLogtailRespBuilder) visitBlkMeta(e *catalog.BlockEntry) (skipData bool) { 427 newEnd := b.end 428 e.RLock() 429 // try to find new end 430 if newest := e.GetLatestCommittedNode(); newest != nil { 431 latestPrepareTs := newest.CloneAll().(*catalog.MetadataMVCCNode).GetPrepare() 432 if latestPrepareTs.Greater(b.end) { 433 newEnd = latestPrepareTs 434 } 435 } 436 mvccNodes := e.ClonePreparedInRange(b.start, newEnd) 437 e.RUnlock() 438 439 for _, node := range mvccNodes { 440 metaNode := node.(*catalog.MetadataMVCCNode) 441 if metaNode.MetaLoc != "" && !metaNode.IsAborted() { 442 b.appendBlkMeta(e, metaNode) 443 } 444 } 445 446 if n := len(mvccNodes); n > 0 { 447 newest := mvccNodes[n-1].(*catalog.MetadataMVCCNode) 448 if e.IsAppendable() { 449 if newest.MetaLoc != "" { 450 // appendable block has been flushed, no need to collect data 451 return true 452 } 453 } else { 454 if newest.DeltaLoc != "" && newest.GetEnd().GreaterEq(b.end) { 455 // non-appendable block has newer delta data on s3, no need to collect data 456 return true 457 } 458 } 459 } 460 return false 461 } 462 463 func (b *TableLogtailRespBuilder) appendBlkMeta(e *catalog.BlockEntry, metaNode *catalog.MetadataMVCCNode) { 464 logutil.Infof("[Logtail] record block meta row %s, %v, %s, %s, %s, %s", 465 e.AsCommonID().String(), e.IsAppendable(), 466 metaNode.CreatedAt.ToString(), metaNode.DeletedAt.ToString(), metaNode.MetaLoc, metaNode.DeltaLoc) 467 is_sorted := false 468 if !e.IsAppendable() && e.GetSchema().HasSortKey() { 469 is_sorted = true 470 } 471 insBatch := b.blkMetaInsBatch 472 insBatch.GetVectorByName(pkgcatalog.BlockMeta_ID).Append(e.ID) 473 insBatch.GetVectorByName(pkgcatalog.BlockMeta_EntryState).Append(e.IsAppendable()) 474 insBatch.GetVectorByName(pkgcatalog.BlockMeta_Sorted).Append(is_sorted) 475 insBatch.GetVectorByName(pkgcatalog.BlockMeta_MetaLoc).Append([]byte(metaNode.MetaLoc)) 476 insBatch.GetVectorByName(pkgcatalog.BlockMeta_DeltaLoc).Append([]byte(metaNode.DeltaLoc)) 477 insBatch.GetVectorByName(pkgcatalog.BlockMeta_CommitTs).Append(metaNode.GetEnd()) 478 insBatch.GetVectorByName(pkgcatalog.BlockMeta_SegmentID).Append(e.GetSegment().ID) 479 insBatch.GetVectorByName(catalog.AttrCommitTs).Append(metaNode.CreatedAt) 480 insBatch.GetVectorByName(catalog.AttrRowID).Append(u64ToRowID(e.ID)) 481 482 if metaNode.HasDropCommitted() { 483 if metaNode.DeletedAt.IsEmpty() { 484 panic(moerr.NewInternalErrorNoCtx("no delete at time in a dropped entry")) 485 } 486 delBatch := b.blkMetaDelBatch 487 delBatch.GetVectorByName(catalog.AttrCommitTs).Append(metaNode.DeletedAt) 488 delBatch.GetVectorByName(catalog.AttrRowID).Append(u64ToRowID(e.ID)) 489 } 490 } 491 492 func (b *TableLogtailRespBuilder) visitBlkData(e *catalog.BlockEntry) (err error) { 493 block := e.GetBlockData() 494 insBatch, err := block.CollectAppendInRange(b.start, b.end, false) 495 if err != nil { 496 return 497 } 498 if insBatch != nil && insBatch.Length() > 0 { 499 b.dataInsBatch.Extend(insBatch) 500 // insBatch is freed, don't use anymore 501 } 502 delBatch, err := block.CollectDeleteInRange(b.start, b.end, false) 503 if err != nil { 504 return 505 } 506 if delBatch != nil && delBatch.Length() > 0 { 507 b.dataDelBatch.Extend(delBatch) 508 // delBatch is freed, don't use anymore 509 } 510 return nil 511 } 512 513 func (b *TableLogtailRespBuilder) VisitBlk(entry *catalog.BlockEntry) error { 514 if b.visitBlkMeta(entry) { 515 // data has been flushed, no need to collect data 516 return nil 517 } 518 return b.visitBlkData(entry) 519 } 520 521 func (b *TableLogtailRespBuilder) BuildResp() (api.SyncLogTailResp, error) { 522 entries := make([]*api.Entry, 0) 523 tryAppendEntry := func(typ api.Entry_EntryType, metaChange bool, batch *containers.Batch) error { 524 if batch.Length() == 0 { 525 return nil 526 } 527 bat, err := containersBatchToProtoBatch(batch) 528 if err != nil { 529 return err 530 } 531 532 tableName := b.tname 533 if metaChange { 534 tableName = fmt.Sprintf("_%d_meta", b.tid) 535 logutil.Infof("[Logtail] send block meta for %q", b.tname) 536 } 537 if metaChange { 538 logutil.Infof("[logtail] table meta [%v] %d-%s: %s", typ, b.tid, b.tname, 539 DebugBatchToString("meta", batch, true, zap.InfoLevel)) 540 } else { 541 logutil.Infof("[logtail] table data [%v] %d-%s: %s", typ, b.tid, b.tname, 542 DebugBatchToString("data", batch, false, zap.InfoLevel)) 543 } 544 545 entry := &api.Entry{ 546 EntryType: typ, 547 TableId: b.tid, 548 TableName: tableName, 549 DatabaseId: b.did, 550 DatabaseName: b.dname, 551 Bat: bat, 552 } 553 entries = append(entries, entry) 554 return nil 555 } 556 557 empty := api.SyncLogTailResp{} 558 if err := tryAppendEntry(api.Entry_Insert, true, b.blkMetaInsBatch); err != nil { 559 return empty, err 560 } 561 if err := tryAppendEntry(api.Entry_Delete, true, b.blkMetaDelBatch); err != nil { 562 return empty, err 563 } 564 if err := tryAppendEntry(api.Entry_Insert, false, b.dataInsBatch); err != nil { 565 return empty, err 566 } 567 if err := tryAppendEntry(api.Entry_Delete, false, b.dataDelBatch); err != nil { 568 return empty, err 569 } 570 571 return api.SyncLogTailResp{ 572 CkpLocation: b.checkpoint, 573 Commands: entries, 574 }, nil 575 } 576 577 func LoadCheckpointEntries( 578 ctx context.Context, 579 metLoc string, 580 tableID uint64, 581 tableName string, 582 dbID uint64, 583 dbName string, 584 fs fileservice.FileService) (entries []*api.Entry, err error) { 585 if metLoc == "" { 586 return 587 } 588 589 locations := strings.Split(metLoc, ";") 590 datas := make([]*CheckpointData, len(locations)) 591 jobs := make([]*tasks.Job, len(locations)) 592 defer func() { 593 for idx, data := range datas { 594 if jobs[idx] != nil { 595 jobs[idx].WaitDone() 596 } 597 if data != nil { 598 data.Close() 599 } 600 } 601 }() 602 603 // TODO: using a global job scheduler 604 jobScheduler := tasks.NewParallelJobScheduler(200) 605 defer jobScheduler.Stop() 606 607 makeJob := func(i int) (job *tasks.Job) { 608 location := locations[i] 609 exec := func(ctx context.Context) (result *tasks.JobResult) { 610 result = &tasks.JobResult{} 611 reader, err := blockio.NewCheckpointReader(ctx, fs, location) 612 if err != nil { 613 result.Err = err 614 return 615 } 616 data := NewCheckpointData() 617 if err = data.ReadFrom(reader, nil, common.DefaultAllocator); err != nil { 618 result.Err = err 619 return 620 } 621 datas[i] = data 622 return 623 } 624 job = tasks.NewJob( 625 fmt.Sprintf("load-%s", location), 626 context.Background(), 627 exec) 628 return 629 } 630 631 for i := range locations { 632 jobs[i] = makeJob(i) 633 if err = jobScheduler.Schedule(jobs[i]); err != nil { 634 return 635 } 636 } 637 638 for _, job := range jobs { 639 result := job.WaitDone() 640 if err = result.Err; err != nil { 641 return 642 } 643 } 644 645 entries = make([]*api.Entry, 0) 646 for i := range locations { 647 data := datas[i] 648 ins, del, cnIns, err := data.GetTableData(tableID) 649 if err != nil { 650 return nil, err 651 } 652 if tableName != pkgcatalog.MO_DATABASE && 653 tableName != pkgcatalog.MO_COLUMNS && 654 tableName != pkgcatalog.MO_TABLES { 655 tableName = fmt.Sprintf("_%d_meta", tableID) 656 } 657 if ins != nil { 658 entry := &api.Entry{ 659 EntryType: api.Entry_Insert, 660 TableId: tableID, 661 TableName: tableName, 662 DatabaseId: dbID, 663 DatabaseName: dbName, 664 Bat: ins, 665 } 666 entries = append(entries, entry) 667 } 668 if cnIns != nil { 669 entry := &api.Entry{ 670 EntryType: api.Entry_Insert, 671 TableId: tableID, 672 TableName: tableName, 673 DatabaseId: dbID, 674 DatabaseName: dbName, 675 Bat: cnIns, 676 } 677 entries = append(entries, entry) 678 } 679 if del != nil { 680 entry := &api.Entry{ 681 EntryType: api.Entry_Delete, 682 TableId: tableID, 683 TableName: tableName, 684 DatabaseId: dbID, 685 DatabaseName: dbName, 686 Bat: del, 687 } 688 entries = append(entries, entry) 689 } 690 } 691 return 692 }