protected <T> Future<List<FDBMetric.Value<T>>> readAllValues(
     Transaction tr, final BaseMetricImpl<T> metric) {
   tr.options().setAccessSystemKeys();
   return tr.getRange(
           dataSubspace.pack(
               tupleFrom(metric.getType(), metric.getName(), address, DEFAULT_ID, 0)),
           dataSubspace.pack(
               tupleFrom(metric.getType(), metric.getName(), address, DEFAULT_ID, NLEVELS)))
       .asList()
       .map(
           new Function<List<KeyValue>, List<FDBMetric.Value<T>>>() {
             @Override
             public List<FDBMetric.Value<T>> apply(List<KeyValue> kvs) {
               List<FDBMetric.Value<T>> result = new ArrayList<>();
               for (KeyValue kv : kvs) {
                 result.addAll(metric.decodeValues(kv.getValue()));
               }
               // Merge all levels.
               Collections.sort(
                   result,
                   new Comparator<FDBMetric.Value<T>>() {
                     @Override
                     public int compare(FDBMetric.Value<T> v1, FDBMetric.Value<T> v2) {
                       if (v1.time < v2.time) return -1;
                       else if (v1.time > v2.time) return +1;
                       else return 0;
                     }
                   });
               return result;
             }
           });
 }
 protected Map<List<String>, byte[]> readConf(Transaction tr) {
   tr.options().setAccessSystemKeys();
   byte[] confKey = confSubspace.getKey();
   List<KeyValue> kvs = tr.getRange(Range.startsWith(confKey)).asList().get();
   Map<List<String>, byte[]> result = new HashMap<>();
   for (KeyValue kv : kvs) {
     byte[] tupleBytes = new byte[kv.getKey().length - confKey.length];
     System.arraycopy(kv.getKey(), confKey.length, tupleBytes, 0, tupleBytes.length);
     // TODO: It's a shame that there isn't a fromBytes with index offets.
     Tuple2 tuple = Tuple2.fromBytes(tupleBytes);
     List<String> list = new ArrayList<>(tuple.size());
     for (int i = 0; i < tuple.size(); i++) {
       if (BINARY_STRINGS) {
         try {
           list.add(new String(tuple.getBytes(i), "UTF-8"));
         } catch (UnsupportedEncodingException ex) {
           throw new AkibanInternalException("Error decoding binary string", ex);
         }
       } else {
         list.add(tuple.getString(i));
       }
     }
     result.put(list, kv.getValue());
   }
   // Initiate a watch (from this same transaction) for changes to the key
   // used to signal configuration changes.
   tr.watch(confChangesSubspace.getKey())
       .onReady(
           new Runnable() {
             @Override
             public void run() {
               confChanged = true;
               notifyBackground();
             }
           });
   return result;
 }
 public void deleteLongMetric(Transaction tr, String name) {
   tr.clear(Range.startsWith(dataSubspace.pack(tupleFrom(LONG_TYPE, name))));
   tr.clear(Range.startsWith(confSubspace.pack(tupleFrom(LONG_TYPE, name))));
 }
 public void deleteBooleanMetric(Transaction tr, String name) {
   tr.clear(Range.startsWith(dataSubspace.pack(tupleFrom(BOOLEAN_TYPE, name))));
   tr.clear(Range.startsWith(confSubspace.pack(tupleFrom(BOOLEAN_TYPE, name))));
 }