Skip to content

Unable to ThreadLocal ConfigCache.getOrCreate() #283

Description

@suankan

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions