// PutVoxels persists voxels from a subvolume into the storage engine. // The subvolume must be aligned to blocks of the data instance, which simplifies // the routine if the PUT is a mutation (signals MutateBlockEvent) instead of ingestion. func (d *Data) PutVoxels(v dvid.VersionID, mutID uint64, vox *Voxels, roiname dvid.InstanceName, mutate bool) error { r, err := GetROI(v, roiname, vox) if err != nil { return err } // Make sure vox is block-aligned if !dvid.BlockAligned(vox, d.BlockSize()) { return fmt.Errorf("cannot store voxels in non-block aligned geometry %s -> %s", vox.StartPoint(), vox.EndPoint()) } wg := new(sync.WaitGroup) // Only do one request at a time, although each request can start many goroutines. server.SpawnGoroutineMutex.Lock() defer server.SpawnGoroutineMutex.Unlock() // Keep track of changing extents and mark repo as dirty if changed. var extentChanged bool defer func() { if extentChanged { err := datastore.SaveDataByVersion(v, d) if err != nil { dvid.Infof("Error in trying to save repo on change: %v\n", err) } } }() // Track point extents extents := d.Extents() if extents.AdjustPoints(vox.StartPoint(), vox.EndPoint()) { extentChanged = true } // extract buffer interface if it exists var putbuffer storage.RequestBuffer store, err := d.GetOrderedKeyValueDB() if err != nil { return fmt.Errorf("Data type imageblk had error initializing store: %v\n", err) } if req, ok := store.(storage.KeyValueRequester); ok { ctx := datastore.NewVersionedCtx(d, v) putbuffer = req.NewBuffer(ctx) } // Iterate through index space for this data. for it, err := vox.IndexIterator(d.BlockSize()); err == nil && it.Valid(); it.NextSpan() { i0, i1, err := it.IndexSpan() if err != nil { return err } ptBeg := i0.Duplicate().(dvid.ChunkIndexer) ptEnd := i1.Duplicate().(dvid.ChunkIndexer) begX := ptBeg.Value(0) endX := ptEnd.Value(0) if extents.AdjustIndices(ptBeg, ptEnd) { extentChanged = true } wg.Add(int(endX-begX) + 1) c := dvid.ChunkPoint3d{begX, ptBeg.Value(1), ptBeg.Value(2)} for x := begX; x <= endX; x++ { c[0] = x curIndex := dvid.IndexZYX(c) // Don't PUT if this index is outside a specified ROI if r != nil && r.Iter != nil && !r.Iter.InsideFast(curIndex) { wg.Done() continue } kv := &storage.TKeyValue{K: NewTKey(&curIndex)} putOp := &putOperation{vox, curIndex, v, mutate, mutID} op := &storage.ChunkOp{putOp, wg} d.PutChunk(&storage.Chunk{op, kv}, putbuffer) } } wg.Wait() // if a bufferable op, flush if putbuffer != nil { putbuffer.Flush() } return nil }