Skip to content

Making DataLoaderOptions immutable #176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 190 additions & 50 deletions src/main/java/org/dataloader/DataLoaderOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,41 @@
package org.dataloader;

import org.dataloader.annotations.PublicApi;
import org.dataloader.impl.Assertions;
import org.dataloader.scheduler.BatchLoaderScheduler;
import org.dataloader.stats.NoOpStatisticsCollector;
import org.dataloader.stats.StatisticsCollector;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

import static org.dataloader.impl.Assertions.nonNull;

/**
* Configuration options for {@link DataLoader} instances.
* Configuration options for {@link DataLoader} instances. This is an immutable class so each time
* you change a value it returns a new object.
*
* @author <a href="https://github.com/aschrijver/">Arnold Schrijver</a>
*/
@PublicApi
public class DataLoaderOptions {

private static final BatchLoaderContextProvider NULL_PROVIDER = () -> null;

private boolean batchingEnabled;
private boolean cachingEnabled;
private boolean cachingExceptionsEnabled;
private CacheKey<?> cacheKeyFunction;
private CacheMap<?, ?> cacheMap;
private ValueCache<?, ?> valueCache;
private int maxBatchSize;
private Supplier<StatisticsCollector> statisticsCollector;
private BatchLoaderContextProvider environmentProvider;
private ValueCacheOptions valueCacheOptions;
private BatchLoaderScheduler batchLoaderScheduler;
private static final Supplier<StatisticsCollector> NOOP_COLLECTOR = NoOpStatisticsCollector::new;
private static final ValueCacheOptions DEFAULT_VALUE_CACHE_OPTIONS = ValueCacheOptions.newOptions();

private final boolean batchingEnabled;
private final boolean cachingEnabled;
private final boolean cachingExceptionsEnabled;
private final CacheKey<?> cacheKeyFunction;
private final CacheMap<?, ?> cacheMap;
private final ValueCache<?, ?> valueCache;
private final int maxBatchSize;
private final Supplier<StatisticsCollector> statisticsCollector;
private final BatchLoaderContextProvider environmentProvider;
private final ValueCacheOptions valueCacheOptions;
private final BatchLoaderScheduler batchLoaderScheduler;

/**
* Creates a new data loader options with default settings.
Expand All @@ -56,13 +60,30 @@ public DataLoaderOptions() {
batchingEnabled = true;
cachingEnabled = true;
cachingExceptionsEnabled = true;
cacheKeyFunction = null;
cacheMap = null;
valueCache = null;
maxBatchSize = -1;
statisticsCollector = NoOpStatisticsCollector::new;
statisticsCollector = NOOP_COLLECTOR;
environmentProvider = NULL_PROVIDER;
valueCacheOptions = ValueCacheOptions.newOptions();
valueCacheOptions = DEFAULT_VALUE_CACHE_OPTIONS;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

batchLoaderScheduler = null;
}

private DataLoaderOptions(Builder builder) {
this.batchingEnabled = builder.batchingEnabled;
this.cachingEnabled = builder.cachingEnabled;
this.cachingExceptionsEnabled = builder.cachingExceptionsEnabled;
this.cacheKeyFunction = builder.cacheKeyFunction;
this.cacheMap = builder.cacheMap;
this.valueCache = builder.valueCache;
this.maxBatchSize = builder.maxBatchSize;
this.statisticsCollector = builder.statisticsCollector;
this.environmentProvider = builder.environmentProvider;
this.valueCacheOptions = builder.valueCacheOptions;
this.batchLoaderScheduler = builder.batchLoaderScheduler;
}

/**
* Clones the provided data loader options.
*
Expand Down Expand Up @@ -90,6 +111,51 @@ public static DataLoaderOptions newOptions() {
return new DataLoaderOptions();
}

/**
* @return a new default data loader options {@link Builder} that you can then customize
*/
public static DataLoaderOptions.Builder newOptionsBuilder() {
return new DataLoaderOptions.Builder();
}

/**
* @param otherOptions the options to copy
* @return a new default data loader options {@link Builder} from the specified one that you can then customize
*/
public static DataLoaderOptions.Builder newDataLoaderOptions(DataLoaderOptions otherOptions) {
return new DataLoaderOptions.Builder(otherOptions);
}

/**
* Will transform the current options in to a builder ands allow you to build a new set of options
*
* @param builderConsumer the consumer of a builder that has this objects starting values
* @return a new {@link DataLoaderOptions} object
*/
public DataLoaderOptions transform(Consumer<Builder> builderConsumer) {
Builder builder = newOptionsBuilder();
builderConsumer.accept(builder);
return builder.build();
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
DataLoaderOptions that = (DataLoaderOptions) o;
return batchingEnabled == that.batchingEnabled
&& cachingEnabled == that.cachingEnabled
&& cachingExceptionsEnabled == that.cachingExceptionsEnabled
&& maxBatchSize == that.maxBatchSize
&& Objects.equals(cacheKeyFunction, that.cacheKeyFunction) &&
Objects.equals(cacheMap, that.cacheMap) &&
Objects.equals(valueCache, that.valueCache) &&
Objects.equals(statisticsCollector, that.statisticsCollector) &&
Objects.equals(environmentProvider, that.environmentProvider) &&
Objects.equals(valueCacheOptions, that.valueCacheOptions) &&
Objects.equals(batchLoaderScheduler, that.batchLoaderScheduler);
}


/**
* Option that determines whether to use batching (the default), or not.
*
Expand All @@ -103,12 +169,10 @@ public boolean batchingEnabled() {
* Sets the option that determines whether batch loading is enabled.
*
* @param batchingEnabled {@code true} to enable batch loading, {@code false} otherwise
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setBatchingEnabled(boolean batchingEnabled) {
this.batchingEnabled = batchingEnabled;
return this;
return builder().setBatchingEnabled(batchingEnabled).build();
}

/**
Expand All @@ -124,17 +188,15 @@ public boolean cachingEnabled() {
* Sets the option that determines whether caching is enabled.
*
* @param cachingEnabled {@code true} to enable caching, {@code false} otherwise
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setCachingEnabled(boolean cachingEnabled) {
this.cachingEnabled = cachingEnabled;
return this;
return builder().setCachingEnabled(cachingEnabled).build();
}

/**
* Option that determines whether to cache exceptional values (the default), or not.
*
* <p>
* For short-lived caches (that is request caches) it makes sense to cache exceptions since
* it's likely the key is still poisoned. However, if you have long-lived caches, then it may make
* sense to set this to false since the downstream system may have recovered from its failure
Expand All @@ -150,12 +212,10 @@ public boolean cachingExceptionsEnabled() {
* Sets the option that determines whether exceptional values are cache enabled.
*
* @param cachingExceptionsEnabled {@code true} to enable caching exceptional values, {@code false} otherwise
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setCachingExceptionsEnabled(boolean cachingExceptionsEnabled) {
this.cachingExceptionsEnabled = cachingExceptionsEnabled;
return this;
return builder().setCachingExceptionsEnabled(cachingExceptionsEnabled).build();
}

/**
Expand All @@ -173,12 +233,10 @@ public Optional<CacheKey> cacheKeyFunction() {
* Sets the function to use for creating the cache key, if caching is enabled.
*
* @param cacheKeyFunction the cache key function to use
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setCacheKeyFunction(CacheKey<?> cacheKeyFunction) {
this.cacheKeyFunction = cacheKeyFunction;
return this;
return builder().setCacheKeyFunction(cacheKeyFunction).build();
}

/**
Expand All @@ -196,12 +254,10 @@ public DataLoaderOptions setCacheKeyFunction(CacheKey<?> cacheKeyFunction) {
* Sets the cache map implementation to use for caching, if caching is enabled.
*
* @param cacheMap the cache map instance
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setCacheMap(CacheMap<?, ?> cacheMap) {
this.cacheMap = cacheMap;
return this;
return builder().setCacheMap(cacheMap).build();
}

/**
Expand All @@ -219,12 +275,10 @@ public int maxBatchSize() {
* before they are split into multiple class
*
* @param maxBatchSize the maximum batch size
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setMaxBatchSize(int maxBatchSize) {
this.maxBatchSize = maxBatchSize;
return this;
return builder().setMaxBatchSize(maxBatchSize).build();
}

/**
Expand All @@ -240,12 +294,10 @@ public StatisticsCollector getStatisticsCollector() {
* a common value
*
* @param statisticsCollector the statistics collector to use
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setStatisticsCollector(Supplier<StatisticsCollector> statisticsCollector) {
this.statisticsCollector = nonNull(statisticsCollector);
return this;
return builder().setStatisticsCollector(nonNull(statisticsCollector)).build();
}

/**
Expand All @@ -259,12 +311,10 @@ public BatchLoaderContextProvider getBatchLoaderContextProvider() {
* Sets the batch loader environment provider that will be used to give context to batch load functions
*
* @param contextProvider the batch loader context provider
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setBatchLoaderContextProvider(BatchLoaderContextProvider contextProvider) {
this.environmentProvider = nonNull(contextProvider);
return this;
return builder().setBatchLoaderContextProvider(nonNull(contextProvider)).build();
}

/**
Expand All @@ -282,12 +332,10 @@ public DataLoaderOptions setBatchLoaderContextProvider(BatchLoaderContextProvide
* Sets the value cache implementation to use for caching values, if caching is enabled.
*
* @param valueCache the value cache instance
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setValueCache(ValueCache<?, ?> valueCache) {
this.valueCache = valueCache;
return this;
return builder().setValueCache(valueCache).build();
}

/**
Expand All @@ -301,12 +349,10 @@ public ValueCacheOptions getValueCacheOptions() {
* Sets the {@link ValueCacheOptions} that control how the {@link ValueCache} will be used
*
* @param valueCacheOptions the value cache options
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setValueCacheOptions(ValueCacheOptions valueCacheOptions) {
this.valueCacheOptions = Assertions.nonNull(valueCacheOptions);
return this;
return builder().setValueCacheOptions(nonNull(valueCacheOptions)).build();
}

/**
Expand All @@ -321,11 +367,105 @@ public BatchLoaderScheduler getBatchLoaderScheduler() {
* to some future time.
*
* @param batchLoaderScheduler the scheduler
*
* @return the data loader options for fluent coding
*/
public DataLoaderOptions setBatchLoaderScheduler(BatchLoaderScheduler batchLoaderScheduler) {
this.batchLoaderScheduler = batchLoaderScheduler;
return this;
return builder().setBatchLoaderScheduler(batchLoaderScheduler).build();
}

private Builder builder() {
return new Builder(this);
}

public static class Builder {
private boolean batchingEnabled;
private boolean cachingEnabled;
private boolean cachingExceptionsEnabled;
private CacheKey<?> cacheKeyFunction;
private CacheMap<?, ?> cacheMap;
private ValueCache<?, ?> valueCache;
private int maxBatchSize;
private Supplier<StatisticsCollector> statisticsCollector;
private BatchLoaderContextProvider environmentProvider;
private ValueCacheOptions valueCacheOptions;
private BatchLoaderScheduler batchLoaderScheduler;

public Builder() {
this(new DataLoaderOptions()); // use the defaults of the DataLoaderOptions for this builder
}

Builder(DataLoaderOptions other) {
this.batchingEnabled = other.batchingEnabled;
this.cachingEnabled = other.cachingEnabled;
this.cachingExceptionsEnabled = other.cachingExceptionsEnabled;
this.cacheKeyFunction = other.cacheKeyFunction;
this.cacheMap = other.cacheMap;
this.valueCache = other.valueCache;
this.maxBatchSize = other.maxBatchSize;
this.statisticsCollector = other.statisticsCollector;
this.environmentProvider = other.environmentProvider;
this.valueCacheOptions = other.valueCacheOptions;
this.batchLoaderScheduler = other.batchLoaderScheduler;
}

public Builder setBatchingEnabled(boolean batchingEnabled) {
this.batchingEnabled = batchingEnabled;
return this;
}

public Builder setCachingEnabled(boolean cachingEnabled) {
this.cachingEnabled = cachingEnabled;
return this;
}

public Builder setCachingExceptionsEnabled(boolean cachingExceptionsEnabled) {
this.cachingExceptionsEnabled = cachingExceptionsEnabled;
return this;
}

public Builder setCacheKeyFunction(CacheKey<?> cacheKeyFunction) {
this.cacheKeyFunction = cacheKeyFunction;
return this;
}

public Builder setCacheMap(CacheMap<?, ?> cacheMap) {
this.cacheMap = cacheMap;
return this;
}

public Builder setValueCache(ValueCache<?, ?> valueCache) {
this.valueCache = valueCache;
return this;
}

public Builder setMaxBatchSize(int maxBatchSize) {
this.maxBatchSize = maxBatchSize;
return this;
}

public Builder setStatisticsCollector(Supplier<StatisticsCollector> statisticsCollector) {
this.statisticsCollector = statisticsCollector;
return this;
}

public Builder setBatchLoaderContextProvider(BatchLoaderContextProvider environmentProvider) {
this.environmentProvider = environmentProvider;
return this;
}

public Builder setValueCacheOptions(ValueCacheOptions valueCacheOptions) {
this.valueCacheOptions = valueCacheOptions;
return this;
}

public Builder setBatchLoaderScheduler(BatchLoaderScheduler batchLoaderScheduler) {
this.batchLoaderScheduler = batchLoaderScheduler;
return this;
}

public DataLoaderOptions build() {
return new DataLoaderOptions(this);
}

}
}
Loading