-
Notifications
You must be signed in to change notification settings - Fork 285
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
add DgsDataLoaderOptionsCustomizer #202
base: master
Are you sure you want to change the base?
Conversation
Could you also provide an example of what the DgsDataLoaderCustomizer would look like? Also, the tests don't actually verify that the custom options are set. Is there a way to test for that? |
Yeah, actually in our system, we hava an demand to customize the implementation of public class HotKeyCacheMap<U, V> implements CacheMap<U, V> {
private Map<U, V> cache;
private HotKeyCache hotKeyCache;
/**
* Default constructor
*/
public HotKeyCacheMap(HotKeyCache hotKeyCache) {
cache = new HashMap<>();
this.hotKeyCache = hotKeyCache;
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsKey(U key) {
return cache.containsKey(key) || hotKeyCacheContainsKey(key);
}
private boolean hotKeyCacheContainsKey(U key){
ValueModel valueModel = hotKeyCache.getValueModel(key.toString());
return valueModel != null && !valueModel.isDefaultValue();
}
/**
* {@inheritDoc}
*/
@Override
public V get(U key) {
if(cache.containsKey(key)) {
return cache.get(key);
}
else {
return (V)hotKeyCache.getValueModel(key.toString()).getValue();
}
}
/**
* {@inheritDoc}
*/
@Override
public CacheMap<U, V> set(U key, V value) {
cache.put(key, value);
hotKeyCache.setHotValue(key.toString(), value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public CacheMap<U, V> delete(U key) {
cache.remove(key);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public CacheMap<U, V> clear() {
cache.clear();
return this;
}
}
@Configuration
public class HotkeyConfiguration {
@Bean
public DgsDataLoaderOptionsCustomizer dgsDataLoaderOptionsCustomizer(HotKeyCacheMapService hotKeyCacheMapService){
return (dgsDataLoader, dataLoaderOptions) -> {
dataLoaderOptions.setCacheMap(hotKeyCacheMapService.getCacheMap());
dataLoaderOptions.setCacheKeyFunction(input -> dgsDataLoader.name() + "-" + input);
};
}
}
About the tests, I will provide a specific implementation to verify that custom options. |
The changes are looking good to me. |
@paulbakker @berngp - could you also take a look? |
Tested these changes and it works well. |
@lmy654 thanks for working on this. It looks like the mechanism is designed to have all dataloaders be configured in the same way. Is that correct? I'm wondering if it's common to have different configurations, eg. different caching strategies, for different dataloaders. I guess you could still do that based on the dataloader reference passed in into the customizer. As an alternative, would something like a Just bouncing ideas to get an understanding of what the best approach would be. |
@paulbakker Thanks for review. I understand your concern. Before I thought configurations like cacheMap, cacheKeyFuntion should be common for dataloaders in most scenarios, whereas other configurations like batchingEnabled, maxBatchSize are not common and have been customized by DgsDataLoader's properties. Now I think again, as a framework, we should always give user an option. Although now it could be done based on the dataloader reference passed in into the customizer, it is not good way. As @Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface DgsDataLoader {
String name();
boolean caching() default true;
boolean batching() default true;
int maxBatchSize() default 0;
String optionsCustomizerName() default "";
} class DgsDataLoaderProvider(private val applicationContext: ApplicationContext) {
// omit some code...
private fun createDataLoader(
batchLoader: BatchLoader<*, *>,
dgsDataLoader: DgsDataLoader
): DataLoader<out Any, out Any>? {
val options = DataLoaderOptions.newOptions()
.setBatchingEnabled(dgsDataLoader.batching)
.setCachingEnabled(dgsDataLoader.caching)
if (dgsDataLoader.maxBatchSize > 0) {
options.setMaxBatchSize(dgsDataLoader.maxBatchSize)
}
if (dgsDataLoader.optionsCustomizerName.isNotBlank()) {
applicationContext.getBean(dgsDataLoader.optionsCustomizerName, DgsDataLoaderOptionsCustomizer::class.java)
.customize(dgsDataLoader, options)
}
val extendedBatchLoader = wrappedDataLoader(batchLoader, dgsDataLoader.name)
return DataLoader.newDataLoader(extendedBatchLoader, options)
}
// omit some code
} application code: @Configuration
public class ExampleConfiguration {
@Bean
public DgsDataLoaderOptionsCustomizer exampleDgsDataLoaderOptionsCustomizer
(HotKeyCacheMapService hotKeyCacheMapService){
return (dgsDataLoader, dataLoaderOptions) -> {
dataLoaderOptions.setCacheMap(hotKeyCacheMapService.getCacheMap());
dataLoaderOptions.setCacheKeyFunction(input -> dgsDataLoader.name() + "-" + input);
};
}
} @DgsDataLoader(name = "exampleLoader", optionsCustomizerName = "exampleDgsDataLoaderOptionsCustomizer")
class ExampleBatchLoader : BatchLoader<String, String> {
override fun load(keys: MutableList<String>?): CompletionStage<MutableList<String>> {
return CompletableFuture.supplyAsync { mutableListOf("a", "b", "c") }
}
} I have already tested on my test environment, and pushed updated code. |
@lmy654 I think specifying it in the annotation is a good way. I'm wondering if we can use a
WDYT? |
@paulbakker I think it might be hard to use a @Configuration
public class ExampleConfiguration {
@Bean
public DgsDataLoaderOptionsCustomizer exampleDgsDataLoaderOptionsCustomizer1
(HotKeyCacheMapService hotKeyCacheMapService){
return (dgsDataLoader, dataLoaderOptions) -> {
dataLoaderOptions.setCacheMap(hotKeyCacheMapService.getCacheMap());
};
}
@Bean
public DgsDataLoaderOptionsCustomizer exampleDgsDataLoaderOptionsCustomizer2() {
return (dgsDataLoader, dataLoaderOptions) -> {
dataLoaderOptions.setCacheKeyFunction(input -> dgsDataLoader.name() + "-" + input);
};
}
} @DgsDataLoader(name = "exampleLoader1", optionsCustomizerName = "exampleDgsDataLoaderOptionsCustomizer1")
class ExampleBatchLoader1 : BatchLoader<String, String> {
override fun load(keys: MutableList<String>?): CompletionStage<MutableList<String>> {
return CompletableFuture.supplyAsync { mutableListOf("a", "b", "c") }
}
}
@DgsDataLoader(name = "exampleLoader2", optionsCustomizerName = "exampleDgsDataLoaderOptionsCustomizer2")
class ExampleBatchLoader2 : BatchLoader<String, String> {
override fun load(keys: MutableList<String>?): CompletionStage<MutableList<String>> {
return CompletableFuture.supplyAsync { mutableListOf("d", "e", "f") }
}
} |
...tlin/com/netflix/graphql/dgs/internal/MultiDataLoaderInstrumentationProvidersProviderTest.kt
Show resolved
Hide resolved
...tlin/com/netflix/graphql/dgs/internal/MultiDataLoaderInstrumentationProvidersProviderTest.kt
Show resolved
Hide resolved
Any progress on this? |
Anything stopping this from being merged than the conflicts? |
#1485
|
Pull Request type
Changes in this PR
Be able to cusomize more about a DataLoader's DataLoaderOptions, #201