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  }