@Test public void stringSerialization() { // Characters DataOutput out = serialize.getDataOutput(((int) Character.MAX_VALUE) * 2 + 8, true); for (char c = Character.MIN_VALUE; c < Character.MAX_VALUE; c++) { out.writeObjectNotNull(Character.valueOf(c)); } ReadBuffer b = out.getStaticBuffer().asReadBuffer(); for (char c = Character.MIN_VALUE; c < Character.MAX_VALUE; c++) { assertEquals(c, serialize.readObjectNotNull(b, Character.class).charValue()); } // String for (int t = 0; t < 10000; t++) { DataOutput out1 = serialize.getDataOutput(32 + 5, true); DataOutput out2 = serialize.getDataOutput(32 + 5, true); String s1 = RandomGenerator.randomString(1, 32); String s2 = RandomGenerator.randomString(1, 32); out1.writeObjectNotNull(s1); out2.writeObjectNotNull(s2); StaticBuffer b1 = out1.getStaticBuffer(); StaticBuffer b2 = out2.getStaticBuffer(); assertEquals(s1, serialize.readObjectNotNull(b1.asReadBuffer(), String.class)); assertEquals(s2, serialize.readObjectNotNull(b2.asReadBuffer(), String.class)); assertEquals( s1 + " vs " + s2, Integer.signum(s1.compareTo(s2)), Integer.signum(b1.compareTo(b2))); } }
/** * 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; }
@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); }
@Override public int hashCode() { if (hashcode == 0) { hashcode = key.hashCode() * 102329 + super.hashCode(); if (hashcode == 0) hashcode = 1; } return hashcode; }
public boolean subsumes(KeySliceQuery oth) { return key.equals(oth.key) && super.subsumes(oth); }