mirror of
https://github.com/google/nomulus.git
synced 2025-07-08 20:23:24 +02:00
Refactor LevelDbFileBuilder to accept DS Entity (#599)
* Refactor LevelDbFileBuilder to accept DS Entity Builder now can directly work with Datastore Entity objects. No need to wrap data in ComparableEntity.
This commit is contained in:
parent
26fb5388a4
commit
2b794347e6
8 changed files with 155 additions and 107 deletions
|
@ -19,7 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
|
||||
import com.google.common.io.Resources;
|
||||
import google.registry.testing.DatastoreEntityExtension;
|
||||
import google.registry.tools.LevelDbFileBuilder.Property;
|
||||
import google.registry.tools.EntityWrapper.Property;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -71,30 +71,38 @@ public class CompareDbBackupsTest {
|
|||
// Create two directories corresponding to data dumps.
|
||||
File dump1 = tempFs.newFolder("dump1");
|
||||
LevelDbFileBuilder builder = new LevelDbFileBuilder(new File(dump1, "output-data1"));
|
||||
builder.addEntityProto(
|
||||
BASE_ID,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L));
|
||||
builder.addEntityProto(
|
||||
BASE_ID + 1,
|
||||
Property.create("moxey", 100L),
|
||||
Property.create("minney", 200L),
|
||||
Property.create("motz", 300L));
|
||||
builder.addEntity(
|
||||
EntityWrapper.from(
|
||||
BASE_ID,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L))
|
||||
.getEntity());
|
||||
builder.addEntity(
|
||||
EntityWrapper.from(
|
||||
BASE_ID + 1,
|
||||
Property.create("moxey", 100L),
|
||||
Property.create("minney", 200L),
|
||||
Property.create("motz", 300L))
|
||||
.getEntity());
|
||||
builder.build();
|
||||
|
||||
File dump2 = tempFs.newFolder("dump2");
|
||||
builder = new LevelDbFileBuilder(new File(dump2, "output-data2"));
|
||||
builder.addEntityProto(
|
||||
BASE_ID + 1,
|
||||
Property.create("moxey", 100L),
|
||||
Property.create("minney", 200L),
|
||||
Property.create("motz", 300L));
|
||||
builder.addEntityProto(
|
||||
BASE_ID + 2,
|
||||
Property.create("blutzy", 100L),
|
||||
Property.create("fishey", 200L),
|
||||
Property.create("strutz", 300L));
|
||||
builder.addEntity(
|
||||
EntityWrapper.from(
|
||||
BASE_ID + 1,
|
||||
Property.create("moxey", 100L),
|
||||
Property.create("minney", 200L),
|
||||
Property.create("motz", 300L))
|
||||
.getEntity());
|
||||
builder.addEntity(
|
||||
EntityWrapper.from(
|
||||
BASE_ID + 2,
|
||||
Property.create("blutzy", 100L),
|
||||
Property.create("fishey", 200L),
|
||||
Property.create("strutz", 300L))
|
||||
.getEntity());
|
||||
builder.build();
|
||||
|
||||
CompareDbBackups.main(new String[] {dump1.getCanonicalPath(), dump2.getCanonicalPath()});
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.junit.runner.RunWith;
|
|||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public final class ComparableEntityTest {
|
||||
public final class EntityWrapperTest {
|
||||
|
||||
private static final String TEST_ENTITY_KIND = "TestEntity";
|
||||
private static final int ARBITRARY_KEY_ID = 1001;
|
||||
|
@ -63,13 +63,13 @@ public final class ComparableEntityTest {
|
|||
Entity e2 = EntityTranslator.createFromPb(proto2);
|
||||
|
||||
// Ensure that we have a normalized representation.
|
||||
ComparableEntity ce1 = new ComparableEntity(e1);
|
||||
ComparableEntity ce2 = new ComparableEntity(e2);
|
||||
EntityWrapper ce1 = new EntityWrapper(e1);
|
||||
EntityWrapper ce2 = new EntityWrapper(e2);
|
||||
assertThat(ce1).isEqualTo(ce2);
|
||||
assertThat(ce1.hashCode()).isEqualTo(ce2.hashCode());
|
||||
|
||||
// Ensure that the original entity is equal.
|
||||
assertThat(new ComparableEntity(entity)).isEqualTo(ce1);
|
||||
assertThat(new EntityWrapper(entity)).isEqualTo(ce1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -90,8 +90,8 @@ public final class ComparableEntityTest {
|
|||
Entity e1 = EntityTranslator.createFromPb(proto1);
|
||||
Entity e2 = EntityTranslator.createFromPb(proto2);
|
||||
|
||||
ComparableEntity ce1 = new ComparableEntity(e1);
|
||||
ComparableEntity ce2 = new ComparableEntity(e2);
|
||||
EntityWrapper ce1 = new EntityWrapper(e1);
|
||||
EntityWrapper ce2 = new EntityWrapper(e2);
|
||||
assertThat(e1).isEqualTo(e2); // The keys should still be the same.
|
||||
assertThat(ce1).isNotEqualTo(ce2);
|
||||
assertThat(ce1.hashCode()).isNotEqualTo(ce2.hashCode());
|
||||
|
@ -108,15 +108,15 @@ public final class ComparableEntityTest {
|
|||
Entity e1 = EntityTranslator.createFromPb(proto1);
|
||||
Entity e2 = EntityTranslator.createFromPb(proto2);
|
||||
|
||||
ComparableEntity ce1 = new ComparableEntity(e1);
|
||||
ComparableEntity ce2 = new ComparableEntity(e2);
|
||||
EntityWrapper ce1 = new EntityWrapper(e1);
|
||||
EntityWrapper ce2 = new EntityWrapper(e2);
|
||||
assertThat(ce1).isNotEqualTo(ce2);
|
||||
assertThat(ce1.hashCode()).isNotEqualTo(ce2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparisonAgainstNonComparableEntities() {
|
||||
ComparableEntity ce = new ComparableEntity(new Entity(TEST_ENTITY_KIND, ARBITRARY_KEY_ID));
|
||||
EntityWrapper ce = new EntityWrapper(new Entity(TEST_ENTITY_KIND, ARBITRARY_KEY_ID));
|
||||
// Note: this has to be "isNotEqualTo()" and not isNotNull() because we want to test the
|
||||
// equals() method and isNotNull() just checks for "ce != null".
|
||||
assertThat(ce).isNotEqualTo(null);
|
|
@ -19,7 +19,6 @@ import static google.registry.tools.LevelDbLogReader.HEADER_SIZE;
|
|||
|
||||
import com.google.appengine.api.datastore.Entity;
|
||||
import com.google.appengine.api.datastore.EntityTranslator;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
|
||||
import google.registry.tools.LevelDbLogReader.ChunkType;
|
||||
import java.io.File;
|
||||
|
@ -28,30 +27,19 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
|
||||
/** Utility class for building a leveldb logfile. */
|
||||
final class LevelDbFileBuilder {
|
||||
private static final String TEST_ENTITY_KIND = "TestEntity";
|
||||
|
||||
public final class LevelDbFileBuilder {
|
||||
private final FileOutputStream out;
|
||||
private byte[] currentBlock = new byte[BLOCK_SIZE];
|
||||
|
||||
// Write position in the current block.
|
||||
private int currentPos = 0;
|
||||
|
||||
LevelDbFileBuilder(File file) throws FileNotFoundException {
|
||||
public LevelDbFileBuilder(File file) throws FileNotFoundException {
|
||||
out = new FileOutputStream(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a record containing a new entity protobuf to the file.
|
||||
*
|
||||
* <p>Returns the ComparableEntity object rather than "this" so that we can check for the presence
|
||||
* of the entity in the result set.
|
||||
*/
|
||||
ComparableEntity addEntityProto(int id, Property... properties) throws IOException {
|
||||
Entity entity = new Entity(TEST_ENTITY_KIND, id);
|
||||
for (Property prop : properties) {
|
||||
entity.setProperty(prop.name(), prop.value());
|
||||
}
|
||||
/** Adds an {@link Entity Datastore Entity object} to the leveldb log file. */
|
||||
LevelDbFileBuilder addEntity(Entity entity) throws IOException {
|
||||
EntityProto proto = EntityTranslator.convertToPb(entity);
|
||||
byte[] protoBytes = proto.toByteArray();
|
||||
if (protoBytes.length > BLOCK_SIZE - (currentPos + HEADER_SIZE)) {
|
||||
|
@ -61,7 +49,7 @@ final class LevelDbFileBuilder {
|
|||
}
|
||||
|
||||
currentPos = LevelDbUtil.addRecord(currentBlock, currentPos, ChunkType.FULL, protoBytes);
|
||||
return new ComparableEntity(entity);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Writes all remaining data and closes the block. */
|
||||
|
@ -69,15 +57,4 @@ final class LevelDbFileBuilder {
|
|||
out.write(currentBlock);
|
||||
out.close();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class Property {
|
||||
static Property create(String name, Object value) {
|
||||
return new AutoValue_LevelDbFileBuilder_Property(name, value);
|
||||
}
|
||||
|
||||
abstract String name();
|
||||
|
||||
abstract Object value();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,17 @@
|
|||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.appengine.api.datastore.Entity;
|
||||
import com.google.appengine.api.datastore.EntityTranslator;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.tools.LevelDbFileBuilder.Property;
|
||||
import google.registry.testing.DatastoreHelper;
|
||||
import google.registry.tools.EntityWrapper.Property;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.junit.Rule;
|
||||
|
@ -45,19 +49,18 @@ public class LevelDbFileBuilderTest {
|
|||
File subdir = tempFs.newFolder("folder");
|
||||
File logFile = new File(subdir, "testfile");
|
||||
LevelDbFileBuilder builder = new LevelDbFileBuilder(logFile);
|
||||
ComparableEntity entity =
|
||||
builder.addEntityProto(
|
||||
EntityWrapper entity =
|
||||
EntityWrapper.from(
|
||||
BASE_ID, Property.create("first", 100L), Property.create("second", 200L));
|
||||
builder.addEntity(entity.getEntity());
|
||||
builder.build();
|
||||
|
||||
ImmutableList<byte[]> records = ImmutableList.copyOf(LevelDbLogReader.from(logFile.getPath()));
|
||||
assertThat(records).hasSize(1);
|
||||
|
||||
// Reconstitute an entity, make sure that what we've got is the same as what we started with.
|
||||
EntityProto proto = new EntityProto();
|
||||
proto.parseFrom(records.get(0));
|
||||
Entity materializedEntity = EntityTranslator.createFromPb(proto);
|
||||
assertThat(new ComparableEntity(materializedEntity)).isEqualTo(entity);
|
||||
Entity materializedEntity = rawRecordToEntity(records.get(0));
|
||||
assertThat(new EntityWrapper(materializedEntity)).isEqualTo(entity);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -68,25 +71,50 @@ public class LevelDbFileBuilderTest {
|
|||
|
||||
// Generate enough records to cross a block boundary. These records end up being around 80
|
||||
// bytes, so 1000 works.
|
||||
ImmutableList.Builder<ComparableEntity> originalEntitiesBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<EntityWrapper> originalEntitiesBuilder = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
ComparableEntity entity =
|
||||
builder.addEntityProto(
|
||||
EntityWrapper entity =
|
||||
EntityWrapper.from(
|
||||
BASE_ID + i, Property.create("first", 100L), Property.create("second", 200L));
|
||||
builder.addEntity(entity.getEntity());
|
||||
originalEntitiesBuilder.add(entity);
|
||||
}
|
||||
builder.build();
|
||||
ImmutableList<ComparableEntity> originalEntities = originalEntitiesBuilder.build();
|
||||
ImmutableList<EntityWrapper> originalEntities = originalEntitiesBuilder.build();
|
||||
|
||||
ImmutableList<byte[]> records = ImmutableList.copyOf(LevelDbLogReader.from(logFile.getPath()));
|
||||
assertThat(records).hasSize(1000);
|
||||
int index = 0;
|
||||
for (byte[] record : records) {
|
||||
EntityProto proto = new EntityProto();
|
||||
proto.parseFrom(record);
|
||||
Entity materializedEntity = EntityTranslator.createFromPb(proto);
|
||||
assertThat(new ComparableEntity(materializedEntity)).isEqualTo(originalEntities.get(index));
|
||||
Entity materializedEntity = rawRecordToEntity(record);
|
||||
assertThat(new EntityWrapper(materializedEntity)).isEqualTo(originalEntities.get(index));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfyEntityWrite() throws Exception {
|
||||
File subdir = tempFs.newFolder("folder");
|
||||
File logFile = new File(subdir, "testfile");
|
||||
LevelDbFileBuilder builder = new LevelDbFileBuilder(logFile);
|
||||
|
||||
ContactResource contact = DatastoreHelper.newContactResource("contact");
|
||||
builder.addEntity(tm().transact(() -> ofy().save().toEntity(contact)));
|
||||
builder.build();
|
||||
|
||||
ImmutableList<byte[]> records = ImmutableList.copyOf(LevelDbLogReader.from(logFile.getPath()));
|
||||
assertThat(records).hasSize(1);
|
||||
ContactResource ofyEntity = rawRecordToOfyEntity(records.get(0), ContactResource.class);
|
||||
assertThat(ofyEntity.getContactId()).isEqualTo(contact.getContactId());
|
||||
}
|
||||
|
||||
private static Entity rawRecordToEntity(byte[] record) {
|
||||
EntityProto proto = new EntityProto();
|
||||
proto.parseFrom(record);
|
||||
return EntityTranslator.createFromPb(proto);
|
||||
}
|
||||
|
||||
private static <T> T rawRecordToOfyEntity(byte[] record, Class<T> expectedType) {
|
||||
return expectedType.cast(ofy().load().fromEntity(rawRecordToEntity(record)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.tools.LevelDbFileBuilder.Property;
|
||||
import google.registry.tools.EntityWrapper.Property;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.junit.Rule;
|
||||
|
@ -44,39 +44,44 @@ public class RecordAccumulatorTest {
|
|||
|
||||
// Note that we need to specify property values as "Long" for property comparisons to work
|
||||
// correctly because that's how they are deserialized from protos.
|
||||
ComparableEntity e1 =
|
||||
builder.addEntityProto(
|
||||
EntityWrapper e1 =
|
||||
EntityWrapper.from(
|
||||
BASE_ID,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L));
|
||||
ComparableEntity e2 =
|
||||
builder.addEntityProto(
|
||||
builder.addEntity(e1.getEntity());
|
||||
EntityWrapper e2 =
|
||||
EntityWrapper.from(
|
||||
BASE_ID + 1,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L));
|
||||
builder.addEntity(e2.getEntity());
|
||||
builder.build();
|
||||
|
||||
builder = new LevelDbFileBuilder(new File(subdir, "data2"));
|
||||
|
||||
// Duplicate of the record in the other file.
|
||||
builder.addEntityProto(
|
||||
BASE_ID,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L));
|
||||
builder.addEntity(
|
||||
EntityWrapper.from(
|
||||
BASE_ID,
|
||||
Property.create("eeny", 100L),
|
||||
Property.create("meeny", 200L),
|
||||
Property.create("miney", 300L))
|
||||
.getEntity());
|
||||
|
||||
ComparableEntity e3 =
|
||||
builder.addEntityProto(
|
||||
EntityWrapper e3 =
|
||||
EntityWrapper.from(
|
||||
BASE_ID + 2,
|
||||
Property.create("moxy", 100L),
|
||||
Property.create("fruvis", 200L),
|
||||
Property.create("cortex", 300L));
|
||||
builder.addEntity(e3.getEntity());
|
||||
builder.build();
|
||||
|
||||
ImmutableSet<ComparableEntity> entities =
|
||||
RecordAccumulator.readDirectory(subdir, any -> true).getComparableEntitySet();
|
||||
ImmutableSet<EntityWrapper> entities =
|
||||
RecordAccumulator.readDirectory(subdir, any -> true).getEntityWrapperSet();
|
||||
assertThat(entities).containsExactly(e1, e2, e3);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue