github.com/janelia-flyem/dvid@v1.0.0/datatype/labelarray/sync.go (about) 1 /* 2 This file supports interactive syncing between data instances. It is different 3 from ingestion syncs that can more effectively batch changes. 4 */ 5 6 package labelarray 7 8 import ( 9 "sync" 10 "time" 11 12 "github.com/janelia-flyem/dvid/datastore" 13 "github.com/janelia-flyem/dvid/datatype/common/downres" 14 "github.com/janelia-flyem/dvid/datatype/common/labels" 15 "github.com/janelia-flyem/dvid/dvid" 16 ) 17 18 const ( 19 numMutateHandlers = 16 // goroutines used to process mutations on blocks 20 numLabelHandlers = 256 // goroutines used to do get/put tx on label indices 21 ) 22 23 // IngestedBlock is the unit of delta for a IngestBlockEvent. 24 type IngestedBlock struct { 25 MutID uint64 26 BCoord dvid.IZYXString 27 Data *labels.Block 28 } 29 30 // MutatedBlock tracks previous and updated block data. 31 // It is the unit of delta for a MutateBlockEvent. 32 type MutatedBlock struct { 33 MutID uint64 34 BCoord dvid.IZYXString 35 Prev *labels.Block 36 Data *labels.Block 37 } 38 39 type procMsg struct { 40 v dvid.VersionID 41 op interface{} 42 } 43 44 type downresOp struct { 45 mutID uint64 46 bcoord dvid.IZYXString // the high-res block coord corresponding to the Block 47 block *labels.Block 48 } 49 50 type mergeOp struct { 51 labels.MergeOp 52 mutID uint64 53 bcoord dvid.IZYXString 54 downresMut *downres.Mutation 55 } 56 57 type splitOp struct { 58 labels.SplitOp 59 mutID uint64 60 bcoord dvid.IZYXString 61 deleteBlkCh chan dvid.IZYXString 62 downresMut *downres.Mutation 63 } 64 65 // InitDataHandlers launches goroutines to handle each labelarray instance's syncs. 66 func (d *Data) InitDataHandlers() error { 67 if d.mutateCh[0] != nil { 68 return nil 69 } 70 71 // Start N goroutines to process mutations for each block that will be consistently 72 // assigned to one of the N goroutines. 73 for i := 0; i < numMutateHandlers; i++ { 74 d.mutateCh[i] = make(chan procMsg, 10) 75 go d.mutateBlock(d.mutateCh[i]) 76 } 77 78 dvid.Infof("Launched mutation handlers for data %q...\n", d.DataName()) 79 return nil 80 } 81 82 func (d *Data) queuedSize() int { 83 var queued int 84 for i := 0; i < numMutateHandlers; i++ { 85 queued += len(d.mutateCh[i]) 86 } 87 return queued 88 } 89 90 // Shutdown terminates blocks until syncs are done then terminates background goroutines processing data. 91 func (d *Data) Shutdown(wg *sync.WaitGroup) { 92 var elapsed int 93 for { 94 queued := d.queuedSize() 95 if queued > 0 { 96 if elapsed >= datastore.DataShutdownTime { 97 dvid.Infof("Timed out after %d seconds waiting for data %q mutations: %d still to be processed", elapsed, d.DataName(), queued) 98 break 99 } 100 dvid.Infof("After %d seconds, data %q has %d mutations in queue pending.", elapsed, d.DataName(), queued) 101 time.Sleep(1 * time.Second) 102 elapsed++ 103 } else { 104 break 105 } 106 } 107 for i := 0; i < numMutateHandlers; i++ { 108 close(d.mutateCh[i]) 109 } 110 if indexCache != nil { 111 var hitrate float64 112 if metaAttempts > 0 { 113 hitrate = (float64(metaHits) / float64(metaAttempts)) * 100.0 114 } 115 dvid.Infof("Cache for data %q: got %d meta cache hits on %d attempts (%5.2f)\n", d.DataName(), metaHits, metaAttempts, hitrate) 116 } 117 wg.Done() 118 }