github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/contract/trace_scheduler.go (about) 1 package contract 2 3 import ( 4 "errors" 5 "math" 6 "sync" 7 "time" 8 9 "github.com/bytom/bytom/protocol/bc" 10 log "github.com/sirupsen/logrus" 11 "golang.org/x/sync/semaphore" 12 ) 13 14 var errInstQueueOverflow = errors.New("instance queue is overflow") 15 16 type traceScheduler struct { 17 weighted *semaphore.Weighted 18 instances *sync.Map 19 tracerService *TraceService 20 infra *Infrastructure 21 22 tracer *tracer 23 currentHeight uint64 24 currentHash bc.Hash 25 } 26 27 func newTraceScheduler(infra *Infrastructure) *traceScheduler { 28 scheduler := &traceScheduler{ 29 weighted: semaphore.NewWeighted(1000), 30 instances: new(sync.Map), 31 infra: infra, 32 } 33 return scheduler 34 } 35 36 func (t *traceScheduler) start(service *TraceService) { 37 t.tracerService = service 38 go t.processLoop() 39 } 40 41 func (t *traceScheduler) addNewJob(instance *Instance) error { 42 if !t.weighted.TryAcquire(1) { 43 return errInstQueueOverflow 44 } 45 46 t.instances.Store(instance.TraceID, instance) 47 return nil 48 } 49 50 func (t *traceScheduler) processLoop() { 51 ticker := time.NewTicker(6 * time.Second) 52 defer ticker.Stop() 53 54 for range ticker.C { 55 jobs, beginHeight, beginHash := t.prepareJobs() 56 if beginHeight > t.tracerService.BestHeight() { 57 continue 58 } 59 t.tracer = newTracer(jobs[beginHash]) 60 61 for t.currentHeight, t.currentHash = beginHeight, beginHash;; { 62 if t.currentHeight == t.tracerService.BestHeight() { 63 if err := t.finishJobs(jobs); err != nil { 64 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("finish jobs") 65 } else { 66 break 67 } 68 } 69 70 if ok, err := t.tryAttach(jobs); err != nil { 71 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("try attach on trace scheduler") 72 } else if !ok { 73 if err := t.detach(jobs); err != nil { 74 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("detach on trace scheduler") 75 } 76 } 77 } 78 } 79 } 80 81 func (t *traceScheduler) prepareJobs() (map[bc.Hash][]*Instance, uint64, bc.Hash) { 82 beginHeight, beginHash := uint64(math.MaxUint64), bc.Hash{} 83 hashToJobs := make(map[bc.Hash][]*Instance) 84 t.instances.Range(func(_, value interface{}) bool { 85 inst := value.(*Instance) 86 hashToJobs[inst.ScannedHash] = append(hashToJobs[inst.ScannedHash], inst) 87 if inst.ScannedHeight < beginHeight { 88 beginHeight = inst.ScannedHeight 89 beginHash = inst.ScannedHash 90 } 91 return true 92 }) 93 return hashToJobs, beginHeight, beginHash 94 } 95 96 func (t *traceScheduler) tryAttach(jobs map[bc.Hash][]*Instance) (bool, error) { 97 if t.currentHash == t.tracerService.BestHash() { 98 return true, nil 99 } 100 101 block, err := t.infra.Chain.GetBlockByHeight(t.currentHeight+1) 102 if err != nil { 103 return false, err 104 } 105 106 if block.PreviousBlockHash != t.currentHash { 107 return false, nil 108 } 109 110 t.tracer.applyBlock(block) 111 t.currentHeight++ 112 t.currentHash = block.Hash() 113 114 if instances, ok := jobs[block.Hash()]; ok { 115 t.tracer.addInstances(instances) 116 } 117 return true, nil 118 } 119 120 func (t *traceScheduler) detach(jobs map[bc.Hash][]*Instance) error { 121 block, err := t.infra.Chain.GetBlockByHash(&t.currentHash) 122 if err != nil { 123 return err 124 } 125 126 if instances, ok := jobs[block.Hash()]; ok { 127 for _, inst := range instances { 128 t.tracer.removeInstance(inst.TraceID) 129 } 130 } 131 132 t.tracer.detachBlock(block) 133 t.currentHeight-- 134 t.currentHash = block.PreviousBlockHash 135 return nil 136 } 137 138 func (t *traceScheduler) finishJobs(jobs map[bc.Hash][]*Instance) error { 139 inSyncInstances := t.tracer.allInstances() 140 inSyncMap := make(map[string]bool) 141 for _, inst := range inSyncInstances { 142 inSyncMap[inst.TraceID] = true 143 } 144 145 var offChainInstances []*Instance 146 for _, instances := range jobs { 147 for _, inst := range instances { 148 if _, ok := inSyncMap[inst.TraceID]; !ok { 149 inst.Status = OffChain 150 offChainInstances = append(offChainInstances, inst) 151 } 152 } 153 } 154 155 if err := t.infra.Repository.SaveInstances(offChainInstances); err != nil { 156 return err 157 } 158 159 t.releaseInstances(offChainInstances) 160 161 if len(inSyncInstances) != 0 { 162 if ok := t.tracerService.takeOverInstances(inSyncInstances, t.currentHash); ok { 163 t.releaseInstances(inSyncInstances) 164 } 165 } 166 return nil 167 } 168 169 func (t *traceScheduler) releaseInstances(instances []*Instance) { 170 t.weighted.Release(int64(len(instances))) 171 for _, inst := range instances { 172 t.instances.Delete(inst.TraceID) 173 } 174 }