Example #1
0
 /**
  * Extracts all the tags we must use to group results.
  *
  * <ul>
  *   <li>If a tag has the form {@code name=*} then we'll create one group per value we find for
  *       that tag.
  *   <li>If a tag has the form {@code name={v1,v2,..,vN}} then we'll create {@code N} groups.
  * </ul>
  *
  * In the both cases above, {@code name} will be stored in the {@code group_bys} attribute. In the
  * second case specifically, the {@code N} values would be stored in {@code group_by_values}, the
  * key in this map being {@code name}.
  *
  * @param tags The tags from which to extract the 'GROUP BY's. Each tag that represents a 'GROUP
  *     BY' will be removed from the map passed in argument.
  */
 private void findGroupBys(final Map<String, String> tags) {
   final Iterator<Map.Entry<String, String>> i = tags.entrySet().iterator();
   while (i.hasNext()) {
     final Map.Entry<String, String> tag = i.next();
     final String tagvalue = tag.getValue();
     if (tagvalue.equals("*") // 'GROUP BY' with any value.
         || tagvalue.indexOf('|', 1) >= 0) { // Multiple possible values.
       if (group_bys == null) {
         group_bys = new ArrayList<byte[]>();
       }
       group_bys.add(tsdb.tag_names.getId(tag.getKey()));
       i.remove();
       if (tagvalue.charAt(0) == '*') {
         continue; // For a 'GROUP BY' with any value, we're done.
       }
       // 'GROUP BY' with specific values.  Need to split the values
       // to group on and store their IDs in group_by_values.
       final String[] values = Tags.splitString(tagvalue, '|');
       if (group_by_values == null) {
         group_by_values = new ByteMap<byte[][]>();
       }
       final short value_width = tsdb.tag_values.width();
       final byte[][] value_ids = new byte[values.length][value_width];
       group_by_values.put(tsdb.tag_names.getId(tag.getKey()), value_ids);
       for (int j = 0; j < values.length; j++) {
         final byte[] value_id = tsdb.tag_values.getId(values[j]);
         System.arraycopy(value_id, 0, value_ids[j], 0, value_width);
       }
     }
   }
 }
Example #2
0
  /**
   * Sets the server-side regexp filter on the scanner. In order to find the rows with the relevant
   * tags, we use a server-side filter that matches a regular expression on the row key.
   *
   * @param scanner The scanner on which to add the filter.
   */
  void createAndSetFilter(final Scanner scanner) {
    if (group_bys != null) {
      Collections.sort(group_bys, Bytes.MEMCMP);
    }
    final short name_width = tsdb.tag_names.width();
    final short value_width = tsdb.tag_values.width();
    final short tagsize = (short) (name_width + value_width);
    // Generate a regexp for our tags.  Say we have 2 tags: { 0 0 1 0 0 2 }
    // and { 4 5 6 9 8 7 }, the regexp will be:
    // "^.{7}(?:.{6})*\\Q\000\000\001\000\000\002\\E(?:.{6})*\\Q\004\005\006\011\010\007\\E(?:.{6})*$"
    final StringBuilder buf =
        new StringBuilder(
            15 // "^.{N}" + "(?:.{M})*" + "$"
                + ((13 + tagsize) // "(?:.{M})*\\Q" + tagsize bytes + "\\E"
                    * (tags.size() + (group_bys == null ? 0 : group_bys.size() * 3))));
    // In order to avoid re-allocations, reserve a bit more w/ groups ^^^

    // Alright, let's build this regexp.  From the beginning...
    buf.append(
            "(?s)" // Ensure we use the DOTALL flag.
                + "^.{")
        // ... start by skipping the metric ID and timestamp.
        .append(tsdb.metrics.width() + Const.TIMESTAMP_BYTES)
        .append("}");
    final Iterator<byte[]> tags = this.tags.iterator();
    final Iterator<byte[]> group_bys =
        (this.group_bys == null ? new ArrayList<byte[]>(0).iterator() : this.group_bys.iterator());
    byte[] tag = tags.hasNext() ? tags.next() : null;
    byte[] group_by = group_bys.hasNext() ? group_bys.next() : null;
    // Tags and group_bys are already sorted.  We need to put them in the
    // regexp in order by ID, which means we just merge two sorted lists.
    do {
      // Skip any number of tags.
      buf.append("(?:.{").append(tagsize).append("})*\\Q");
      if (isTagNext(name_width, tag, group_by)) {
        addId(buf, tag);
        tag = tags.hasNext() ? tags.next() : null;
      } else { // Add a group_by.
        addId(buf, group_by);
        final byte[][] value_ids = (group_by_values == null ? null : group_by_values.get(group_by));
        if (value_ids == null) { // We don't want any specific ID...
          buf.append(".{").append(value_width).append('}'); // Any value ID.
        } else { // We want specific IDs.  List them: /(AAA|BBB|CCC|..)/
          buf.append("(?:");
          for (final byte[] value_id : value_ids) {
            buf.append("\\Q");
            addId(buf, value_id);
            buf.append('|');
          }
          // Replace the pipe of the last iteration.
          buf.setCharAt(buf.length() - 1, ')');
        }
        group_by = group_bys.hasNext() ? group_bys.next() : null;
      }
    } while (tag != group_by); // Stop when they both become null.
    // Skip any number of tags before the end.
    buf.append("(?:.{").append(tagsize).append("})*$");
    scanner.setKeyRegexp(buf.toString(), CHARSET);
  }
Example #3
0
  public String toString() {
    final StringBuilder buf = new StringBuilder();
    buf.append("TsdbQuery(start_time=")
        .append(getStartTime())
        .append(", end_time=")
        .append(getEndTime())
        .append(", metric=")
        .append(Arrays.toString(metric));
    try {
      buf.append(" (").append(tsdb.metrics.getName(metric));
    } catch (NoSuchUniqueId e) {
      buf.append(" (<").append(e.getMessage()).append('>');
    }
    try {
      buf.append("), tags=").append(Tags.resolveIds(tsdb, tags));
    } catch (NoSuchUniqueId e) {
      buf.append("), tags=<").append(e.getMessage()).append('>');
    }
    buf.append(", rate=")
        .append(rate)
        .append(", nointerpolation=")
        .append(noInterpolation)
        .append(", aggregator=")
        .append(aggregator)
        .append(", group_bys=(");

    if (group_bys != null) {
      for (final byte[] tag_id : group_bys) {
        try {
          buf.append(tsdb.tag_names.getName(tag_id));
        } catch (NoSuchUniqueId e) {
          buf.append('<').append(e.getMessage()).append('>');
        }
        buf.append(' ').append(Arrays.toString(tag_id));
        if (group_by_values != null) {
          final byte[][] value_ids = group_by_values.get(tag_id);
          if (value_ids == null) {
            continue;
          }
          buf.append("={");
          for (final byte[] value_id : value_ids) {
            try {
              buf.append(tsdb.tag_values.getName(value_id));
            } catch (NoSuchUniqueId e) {
              buf.append('<').append(e.getMessage()).append('>');
            }
            buf.append(' ').append(Arrays.toString(value_id)).append(", ");
          }
          buf.append('}');
        }
        buf.append(", ");
      }
    }
    buf.append("))");
    return buf.toString();
  }
Example #4
0
  /**
   * Creates the {@link SpanGroup}s to form the final results of this query.
   *
   * @param spans The {@link Span}s found for this query ({@link #findSpans}). Can be {@code null},
   *     in which case the array returned will be empty.
   * @return A possibly empty array of {@link SpanGroup}s built according to any 'GROUP BY'
   *     formulated in this query.
   */
  private DataPoints[] groupByAndAggregate(final TreeMap<byte[], Span> spans) {
    if (spans == null || spans.size() <= 0) {
      return NO_RESULT;
    }
    if (group_bys == null) {
      // We haven't been asked to find groups, so let's put all the spans
      // together in the same group.
      final SpanGroup group =
          new SpanGroup(
              tsdb,
              getScanStartTime(),
              getScanEndTime(),
              spans.values(),
              rate,
              this.noInterpolation,
              aggregator,
              sample_interval,
              downsampler);
      return new SpanGroup[] {group};
    }

    // Maps group value IDs to the SpanGroup for those values.  Say we've
    // been asked to group by two things: foo=* bar=* Then the keys in this
    // map will contain all the value IDs combinations we've seen.  If the
    // name IDs for `foo' and `bar' are respectively [0, 0, 7] and [0, 0, 2]
    // then we'll have group_bys=[[0, 0, 2], [0, 0, 7]] (notice it's sorted
    // by ID, so bar is first) and say we find foo=LOL bar=OMG as well as
    // foo=LOL bar=WTF and that the IDs of the tag values are:
    //   LOL=[0, 0, 1]  OMG=[0, 0, 4]  WTF=[0, 0, 3]
    // then the map will have two keys:
    //   - one for the LOL-OMG combination: [0, 0, 1, 0, 0, 4] and,
    //   - one for the LOL-WTF combination: [0, 0, 1, 0, 0, 3].
    final ByteMap<SpanGroup> groups = new ByteMap<SpanGroup>();
    final short value_width = tsdb.tag_values.width();
    final byte[] group = new byte[group_bys.size() * value_width];
    for (final Map.Entry<byte[], Span> entry : spans.entrySet()) {
      final byte[] row = entry.getKey();
      byte[] value_id = null;
      int i = 0;
      // TODO(tsuna): The following loop has a quadratic behavior.  We can
      // make it much better since both the row key and group_bys are sorted.
      for (final byte[] tag_id : group_bys) {
        value_id = Tags.getValueId(tsdb, row, tag_id);
        if (value_id == null) {
          break;
        }
        System.arraycopy(value_id, 0, group, i, value_width);
        i += value_width;
      }
      if (value_id == null) {
        LOG.error(
            "WTF?  Dropping span for row "
                + Arrays.toString(row)
                + " as it had no matching tag from the requested groups,"
                + " which is unexpected.  Query="
                + this);
        continue;
      }
      // LOG.info("Span belongs to group " + Arrays.toString(group) + ": " + Arrays.toString(row));
      SpanGroup thegroup = groups.get(group);
      if (thegroup == null) {
        thegroup =
            new SpanGroup(
                tsdb,
                getScanStartTime(),
                getScanEndTime(),
                null,
                rate,
                noInterpolation,
                aggregator,
                sample_interval,
                downsampler);
        // Copy the array because we're going to keep `group' and overwrite
        // its contents.  So we want the collection to have an immutable copy.
        final byte[] group_copy = new byte[group.length];
        System.arraycopy(group, 0, group_copy, 0, group.length);
        groups.put(group_copy, thegroup);
      }
      thegroup.add(entry.getValue());
    }
    // for (final Map.Entry<byte[], SpanGroup> entry : groups) {
    //  LOG.info("group for " + Arrays.toString(entry.getKey()) + ": " + entry.getValue());
    // }
    return groups.values().toArray(new SpanGroup[groups.size()]);
  }