matteobaccan / owner

Get rid of the boilerplate code in properties based configuration.

Home Page:https://matteobaccan.github.io/owner/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to ThreadLocal ConfigCache.getOrCreate()

suankan opened this issue · comments

owner version: 1.0.12

Use case

I have a scenario where:

  • I first create my config in the Owner supported way:
@LoadPolicy(LoadType.MERGE)
@Sources({"system:properties", "classpath:tests.properties"})
public interface Config extends Mutable {
    @Key("prop")
    String prop();
}
  • But later at runtime I need to replace existing properties with new values.

So that when I'm running multi-thread,
I start with the same set of properties,
And each thread replaces properties at their own pace.

Roughly speaking I need to maintain an isolated instance of the config for each thread. But at the same time I don't want to create a new instance of my config within each thread.

Problem description

Documentation says that ConfigCache.getOrCreate is a thread-safe Singleton.

So I tried to instantiate my ConfigCache.getOrCreate as a ThreadLocal<Config>.

But my tests show that I'm unable to make it ThreadLocal at all. All the threads still get access to the same instance of the config.

Example

public class AppShort {
    public static void main(String[] args) {
        new Thread(() -> useConfig()).start();
        new Thread(() -> useConfig()).start();
    }

    private static void useConfig() {
        ThreadLocal<Config> threadLocalConfig = ThreadLocal.withInitial(() -> ConfigCache.getOrCreate(Config.class));
        System.out.println(Thread.currentThread().getName() + ": Current config id: " + System.identityHashCode(threadLocalConfig.get()));
        System.out.println(Thread.currentThread().getName() + ": Replacing property in config");
        String propOld = threadLocalConfig.get().setProperty("prop", Thread.currentThread().getName());
        System.out.println(Thread.currentThread().getName() + ": Replaced property, old value: " + propOld + ", new value: " + threadLocalConfig.get().prop());
    }
}

Execute

Running with -Dprop=origValue gives me this output:

Thread-2: Current config id: 1423910510
Thread-1: Current config id: 1423910510
Thread-2: Replacing property in config
Thread-1: Replacing property in config
Thread-2: Replaced property, old value: origValue, new value: Thread-1
Thread-1: Replaced property, old value: Thread-2, new value: Thread-1

We can see the same config Id is obtained in both threads.

Question

Is this by design or there still any way to make ConfigCache.getOrCreate() thread-isolated?

Workaround

I have to just use ConfigFactory.create() and wrap it into a Singleton myself inside each thread.
I don't have to wrap it into ThreadLocal and getting different config objects inside each thread by default.

Nice to have

It would be great if I could just use ConfigCache.getOrCreate() isolated inside each of my threads.