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 }