Example #1
0
 @Override
 public boolean equals(Object other) {
   if (this == other) return true;
   else if (other == null) return false;
   else if (!getClass().isInstance(other)) return false;
   KeySliceQuery oth = (KeySliceQuery) other;
   return key.equals(oth.key) && super.equals(oth);
 }
  /**
   * This class relies heavily on the behavior of segmented scans with respect to which hash keys
   * are scanned by each segment. Here's a rough ASCII example to help illustrate:
   * ___________________________ |hk:A |hk:B | ---------------------------- ^segment 1 ^segment 2
   *
   * <p>Because we are scanning in segments across the entire hash key space, it is possible for the
   * same hash key to appear in two different segments. We are also running all of the scan segments
   * in parallel, so we have no control over which segment returns first.
   *
   * <p>In the example, if segment 2 was the first segment to post a result, we would store hk:B as
   * a "boundary" key. That way when segment 1 eventually reaches hk:B in its scan, we know that
   * another segment has already returned this hash key and we can safely skip returning it.
   *
   * <p>By doing this, we avoid returning a RecordIterator for the same hash key twice and we only
   * need to store at most 2 hash keys per segment.
   */
  @Override
  public List<SingleKeyRecordIterator> buildRecordIterators(ScanContext scanContext) {
    final ScanResult dynamoDbResult = scanContext.getScanResult();
    final int segment = scanContext.getScanRequest().getSegment();
    final List<Map<String, AttributeValue>> items = dynamoDbResult.getItems();
    // If the scan returned no results, we need to shortcut and just throw back an empty result set
    if (items.isEmpty()) {
      return Collections.emptyList();
    }

    List<SingleKeyRecordIterator> recordIterators = Lists.newLinkedList();

    final Iterator<Map<String, AttributeValue>> itemIterator = items.iterator();
    final Map<String, AttributeValue> firstItem = itemIterator.next();
    final StaticBuffer firstKey = new KeyBuilder(firstItem).build(Constants.TITAN_HASH_KEY);

    // Computes the full set of boundary keys up to this point. This includes the previous end key
    // for this segment.
    final ImmutableSet<StaticBuffer> boundaryKeys = aggregateBoundaryKeys();

    // The first key in this scan segment might already have been returned by a previous scan
    // segment
    if (!boundaryKeys.contains(firstKey)) {
      recordIterators.add(buildRecordIteratorForHashKey(firstKey));
    }

    StaticBuffer hashKey = firstKey;
    while (itemIterator.hasNext()) {
      final Optional<StaticBuffer> nextKey = findNextHashKey(itemIterator, hashKey);
      if (nextKey.isPresent()) {
        // Found a new hash key. Make a record iterator and look for the next unique hash key
        hashKey = nextKey.get();
        recordIterators.add(buildRecordIteratorForHashKey(hashKey));
      }
    }

    // If we've already seen the final hashKey in a previous scan segment result, we want to avoid
    // returning it again.
    if (!hashKey.equals(firstKey) && boundaryKeys.contains(hashKey)) {
      recordIterators.remove(recordIterators.size() - 1);
    }

    // Update the boundary keys for this segment
    if (scanContext.isFirstResult()) {
      setInitialBoundaryKeys(segment, firstKey, hashKey);
    } else {
      updateLastKey(segment, hashKey);
    }
    return recordIterators;
  }
  private Optional<StaticBuffer> findNextHashKey(
      Iterator<Map<String, AttributeValue>> itemIterator, StaticBuffer previousKey) {
    Optional<StaticBuffer> result = Optional.absent();

    while (itemIterator.hasNext() && !result.isPresent()) {
      final StaticBuffer nextKey =
          new KeyBuilder(itemIterator.next()).build(Constants.TITAN_HASH_KEY);
      if (!nextKey.equals(previousKey)) {
        result = Optional.of(nextKey);
      }
    }

    return result;
  }
Example #4
0
 public boolean subsumes(KeySliceQuery oth) {
   return key.equals(oth.key) && super.subsumes(oth);
 }