github.com/janelia-flyem/dvid@v1.0.0/datatype/labelmap/mutate.go (about)

     1  /*
     2  	This file contains code that manages labelblk mutations at a low-level, using sharding
     3  	to specific goroutines depending on the block coordinate being mutated.
     4  	TODO: Move ingest/mutate/delete block ops in write.go into the same system.  Currently,
     5  	we assume that merge/split ops in a version do not overlap the raw block label mutations.
     6  */
     7  
     8  package labelmap
     9  
    10  import (
    11  	"encoding/json"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"sort"
    16  	"time"
    17  
    18  	"github.com/janelia-flyem/dvid/datastore"
    19  	"github.com/janelia-flyem/dvid/datatype/common/downres"
    20  	"github.com/janelia-flyem/dvid/datatype/common/labels"
    21  	"github.com/janelia-flyem/dvid/datatype/common/proto"
    22  	"github.com/janelia-flyem/dvid/dvid"
    23  	"github.com/janelia-flyem/dvid/server"
    24  	"github.com/janelia-flyem/dvid/storage"
    25  )
    26  
    27  type sizeChange struct {
    28  	oldSize, newSize uint64
    29  }
    30  
    31  // MergeLabels synchronously merges any number of labels throughout the various label
    32  // data structures.  It assumes that the merges aren't cascading, e.g., there is no
    33  // attempt to merge label 3 into 4 and also 4 into 5.  The caller should have flattened
    34  // the merges.
    35  // TODO: Provide some indication that subset of labels are under evolution, returning
    36  //
    37  //	an "unavailable" status or 203 for non-authoritative response.  This might not be
    38  //	feasible for clustered DVID front-ends due to coordination issues.
    39  //
    40  // # EVENTS
    41  //
    42  // labels.MergeStartEvent occurs at very start of merge and transmits labels.DeltaMergeStart struct.
    43  //
    44  // labels.MergeBlockEvent occurs for every block of a merged label and transmits labels.DeltaMerge struct.
    45  //
    46  // labels.MergeEndEvent occurs at end of merge and transmits labels.DeltaMergeEnd struct.
    47  func (d *Data) MergeLabels(v dvid.VersionID, op labels.MergeOp, info dvid.ModInfo) (mutID uint64, err error) {
    48  	if len(op.Merged) == 0 {
    49  		return 0, fmt.Errorf("merge requested without any labels to merge")
    50  	}
    51  	dvid.Debugf("Merging %s into label %d ...\n", op.Merged, op.Target)
    52  
    53  	d.StartUpdate()
    54  	defer d.StopUpdate()
    55  
    56  	timedLog := dvid.NewTimeLog()
    57  	mutID = d.NewMutationID()
    58  	op.MutID = mutID
    59  
    60  	// send kafka merge event to instance-uuid topic
    61  	lbls := make([]uint64, 0, len(op.Merged))
    62  	for label := range op.Merged {
    63  		lbls = append(lbls, label)
    64  	}
    65  
    66  	versionuuid, _ := datastore.UUIDFromVersion(v)
    67  	msginfo := map[string]interface{}{
    68  		"Action":     "merge",
    69  		"UUID":       string(versionuuid),
    70  		"MutationID": mutID,
    71  		"Target":     op.Target,
    72  		"Labels":     lbls,
    73  		"Timestamp":  time.Now().String(),
    74  	}
    75  	if info.User != "" {
    76  		msginfo["User"] = info.User
    77  	}
    78  	if info.App != "" {
    79  		msginfo["App"] = info.App
    80  	}
    81  	delta := labels.DeltaMerge{
    82  		MergeOp: op,
    83  	}
    84  
    85  	// Get all the affected blocks in the merge.
    86  	var targetIdx, mergeIdx *labels.Index
    87  	if targetIdx, err = GetLabelIndex(d, v, op.Target, false); err != nil {
    88  		err = fmt.Errorf("error accessing index of merge target label %d: %v", op.Target, err)
    89  		return
    90  	}
    91  	if targetIdx == nil {
    92  		err = fmt.Errorf("can't merge into a non-existent label %d", op.Target)
    93  		return
    94  	}
    95  	if err := d.addMutcache(v, mutID, targetIdx); err != nil {
    96  		dvid.Criticalf("unable to add merge mutid %d target index %d: %v\n", mutID, op.Target, err)
    97  	}
    98  	delta.TargetVoxels = targetIdx.NumVoxels()
    99  	if mergeIdx, err = d.getMergedIndex(v, mutID, op.Merged, dvid.Bounds{}); err != nil {
   100  		err = fmt.Errorf("can't get block indices of merge labels %s: %v", op.Merged, err)
   101  		return
   102  	}
   103  	if mergeIdx == nil {
   104  		err = fmt.Errorf("can't renumber non-existant merge bodies with labels: %s", op.Merged)
   105  		return
   106  	}
   107  	delta.MergedVoxels = mergeIdx.NumVoxels()
   108  
   109  	jsonBytes, _ := json.Marshal(msginfo)
   110  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   111  		dvid.Errorf("can't send merge op for %q to kafka: %v\n", d.DataName(), err)
   112  	}
   113  
   114  	// Signal that we are starting a merge.
   115  	evt := datastore.SyncEvent{d.DataUUID(), labels.MergeStartEvent}
   116  	msg := datastore.SyncMessage{labels.MergeStartEvent, v, labels.DeltaMergeStart{op}}
   117  	if err = datastore.NotifySubscribers(evt, msg); err != nil {
   118  		return
   119  	}
   120  
   121  	supervoxels := mergeIdx.GetSupervoxels()
   122  	if err = addMergeToMapping(d, v, mutID, op.Target, supervoxels); err != nil {
   123  		return
   124  	}
   125  
   126  	if mergeIdx != nil && len(mergeIdx.Blocks) != 0 {
   127  		err = targetIdx.Add(mergeIdx)
   128  		if err != nil {
   129  			return
   130  		}
   131  		targetIdx.LastMutId = mutID
   132  		targetIdx.LastModUser = info.User
   133  		targetIdx.LastModTime = info.Time
   134  		targetIdx.LastModApp = info.App
   135  		dvid.Infof("putting targetIdx with user %s\n", targetIdx.LastModUser)
   136  		if err = PutLabelIndex(d, v, op.Target, targetIdx); err != nil {
   137  			return
   138  		}
   139  	}
   140  	for merged := range delta.Merged {
   141  		DeleteLabelIndex(d, v, merged)
   142  	}
   143  	if err = labels.LogMerge(d, v, op); err != nil {
   144  		return
   145  	}
   146  
   147  	dvid.Infof("merge label %d: %d supervoxels, %d blocks\n", op.Target, len(mergeIdx.GetSupervoxels()), len(mergeIdx.Blocks))
   148  
   149  	delta.Blocks = targetIdx.GetBlockIndices()
   150  	evt = datastore.SyncEvent{d.DataUUID(), labels.MergeBlockEvent}
   151  	msg = datastore.SyncMessage{labels.MergeBlockEvent, v, delta}
   152  	if err = datastore.NotifySubscribers(evt, msg); err != nil {
   153  		err = fmt.Errorf("can't notify subscribers for event %v: %v\n", evt, err)
   154  		return
   155  	}
   156  
   157  	evt = datastore.SyncEvent{d.DataUUID(), labels.MergeEndEvent}
   158  	msg = datastore.SyncMessage{labels.MergeEndEvent, v, labels.DeltaMergeEnd{delta.MergeOp}}
   159  	if err := datastore.NotifySubscribers(evt, msg); err != nil {
   160  		dvid.Criticalf("can't notify subscribers for event %v: %v\n", evt, err)
   161  	}
   162  
   163  	timedLog.Infof("merge %s -> %d, data %q, resulting in %d blocks", delta.Merged, delta.Target, d.DataName(), len(delta.Blocks))
   164  
   165  	msginfo["Action"] = "merge-complete"
   166  	msginfo["Timestamp"] = time.Now().String()
   167  	jsonBytes, _ = json.Marshal(msginfo)
   168  
   169  	if err := server.LogJSONMutation(versionuuid, d.DataUUID(), jsonBytes); err != nil {
   170  		dvid.Criticalf("can't log merge to data %q, version %s: %s\n", d.DataName(), versionuuid, jsonBytes)
   171  	}
   172  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   173  		dvid.Criticalf("error on sending merge-complete op to kafka: %v\n", err)
   174  	}
   175  	return
   176  }
   177  
   178  // RenumberLabels synchronously changes a label to another label, also modifying the
   179  // mapping of constituent supervoxels appropriately.  For sync purposes, the Merge
   180  // events are used since renumbering label A->B is same as merging label A->B where
   181  // B is an empty, new label.  However, from a mapping perspective, a renumbered label
   182  // can be mapped to zero since it cannot have an underlying supervoxel, but a merged
   183  // label could have a supervoxel with the same ID.
   184  //
   185  // # EVENTS
   186  //
   187  // labels.MergeStartEvent occurs at very start of merge and transmits labels.DeltaMergeStart struct.
   188  //
   189  // labels.MergeBlockEvent occurs for every block of a merged label and transmits labels.DeltaMerge struct.
   190  //
   191  // labels.MergeEndEvent occurs at end of merge and transmits labels.DeltaMergeEnd struct.
   192  func (d *Data) RenumberLabels(v dvid.VersionID, origLabel, newLabel uint64, info dvid.ModInfo) (mutID uint64, err error) {
   193  	var isPresent bool
   194  	isPresent, err = d.labelIndexExists(v, newLabel)
   195  	if err != nil {
   196  		return
   197  	}
   198  	if isPresent {
   199  		err = fmt.Errorf("target label for renumber (%d) already exists in mapping", newLabel)
   200  		return
   201  	}
   202  
   203  	d.StartUpdate()
   204  	defer d.StopUpdate()
   205  
   206  	timedLog := dvid.NewTimeLog()
   207  	mutID = d.NewMutationID()
   208  	op := labels.MergeOp{
   209  		MutID:  mutID,
   210  		Target: newLabel,
   211  		Merged: labels.Set{origLabel: struct{}{}},
   212  	}
   213  
   214  	// send kafka merge event to instance-uuid topic
   215  	versionuuid, _ := datastore.UUIDFromVersion(v)
   216  	msginfo := map[string]interface{}{
   217  		"Action":     "renumber",
   218  		"UUID":       string(versionuuid),
   219  		"MutationID": mutID,
   220  		"NewLabel":   newLabel,
   221  		"OrigLabel":  origLabel,
   222  		"Timestamp":  time.Now().String(),
   223  	}
   224  	if info.User != "" {
   225  		msginfo["User"] = info.User
   226  	}
   227  	if info.App != "" {
   228  		msginfo["App"] = info.App
   229  	}
   230  	delta := labels.DeltaMerge{
   231  		MergeOp: op,
   232  	}
   233  
   234  	// Get all the affected blocks in the merge.
   235  	var targetIdx, mergeIdx *labels.Index
   236  	if targetIdx, err = GetLabelIndex(d, v, newLabel, false); err != nil {
   237  		err = fmt.Errorf("error accessing index of renumber target label %d: %v", newLabel, err)
   238  		return
   239  	}
   240  	if targetIdx != nil {
   241  		err = fmt.Errorf("can't renumber into an existing label %d", newLabel)
   242  		return
   243  	}
   244  	delta.TargetVoxels = 0
   245  	if mergeIdx, err = GetLabelIndex(d, v, origLabel, false); err != nil {
   246  		err = fmt.Errorf("can't get block indices of renumbered label %d: %v", origLabel, err)
   247  		return
   248  	}
   249  	if err := d.addMutcache(v, mutID, mergeIdx); err != nil {
   250  		dvid.Criticalf("unable to add mutid %d index, renumber %d -> %d: %v\n", mutID, origLabel, newLabel, err)
   251  	}
   252  	if mergeIdx == nil {
   253  		err = fmt.Errorf("can't renumber non-existant body with label %d", origLabel)
   254  		return
   255  	}
   256  	delta.MergedVoxels = mergeIdx.NumVoxels()
   257  
   258  	jsonBytes, _ := json.Marshal(msginfo)
   259  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   260  		dvid.Errorf("can't send renumber op for %q to kafka: %v\n", d.DataName(), err)
   261  	}
   262  
   263  	// Signal that we are starting a merge.
   264  	evt := datastore.SyncEvent{d.DataUUID(), labels.MergeStartEvent}
   265  	msg := datastore.SyncMessage{labels.MergeStartEvent, v, labels.DeltaMergeStart{op}}
   266  	if err = datastore.NotifySubscribers(evt, msg); err != nil {
   267  		return
   268  	}
   269  
   270  	supervoxels := mergeIdx.GetSupervoxels()
   271  	if err = addRenumberToMapping(d, v, mutID, origLabel, newLabel, supervoxels); err != nil {
   272  		return
   273  	}
   274  	if err = labels.LogRenumber(d, v, mutID, origLabel, newLabel); err != nil {
   275  		return
   276  	}
   277  
   278  	if mergeIdx != nil && len(mergeIdx.Blocks) != 0 {
   279  		targetIdx = mergeIdx
   280  		targetIdx.LastMutId = mutID
   281  		targetIdx.LastModUser = info.User
   282  		targetIdx.LastModTime = info.Time
   283  		targetIdx.LastModApp = info.App
   284  		if err = PutLabelIndex(d, v, newLabel, targetIdx); err != nil {
   285  			return
   286  		}
   287  	}
   288  	DeleteLabelIndex(d, v, origLabel)
   289  
   290  	dvid.Infof("renumber label %d: %d supervoxels, %d blocks\n", newLabel, len(mergeIdx.GetSupervoxels()), len(mergeIdx.Blocks))
   291  
   292  	delta.Blocks = targetIdx.GetBlockIndices()
   293  	evt = datastore.SyncEvent{d.DataUUID(), labels.MergeBlockEvent}
   294  	msg = datastore.SyncMessage{labels.MergeBlockEvent, v, delta}
   295  	if err = datastore.NotifySubscribers(evt, msg); err != nil {
   296  		err = fmt.Errorf("can't notify subscribers for event %v: %v\n", evt, err)
   297  		return
   298  	}
   299  
   300  	evt = datastore.SyncEvent{d.DataUUID(), labels.MergeEndEvent}
   301  	msg = datastore.SyncMessage{labels.MergeEndEvent, v, labels.DeltaMergeEnd{delta.MergeOp}}
   302  	if err := datastore.NotifySubscribers(evt, msg); err != nil {
   303  		dvid.Criticalf("can't notify subscribers for event %v: %v\n", evt, err)
   304  	}
   305  
   306  	timedLog.Infof("renumber %s -> %d, data %q, resulting in %d blocks", origLabel, newLabel, d.DataName(), len(delta.Blocks))
   307  
   308  	msginfo["Action"] = "renumber-complete"
   309  	msginfo["Timestamp"] = time.Now().String()
   310  	jsonBytes, _ = json.Marshal(msginfo)
   311  
   312  	if err := server.LogJSONMutation(versionuuid, d.DataUUID(), jsonBytes); err != nil {
   313  		dvid.Criticalf("can't log renumber to data %q, version %s: %s\n", d.DataName(), versionuuid, jsonBytes)
   314  	}
   315  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   316  		dvid.Criticalf("error on sending renumber-complete op to kafka: %v\n", err)
   317  	}
   318  	return
   319  }
   320  
   321  // CleaveLabel synchornously cleaves a label given supervoxels to be cleaved.
   322  // Requires JSON in request body using the following format:
   323  //
   324  //	[supervoxel1, supervoxel2, ...]
   325  //
   326  // Each element of the JSON array is a supervoxel to be cleaved from the label and either
   327  // given a new label or the one optionally supplied via the "cleavelabel" query string.
   328  // A cleave label can be specified via the "toLabel" parameter, which if 0 will have an
   329  // automatic label ID selected for the cleaved body.
   330  func (d *Data) CleaveLabel(v dvid.VersionID, label uint64, info dvid.ModInfo, r io.ReadCloser) (cleaveLabel, mutID uint64, err error) {
   331  	if r == nil {
   332  		err = fmt.Errorf("no cleave supervoxels JSON was POSTed")
   333  		return
   334  	}
   335  
   336  	cleaveLabel, err = d.newLabel(v)
   337  	if err != nil {
   338  		return
   339  	}
   340  	dvid.Debugf("Cleaving subset of label %d into new label %d.\n", label, cleaveLabel)
   341  
   342  	var data []byte
   343  	data, err = ioutil.ReadAll(r)
   344  	if err != nil {
   345  		err = fmt.Errorf("bad POSTed data for merge; should be JSON parsable: %v", err)
   346  		return
   347  	}
   348  	if len(data) == 0 {
   349  		err = fmt.Errorf("no cleave supervoxels JSON was POSTed")
   350  		return
   351  	}
   352  	var cleaveSupervoxels []uint64
   353  	if err = json.Unmarshal(data, &cleaveSupervoxels); err != nil {
   354  		err = fmt.Errorf("bad cleave supervoxels JSON: %v", err)
   355  		return
   356  	}
   357  
   358  	// send kafka cleave event to instance-uuid topic
   359  	mutID = d.NewMutationID()
   360  	versionuuid, _ := datastore.UUIDFromVersion(v)
   361  	msginfo := map[string]interface{}{
   362  		"Action":             "cleave",
   363  		"OrigLabel":          label,
   364  		"CleavedLabel":       cleaveLabel,
   365  		"CleavedSupervoxels": cleaveSupervoxels,
   366  		"MutationID":         mutID,
   367  		"UUID":               string(versionuuid),
   368  		"Timestamp":          time.Now().String(),
   369  	}
   370  	if info.User != "" {
   371  		msginfo["User"] = info.User
   372  	}
   373  	if info.App != "" {
   374  		msginfo["App"] = info.App
   375  	}
   376  	jsonBytes, _ := json.Marshal(msginfo)
   377  	if len(jsonBytes) > storage.KafkaMaxMessageSize {
   378  		var postRef string
   379  		if postRef, err = d.PutBlob(jsonBytes); err != nil {
   380  			dvid.Errorf("couldn't post large payload for cleave labelmap %q: %v", d.DataName(), err)
   381  		}
   382  		delete(msginfo, "CleavedSupervoxels")
   383  		msginfo["DataRef"] = postRef
   384  		jsonBytes, _ = json.Marshal(msginfo)
   385  	}
   386  	if err = d.PublishKafkaMsg(jsonBytes); err != nil {
   387  		dvid.Errorf("error on sending cleave op to kafka: %v\n", err)
   388  	}
   389  
   390  	d.StartUpdate()
   391  	defer d.StopUpdate()
   392  
   393  	op := labels.CleaveOp{
   394  		MutID:              mutID,
   395  		Target:             label,
   396  		CleavedLabel:       cleaveLabel,
   397  		CleavedSupervoxels: cleaveSupervoxels,
   398  	}
   399  	var cleavedSize, remainSize uint64
   400  	if cleavedSize, remainSize, err = d.cleaveIndex(v, op, info); err != nil {
   401  		return
   402  	}
   403  	if err = addCleaveToMapping(d, v, op); err != nil {
   404  		return
   405  	}
   406  	if err = labels.LogCleave(d, v, op); err != nil {
   407  		return
   408  	}
   409  
   410  	// notify syncs after processing because downstream sync might rely on changes
   411  	evt := datastore.SyncEvent{d.DataUUID(), labels.CleaveLabelEvent}
   412  	msg := datastore.SyncMessage{labels.CleaveLabelEvent, v, op}
   413  	if err = datastore.NotifySubscribers(evt, msg); err != nil {
   414  		err = fmt.Errorf("can't notify subscribers for event %v: %v", evt, err)
   415  		return
   416  	}
   417  
   418  	msginfo["Action"] = "cleave-complete"
   419  	msginfo["CleavedSize"] = cleavedSize
   420  	msginfo["RemainSize"] = remainSize
   421  	msginfo["Timestamp"] = time.Now().String()
   422  	jsonBytes, _ = json.Marshal(msginfo)
   423  
   424  	if err := server.LogJSONMutation(versionuuid, d.DataUUID(), jsonBytes); err != nil {
   425  		dvid.Criticalf("can't log cleave to data %q, version %s: %s\n", d.DataName(), versionuuid, jsonBytes)
   426  	}
   427  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   428  		dvid.Criticalf("error on sending cleave complete op to kafka: %v\n", err)
   429  	}
   430  	return
   431  }
   432  
   433  // created while iterating over all split RLEs and computing what the
   434  // split supervoxels should be and the # voxels split for each supervoxel per block.
   435  type blockSplitsMap map[uint64]map[uint64]labels.SVSplitCount
   436  
   437  // 1st pass: retrieve and check blocks intersecting split RLEs and get mappings of current supervoxels
   438  // to new split supervoxels.
   439  func (d *Data) splitPass1(ctx *datastore.VersionedCtx, splitmap dvid.BlockRLEs, splitblks dvid.IZYXSlice) (blockSplitsMap, *labels.SVSplitMap, error) {
   440  	timedLog := dvid.NewTimeLog()
   441  	svsplit := new(labels.SVSplitMap)
   442  	newLabelFunc := func() (uint64, error) {
   443  		return d.newLabel(ctx.VersionID())
   444  	}
   445  
   446  	errCh := make(chan error, len(splitblks))
   447  	blockCh := make(chan *labels.PositionedBlock, len(splitblks))
   448  	defer close(blockCh)
   449  
   450  	var maxQueue int
   451  	blockSplits := make(blockSplitsMap)
   452  	go func() {
   453  		for pb := range blockCh {
   454  			blockRLEs := splitmap[pb.BCoord]
   455  			counts, err := pb.SplitStats(blockRLEs, svsplit, newLabelFunc)
   456  			if err != nil {
   457  				errCh <- fmt.Errorf("issue with computing split remap in block %s: %v", pb.BCoord, err)
   458  			} else {
   459  				zyx, err := labels.IZYXStringToBlockIndex(pb.BCoord)
   460  				if err != nil {
   461  					errCh <- fmt.Errorf("unable to convert block %s to block index: %v", pb.BCoord, err)
   462  				} else {
   463  					blockSplits[zyx] = counts
   464  					errCh <- nil
   465  				}
   466  			}
   467  			curQueue := len(blockCh)
   468  			if curQueue > maxQueue {
   469  				maxQueue = curQueue
   470  			}
   471  		}
   472  	}()
   473  
   474  	var numBlocks int
   475  	var scale uint8
   476  	getLog := dvid.NewTimeLog()
   477  	for _, izyx := range splitblks {
   478  		pb, err := d.getLabelPositionedBlock(ctx, scale, izyx)
   479  		if err != nil {
   480  			return nil, nil, err
   481  		}
   482  		if pb == nil {
   483  			return nil, nil, fmt.Errorf("split on block %s attempted but block doesn't exist", izyx)
   484  		}
   485  		numBlocks++
   486  		blockCh <- pb
   487  	}
   488  	getLog.Debugf("split pass 1: got %d blocks under split volume", numBlocks)
   489  
   490  	var numErr int
   491  	var lastErr error
   492  	for i := 0; i < numBlocks; i++ {
   493  		processErr := <-errCh
   494  		if processErr != nil {
   495  			lastErr = processErr
   496  			numErr++
   497  		}
   498  	}
   499  
   500  	timedLog.Debugf("split pass 1 completed: %d blocks with %d errors (max queue %d/%d)\n", numBlocks, numErr, maxQueue, len(splitblks))
   501  	return blockSplits, svsplit, lastErr
   502  }
   503  
   504  // if every split supervoxel in a split block is fully replaceable, we can just switch header.
   505  func checkSplitBlockReplace(blockSplits map[uint64]labels.SVSplitCount, allSplits map[uint64]labels.SVSplit, idxsvc *proto.SVCount) (map[uint64]uint64, bool) {
   506  	mapping := make(map[uint64]uint64, len(idxsvc.Counts))
   507  	replaceable := true
   508  	for supervoxel, count := range idxsvc.Counts {
   509  		split, found := blockSplits[supervoxel] // the supervoxels split in this particular block
   510  		if found {
   511  			if split.Voxels != count { // this is not a complete replacement.
   512  				replaceable = false
   513  				break
   514  			}
   515  			mapping[supervoxel] = split.Split
   516  		} else {
   517  			split, found := allSplits[supervoxel]
   518  			if found {
   519  				// this is supervoxel split elsewhere but not in this block, so it
   520  				// must be part of remainder.
   521  				mapping[supervoxel] = split.Remain
   522  			}
   523  		}
   524  	}
   525  	return mapping, replaceable
   526  }
   527  
   528  func checkNonsplitBlockAffected(allSplits map[uint64]labels.SVSplit, idxsvc *proto.SVCount) (map[uint64]uint64, bool) {
   529  	mapping := make(map[uint64]uint64, len(idxsvc.Counts))
   530  	for sv, svsplit := range allSplits {
   531  		_, found := idxsvc.Counts[sv]
   532  		if found {
   533  			mapping[sv] = svsplit.Remain // since this is a non-split block (not in split RLE)
   534  		}
   535  	}
   536  	return mapping, len(mapping) > 0
   537  }
   538  
   539  type headerMod struct {
   540  	bcoord  dvid.IZYXString
   541  	mapping map[uint64]uint64
   542  	pb      *labels.PositionedBlock
   543  }
   544  
   545  type voxelMod struct {
   546  	bcoord   dvid.IZYXString
   547  	split    dvid.RLEs
   548  	svsplits map[uint64]labels.SVSplit
   549  	pb       *labels.PositionedBlock
   550  }
   551  
   552  func (d *Data) modifyBlocks(ctx *datastore.VersionedCtx, downresMut *downres.Mutation, modCh chan interface{}, errCh chan error) {
   553  	var err error
   554  	var scale uint8
   555  	for mod := range modCh {
   556  		var bcoord dvid.IZYXString
   557  		var block *labels.Block
   558  		switch m := mod.(type) {
   559  		case headerMod:
   560  			bcoord = m.bcoord
   561  			block, _, err = m.pb.ReplaceLabels(m.mapping)
   562  			if err != nil {
   563  				errCh <- fmt.Errorf("issue with header modification, block %s: %v", bcoord, err)
   564  				return
   565  			}
   566  		case voxelMod:
   567  			bcoord = m.bcoord
   568  			block, err = m.pb.SplitSupervoxels(m.split, m.svsplits)
   569  			if err != nil {
   570  				errCh <- fmt.Errorf("issue with voxel modification, block %s: %v", bcoord, err)
   571  				return
   572  			}
   573  		default:
   574  			errCh <- fmt.Errorf("received bad mod type: %v", mod)
   575  			return
   576  		}
   577  		splitpb := labels.PositionedBlock{Block: *block, BCoord: bcoord}
   578  		if err := d.putLabelBlock(ctx, scale, &splitpb); err != nil {
   579  			errCh <- fmt.Errorf("unable to put block %s in split, data %q: %v", bcoord, d.DataName(), err)
   580  			return
   581  		}
   582  		if err := downresMut.BlockMutated(bcoord, block); err != nil {
   583  			errCh <- fmt.Errorf("data %q publishing downres: %v", d.DataName(), err)
   584  			return
   585  		}
   586  		errCh <- nil
   587  	}
   588  }
   589  
   590  // 2nd pass: go through all blocks with affected supervoxels, compare affected supervoxels counts
   591  // in each block, and either modify header or rewrite the voxel labels.  Activate downres for affected
   592  // blocks.
   593  func (d *Data) splitPass2(ctx *datastore.VersionedCtx, downresMut *downres.Mutation, idx *labels.Index, affectedBlocks dvid.IZYXSlice, svsplits map[uint64]labels.SVSplit, splitmap dvid.BlockRLEs, blockSplits blockSplitsMap) error {
   594  	// note that the blockSplits give supervoxel relabelings and counts for blocks in split volume,
   595  	// but does not include blocks that may have been affected outside of split volume.
   596  	timedLog := dvid.NewTimeLog()
   597  	modCh := make(chan interface{}, len(affectedBlocks))
   598  	errCh := make(chan error, len(affectedBlocks))
   599  
   600  	defer close(modCh)
   601  
   602  	numHandlers := 16
   603  	for i := 0; i < numHandlers; i++ {
   604  		go d.modifyBlocks(ctx, downresMut, modCh, errCh)
   605  	}
   606  
   607  	var scale uint8
   608  	var maxQueue int
   609  	var numHeaderMod, numVoxelMod int
   610  	getLog := dvid.NewTimeLog()
   611  	for _, izyxStr := range affectedBlocks {
   612  		zyx, err := labels.IZYXStringToBlockIndex(izyxStr)
   613  		if err != nil {
   614  			return fmt.Errorf("unable to convert %s to block index: %v", izyxStr, err)
   615  		}
   616  		isvc, found := idx.Blocks[zyx]
   617  		if !found {
   618  			return fmt.Errorf("split affected block %s was not found in index", izyxStr)
   619  		}
   620  		pb, err := d.getLabelPositionedBlock(ctx, scale, izyxStr)
   621  		if err != nil {
   622  			return fmt.Errorf("error in getting block %s: %v", izyxStr, err)
   623  		}
   624  		if pb == nil {
   625  			return fmt.Errorf("block %s doesn't exist for split modification", izyxStr)
   626  		}
   627  		bsvc, inSplit := blockSplits[zyx]
   628  		if inSplit {
   629  			splitRLEs, found := splitmap[izyxStr]
   630  			if !found {
   631  				return fmt.Errorf("block %s supposedly in split but not found in splitmap", izyxStr)
   632  			}
   633  			mapping, canReplace := checkSplitBlockReplace(bsvc, svsplits, isvc)
   634  			if canReplace {
   635  				numHeaderMod++
   636  				modCh <- headerMod{bcoord: izyxStr, mapping: mapping, pb: pb}
   637  			} else {
   638  				numVoxelMod++
   639  				modCh <- voxelMod{bcoord: izyxStr, split: splitRLEs, svsplits: svsplits, pb: pb}
   640  			}
   641  		} else {
   642  			mapping, affected := checkNonsplitBlockAffected(svsplits, isvc)
   643  			if affected {
   644  				numHeaderMod++
   645  				modCh <- headerMod{bcoord: izyxStr, mapping: mapping, pb: pb}
   646  			}
   647  		}
   648  		curQueue := len(modCh)
   649  		if curQueue > maxQueue {
   650  			maxQueue = curQueue
   651  		}
   652  	}
   653  	getLog.Infof("split pass 2: got %d out of %d affected blocks\n", numHeaderMod+numVoxelMod, len(affectedBlocks))
   654  	var numErr int
   655  	var err error
   656  	for i := 0; i < numHeaderMod+numVoxelMod; i++ {
   657  		processErr := <-errCh
   658  		if processErr != nil {
   659  			err = processErr
   660  			numErr++
   661  		}
   662  	}
   663  	if err != nil {
   664  		return fmt.Errorf("had %d errors in splitting data %q blocks, last one: %v", numErr, len(blockSplits), err)
   665  	}
   666  	timedLog.Infof("split pass 2 completed: %d blocks (max queue %d/%d): %d header changes, %d voxel relabels\n", numHeaderMod+numVoxelMod, maxQueue, len(idx.Blocks), numHeaderMod, numVoxelMod)
   667  	return nil
   668  }
   669  
   670  func getAffectedBlocks(idx *labels.Index, svsplit *labels.SVSplitMap) (affectedBlocks dvid.IZYXSlice) {
   671  	for zyx, svc := range idx.Blocks {
   672  		for supervoxel := range svsplit.Splits {
   673  			_, found := svc.Counts[supervoxel]
   674  			if found {
   675  				izyxStr := labels.BlockIndexToIZYXString(zyx)
   676  				affectedBlocks = append(affectedBlocks, izyxStr)
   677  				break
   678  			}
   679  		}
   680  	}
   681  	sort.Sort(affectedBlocks)
   682  	return
   683  }
   684  
   685  // SplitLabels splits a portion of a label's voxels into a given split label or, if the given split
   686  // label is 0, a new label, which is returned.  The input is a binary sparse volume and should
   687  // preferably be the smaller portion of a labeled region.  In other words, the caller should chose
   688  // to submit for relabeling the smaller portion of any split.  It is assumed that the given split
   689  // voxels are within the fromLabel set of voxels and will generate unspecified behavior if this is
   690  // not the case.
   691  func (d *Data) SplitLabels(v dvid.VersionID, fromLabel uint64, r io.ReadCloser, info dvid.ModInfo) (toLabel, mutID uint64, err error) {
   692  	timedLog := dvid.NewTimeLog()
   693  
   694  	// Create a new label id for this version that will persist to store
   695  	toLabel, err = d.newLabel(v)
   696  	if err != nil {
   697  		return
   698  	}
   699  	dvid.Debugf("Splitting subset of label %d into new label %d ...\n", fromLabel, toLabel)
   700  
   701  	// Read the sparse volume from reader.
   702  	var split dvid.RLEs
   703  	split, err = dvid.ReadRLEs(r)
   704  	if err != nil {
   705  		return
   706  	}
   707  	splitSize, _ := split.Stats()
   708  	if splitSize == 0 {
   709  		err = fmt.Errorf("bad split since split volume was zero voxels")
   710  		return
   711  	}
   712  
   713  	// read label index and do simple check on split size
   714  	shard := fromLabel % numIndexShards
   715  	indexMu[shard].Lock()
   716  	defer indexMu[shard].Unlock()
   717  
   718  	var idx *labels.Index
   719  	idx, err = getCachedLabelIndex(d, v, fromLabel)
   720  	if err != nil {
   721  		err = fmt.Errorf("modify split index for data %q, label %d: %v", d.DataName(), fromLabel, err)
   722  		return
   723  	}
   724  	if idx == nil {
   725  		err = fmt.Errorf("unable to modify split index for data %q: missing label %d", d.DataName(), fromLabel)
   726  		return
   727  	}
   728  	mutID = d.NewMutationID()
   729  	if err := d.addMutcache(v, mutID, idx); err != nil {
   730  		dvid.Criticalf("unable to add cleaved mutid %d index %d: %v\n", mutID, fromLabel, err)
   731  	}
   732  
   733  	fromLabelSize := idx.NumVoxels()
   734  	if splitSize >= fromLabelSize {
   735  		err = fmt.Errorf("split volume of %d voxels >= %d of label %d", splitSize, fromLabelSize, fromLabel)
   736  		return
   737  	}
   738  
   739  	// Only do voxel-based mutations one at a time.  This lets us remove handling for block-level concurrency.
   740  	d.voxelMu.Lock()
   741  	defer d.voxelMu.Unlock()
   742  
   743  	d.StartUpdate()
   744  	defer d.StopUpdate()
   745  
   746  	// Partition the split spans into blocks.
   747  	blockSize, ok := d.BlockSize().(dvid.Point3d)
   748  	if !ok {
   749  		err = fmt.Errorf("can't do split because block size for instance %s is not 3d: %v", d.DataName(), d.BlockSize())
   750  		return
   751  	}
   752  	var splitmap dvid.BlockRLEs
   753  	splitmap, err = split.Partition(blockSize)
   754  	if err != nil {
   755  		return
   756  	}
   757  	splitblks := splitmap.SortedKeys() // sorted list of blocks that cover split
   758  
   759  	// 1st pass: go through blocks intersecting split RLEs and get mappings of current supervoxels
   760  	// to new split supervoxels
   761  	ctx := datastore.NewVersionedCtx(d, v)
   762  	var blockSplits blockSplitsMap
   763  	var svsplit *labels.SVSplitMap
   764  	if blockSplits, svsplit, err = d.splitPass1(ctx, splitmap, splitblks); err != nil {
   765  		return
   766  	}
   767  	labelSupervoxels := idx.GetSupervoxels()
   768  	for supervoxel := range svsplit.Splits {
   769  		if _, found := labelSupervoxels[supervoxel]; !found {
   770  			err = fmt.Errorf("supervoxel %d was part of split volume yet was not part of body label %d", supervoxel, fromLabel)
   771  			return
   772  		}
   773  	}
   774  	affectedBlocks := getAffectedBlocks(idx, svsplit)
   775  
   776  	// store split info into separate data.
   777  	var splitData []byte
   778  	if splitData, err = split.MarshalBinary(); err != nil {
   779  		return
   780  	}
   781  	var splitRef string
   782  	if splitRef, err = d.PutBlob(splitData); err != nil {
   783  		dvid.Errorf("error storing split data: %v", err)
   784  	}
   785  
   786  	// send kafka split event to instance-uuid topic
   787  	versionuuid, _ := datastore.UUIDFromVersion(v)
   788  	msginfo := map[string]interface{}{
   789  		"Action":     "split",
   790  		"Target":     fromLabel,
   791  		"NewLabel":   toLabel,
   792  		"Split":      splitRef,
   793  		"MutationID": mutID,
   794  		"UUID":       string(versionuuid),
   795  		"SVSplits":   svsplit.Splits,
   796  		"Timestamp":  time.Now().String(),
   797  	}
   798  	if info.User != "" {
   799  		msginfo["User"] = info.User
   800  	}
   801  	if info.App != "" {
   802  		msginfo["App"] = info.App
   803  	}
   804  	jsonBytes, _ := json.Marshal(msginfo)
   805  	if err = d.PublishKafkaMsg(jsonBytes); err != nil {
   806  		dvid.Errorf("error on sending split op to kafka: %v\n", err)
   807  	}
   808  
   809  	// 2nd pass: go through all blocks with affected supervoxels, compare affected supervoxels counts
   810  	// in each block, and either modify header or rewrite the voxel labels.  Activate downres for affected
   811  	// blocks.
   812  	downresMut := downres.NewMutation(d, v, mutID)
   813  	if err = d.splitPass2(ctx, downresMut, idx, affectedBlocks, svsplit.Splits, splitmap, blockSplits); err != nil {
   814  		return
   815  	}
   816  
   817  	// adjust the indices and mappings
   818  	op := labels.SplitOp{
   819  		MutID:    mutID,
   820  		Target:   fromLabel,
   821  		NewLabel: toLabel,
   822  		RLEs:     split,
   823  		SplitMap: svsplit.Splits,
   824  	}
   825  	if err = d.splitIndex(v, info, op, idx, splitmap, blockSplits); err != nil {
   826  		return
   827  	}
   828  	if err = addSplitToMapping(d, v, op); err != nil {
   829  		return
   830  	}
   831  	if err = labels.LogSplit(d, v, op); err != nil {
   832  		return
   833  	}
   834  	if err = downresMut.Execute(); err != nil {
   835  		return
   836  	}
   837  
   838  	timedLog.Debugf("completed labelmap split (%d affected, %d split blocks) of %d -> %d", len(affectedBlocks), len(splitmap), fromLabel, toLabel)
   839  
   840  	deltaSplit := labels.DeltaSplit{
   841  		MutID:        mutID,
   842  		OldLabel:     fromLabel,
   843  		NewLabel:     toLabel,
   844  		Split:        splitmap,
   845  		SortedBlocks: splitblks,
   846  		SplitVoxels:  splitSize,
   847  	}
   848  	evt := datastore.SyncEvent{d.DataUUID(), labels.SplitLabelEvent}
   849  	msg := datastore.SyncMessage{labels.SplitLabelEvent, v, deltaSplit}
   850  	if err := datastore.NotifySubscribers(evt, msg); err != nil {
   851  		dvid.Errorf("can't notify subscribers for event %v: %v\n", evt, err)
   852  	}
   853  
   854  	msginfo["Action"] = "split-complete"
   855  	msginfo["Timestamp"] = time.Now().String()
   856  	jsonBytes, _ = json.Marshal(msginfo)
   857  
   858  	if err := server.LogJSONMutation(versionuuid, d.DataUUID(), jsonBytes); err != nil {
   859  		dvid.Criticalf("can't log split label to data %q, version %s: %s\n", d.DataName(), versionuuid, jsonBytes)
   860  	}
   861  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
   862  		dvid.Criticalf("error on sending split complete op to kafka: %v\n", err)
   863  	}
   864  	return
   865  }
   866  
   867  // SplitSupervoxel splits a portion of a supervoxel's voxels into two new labels, which can be
   868  // optionally set to desired labels if passed in (see splitlabel and remainlabel in parameters).
   869  // The input is a binary sparse volume and should be totally contained by the given supervoxel.
   870  // The first returned label is assigned to the split voxels while the second returned label is
   871  // assigned to the remainder voxels.
   872  func (d *Data) SplitSupervoxel(v dvid.VersionID, svlabel, splitlabel, remainlabel uint64, r io.ReadCloser, info dvid.ModInfo, downscale bool) (splitSupervoxel, remainSupervoxel, mutID uint64, err error) {
   873  	timedLog := dvid.NewTimeLog()
   874  
   875  	// Create new labels for this split that will persist to store
   876  	if splitlabel != 0 {
   877  		splitSupervoxel = splitlabel
   878  		if _, err = d.updateMaxLabel(v, splitlabel); err != nil {
   879  			return
   880  		}
   881  	} else if splitSupervoxel, err = d.newLabel(v); err != nil {
   882  		return
   883  	}
   884  	if remainlabel != 0 {
   885  		remainSupervoxel = remainlabel
   886  		if _, err = d.updateMaxLabel(v, remainlabel); err != nil {
   887  			return
   888  		}
   889  	} else if remainSupervoxel, err = d.newLabel(v); err != nil {
   890  		return
   891  	}
   892  	dvid.Debugf("Splitting subset of label %d into new label %d and renaming remainder to label %d...\n", svlabel, splitSupervoxel, remainSupervoxel)
   893  
   894  	// Read the sparse volume from reader.
   895  	var split dvid.RLEs
   896  	split, err = dvid.ReadRLEs(r)
   897  	if err != nil {
   898  		return
   899  	}
   900  	splitSize, _ := split.Stats()
   901  	if splitSize == 0 {
   902  		dvid.Infof("split on supervoxel %d -> %d was given split size of 0\n", svlabel, remainlabel)
   903  	}
   904  
   905  	// read parent label index and do simple check on split size
   906  	var mapping *VCache
   907  	mapping, err = getMapping(d, v)
   908  	if err != nil {
   909  		return
   910  	}
   911  	label := svlabel
   912  	if mapping != nil {
   913  		if mapped, found := mapping.MappedLabel(v, svlabel); found {
   914  			if mapped == 0 {
   915  				err = fmt.Errorf("cannot get label for supervoxel %d, which has been split and doesn't exist anymore", svlabel)
   916  			}
   917  			label = mapped
   918  		}
   919  	}
   920  	shard := label % numIndexShards
   921  	indexMu[shard].Lock()
   922  	defer indexMu[shard].Unlock()
   923  
   924  	idx, err := getCachedLabelIndex(d, v, label)
   925  	if err != nil {
   926  		err = fmt.Errorf("split supervoxel index for data %q, supervoxel %d: %v", d.DataName(), svlabel, err)
   927  		return
   928  	}
   929  	if idx == nil {
   930  		err = fmt.Errorf("unable to split supervoxel %d for data %q: missing label index %d", svlabel, d.DataName(), label)
   931  		return
   932  	}
   933  	svSize := idx.GetSupervoxelCount(svlabel)
   934  	if splitSize > svSize {
   935  		err = fmt.Errorf("split volume of %d > %d of supervoxel %d", splitSize, svSize, svlabel)
   936  		return
   937  	}
   938  	remainSize := svSize - splitSize
   939  	if remainSize == 0 {
   940  		dvid.Infof("split on supervoxel %d -> %d was given split size %d, which is entire supervoxel\n", svlabel, splitlabel, splitSize)
   941  	}
   942  
   943  	// Only do voxel-based mutations one at a time.  This lets us remove handling for block-level concurrency.
   944  	d.voxelMu.Lock()
   945  	defer d.voxelMu.Unlock()
   946  
   947  	// store split info into separate data.
   948  	var splitData []byte
   949  	if splitData, err = split.MarshalBinary(); err != nil {
   950  		return
   951  	}
   952  	var splitRef string
   953  	if splitRef, err = d.PutBlob(splitData); err != nil {
   954  		dvid.Errorf("error storing split data: %v", err)
   955  	}
   956  
   957  	// send kafka split event to instance-uuid topic
   958  	mutID = d.NewMutationID()
   959  	versionuuid, _ := datastore.UUIDFromVersion(v)
   960  	msginfo := map[string]interface{}{
   961  		"Action":           "split-supervoxel",
   962  		"Supervoxel":       svlabel,
   963  		"Body":             label,
   964  		"SplitSupervoxel":  splitSupervoxel,
   965  		"RemainSupervoxel": remainSupervoxel,
   966  		"Split":            splitRef,
   967  		"MutationID":       mutID,
   968  		"UUID":             string(versionuuid),
   969  		"Timestamp":        time.Now().String(),
   970  	}
   971  	if info.User != "" {
   972  		msginfo["User"] = info.User
   973  	}
   974  	if info.App != "" {
   975  		msginfo["App"] = info.App
   976  	}
   977  	jsonBytes, _ := json.Marshal(msginfo)
   978  	if err = d.PublishKafkaMsg(jsonBytes); err != nil {
   979  		dvid.Errorf("error on sending split op to kafka: %v", err)
   980  	}
   981  
   982  	d.StartUpdate()
   983  	defer func() {
   984  		d.StopUpdate()
   985  	}()
   986  
   987  	var splitmap dvid.BlockRLEs
   988  	blockSize, ok := d.BlockSize().(dvid.Point3d)
   989  	if !ok {
   990  		err = fmt.Errorf("can't do split because block size for instance %s is not 3d: %v", d.DataName(), d.BlockSize())
   991  		return
   992  	}
   993  	splitmap, err = split.Partition(blockSize)
   994  	if err != nil {
   995  		return
   996  	}
   997  	op := labels.SplitSupervoxelOp{
   998  		MutID:            mutID,
   999  		Supervoxel:       svlabel,
  1000  		SplitSupervoxel:  splitSupervoxel,
  1001  		RemainSupervoxel: remainSupervoxel,
  1002  		Split:            splitmap,
  1003  	}
  1004  	var downresMut *downres.Mutation
  1005  	if downscale {
  1006  		downresMut = downres.NewMutation(d, v, mutID)
  1007  	}
  1008  
  1009  	var splitblks dvid.IZYXSlice
  1010  	if splitblks, err = d.splitSupervoxelIndex(v, info, op, idx); err != nil {
  1011  		return
  1012  	}
  1013  
  1014  	getLog := dvid.NewTimeLog()
  1015  	blockCh := make(chan *labels.PositionedBlock, len(splitblks))
  1016  	errCh := make(chan error, len(splitblks))
  1017  	ctx := datastore.NewVersionedCtx(d, v)
  1018  
  1019  	numHandlers := 16
  1020  	for i := 0; i < numHandlers; i++ {
  1021  		go d.splitSupervoxelThread(ctx, downresMut, op, idx.Blocks, blockCh, errCh)
  1022  	}
  1023  
  1024  	origBlocks := make([]*labels.PositionedBlock, len(splitblks))
  1025  	var numBlocks int
  1026  	var scale uint8
  1027  	for _, izyx := range splitblks {
  1028  		var pb *labels.PositionedBlock
  1029  		pb, err = d.getLabelPositionedBlock(ctx, scale, izyx)
  1030  		if err != nil {
  1031  			d.restoreOldBlocks(ctx, numBlocks, origBlocks)
  1032  			return
  1033  		}
  1034  		if pb == nil {
  1035  			dvid.Errorf("supervoxel split of %d: block %s should have been split but was nil\n", svlabel, izyx)
  1036  			continue
  1037  		}
  1038  		origBlocks[numBlocks] = pb
  1039  		numBlocks++
  1040  		blockCh <- pb
  1041  	}
  1042  
  1043  	// Wait for all blocks in supervoxel to be relabeled before returning.
  1044  	getLog.Debugf("supervoxel split of %d: got %d blocks", svlabel, numBlocks)
  1045  	var numErr int
  1046  	for i := 0; i < numBlocks; i++ {
  1047  		processErr := <-errCh
  1048  		if processErr != nil {
  1049  			err = processErr
  1050  			numErr++
  1051  		}
  1052  	}
  1053  	close(blockCh)
  1054  	if err != nil {
  1055  		err = fmt.Errorf("supervoxel split of %d: %d errors, last one: %v", svlabel, numErr, err)
  1056  		d.restoreOldBlocks(ctx, numBlocks, origBlocks)
  1057  		return
  1058  	}
  1059  	if err = addSupervoxelSplitToMapping(d, v, op); err != nil {
  1060  		return
  1061  	}
  1062  	if err = labels.LogSupervoxelSplit(d, v, op); err != nil {
  1063  		return
  1064  	}
  1065  	// store the new split index
  1066  	if err = putCachedLabelIndex(d, v, idx); err != nil {
  1067  		d.restoreOldBlocks(ctx, numBlocks, origBlocks)
  1068  		err = fmt.Errorf("split supervoxel index for data %q, supervoxel %d: %v", d.DataName(), op.Supervoxel, err)
  1069  		return
  1070  	}
  1071  
  1072  	if downresMut != nil {
  1073  		if err = downresMut.Execute(); err != nil {
  1074  			dvid.Criticalf("down-res compute of supervoxel split %d failed with error: %v\n", svlabel, err)
  1075  			dvid.Criticalf("down-res error can lead to sync issue between scale 0 and higher affecting these blocks: %s\n", splitblks)
  1076  			return
  1077  		}
  1078  	}
  1079  
  1080  	timedLog.Debugf("labelmap supervoxel %d split complete (%d blocks split)", op.Supervoxel, len(op.Split))
  1081  
  1082  	evt := datastore.SyncEvent{Data: d.DataUUID(), Event: labels.SupervoxelSplitEvent}
  1083  	msg := datastore.SyncMessage{Event: labels.SupervoxelSplitEvent, Version: v, Delta: op}
  1084  	if err := datastore.NotifySubscribers(evt, msg); err != nil {
  1085  		dvid.Errorf("can't notify subscribers for event %v: %v\n", evt, err)
  1086  	}
  1087  
  1088  	msginfo["Action"] = "split-supervoxel-complete"
  1089  	msginfo["SplitSize"] = splitSize
  1090  	msginfo["RemainSize"] = remainSize
  1091  	msginfo["Timestamp"] = time.Now().String()
  1092  	jsonBytes, _ = json.Marshal(msginfo)
  1093  
  1094  	if err := server.LogJSONMutation(versionuuid, d.DataUUID(), jsonBytes); err != nil {
  1095  		dvid.Criticalf("can't log split supervoxel to data %q, version %s: %s\n", d.DataName(), versionuuid, jsonBytes)
  1096  	}
  1097  	if err := d.PublishKafkaMsg(jsonBytes); err != nil {
  1098  		dvid.Criticalf("error on sending split complete op to kafka: %v", err)
  1099  	}
  1100  	return
  1101  }
  1102  
  1103  func (d *Data) restoreOldBlocks(ctx *datastore.VersionedCtx, numBlocks int, blocks []*labels.PositionedBlock) {
  1104  	var scale uint8
  1105  	for i := 0; i < numBlocks; i++ {
  1106  		if err := d.putLabelBlock(ctx, scale, blocks[i]); err != nil {
  1107  			dvid.Criticalf("unable to put back block %s, data %q after error: %v", blocks[i].BCoord, d.DataName(), err)
  1108  		}
  1109  	}
  1110  }
  1111  
  1112  // splits a set of voxels to a specified label within a block
  1113  func (d *Data) splitSupervoxelThread(ctx *datastore.VersionedCtx, downresMut *downres.Mutation, op labels.SplitSupervoxelOp, idxblocks map[uint64]*proto.SVCount, blockCh chan *labels.PositionedBlock, errCh chan error) {
  1114  	var scale uint8
  1115  	for pb := range blockCh {
  1116  		zyx, err := labels.IZYXStringToBlockIndex(pb.BCoord)
  1117  		if err != nil {
  1118  			errCh <- fmt.Errorf("couldn't convert block coord %s to block index: %v", pb.BCoord, err)
  1119  			continue
  1120  		}
  1121  		svc, found := idxblocks[zyx]
  1122  		if !found {
  1123  			errCh <- fmt.Errorf("tried to supervoxel %d split block %s but was not in label index", op.Supervoxel, pb.BCoord)
  1124  			continue
  1125  		}
  1126  		idxKeptSize := uint64(svc.Counts[op.RemainSupervoxel])
  1127  		idxSplitSize := uint64(svc.Counts[op.SplitSupervoxel])
  1128  
  1129  		splitBlock, keptSize, splitSize, err := pb.SplitSupervoxel(op)
  1130  		if err != nil {
  1131  			errCh <- fmt.Errorf("can't modify supervoxel %d, block %s: %v", op.Supervoxel, pb.BCoord, err)
  1132  			continue
  1133  		}
  1134  		if keptSize != idxKeptSize || splitSize != idxSplitSize {
  1135  			errCh <- fmt.Errorf("ran supervoxel %d split on block %s: got %d split, %d remain voxels, different from label index %d split, %d remain", op.Supervoxel, pb.BCoord, splitSize, keptSize, idxSplitSize, idxKeptSize)
  1136  			continue
  1137  		}
  1138  
  1139  		splitpb := labels.PositionedBlock{*splitBlock, pb.BCoord}
  1140  		if err := d.putLabelBlock(ctx, scale, &splitpb); err != nil {
  1141  			errCh <- fmt.Errorf("unable to put block %s in split of label %d, data %q: %v", pb.BCoord, op.Supervoxel, d.DataName(), err)
  1142  			continue
  1143  		}
  1144  
  1145  		if downresMut != nil {
  1146  			if err = downresMut.BlockMutated(pb.BCoord, splitBlock); err != nil {
  1147  				err = fmt.Errorf("data %q publishing downres, block %s: %v", d.DataName(), pb.BCoord, err)
  1148  			}
  1149  		}
  1150  		errCh <- err
  1151  	}
  1152  }