FDBMetaDataStore extends FDBStoreBase implements RecordMetaDataProvider { @API(API.Status.MAINTAINED) public void saveRecordMetaData(@Nonnull Descriptors.FileDescriptor fileDescriptor) { context.asyncToSync(FDBStoreTimer.Waits.WAIT_SAVE_META_DATA, saveRecordMetaDataAsync(fileDescriptor)); } @API(API.Status.UNSTABLE)  FDBMetaDataStore(@Nonnull FDBRecordContext context, @Nonnull Subspace subspace,
                            @Nullable MetaDataCache cache);  FDBMetaDataStore(@Nonnull FDBRecordContext context, @Nonnull KeySpacePath path); @SpotBugsSuppressWarnings("EI_EXPOSE_REP2") void setDependencies(@Nonnull Descriptors.FileDescriptor[] dependencies); @Nullable Descriptors.FileDescriptor getLocalFileDescriptor(); void setLocalFileDescriptor(@Nullable Descriptors.FileDescriptor localFileDescriptor); @Nonnull MetaDataEvolutionValidator getEvolutionValidator(); void setEvolutionValidator(@Nonnull MetaDataEvolutionValidator evolutionValidator); void setExtensionRegistry(@Nullable ExtensionRegistry extensionRegistry); void setMaintainHistory(boolean maintainHistory); CompletableFuture<RecordMetaDataProto.MetaData> loadAndSetCurrent(boolean checkCache, int currentVersion); CompletableFuture<RecordMetaData> loadVersion(int version); @Nonnull CompletableFuture<Void> saveAndSetCurrent(@Nonnull RecordMetaDataProto.MetaData metaDataProto); CompletableFuture<RecordMetaData> getRecordMetaDataAsync(boolean errorIfMissing); @Nonnull CompletableFuture<Void> preloadMetaData(@Nullable RecordMetaDataProvider metaDataProvider); @Nonnull @Override RecordMetaData getRecordMetaData(); @API(API.Status.MAINTAINED) void saveRecordMetaData(@Nonnull Descriptors.FileDescriptor fileDescriptor); void saveRecordMetaData(@Nonnull RecordMetaDataProvider metaDataProvider); void saveRecordMetaData(@Nonnull RecordMetaDataProto.MetaData metaDataProto); @API(API.Status.MAINTAINED) CompletableFuture<Void> saveRecordMetaDataAsync(@Nonnull Descriptors.FileDescriptor fileDescriptor); void addIndex(@Nonnull String recordType, @Nonnull String indexName, @Nonnull String fieldName); void addIndex(@Nonnull String recordType, @Nonnull String indexName, @Nonnull KeyExpression indexExpression); void addIndex(@Nonnull String recordType, @Nonnull Index index); @Nonnull CompletableFuture<Void> addIndexAsync(@Nonnull String recordType, @Nonnull String indexName, @Nonnull String fieldName); @Nonnull CompletableFuture<Void> addIndexAsync(@Nonnull String recordType, @Nonnull String indexName,
                                                 @Nonnull KeyExpression indexExpression); @Nonnull CompletableFuture<Void> addIndexAsync(@Nonnull String recordType, @Nonnull Index index); void addMultiTypeIndex(@Nullable List<String> recordTypes, @Nonnull Index index); @Nonnull CompletableFuture<Void> addMultiTypeIndexAsync(@Nullable List<String> recordTypes, @Nonnull Index index); void addUniversalIndex(@Nonnull Index index); @Nonnull CompletableFuture<Void> addUniversalIndexAsync(@Nonnull Index index); void dropIndex(@Nonnull String indexName); @Nonnull CompletableFuture<Void> dropIndexAsync(@Nonnull String indexName); @API(API.Status.MAINTAINED) void updateRecords(@Nonnull Descriptors.FileDescriptor recordsDescriptor); @Nonnull @API(API.Status.MAINTAINED) CompletableFuture<Void> updateRecordsAsync(@Nonnull Descriptors.FileDescriptor recordsDescriptor); void mutateMetaData(@Nonnull Consumer<RecordMetaDataProto.MetaData.Builder> mutateMetaDataProto); void mutateMetaData(@Nonnull Consumer<RecordMetaDataProto.MetaData.Builder> mutateMetaDataProto,
                               @Nullable Consumer<RecordMetaDataBuilder> mutateRecordMetaDataBuilder); @Nonnull CompletableFuture<Void> mutateMetaDataAsync(@Nonnull Consumer<RecordMetaDataProto.MetaData.Builder> mutateMetaDataProto); @Nonnull CompletableFuture<Void> mutateMetaDataAsync(@Nonnull Consumer<RecordMetaDataProto.MetaData.Builder> mutateMetaDataProto,
                                                       @Nullable Consumer<RecordMetaDataBuilder> mutateRecordMetaDataBuilder); void updateStoreRecordVersions(boolean storeRecordVersions); @Nonnull CompletableFuture<Void> updateStoreRecordVersionsAsync(boolean storeRecordVersions); void enableSplitLongRecords(); @Nonnull CompletableFuture<Void> enableSplitLongRecordsAsync(); @Nullable @VisibleForTesting MetaDataCache getCache(); static final Tuple CURRENT_KEY; static final Tuple HISTORY_KEY_PREFIX; static final Tuple OLD_FORMAT_KEY; }
@Test public void withoutBumpingVersion() { RecordMetaData metaData = RecordMetaData.build(TestRecords1Proto.getDescriptor()); try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); metaDataStore.saveRecordMetaData(metaData); MetaDataException e = assertThrows(MetaDataException.class, () -> metaDataStore.saveRecordMetaData(metaData)); assertThat(e.getMessage(), containsString("meta-data version must increase")); context.commit(); } }
@Test public void renameFullyQualifiedSimpleRecordType() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecords1Proto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType(".com.apple.foundationdb.record.test1.MySimpleRecord", "MyNewSimpleRecord")); assertThat(e.getMessage(), containsString("No record type found")); } }
@Test public void nestedRecordDefinition() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecordsDoubleNestedProto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType("OuterRecord.MiddleRecord", "OuterRecord.MiddlingRecord")); assertEquals("No record type found with name OuterRecord.MiddleRecord", e.getMessage()); } }
@Test public void newNameSuggestsNestedType() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecordsDoubleNestedProto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType("OuterRecord", "OuterRecord.SomeNestedRecord")); assertEquals("Error converting from protobuf", e.getMessage()); } }
@Test public void dontRenameRecordTypeWhenClashingWithImported() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecordsImportedAndNewProto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); renameRecordType("MySimpleRecord", "MyLocalSimpleRecord"); assertEquals(ImmutableSet.of("MySimpleRecord", "MyOtherRecord"), metaData.getRecordTypes().keySet()); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType("MyOtherRecord", "MySimpleRecord")); assertEquals("Cannot rename record type to MySimpleRecord as an imported record type of that name already exists", e.getMessage()); } }
@Test public void dontRenameNonUnionToUnion() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecords1Proto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); renameRecordType(RecordMetaDataBuilder.DEFAULT_UNION_NAME, "RecordOneUnion"); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType("MySimpleRecord", RecordMetaDataBuilder.DEFAULT_UNION_NAME)); assertEquals("Cannot rename record type to the default union name", e.getMessage()); } }
@Test public void tryRenameNonExistentRecord() { try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); RecordMetaData metaData = RecordMetaData.build(TestRecords1Proto.getDescriptor()); metaDataStore.saveRecordMetaData(metaData); context.commit(); } try (FDBRecordContext context = fdb.openContext()) { openMetaDataStore(context); MetaDataException e = assertThrows(MetaDataException.class, () -> renameRecordType("MyNonExistentRecord", "SomethingElse")); assertEquals("No record type found with name MyNonExistentRecord", e.getMessage()); } }