// GetRange returns a range of values spanning (kStart, kEnd) keys. These key-value // pairs will be sorted in ascending key order. If the keys are versioned, all key-value // pairs for the particular version will be returned. func (db *LevelDB) GetRange(ctx storage.Context, kStart, kEnd storage.TKey) ([]*storage.TKeyValue, error) { if ctx == nil { return nil, fmt.Errorf("Received nil context in GetRange()") } ch := make(chan errorableKV) // Run the range query on a potentially versioned key in a goroutine. go func() { if ctx == nil || !ctx.Versioned() { db.unversionedRange(ctx, kStart, kEnd, ch, false) } else { db.versionedRange(ctx.(storage.VersionedCtx), kStart, kEnd, ch, false) } }() // Consume the key-value pairs. values := []*storage.TKeyValue{} for { result := <-ch if result.KeyValue == nil { return values, nil } if result.error != nil { return nil, result.error } tk, err := ctx.TKeyFromKey(result.KeyValue.K) if err != nil { return nil, err } tkv := storage.TKeyValue{tk, result.KeyValue.V} values = append(values, &tkv) } }
// KeysInRange returns a range of present keys spanning (kStart, kEnd). Values // associated with the keys are not read. If the keys are versioned, only keys // in the ancestor path of the current context's version will be returned. func (db *LevelDB) KeysInRange(ctx storage.Context, kStart, kEnd storage.TKey) ([]storage.TKey, error) { if db == nil { return nil, fmt.Errorf("Can't call KeysInRange on nil LevelDB") } if ctx == nil { return nil, fmt.Errorf("Received nil context in KeysInRange()") } ch := make(chan errorableKV) // Run the range query on a potentially versioned key in a goroutine. go func() { if !ctx.Versioned() { db.unversionedRange(ctx, kStart, kEnd, ch, true) } else { db.versionedRange(ctx.(storage.VersionedCtx), kStart, kEnd, ch, true) } }() // Consume the keys. values := []storage.TKey{} for { result := <-ch if result.KeyValue == nil { return values, nil } if result.error != nil { return nil, result.error } tk, err := ctx.TKeyFromKey(result.KeyValue.K) if err != nil { return nil, err } values = append(values, tk) } }
// DeleteRange removes all key-value pairs with keys in the given range. func (db *LevelDB) DeleteRange(ctx storage.Context, kStart, kEnd storage.TKey) error { if ctx == nil { return fmt.Errorf("Received nil context in DeleteRange()") } // For leveldb, we just iterate over keys in range and delete each one using batch. const BATCH_SIZE = 10000 batch := db.NewBatch(ctx).(*goBatch) ch := make(chan errorableKV) // Run the keys-only range query in a goroutine. go func() { if ctx == nil || !ctx.Versioned() { db.unversionedRange(ctx, kStart, kEnd, ch, true) } else { db.versionedRange(ctx.(storage.VersionedCtx), kStart, kEnd, ch, true) } }() // Consume the key-value pairs. numKV := 0 for { result := <-ch if result.KeyValue == nil { break } if result.error != nil { return result.error } // The key coming down channel is not index but full key, so no need to construct key using context. // If versioned, write a tombstone using current version id since we don't want to delete locked ancestors. // If unversioned, just delete. tk, err := ctx.TKeyFromKey(result.KeyValue.K) if err != nil { return err } batch.Delete(tk) if (numKV+1)%BATCH_SIZE == 0 { if err := batch.Commit(); err != nil { batch.Close() return fmt.Errorf("Error on batch DELETE at key-value pair %d: %v\n", numKV, err) } batch = db.NewBatch(ctx).(*goBatch) } numKV++ } if numKV%BATCH_SIZE != 0 { if err := batch.Commit(); err != nil { batch.Close() return fmt.Errorf("Error on last batch DELETE: %v\n", err) } } dvid.Debugf("Deleted %d key-value pairs via delete range for %s.\n", numKV, ctx) return nil }
// ProcessRange sends a range of key-value pairs to chunk handlers. If the keys are versioned, // only key-value pairs for kStart's version will be transmitted. func (db *LevelDB) ProcessRange(ctx storage.Context, kStart, kEnd storage.TKey, op *storage.ChunkOp, f storage.ChunkFunc) error { if db == nil { return fmt.Errorf("Can't call ProcessRange on nil LevelDB") } if ctx == nil { return fmt.Errorf("Received nil context in ProcessRange()") } ch := make(chan errorableKV) // Run the range query on a potentially versioned key in a goroutine. go func() { if ctx == nil || !ctx.Versioned() { db.unversionedRange(ctx, kStart, kEnd, ch, false) } else { db.versionedRange(ctx.(storage.VersionedCtx), kStart, kEnd, ch, false) } }() // Consume the key-value pairs. for { result := <-ch if result.KeyValue == nil { return nil } if result.error != nil { return result.error } if op.Wg != nil { op.Wg.Add(1) } tk, err := ctx.TKeyFromKey(result.KeyValue.K) if err != nil { return err } tkv := storage.TKeyValue{tk, result.KeyValue.V} chunk := &storage.Chunk{op, &tkv} if err := f(chunk); err != nil { return err } } }
// FoundSparseVol returns true if a sparse volume is found for the given label // within the given bounds. func FoundSparseVol(ctx storage.Context, label uint64, bounds Bounds) (bool, error) { store, err := storage.MutableStore() if err != nil { return false, fmt.Errorf("Data type labelvol had error initializing store: %v\n", err) } // Get the start/end indices for this body's KeyLabelSpatialMap (b + s) keys. minZYX := dvid.MinIndexZYX maxZYX := dvid.MaxIndexZYX blockBounds := bounds.BlockBounds if blockBounds == nil { blockBounds = new(dvid.Bounds) } if minZ, ok := blockBounds.MinZ(); ok { minZYX[2] = minZ } if maxZ, ok := blockBounds.MaxZ(); ok { maxZYX[2] = maxZ } begTKey := NewTKey(label, minZYX.ToIZYXString()) endTKey := NewTKey(label, maxZYX.ToIZYXString()) // Scan through all keys for this range to see if we have any hits. var found bool keyCh := make(storage.KeyChan, 100) wg := new(sync.WaitGroup) wg.Add(1) go func() { defer wg.Done() for { key := <-keyCh if key == nil { return } // Make sure this block is within the optional bounding. tk, err := ctx.TKeyFromKey(key) if err != nil { dvid.Errorf("Unable to get TKey from Key (%v): %v\n", key, err) return } if blockBounds.BoundedX() || blockBounds.BoundedY() { _, blockStr, err := DecodeTKey(tk) if err != nil { dvid.Errorf("Error decoding sparse volume key (%v): %v\n", key, err) return } indexZYX, err := blockStr.IndexZYX() if err != nil { dvid.Errorf("Error decoding block coordinate (%v) for sparse volume: %v\n", blockStr, err) return } blockX, blockY, _ := indexZYX.Unpack() if blockBounds.OutsideX(blockX) || blockBounds.OutsideY(blockY) { continue } } // We have a valid key found = true return } }() if err := store.SendKeysInRange(ctx, begTKey, endTKey, keyCh); err != nil { return false, err } wg.Wait() return found, nil }