Search in sources :

Example 6 with MetaDataException

use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.

the class MetaDataProtoEditor method renameRecordType.

/**
 * Rename a record type. This can be used to update any top-level record type defined within the
 * meta-data's records descriptor, including {@code NESTED} records or the union descriptor. However,
 * it cannot be used to rename nested messages (i.e., messages defined within other messages) or
 * records defined in imported files. In addition to updating the file descriptor, if the record type
 * is not {@code NESTED} or the union descriptor, update any other references to the record type
 * within the meta-data (such as within index definitions).
 *
 * @param metaDataBuilder the meta-data builder
 * @param recordTypeName the name of the existing top-level record type
 * @param newRecordTypeName the new name to give to the record type
 */
public static void renameRecordType(@Nonnull RecordMetaDataProto.MetaData.Builder metaDataBuilder, @Nonnull String recordTypeName, @Nonnull String newRecordTypeName) {
    // Create a copy of the records builder (rather than calling metaDataBuilder.getRecordsBuilder()) to avoid
    // corrupting the records builder in metaDataBuilder before all validation has been done.
    final DescriptorProtos.FileDescriptorProto records = metaDataBuilder.getRecords();
    boolean found = false;
    for (DescriptorProtos.DescriptorProto messageType : records.getMessageTypeList()) {
        if (messageType.getName().equals(recordTypeName)) {
            found = true;
        } else if (messageType.getName().equals(newRecordTypeName)) {
            throw new MetaDataException("Cannot rename record type to " + newRecordTypeName + " as it already exists");
        }
    }
    if (!found) {
        throw new MetaDataException("No record type found with name " + recordTypeName);
    }
    if (recordTypeName.equals(newRecordTypeName)) {
        // From here on in, we can assume that recordTypeName != newRecordTypeName, which simplifies things.
        return;
    }
    final DescriptorProtos.FileDescriptorProto.Builder recordsBuilder = records.toBuilder();
    // Determine the usage of the original record type by looking through the union builder.
    // If we find a field that matches, also update its name to be in the canonical form (i.e., "_" + recordTypeName)
    DescriptorProtos.DescriptorProto.Builder unionBuilder = fetchUnionBuilder(recordsBuilder);
    RecordMetaDataOptionsProto.RecordTypeOptions.Usage usage;
    if (unionBuilder.getName().equals(recordTypeName)) {
        usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.UNION;
    } else {
        DescriptorProtos.FieldDescriptorProto.Builder unionFieldBuilder = fetchUnionFieldBuilder(recordsBuilder, unionBuilder, recordTypeName);
        if (unionFieldBuilder == null) {
            usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.NESTED;
        } else {
            usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.RECORD;
            // Change the name to the "canonical" form unless that would cause a field name conflict
            if (unionFieldBuilder.getName().equals("_" + recordTypeName)) {
                String newFieldName = "_" + newRecordTypeName;
                if (unionBuilder.getFieldBuilderList().stream().noneMatch(otherUnionField -> otherUnionField != unionFieldBuilder && otherUnionField.getName().equals(newFieldName))) {
                    unionFieldBuilder.setName(newFieldName);
                }
            }
        }
    }
    // Do not allow renaming to the default union name unless the record type is already the union
    if (newRecordTypeName.equals(RecordMetaDataBuilder.DEFAULT_UNION_NAME) && !RecordMetaDataOptionsProto.RecordTypeOptions.Usage.UNION.equals(usage)) {
        throw new MetaDataException("Cannot rename record type to the default union name", LogMessageKeys.RECORD_TYPE, recordTypeName);
    }
    // Rename the record type within the file
    renameRecordTypeUsages(recordsBuilder, recordTypeName, newRecordTypeName);
    // If the record type is a top level record type, change its usage elsewhere in the meta-data
    if (RecordMetaDataOptionsProto.RecordTypeOptions.Usage.RECORD.equals(usage)) {
        renameTopLevelRecordType(metaDataBuilder, recordTypeName, newRecordTypeName);
    }
    // Update the file descriptor
    metaDataBuilder.setRecords(recordsBuilder);
}
Also used : DescriptorProtos(com.google.protobuf.DescriptorProtos) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException)

Example 7 with MetaDataException

use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.

the class IndexingBase method validateSameMetadataOrThrow.

protected void validateSameMetadataOrThrow(FDBRecordStore store) {
    final RecordMetaData metaData = store.getRecordMetaData();
    final RecordMetaDataProvider recordMetaDataProvider = common.getRecordStoreBuilder().getMetaDataProvider();
    if (recordMetaDataProvider == null || !metaData.equals(recordMetaDataProvider.getRecordMetaData())) {
        throw new MetaDataException("Store does not have the same metadata");
    }
}
Also used : RecordMetaData(com.apple.foundationdb.record.RecordMetaData) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException)

Example 8 with MetaDataException

use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.

the class FDBMetaDataStore method saveAndSetCurrent.

/**
 * Build meta-data to use from Protobuf and save.
 * @param metaDataProto the Protobuf form of the meta-data to save
 * @return a future that completes when the save is done
 */
@Nonnull
public CompletableFuture<Void> saveAndSetCurrent(@Nonnull RecordMetaDataProto.MetaData metaDataProto) {
    RecordMetaData validatedMetaData = buildMetaData(metaDataProto, true);
    // Load even if not maintaining history so as to get compatibility upgrade before (over-)writing.
    CompletableFuture<Void> future = loadCurrentSerialized().thenApply(oldSerialized -> {
        if (oldSerialized != null) {
            RecordMetaDataProto.MetaData oldProto = parseMetaDataProto(oldSerialized);
            int oldVersion = oldProto.getVersion();
            if (metaDataProto.getVersion() <= oldVersion) {
                LOGGER.warn(KeyValueLogMessage.of("Meta-data version did not increase", subspaceProvider.logKey(), subspaceProvider.toString(context), LogMessageKeys.OLD, oldVersion, LogMessageKeys.NEW, metaDataProto.getVersion()));
                throw new MetaDataException("meta-data version must increase");
            }
            // Build the meta-data, but don't use the local file descriptor as this should validate the original descriptors
            // against each other.
            RecordMetaData oldMetaData = buildMetaData(oldProto, true, false);
            RecordMetaData newMetaData = buildMetaData(metaDataProto, true, false);
            evolutionValidator.validate(oldMetaData, newMetaData);
            if (maintainHistory) {
                SplitHelper.saveWithSplit(context, getSubspace(), HISTORY_KEY_PREFIX.add(oldVersion), oldSerialized, null);
            }
        }
        return null;
    });
    future = future.thenApply(vignore -> {
        recordMetaData = validatedMetaData;
        byte[] serialized = metaDataProto.toByteArray();
        SplitHelper.saveWithSplit(context, getSubspace(), CURRENT_KEY, serialized, null);
        if (cache != null) {
            cache.setCurrentVersion(context, recordMetaData.getVersion());
            addPendingCacheUpdate(recordMetaData);
            addPendingCacheUpdate(serialized);
        }
        return null;
    });
    return instrument(FDBStoreTimer.Events.SAVE_META_DATA, future);
}
Also used : MetaDataEvolutionValidator(com.apple.foundationdb.record.metadata.MetaDataEvolutionValidator) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) SpotBugsSuppressWarnings(com.apple.foundationdb.annotation.SpotBugsSuppressWarnings) LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) Descriptors(com.google.protobuf.Descriptors) LoggerFactory(org.slf4j.LoggerFactory) CompletableFuture(java.util.concurrent.CompletableFuture) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) Subspace(com.apple.foundationdb.subspace.Subspace) ArrayList(java.util.ArrayList) Tuple(com.apple.foundationdb.tuple.Tuple) KeyValueLogMessage(com.apple.foundationdb.record.logging.KeyValueLogMessage) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) RecordMetaDataProto(com.apple.foundationdb.record.RecordMetaDataProto) RecordMetaDataOptionsProto(com.apple.foundationdb.record.RecordMetaDataOptionsProto) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) Logger(org.slf4j.Logger) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) RecordTypeBuilder(com.apple.foundationdb.record.metadata.RecordTypeBuilder) Consumer(java.util.function.Consumer) KeySpacePath(com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpacePath) List(java.util.List) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) Index(com.apple.foundationdb.record.metadata.Index) ExtensionRegistry(com.google.protobuf.ExtensionRegistry) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) API(com.apple.foundationdb.annotation.API) VisibleForTesting(com.google.common.annotations.VisibleForTesting) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) RecordMetaDataProto(com.apple.foundationdb.record.RecordMetaDataProto) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) Nonnull(javax.annotation.Nonnull)

Example 9 with MetaDataException

use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.

the class RecordMetaData method toProto.

/**
 * Serializes the record meta-data to a <code>MetaData</code> proto message. This operates like
 * {@link #toProto()} except that any dependency in the excluded list is not included in the
 * serialized proto message. If the list is set to {@code null}, then all dependencies will be serialized
 * to the proto message including those that are execluded by default.
 *
 * @param excludedDependencies a list of dependencies not to include in the serialized proto
 * @return the serialized <code>MetaData</code> proto message
 * @throws KeyExpression.SerializationException on any serialization failures
 * @throws MetaDataException if this {@code RecordMetaData} was initialized with a
 *      {@linkplain RecordMetaDataBuilder#setLocalFileDescriptor(Descriptors.FileDescriptor) local file descriptor}
 * @see #toProto()
 */
@Nonnull
@SuppressWarnings("deprecation")
public RecordMetaDataProto.MetaData toProto(@Nullable Descriptors.FileDescriptor[] excludedDependencies) throws KeyExpression.SerializationException {
    if (usesLocalRecordsDescriptor) {
        throw new MetaDataException("cannot serialize meta-data with a local records descriptor to proto");
    }
    RecordMetaDataProto.MetaData.Builder builder = RecordMetaDataProto.MetaData.newBuilder();
    // Set the root records.
    builder.setRecords(recordsDescriptor.toProto());
    // Convert the exclusion list to a map
    Map<String, Descriptors.FileDescriptor> excludeMap = null;
    if (excludedDependencies != null) {
        excludeMap = new HashMap<>(excludedDependencies.length);
        for (Descriptors.FileDescriptor dependency : excludedDependencies) {
            excludeMap.put(dependency.getName(), dependency);
        }
    }
    // Add in the rest of dependencies.
    Map<String, Descriptors.FileDescriptor> allDependencies = new TreeMap<>();
    getDependencies(recordsDescriptor, allDependencies, excludeMap);
    for (Descriptors.FileDescriptor dependency : allDependencies.values()) {
        builder.addDependencies(dependency.toProto());
    }
    // Create builders for each index so that we can then add associated record types (etc.).
    Map<String, RecordMetaDataProto.Index.Builder> indexBuilders = new TreeMap<>();
    for (Map.Entry<String, Index> entry : indexes.entrySet()) {
        indexBuilders.put(entry.getKey(), entry.getValue().toProto().toBuilder());
    }
    for (RecordType recordType : getRecordTypes().values()) {
        // Add this record type to each appropriate index.
        for (Index index : recordType.getIndexes()) {
            indexBuilders.get(index.getName()).addRecordType(recordType.getName());
        }
        for (Index index : recordType.getMultiTypeIndexes()) {
            indexBuilders.get(index.getName()).addRecordType(recordType.getName());
        }
        RecordMetaDataProto.RecordType.Builder typeBuilder = builder.addRecordTypesBuilder().setName(recordType.getName()).setPrimaryKey(recordType.getPrimaryKey().toKeyExpression());
        if (recordType.getSinceVersion() != null) {
            typeBuilder.setSinceVersion(recordType.getSinceVersion());
        }
        if (recordType.hasExplicitRecordTypeKey()) {
            typeBuilder.setExplicitKey(LiteralKeyExpression.toProtoValue(recordType.getExplicitRecordTypeKey()));
        }
    }
    indexBuilders.values().forEach(builder::addIndexes);
    // Add in the former indexes.
    for (FormerIndex formerIndex : getFormerIndexes()) {
        builder.addFormerIndexes(formerIndex.toProto());
    }
    // Add in the final options.
    builder.setSplitLongRecords(splitLongRecords);
    builder.setStoreRecordVersions(storeRecordVersions);
    builder.setVersion(version);
    if (usesSubspaceKeyCounter()) {
        builder.setSubspaceKeyCounter(subspaceKeyCounter);
        builder.setUsesSubspaceKeyCounter(true);
    }
    if (recordCountKey != null) {
        builder.setRecordCountKey(recordCountKey.toKeyExpression());
    }
    for (SyntheticRecordType<?> syntheticRecordType : syntheticRecordTypes.values()) {
        if (syntheticRecordType instanceof JoinedRecordType) {
            builder.addJoinedRecordTypes(((JoinedRecordType) syntheticRecordType).toProto());
        }
    }
    return builder.build();
}
Also used : JoinedRecordType(com.apple.foundationdb.record.metadata.JoinedRecordType) FormerIndex(com.apple.foundationdb.record.metadata.FormerIndex) Index(com.apple.foundationdb.record.metadata.Index) TreeMap(java.util.TreeMap) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) FormerIndex(com.apple.foundationdb.record.metadata.FormerIndex) JoinedRecordType(com.apple.foundationdb.record.metadata.JoinedRecordType) RecordType(com.apple.foundationdb.record.metadata.RecordType) SyntheticRecordType(com.apple.foundationdb.record.metadata.SyntheticRecordType) Descriptors(com.google.protobuf.Descriptors) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) Map(java.util.Map) Nonnull(javax.annotation.Nonnull)

Example 10 with MetaDataException

use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.

the class RecordMetaDataBuilder method removeIndex.

public void removeIndex(@Nonnull String name) {
    Index index = indexes.remove(name);
    if (index == null) {
        throw new MetaDataException("No index named " + name + " defined");
    }
    for (RecordTypeBuilder recordType : recordTypes.values()) {
        recordType.getIndexes().remove(index);
        recordType.getMultiTypeIndexes().remove(index);
    }
    universalIndexes.remove(name);
    formerIndexes.add(new FormerIndex(index.getSubspaceKey(), index.getAddedVersion(), ++version, name));
}
Also used : FormerIndex(com.apple.foundationdb.record.metadata.FormerIndex) FormerIndex(com.apple.foundationdb.record.metadata.FormerIndex) Index(com.apple.foundationdb.record.metadata.Index) SyntheticRecordTypeBuilder(com.apple.foundationdb.record.metadata.SyntheticRecordTypeBuilder) RecordTypeBuilder(com.apple.foundationdb.record.metadata.RecordTypeBuilder) JoinedRecordTypeBuilder(com.apple.foundationdb.record.metadata.JoinedRecordTypeBuilder) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException)

Aggregations

MetaDataException (com.apple.foundationdb.record.metadata.MetaDataException)61 Test (org.junit.jupiter.api.Test)33 RecordMetaData (com.apple.foundationdb.record.RecordMetaData)29 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)28 MetaDataProtoTest (com.apple.foundationdb.record.metadata.MetaDataProtoTest)25 Nonnull (javax.annotation.Nonnull)24 Index (com.apple.foundationdb.record.metadata.Index)22 Descriptors (com.google.protobuf.Descriptors)14 Tuple (com.apple.foundationdb.tuple.Tuple)13 List (java.util.List)12 ScanProperties (com.apple.foundationdb.record.ScanProperties)11 TupleRange (com.apple.foundationdb.record.TupleRange)11 KeyExpression (com.apple.foundationdb.record.metadata.expressions.KeyExpression)11 IndexEntry (com.apple.foundationdb.record.IndexEntry)10 IndexTypes (com.apple.foundationdb.record.metadata.IndexTypes)10 DescriptorProtos (com.google.protobuf.DescriptorProtos)10 TestRecords1Proto (com.apple.foundationdb.record.TestRecords1Proto)9 Message (com.google.protobuf.Message)9 Collectors (java.util.stream.Collectors)9 IndexScanType (com.apple.foundationdb.record.IndexScanType)8