Skip to content

Commit

Permalink
Resolves #1478: Support properties passing into FDBRecordStore, confi…
Browse files Browse the repository at this point in the history
…gured by adopter. Firstly used for Lucene indexing's parameters for compression/encryption.
  • Loading branch information
tian-yizuo committed Dec 7, 2021
1 parent 96a8730 commit 93db7e3
Show file tree
Hide file tree
Showing 13 changed files with 529 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/ReleaseNotes.md
Expand Up @@ -24,7 +24,7 @@ This version of the Record Layer changes the Java source and target compatibilit
* **Performance** Improvement 3 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
* **Performance** Improvement 4 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
* **Performance** Improvement 5 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
* **Feature** Feature 1 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
* **Feature** Passing properties configured by adopter into FDBRecordContext [(Issue #1478)](https://github.com/FoundationDB/fdb-record-layer/issues/1478)
* Online Indexer: add time limit to OnlineIndexer.Config [(Issue #1459)](https://github.com/FoundationDB/fdb-record-layer/issues/1459)
* **Feature** Feature 3 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
* **Feature** Feature 4 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN)
Expand Down
Expand Up @@ -276,7 +276,11 @@ public enum LogMessageKeys {
FILE_ID("file_id"),
FILE_NAME("file_name"),
FILE_REFERENCE("file_reference"),
ORIGINAL_DATA_SIZE("original_data-size");
ORIGINAL_DATA_SIZE("original_data-size"),

// Record context properties
PROPERTY_NAME("property_name"),
PROPERTY_TYPE("property_type");

private final String logKey;

Expand Down
Expand Up @@ -36,6 +36,7 @@
import com.apple.foundationdb.record.logging.KeyValueLogMessage;
import com.apple.foundationdb.record.logging.LogMessageKeys;
import com.apple.foundationdb.record.provider.common.StoreTimer;
import com.apple.foundationdb.record.provider.foundationdb.properties.RecordLayerPropertyStorage;
import com.apple.foundationdb.record.util.MapUtils;
import com.apple.foundationdb.system.SystemKeyspace;
import com.apple.foundationdb.tuple.ByteArrayUtil;
Expand Down Expand Up @@ -160,6 +161,8 @@ public class FDBRecordContext extends FDBTransactionContext implements AutoClose
private long trackOpenTimeNanos;
@Nonnull
private final Map<Object, Object> session = new LinkedHashMap<>();
@Nonnull
private final RecordLayerPropertyStorage propertyStorage;

protected FDBRecordContext(@Nonnull FDBDatabase fdb,
@Nonnull Transaction transaction,
Expand Down Expand Up @@ -212,6 +215,7 @@ protected FDBRecordContext(@Nonnull FDBDatabase fdb,
}

this.dirtyStoreState = false;
this.propertyStorage = config.getPropertyStorage();
}

@Nullable
Expand Down Expand Up @@ -1388,4 +1392,14 @@ public synchronized <T> T removeFromSession(@Nonnull String key, @Nonnull Class<
return (T) session.remove(key);
}

/**
* Get the properties configured by adopter for this FDBRecordContext.
*
* @return the wrapper of a mapping of the properties
*/
@API(API.Status.EXPERIMENTAL)
public RecordLayerPropertyStorage getPropertyStorage() {
return propertyStorage;
}

}
Expand Up @@ -22,6 +22,7 @@

import com.apple.foundationdb.TransactionOptions;
import com.apple.foundationdb.record.RecordCoreArgumentException;
import com.apple.foundationdb.record.provider.foundationdb.properties.RecordLayerPropertyStorage;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -51,6 +52,8 @@ public class FDBRecordContextConfig {
private final boolean saveOpenStackTrace;
@Nullable
private final TransactionListener listener;
@Nonnull
private final RecordLayerPropertyStorage propertyStorage;

private FDBRecordContextConfig(@Nonnull Builder builder) {
this.mdcContext = builder.mdcContext;
Expand All @@ -65,6 +68,7 @@ private FDBRecordContextConfig(@Nonnull Builder builder) {
this.trackOpen = builder.trackOpen;
this.saveOpenStackTrace = builder.saveOpenStackTrace;
this.listener = builder.listener;
this.propertyStorage = builder.recordContextProperties;
}

/**
Expand Down Expand Up @@ -195,6 +199,16 @@ public TransactionListener getTransactionListener() {
return listener;
}

/**
* Get the properties for this context configured by adopter.
*
* @return a wrapper of the properties mapping
*/
@Nonnull
public RecordLayerPropertyStorage getPropertyStorage() {
return propertyStorage;
}

/**
* Convert the current configuration to a builder. This will set all options in the builder to their
* current values in this configuration object.
Expand Down Expand Up @@ -227,6 +241,7 @@ public static class Builder {
private boolean trackOpen = false;
private boolean saveOpenStackTrace = false;
private TransactionListener listener = null;
private RecordLayerPropertyStorage recordContextProperties = RecordLayerPropertyStorage.getEmptyInstance();

private Builder() {
}
Expand All @@ -244,6 +259,7 @@ private Builder(@Nonnull FDBRecordContextConfig config) {
this.trackOpen = config.trackOpen;
this.saveOpenStackTrace = config.saveOpenStackTrace;
this.listener = config.listener;
this.recordContextProperties = config.propertyStorage;
}

private Builder(@Nonnull Builder config) {
Expand All @@ -259,6 +275,7 @@ private Builder(@Nonnull Builder config) {
this.trackOpen = config.trackOpen;
this.saveOpenStackTrace = config.saveOpenStackTrace;
this.listener = config.listener;
this.recordContextProperties = config.recordContextProperties;
}

/**
Expand Down Expand Up @@ -558,6 +575,28 @@ public TransactionListener getListener() {
return listener;
}

/**
* Get the properties' wrapper to be used by this context to pass in the parameters configured by adopter.
* If no properties specified, this context will use the {@link RecordLayerPropertyStorage#getEmptyInstance()} instance.
*
* @return the wrapper of the properties
*/
public RecordLayerPropertyStorage getRecordContextProperties() {
return recordContextProperties;
}

/**
* Set the properties' wrapper to be used by this context to pass in the parameters configured by adopter.
* If this method is never called, this context will use the {@link RecordLayerPropertyStorage#getEmptyInstance()} instance.
*
* @param recordContextProperties the wrapper of properties to be used by this context, configured by adopter
* @return this builder
*/
public Builder setRecordContextProperties(@Nonnull final RecordLayerPropertyStorage recordContextProperties) {
this.recordContextProperties = recordContextProperties;
return this;
}

/**
* Create an {@link FDBRecordContextConfig} from this builder.
*
Expand Down
@@ -0,0 +1,114 @@
/*
* RecordLayerPropertyKey.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2021 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.apple.foundationdb.record.provider.foundationdb.properties;

import com.apple.foundationdb.annotation.API;

import javax.annotation.Nonnull;
import java.util.Objects;
import java.util.function.Supplier;

/**
* A key for a property that adopter configures for Record Layer.
* This is not supposed to be defined by adopter outside Record Layer.
* The name, value type, and its default value are defined in this key, which are immutable.
* Call {@link #buildValue(Supplier)} with a {@link Supplier} of the value to build a {@link RecordLayerPropertyValue},
* to override the value for the property, and then build a {@link RecordLayerPropertyStorage} with the new property value.
*
* @param <T> the type of the property's value
*/
@API(API.Status.EXPERIMENTAL)
public final class RecordLayerPropertyKey<T> {
@Nonnull
private final String name;
@Nonnull
private final T defaultValue;
@Nonnull
private final Class<T> type;

private RecordLayerPropertyKey(@Nonnull final String name, @Nonnull T defaultValue, @Nonnull Class<T> type) {
this.name = name;
this.defaultValue = defaultValue;
this.type = type;
}

/**
* The name for each property needs to be unique and in a reverse FQDN format according to the package it is located.
*
* @return the name of this property
*/
@Nonnull
public String getName() {
return name;
}

@Nonnull
public Class<T> getType() {
return type;
}

@Nonnull
public T getDefaultValue() {
return defaultValue;
}

@Nonnull
public RecordLayerPropertyValue<T> buildValue(@Nonnull Supplier<T> valueSupplier) {
return new RecordLayerPropertyValue<>(this, valueSupplier);
}

@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other.getClass() != this.getClass()) {
return false;
}
RecordLayerPropertyKey<?> otherKey = (RecordLayerPropertyKey<?>) other;
return this.name.equals(otherKey.name);
}

@Override
public int hashCode() {
return Objects.hashCode(name);
}

public static RecordLayerPropertyKey<Boolean> booleanPropertyKey(@Nonnull final String name, @Nonnull final boolean defaultValue) {
return new RecordLayerPropertyKey<>(name, defaultValue, Boolean.class);
}

public static RecordLayerPropertyKey<String> stringPropertyKey(@Nonnull final String name, @Nonnull final String defaultValue) {
return new RecordLayerPropertyKey<>(name, defaultValue, String.class);
}

public static RecordLayerPropertyKey<Integer> integerPropertyKey(@Nonnull final String name, @Nonnull final int defaultValue) {
return new RecordLayerPropertyKey<>(name, defaultValue, Integer.class);
}

public static RecordLayerPropertyKey<Long> longPropertyKey(@Nonnull final String name, @Nonnull final long defaultValue) {
return new RecordLayerPropertyKey<>(name, defaultValue, Long.class);
}

public static RecordLayerPropertyKey<Double> doublePropertyKey(@Nonnull final String name, @Nonnull final double defaultValue) {
return new RecordLayerPropertyKey<>(name, defaultValue, Double.class);
}
}
@@ -0,0 +1,116 @@
/*
* RecordLayerPropertyStorage.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2021 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.apple.foundationdb.record.provider.foundationdb.properties;

import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.RecordCoreException;
import com.apple.foundationdb.record.logging.LogMessageKeys;
import com.google.common.collect.ImmutableMap;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
* Adopter can use this mapping of {@link RecordLayerPropertyKey}s to {@link RecordLayerPropertyValue}s to configure Record Layer parameters.
* The instances of {@link RecordLayerPropertyKey} and how they are used are defined in Record Layer.
* Adopters can populate {@link RecordLayerPropertyValue} and put them into this storage.
*/
@API(API.Status.EXPERIMENTAL)
public class RecordLayerPropertyStorage {
private static final RecordLayerPropertyStorage EMPTY_PROPERTY_STORAGE = new RecordLayerPropertyStorage(ImmutableMap.of());

@Nonnull
private final ImmutableMap<RecordLayerPropertyKey<?>, RecordLayerPropertyValue<?>> propertyMap;

private RecordLayerPropertyStorage(@Nonnull ImmutableMap<RecordLayerPropertyKey<?>, RecordLayerPropertyValue<?>> propertyMap) {
this.propertyMap = propertyMap;
}

@Nonnull
public ImmutableMap<RecordLayerPropertyKey<?>, RecordLayerPropertyValue<?>> getPropertyMap() {
return propertyMap;
}

@Nonnull
public <T> T getPropertyValue(@Nonnull RecordLayerPropertyKey<T> propertyKey) {
if (propertyMap.containsKey(propertyKey)) {
Object value = propertyMap.get(propertyKey).getValue();
try {
return propertyKey.getType().cast(value);
} catch (ClassCastException ex) {
throw new RecordCoreException("Invalid type for record context property", ex)
.addLogInfo(LogMessageKeys.PROPERTY_NAME, propertyKey.getName(),
LogMessageKeys.PROPERTY_TYPE, value.getClass().getName());
}
} else {
return propertyKey.getDefaultValue();
}
}

@Nonnull
public Builder toBuilder() {
return new Builder(propertyMap);
}

public static Builder newBuilder() {
return new Builder();
}

public static RecordLayerPropertyStorage getEmptyInstance() {
return EMPTY_PROPERTY_STORAGE;
}

public static class Builder {
private final Map<RecordLayerPropertyKey<?>, RecordLayerPropertyValue<?>> propertyMap;

private Builder() {
this.propertyMap = new HashMap<>();
}

private Builder(ImmutableMap<RecordLayerPropertyKey<?>, RecordLayerPropertyValue<?>> properties) {
this.propertyMap = new HashMap<>(properties);
}

public <T> Builder addProp(@Nonnull RecordLayerPropertyValue<T> propValue) {
if (this.propertyMap.putIfAbsent(propValue.getKey(), propValue) != null) {
throw new RecordCoreException("Duplicate property name is added")
.addLogInfo(LogMessageKeys.PROPERTY_NAME, propValue.getKey().getName());
}
return this;
}

public <T> Builder addProp(@Nonnull RecordLayerPropertyKey<T> propKey, @Nonnull Supplier<T> valueSupplier) {
final RecordLayerPropertyValue<T> propValue = propKey.buildValue(valueSupplier);
return this.addProp(propValue);
}

public <T> Builder addProp(@Nonnull RecordLayerPropertyKey<T> propKey, @Nonnull T value) {
final RecordLayerPropertyValue<T> propValue = propKey.buildValue(() -> value);
return this.addProp(propValue);
}

public RecordLayerPropertyStorage build() {
return new RecordLayerPropertyStorage(ImmutableMap.copyOf(propertyMap));
}
}
}

0 comments on commit 93db7e3

Please sign in to comment.