github.com/janelia-flyem/dvid@v1.0.0/datatype/labelvol/sync.go (about) 1 package labelvol 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "sync" 7 8 "github.com/janelia-flyem/dvid/datastore" 9 "github.com/janelia-flyem/dvid/datatype/common/labels" 10 "github.com/janelia-flyem/dvid/datatype/imageblk" 11 "github.com/janelia-flyem/dvid/datatype/labelblk" 12 "github.com/janelia-flyem/dvid/dvid" 13 "github.com/janelia-flyem/dvid/storage" 14 ) 15 16 // TODO 17 // var ( 18 // // These are the labels that are in the process of modification from merge, split, or other sync events. 19 // dirtyBlocks dvid.DirtyBlocks 20 // ) 21 22 // Number of change messages we can buffer before blocking on sync channel. 23 const syncBufferSize = 100 24 25 // InitDataHandlers launches goroutines to handle each labelvol instance's syncs. 26 func (d *Data) InitDataHandlers() error { 27 if d.syncCh != nil || d.syncDone != nil { 28 return nil 29 } 30 d.syncCh = make(chan datastore.SyncMessage, syncBufferSize) 31 d.syncDone = make(chan *sync.WaitGroup) 32 33 go d.handleBlockEvent() 34 return nil 35 } 36 37 // Shutdown terminates blocks until syncs are done then terminates background goroutines processing data. 38 func (d *Data) Shutdown(wg *sync.WaitGroup) { 39 if d.syncDone != nil { 40 dwg := new(sync.WaitGroup) 41 dwg.Add(1) 42 d.syncDone <- dwg 43 dwg.Wait() // Block until we are done. 44 } 45 wg.Done() 46 } 47 48 // GetSyncSubs implements the datastore.Syncer interface 49 func (d *Data) GetSyncSubs(synced dvid.Data) (datastore.SyncSubs, error) { 50 if d.syncCh == nil { 51 if err := d.InitDataHandlers(); err != nil { 52 return nil, fmt.Errorf("unable to initialize handlers for data %q: %v\n", d.DataName(), err) 53 } 54 } 55 56 var evts []string 57 switch synced.TypeName() { 58 case "labelblk": 59 source, err := labelblk.GetByDataUUID(synced.DataUUID()) 60 if err != nil { 61 return nil, fmt.Errorf("labelvol %q can't sync with labelblk %q: %v", d.DataName(), synced.DataName(), err) 62 } 63 syncedBlockSize, ok := source.BlockSize().(dvid.Point3d) 64 if !ok { 65 return nil, fmt.Errorf("synced labelblk %q block size is not 3d: %s", synced.DataName(), source.BlockSize()) 66 } 67 if !syncedBlockSize.Equals(d.BlockSize) { 68 return nil, fmt.Errorf("labelvol %q block size %s does not equal labelblk %q block size %s", d.DataName(), d.BlockSize, synced.DataName(), syncedBlockSize) 69 } 70 evts = []string{ 71 labels.IngestBlockEvent, labels.MutateBlockEvent, labels.DeleteBlockEvent, 72 } 73 default: 74 return nil, fmt.Errorf("Unable to sync %s with %s since datatype %q is not supported.", d.DataName(), synced.DataName(), synced.TypeName()) 75 } 76 77 subs := make(datastore.SyncSubs, len(evts)) 78 for i, evt := range evts { 79 subs[i] = datastore.SyncSub{ 80 Event: datastore.SyncEvent{synced.DataUUID(), evt}, 81 Notify: d.DataUUID(), 82 Ch: d.syncCh, 83 } 84 } 85 return subs, nil 86 } 87 88 // Processes each change as we get it. 89 // TODO -- accumulate larger # of changes before committing to prevent 90 // excessive compaction time? This assumes LSM storage engine, which 91 // might not always hold in future, so stick with incremental update 92 // until proven to be a bottleneck. 93 func (d *Data) handleBlockEvent() { 94 store, err := datastore.GetOrderedKeyValueDB(d) 95 if err != nil { 96 dvid.Errorf("Data type labelvol had error initializing store: %v\n", err) 97 return 98 } 99 batcher, ok := store.(storage.KeyValueBatcher) 100 if !ok { 101 dvid.Errorf("Data type labelvol requires batch-enabled store, which %q is not\n", store) 102 return 103 } 104 var stop bool 105 var wg *sync.WaitGroup 106 for { 107 select { 108 case wg = <-d.syncDone: 109 queued := len(d.syncCh) 110 if queued > 0 { 111 dvid.Infof("Received shutdown signal for %q sync events (%d in queue)\n", d.DataName(), queued) 112 stop = true 113 } else { 114 dvid.Infof("Shutting down sync event handler for instance %q...\n", d.DataName()) 115 wg.Done() 116 return 117 } 118 case msg := <-d.syncCh: 119 d.StartUpdate() 120 ctx := datastore.NewVersionedCtx(d, msg.Version) 121 switch delta := msg.Delta.(type) { 122 case imageblk.Block: 123 d.ingestBlock(ctx, delta, batcher) 124 case imageblk.MutatedBlock: 125 d.mutateBlock(ctx, delta, batcher) 126 default: 127 dvid.Criticalf("Cannot sync labelvol from block event. Got unexpected delta: %v\n", msg) 128 } 129 d.StopUpdate() 130 131 if stop && len(d.syncCh) == 0 { 132 dvid.Infof("Shutting down sync even handler for instance %q after draining sync events.\n", d.DataName()) 133 wg.Done() 134 return 135 } 136 } 137 } 138 } 139 140 // Note that this does not delete any removed labels in the block since we only get the CURRENT block 141 // and not PAST blocks. To allow mutation of label blocks, use mutateBlock(). 142 func (d *Data) ingestBlock(ctx *datastore.VersionedCtx, block imageblk.Block, batcher storage.KeyValueBatcher) { 143 // Iterate through this block of labels and create RLEs for each label. 144 blockBytes := len(block.Data) 145 if blockBytes != int(d.BlockSize.Prod())*8 { 146 dvid.Criticalf("Deserialized label block %d bytes, not uint64 size times %d block elements\n", 147 blockBytes, d.BlockSize.Prod()) 148 return 149 } 150 labelRLEs := make(map[uint64]dvid.RLEs, 10) 151 firstPt := block.Index.MinPoint(d.BlockSize) 152 lastPt := block.Index.MaxPoint(d.BlockSize) 153 154 var curStart dvid.Point3d 155 var voxelLabel, curLabel, maxLabel uint64 156 var z, y, x, curRun int32 157 start := 0 158 for z = firstPt.Value(2); z <= lastPt.Value(2); z++ { 159 for y = firstPt.Value(1); y <= lastPt.Value(1); y++ { 160 for x = firstPt.Value(0); x <= lastPt.Value(0); x++ { 161 voxelLabel = binary.LittleEndian.Uint64(block.Data[start : start+8]) 162 if maxLabel < voxelLabel { 163 maxLabel = voxelLabel 164 } 165 start += 8 166 167 // If we hit background or have switched label, save old run and start new one. 168 if voxelLabel == 0 || voxelLabel != curLabel { 169 // Save old run 170 if curRun > 0 { 171 labelRLEs[curLabel] = append(labelRLEs[curLabel], dvid.NewRLE(curStart, curRun)) 172 } 173 // Start new one if not zero label. 174 if voxelLabel != 0 { 175 curStart = dvid.Point3d{x, y, z} 176 curRun = 1 177 } else { 178 curRun = 0 179 } 180 curLabel = voxelLabel 181 } else { 182 curRun++ 183 } 184 } 185 // Force break of any runs when we finish x scan. 186 if curRun > 0 { 187 labelRLEs[curLabel] = append(labelRLEs[curLabel], dvid.NewRLE(curStart, curRun)) 188 curLabel = 0 189 curRun = 0 190 } 191 } 192 } 193 194 // Store the RLEs for each label in this block. 195 if maxLabel > 0 { 196 batch := batcher.NewBatch(ctx) 197 blockStr := block.Index.ToIZYXString() 198 for label, rles := range labelRLEs { 199 tk := NewTKey(label, blockStr) 200 rleBytes, err := rles.MarshalBinary() 201 if err != nil { 202 dvid.Errorf("Bad encoding labelvol keys for label %d: %v\n", label, err) 203 return 204 } 205 batch.Put(tk, rleBytes) 206 } 207 // compare-and-set MaxLabel and batch commit 208 d.casMaxLabel(batch, ctx.VersionID(), maxLabel) 209 } 210 } 211 212 func (d *Data) mutateBlock(ctx *datastore.VersionedCtx, block imageblk.MutatedBlock, batcher storage.KeyValueBatcher) { 213 // Iterate through previous and current labels, detecting set of previous labels and RLEs for current labels. 214 blockBytes := len(block.Data) 215 if blockBytes != int(d.BlockSize.Prod())*8 { 216 dvid.Criticalf("Deserialized label block %d bytes, not uint64 size times %d block elements\n", 217 blockBytes, d.BlockSize.Prod()) 218 return 219 } 220 labelRLEs := make(map[uint64]dvid.RLEs, 10) 221 labelDiff := make(map[uint64]bool, 10) 222 223 firstPt := block.Index.MinPoint(d.BlockSize) 224 lastPt := block.Index.MaxPoint(d.BlockSize) 225 226 var curStart dvid.Point3d 227 var voxelLabel, curLabel, maxLabel uint64 228 var z, y, x, curRun int32 229 start := 0 230 for z = firstPt.Value(2); z <= lastPt.Value(2); z++ { 231 for y = firstPt.Value(1); y <= lastPt.Value(1); y++ { 232 for x = firstPt.Value(0); x <= lastPt.Value(0); x++ { 233 var pastLabel uint64 234 if block.Prev == nil || len(block.Prev) == 0 { 235 pastLabel = 0 236 } else { 237 pastLabel = binary.LittleEndian.Uint64(block.Prev[start : start+8]) 238 } 239 voxelLabel = binary.LittleEndian.Uint64(block.Data[start : start+8]) 240 if maxLabel < voxelLabel { 241 maxLabel = voxelLabel 242 } 243 if pastLabel != 0 { 244 if pastLabel != voxelLabel { 245 labelDiff[pastLabel] = true 246 } else { 247 _, found := labelDiff[pastLabel] 248 if !found { 249 labelDiff[pastLabel] = false 250 } 251 } 252 } 253 start += 8 254 255 // If we hit background or have switched label, save old run and start new one. 256 if voxelLabel == 0 || voxelLabel != curLabel { 257 // Save old run 258 if curRun > 0 { 259 labelRLEs[curLabel] = append(labelRLEs[curLabel], dvid.NewRLE(curStart, curRun)) 260 } 261 // Start new one if not zero label. 262 if voxelLabel != 0 { 263 curStart = dvid.Point3d{x, y, z} 264 curRun = 1 265 } else { 266 curRun = 0 267 } 268 curLabel = voxelLabel 269 } else { 270 curRun++ 271 } 272 } 273 // Force break of any runs when we finish x scan. 274 if curRun > 0 { 275 labelRLEs[curLabel] = append(labelRLEs[curLabel], dvid.NewRLE(curStart, curRun)) 276 curLabel = 0 277 curRun = 0 278 } 279 } 280 } 281 282 // If a previous label has no change with current label RLE, then delete the label RLE since no changes 283 // are necessary. Else if previous label is not present in current label RLEs, delete labelvol. 284 var deletes []storage.TKey 285 blockStr := block.Index.ToIZYXString() 286 for label, diff := range labelDiff { 287 _, found := labelRLEs[label] 288 if diff && !found { 289 // mark previous label's RLEs for deletion 290 tk := NewTKey(label, blockStr) 291 deletes = append(deletes, tk) 292 } else if !diff && found { 293 // delete current label's RLEs because there's no difference with past RLE 294 delete(labelRLEs, label) 295 } 296 } 297 if len(deletes) > 0 { 298 batch := batcher.NewBatch(ctx) 299 for _, tk := range deletes { 300 batch.Delete(tk) 301 } 302 if err := batch.Commit(); err != nil { 303 dvid.Errorf("batch commit on deleting previous labels' labelvols: %v\n", err) 304 } 305 } 306 307 // Store the RLEs for each label in this block that are new or modified. 308 if len(labelRLEs) > 0 { 309 batch := batcher.NewBatch(ctx) 310 for label, rles := range labelRLEs { 311 tk := NewTKey(label, blockStr) 312 rleBytes, err := rles.MarshalBinary() 313 if err != nil { 314 dvid.Errorf("Bad encoding labelvol keys for label %d: %v\n", label, err) 315 return 316 } 317 batch.Put(tk, rleBytes) 318 } 319 // compare-and-set MaxLabel and batch commit 320 d.casMaxLabel(batch, ctx.VersionID(), maxLabel) 321 } 322 }