public MutationBatch write(final ApplicationScope collectionScope, UniqueValue value) { Preconditions.checkNotNull(value, "value is required"); final Id entityId = value.getEntityId(); final UUID entityVersion = value.getEntityVersion(); final Field<?> field = value.getField(); ValidationUtils.verifyIdentity(entityId); ValidationUtils.verifyVersion(entityVersion); final EntityVersion ev = new EntityVersion(entityId, entityVersion); final UniqueFieldEntry uniqueFieldEntry = new UniqueFieldEntry(entityVersion, field); return doWrite( collectionScope, value, new RowOp() { @Override public void doLookup(final ColumnListMutation<EntityVersion> colMutation) { colMutation.putColumn(ev, COL_VALUE); } @Override public void doLog(final ColumnListMutation<UniqueFieldEntry> colMutation) { colMutation.putColumn(uniqueFieldEntry, COL_VALUE); } }); }
@Test public void testBasicOperation() throws ConnectionException, InterruptedException { ApplicationScope scope = new ApplicationScopeImpl(new SimpleId("organization")); IntegerField field = new IntegerField("count", 5); Id entityId = new SimpleId(UUIDGenerator.newTimeUUID(), "entity"); UUID version = UUIDGenerator.newTimeUUID(); UniqueValue stored = new UniqueValueImpl(field, entityId, version); strategy.write(scope, stored).execute(); UniqueValueSet fields = strategy.load(scope, entityId.getType(), Collections.<Field>singleton(field)); UniqueValue retrieved = fields.getValue(field.getName()); Assert.assertNotNull(retrieved); assertEquals(stored, retrieved); Iterator<UniqueValue> allFieldsWritten = strategy.getAllUniqueFields(scope, entityId); assertTrue(allFieldsWritten.hasNext()); // test this interface. In most cases, we won't know the field name, so we want them all UniqueValue allFieldsValue = allFieldsWritten.next(); Assert.assertNotNull(allFieldsValue); assertEquals(field, allFieldsValue.getField()); assertEquals(version, allFieldsValue.getEntityVersion()); assertFalse(allFieldsWritten.hasNext()); }
/** * Do the column update or delete for the given column and row key * * @param applicationScope We need to use this when getting the keyspace * @param uniqueValue The unique value to write * @param op The operation to write */ private MutationBatch doWrite( ApplicationScope applicationScope, UniqueValue uniqueValue, RowOp op) { final MutationBatch batch = keyspace.prepareMutationBatch(); final Id applicationId = applicationScope.getApplication(); final FieldKey fieldKey = createUniqueValueKey( applicationId, uniqueValue.getEntityId().getType(), uniqueValue.getField()); op.doLookup(batch.withRow(CF_UNIQUE_VALUES, ScopedRowKey.fromKey(applicationId, fieldKey))); final EntityKey entityKey = createEntityUniqueLogKey(applicationId, uniqueValue.getEntityId()); op.doLog( batch.withRow(CF_ENTITY_UNIQUE_VALUE_LOG, ScopedRowKey.fromKey(applicationId, entityKey))); if (log.isDebugEnabled()) { log.debug( "Writing unique value version={} name={} value={} ", new Object[] { uniqueValue.getEntityVersion(), uniqueValue.getField().getName(), uniqueValue.getField().getValue() }); } return batch; }
@Override public MutationBatch write( final ApplicationScope collectionScope, final UniqueValue value, final int timeToLive) { Preconditions.checkNotNull(value, "value is required"); Preconditions.checkArgument(timeToLive > 0, "timeToLive must be greater than 0 is required"); final Id entityId = value.getEntityId(); final UUID entityVersion = value.getEntityVersion(); final Field<?> field = value.getField(); ValidationUtils.verifyIdentity(entityId); ValidationUtils.verifyVersion(entityVersion); final EntityVersion ev = new EntityVersion(entityId, entityVersion); final UniqueFieldEntry uniqueFieldEntry = new UniqueFieldEntry(entityVersion, field); return doWrite( collectionScope, value, new RowOp() { @Override public void doLookup(final ColumnListMutation<EntityVersion> colMutation) { colMutation.putColumn(ev, COL_VALUE, timeToLive); } // we purposefully leave out TTL. Worst case we issue deletes against tombstoned columns // best case, we clean up an invalid secondary index entry when the log is used @Override public void doLog(final ColumnListMutation<UniqueFieldEntry> colMutation) { colMutation.putColumn(uniqueFieldEntry, COL_VALUE); } }); }
@Test public void testCapitalizationFixes() throws ConnectionException { ApplicationScope scope = new ApplicationScopeImpl(new SimpleId("organization")); StringField field = new StringField("count", "MiXeD CaSe"); Id entityId = new SimpleId(UUIDGenerator.newTimeUUID(), "entity"); UUID version = UUIDGenerator.newTimeUUID(); UniqueValue stored = new UniqueValueImpl(field, entityId, version); strategy.write(scope, stored).execute(); UniqueValueSet fields = strategy.load(scope, entityId.getType(), Collections.<Field>singleton(field)); UniqueValue value = fields.getValue(field.getName()); assertEquals(field.getName(), value.getField().getName()); assertEquals(entityId, value.getEntityId()); // now test will all upper and all lower, we should get it all the same fields = strategy.load( scope, entityId.getType(), Collections.<Field>singleton(new StringField(field.getName(), "MIXED CASE"))); value = fields.getValue(field.getName()); assertEquals(field.getName(), value.getField().getName()); assertEquals(entityId, value.getEntityId()); fields = strategy.load( scope, entityId.getType(), Collections.<Field>singleton(new StringField(field.getName(), "mixed case"))); value = fields.getValue(field.getName()); assertEquals(field.getName(), value.getField().getName()); assertEquals(entityId, value.getEntityId()); Iterator<UniqueValue> allFieldsWritten = strategy.getAllUniqueFields(scope, entityId); assertTrue(allFieldsWritten.hasNext()); // test this interface. In most cases, we won't know the field name, so we want them all UniqueValue writtenFieldEntry = allFieldsWritten.next(); Assert.assertNotNull(writtenFieldEntry); assertEquals(field.getName(), writtenFieldEntry.getField().getName()); assertEquals(field.getValue().toLowerCase(), writtenFieldEntry.getField().getValue()); assertEquals(version, writtenFieldEntry.getEntityVersion()); assertFalse(allFieldsWritten.hasNext()); }
@Test public void testWriteWithTTL() throws InterruptedException, ConnectionException { ApplicationScope scope = new ApplicationScopeImpl(new SimpleId("organization")); // write object that lives 2 seconds IntegerField field = new IntegerField("count", 5); Id entityId = new SimpleId(UUIDGenerator.newTimeUUID(), "entity"); UUID version = UUIDGenerator.newTimeUUID(); UniqueValue stored = new UniqueValueImpl(field, entityId, version); strategy.write(scope, stored, 5).execute(); Thread.sleep(1000); // waited one sec, should be still here UniqueValueSet fields = strategy.load(scope, entityId.getType(), Collections.<Field>singleton(field)); UniqueValue retrieved = fields.getValue(field.getName()); Assert.assertNotNull(retrieved); assertEquals(stored, retrieved); Thread.sleep(5000); // wait another second, should be gone now fields = strategy.load(scope, entityId.getType(), Collections.<Field>singleton(field)); UniqueValue nullExpected = fields.getValue(field.getName()); Assert.assertNull(nullExpected); // we still want to retain the log entry, even if we don't retain the unique value. Deleting // something // that doesn't exist is a tombstone, but so is the timeout. Iterator<UniqueValue> allFieldsWritten = strategy.getAllUniqueFields(scope, entityId); assertTrue(allFieldsWritten.hasNext()); // test this interface. In most cases, we won't know the field name, so we want them all UniqueValue writtenFieldEntry = allFieldsWritten.next(); Assert.assertNotNull(writtenFieldEntry); assertEquals(field, writtenFieldEntry.getField()); assertEquals(version, writtenFieldEntry.getEntityVersion()); assertFalse(allFieldsWritten.hasNext()); }
@Test public void twoFieldsPerVersion() throws ConnectionException, InterruptedException { ApplicationScope scope = new ApplicationScopeImpl(new SimpleId("organization")); Id entityId = new SimpleId(UUIDGenerator.newTimeUUID(), "entity"); final UUID version1 = UUIDGenerator.newTimeUUID(); // write V1 of everything IntegerField version1Field1 = new IntegerField("count", 1); StringField version1Field2 = new StringField("field", "v1value"); UniqueValue version1Field1Value = new UniqueValueImpl(version1Field1, entityId, version1); UniqueValue version1Field2Value = new UniqueValueImpl(version1Field2, entityId, version1); final MutationBatch batch = strategy.write(scope, version1Field1Value); batch.mergeShallow(strategy.write(scope, version1Field2Value)); // write V2 of everything final UUID version2 = UUIDGenerator.newTimeUUID(); IntegerField version2Field1 = new IntegerField("count", 2); StringField version2Field2 = new StringField("field", "v2value"); UniqueValue version2Field1Value = new UniqueValueImpl(version2Field1, entityId, version2); UniqueValue version2Field2Value = new UniqueValueImpl(version2Field2, entityId, version2); batch.mergeShallow(strategy.write(scope, version2Field1Value)); batch.mergeShallow(strategy.write(scope, version2Field2Value)); batch.execute(); UniqueValueSet fields = strategy.load( scope, entityId.getType(), Arrays.<Field>asList(version1Field1, version1Field2)); UniqueValue retrieved = fields.getValue(version1Field1.getName()); assertEquals(version1Field1Value, retrieved); retrieved = fields.getValue(version1Field2.getName()); assertEquals(version1Field2Value, retrieved); Iterator<UniqueValue> allFieldsWritten = strategy.getAllUniqueFields(scope, entityId); assertTrue(allFieldsWritten.hasNext()); // test this interface. In most cases, we won't know the field name, so we want them all UniqueValue allFieldsValue = allFieldsWritten.next(); // version 2 fields should come first, ordered by field name assertEquals(version2Field1, allFieldsValue.getField()); assertEquals(version2, allFieldsValue.getEntityVersion()); allFieldsValue = allFieldsWritten.next(); assertEquals(version2Field2, allFieldsValue.getField()); assertEquals(version2, allFieldsValue.getEntityVersion()); // version 1 should come next ordered by field name allFieldsValue = allFieldsWritten.next(); assertEquals(version1Field1, allFieldsValue.getField()); assertEquals(version1, allFieldsValue.getEntityVersion()); allFieldsValue = allFieldsWritten.next(); assertEquals(version1Field2, allFieldsValue.getField()); assertEquals(version1, allFieldsValue.getEntityVersion()); assertFalse(allFieldsWritten.hasNext()); }