Best Way To Pass Tenant ID To New Thread In Java

Multi Tenant Database, Concurrency

Md Ariful Islam Rana
3 min readJun 26, 2019

In Java multi tenant database application, we resolve tenant ID for every web transaction. After resolving tenant ID, we set it to tenant context and retrieve it later from anywhere in application to access database within the scope of current thread. Typical tenant context should looks as follows:

public class TenantContext {
private static final ThreadLocal<Tenant> tenantHolder =
new ThreadLocal<>();

public static Tenant getTenant() {
Tenant tenant = tenantHolder.get();
return Objects.isNull(tenant) ? Tenant.DEFAULT : tenant;
}

public static void setTenant(Tenant tenant) {
tenantHolder.set(tenant);
}

public static void clearTenant() {
tenantHolder.remove();
}
}

Let’s assume, Tenant is an Enum which holds all tenant IDs.

The above tenant context is thread specific. When we first resolve tenant, we have to set it to tenant context, then we can access it from anywhere in the current thread only. The problem arises when we have to create new thread to do some time consuming operation. And that operation needs to access database.

Accessing tenant specific database from new thread

Normally, we start thread by passing Runnable as follows:

new Thread(() -> {
// doing operation
}).start();

In new thread, if we call TenantContext.getTenant(), it will return DEFAULT tenant instead of our desired tenant ID as it’s in the scope of new thread. We can easily solve this as follows:

Tenant tenant = TenantContext.getTenant();
new Thread(() -> {
// set tenant
// doing operation
// clear tenant
}).start();

But this isn’t best approach. Because we have to concern to pass, set and clear tenant every time. Clearing tenant is very important, otherwise it may leads to memory leak. Developers have to put extra care to pass, set and clear tenant along with business logic. To remove this hassle, we can do it in a better way. For this, we have to create custom thread class as follows:

public class TenantAwareThread extends Thread {
private Tenant tenant = null;

public TenantAwareThread(Runnable target) {
super(target);
this.tenant = TenantContext.getTenant();
}

@Override
public void run() {
TenantContext.setTenant(this.tenant);
super.run();
TenantContext.clearTenant();
}
}

And we have to create new thread as follows:

new TenantAwareThread(() -> {
// doing operation
}).start();

The logic behind this is very simple. When we create TenantAwareThread, it just create a new object within the scope of current thread where we set current tenant ID to a field. But when we call start(), it will run new thread, so previous thread’s scope is lost. We set tenant in overridden run() method from the field value and doing operation defined in Runnable target. So, if application need to access database from different part/module, TenantContext provide correct tenant for the newly created thread. When finishing task, new thread clear tenant and remove reference of it from the tenant context.

This practice will remove headache to pass tenant every time in creating new thread and produce very clean code. Cheers!

See also: Concurrency in Java

--

--