func (d *Data) putChunk(chunk *storage.Chunk, putbuffer storage.RequestBuffer) { defer func() { // After processing a chunk, return the token. server.HandlerToken <- 1 // Notify the requestor that this chunk is done. if chunk.Wg != nil { chunk.Wg.Done() } }() op, ok := chunk.Op.(*putOperation) if !ok { log.Fatalf("Illegal operation passed to ProcessChunk() for data %s\n", d.DataName()) } // Make sure our received chunk is valid. if chunk == nil { dvid.Errorf("Received nil chunk in ProcessChunk. Ignoring chunk.\n") return } if chunk.K == nil { dvid.Errorf("Received nil chunk key in ProcessChunk. Ignoring chunk.\n") return } // Initialize the block buffer using the chunk of data. For voxels, this chunk of // data needs to be uncompressed and deserialized. var blockData []byte var err error if chunk.V == nil { blockData = d.BackgroundBlock() } else { blockData, _, err = dvid.DeserializeData(chunk.V, true) if err != nil { dvid.Errorf("Unable to deserialize block in %q: %v\n", d.DataName(), err) return } } // If we are mutating, get the previous block of data. var oldBlock []byte if op.mutate { oldBlock, err = d.loadOldBlock(op.version, chunk.K) if err != nil { dvid.Errorf("Unable to load previous block in %q, key %v: %v\n", d.DataName(), chunk.K, err) return } } // Perform the operation. block := &storage.TKeyValue{K: chunk.K, V: blockData} if err = op.voxels.WriteBlock(block, d.BlockSize()); err != nil { dvid.Errorf("Unable to WriteBlock() in %q: %v\n", d.DataName(), err) return } serialization, err := dvid.SerializeData(blockData, d.Compression(), d.Checksum()) if err != nil { dvid.Errorf("Unable to serialize block in %q: %v\n", d.DataName(), err) return } store, err := d.GetOrderedKeyValueDB() if err != nil { dvid.Errorf("Data type imageblk had error initializing store: %v\n", err) return } ready := make(chan error, 1) callback := func() { // Notify any subscribers that you've changed block. resperr := <-ready if resperr != nil { dvid.Errorf("Unable to PUT voxel data for key %v: %v\n", chunk.K, resperr) return } var event string var delta interface{} if op.mutate { event = MutateBlockEvent delta = MutatedBlock{&op.indexZYX, oldBlock, block.V, op.mutID} } else { event = IngestBlockEvent delta = Block{&op.indexZYX, block.V, op.mutID} } evt := datastore.SyncEvent{d.DataUUID(), event} msg := datastore.SyncMessage{event, op.version, delta} if err := datastore.NotifySubscribers(evt, msg); err != nil { dvid.Errorf("Unable to notify subscribers of event %s in %s\n", event, d.DataName()) } } // put data -- use buffer if available ctx := datastore.NewVersionedCtx(d, op.version) if putbuffer != nil { go callback() putbuffer.PutCallback(ctx, chunk.K, serialization, ready) } else { if err := store.Put(ctx, chunk.K, serialization); err != nil { dvid.Errorf("Unable to PUT voxel data for key %v: %v\n", chunk.K, err) return } ready <- nil callback() } }
// 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 }