use of io.pravega.segmentstore.contracts.TooManyAttributesException in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testMaxAttributeLimit.
/**
* Tests the ability of the ContainerMetadataUpdateTransaction to enforce the maximum attribute limit on Segments.
*/
@Test
public void testMaxAttributeLimit() throws Exception {
// We check all operations that can update attributes.
val ops = new HashMap<String, Function<Collection<AttributeUpdate>, Operation>>();
ops.put("UpdateAttributes", u -> new UpdateAttributesOperation(SEGMENT_ID, u));
ops.put("Append", u -> new StreamSegmentAppendOperation(SEGMENT_ID, DEFAULT_APPEND_DATA, u));
// Set the maximum allowed number of attributes on a segment.
UpdateableContainerMetadata metadata = createMetadata();
val initialUpdates = new ArrayList<AttributeUpdate>(SegmentMetadata.MAXIMUM_ATTRIBUTE_COUNT);
val expectedValues = new HashMap<UUID, Long>();
for (int i = 0; i < SegmentMetadata.MAXIMUM_ATTRIBUTE_COUNT; i++) {
UUID attributeId;
do {
attributeId = UUID.randomUUID();
} while (expectedValues.containsKey(attributeId));
initialUpdates.add(new AttributeUpdate(attributeId, AttributeUpdateType.None, i));
expectedValues.put(attributeId, (long) i);
}
// And load them up into an UpdateTransaction.
val txn = createUpdateTransaction(metadata);
val initialOp = new UpdateAttributesOperation(SEGMENT_ID, initialUpdates);
txn.preProcessOperation(initialOp);
txn.acceptOperation(initialOp);
// invokes preProcessOperation() - which is responsible with validation, so no changes are made to the UpdateTransaction.
for (val opGenerator : ops.entrySet()) {
// Value replacement.
val replacementUpdates = new ArrayList<AttributeUpdate>();
int i = 0;
for (val e : expectedValues.entrySet()) {
AttributeUpdate u;
switch((i++) % 4) {
case 0:
u = new AttributeUpdate(e.getKey(), AttributeUpdateType.ReplaceIfEquals, e.getValue() + 1, e.getValue());
break;
case 1:
u = new AttributeUpdate(e.getKey(), AttributeUpdateType.ReplaceIfGreater, e.getValue() + 1);
break;
case 2:
u = new AttributeUpdate(e.getKey(), AttributeUpdateType.Accumulate, 1);
break;
default:
u = new AttributeUpdate(e.getKey(), AttributeUpdateType.Replace, 1);
break;
}
replacementUpdates.add(u);
}
// This should not throw anything.
txn.preProcessOperation(opGenerator.getValue().apply(replacementUpdates));
// Removal - this should not throw anything either.
val toRemoveId = initialUpdates.get(0).getAttributeId();
val toRemoveUpdate = new AttributeUpdate(toRemoveId, AttributeUpdateType.Replace, SegmentMetadata.NULL_ATTRIBUTE_VALUE);
txn.preProcessOperation(opGenerator.getValue().apply(Collections.singleton(toRemoveUpdate)));
// Addition - this should throw.
UUID toAddId;
do {
toAddId = UUID.randomUUID();
} while (expectedValues.containsKey(toAddId));
val toAddUpdate = new AttributeUpdate(toAddId, AttributeUpdateType.None, 1);
AssertExtensions.assertThrows("Too many attributes were accepted for operation " + opGenerator.getKey(), () -> txn.preProcessOperation(opGenerator.getValue().apply(Collections.singleton(toAddUpdate))), ex -> ex instanceof TooManyAttributesException);
// Removal+Addition+Replacement: this particular setup should not throw anything.
val mixedUpdates = Arrays.asList(new AttributeUpdate(toAddId, AttributeUpdateType.None, 1), new AttributeUpdate(toRemoveId, AttributeUpdateType.Replace, SegmentMetadata.NULL_ATTRIBUTE_VALUE), new AttributeUpdate(initialUpdates.get(1).getAttributeId(), AttributeUpdateType.Replace, 10));
txn.preProcessOperation(opGenerator.getValue().apply(mixedUpdates));
}
}
use of io.pravega.segmentstore.contracts.TooManyAttributesException in project pravega by pravega.
the class SegmentMetadataUpdateTransaction method preProcessAttributes.
/**
* Pre-processes a collection of attributes.
* After this method returns, all AttributeUpdates in the given collection will have the actual (and updated) value
* of that attribute in the Segment.
*
* @param attributeUpdates The Updates to process (if any).
* @throws BadAttributeUpdateException If any of the given AttributeUpdates is invalid given the current state of
* the segment.
* @throws TooManyAttributesException If, as a result of applying the given updates, the Segment would exceed the
* maximum allowed number of Attributes.
*/
private void preProcessAttributes(Collection<AttributeUpdate> attributeUpdates) throws BadAttributeUpdateException, TooManyAttributesException {
if (attributeUpdates == null) {
return;
}
int newAttributeCount = this.attributeValues.size();
for (AttributeUpdate u : attributeUpdates) {
AttributeUpdateType updateType = u.getUpdateType();
long previousValue = this.attributeValues.getOrDefault(u.getAttributeId(), SegmentMetadata.NULL_ATTRIBUTE_VALUE);
// Perform validation, and set the AttributeUpdate.value to the updated value, if necessary.
switch(updateType) {
case ReplaceIfGreater:
// Verify value against existing value, if any.
boolean hasValue = previousValue != SegmentMetadata.NULL_ATTRIBUTE_VALUE;
if (hasValue && u.getValue() <= previousValue) {
throw new BadAttributeUpdateException(this.name, u, String.format("Expected greater than '%s'.", previousValue));
}
break;
case ReplaceIfEquals:
// Verify value against existing value, if any.
if (u.getComparisonValue() != previousValue) {
throw new BadAttributeUpdateException(this.name, u, String.format("Expected existing value to be '%s', actual '%s'.", u.getComparisonValue(), previousValue));
}
break;
case None:
// Verify value is not already set.
if (previousValue != SegmentMetadata.NULL_ATTRIBUTE_VALUE) {
throw new BadAttributeUpdateException(this.name, u, String.format("Attribute value already set (%s).", previousValue));
}
break;
case Accumulate:
if (previousValue != SegmentMetadata.NULL_ATTRIBUTE_VALUE) {
u.setValue(previousValue + u.getValue());
}
break;
case Replace:
break;
default:
throw new BadAttributeUpdateException(this.name, u, "Unexpected update type: " + updateType);
}
if (previousValue == SegmentMetadata.NULL_ATTRIBUTE_VALUE && u.getValue() != SegmentMetadata.NULL_ATTRIBUTE_VALUE) {
// This attribute did not exist and is about to be added.
newAttributeCount++;
} else if (previousValue != SegmentMetadata.NULL_ATTRIBUTE_VALUE && u.getValue() == SegmentMetadata.NULL_ATTRIBUTE_VALUE) {
// This attribute existed and is about to be removed.
newAttributeCount--;
}
}
if (newAttributeCount > SegmentMetadata.MAXIMUM_ATTRIBUTE_COUNT && newAttributeCount > this.attributeValues.size()) {
// attributes of existing segments, but not increase their count.
throw new TooManyAttributesException(this.name, SegmentMetadata.MAXIMUM_ATTRIBUTE_COUNT);
}
}
Aggregations