Hi, i have a big issue with my multitenant applica...
# server
j
Hi, i have a big issue with my multitenant application in Ktor. I store the context in the ThreadLocal with the "asContextElement". please find un attachement. But sometime, when the function is suspended the context is lost... Any idea ?
Copy code
import io.ktor.application.ApplicationCall
import io.ktor.request.header
import kotlinx.coroutines.asContextElement

const val TENANT_HEADER_VALUE = "tenantID";
const val TENANT_HEADER_URL_VALUE = "tenantURL";
const val CLIENT_HEADER_URL_VALUE = "clientURL";
const val TENANTID_DEFAULT_VALUE = "master"

object ContextManager {
    private val context = ThreadLocal<Context>();

    /**
     * Register a request in the context
     */
    fun registerCall(call: ApplicationCall) {

        var requestContext = getContext();

        var tenantID = call.request.header(TENANT_HEADER_VALUE);
        LOGGER.trace("Find ${tenantID} in ${call.request.headers}")
        if (tenantID == null || tenantID.isEmpty()) {
            tenantID = TENANTID_DEFAULT_VALUE;
        }
        requestContext.tenantID = tenantID;
        requestContext.tenantUrl = call.request.header(TENANT_HEADER_URL_VALUE).toString();
        requestContext.clientURL = call.request.header(CLIENT_HEADER_URL_VALUE).toString();


        setContext(requestContext);
    }

    fun setContext(context: Context) {
        this.context.set(context);
        this.context.asContextElement(context);
    }

    fun getContext(): Context {
        var requestContext = context.get();
        if (requestContext == null) {
            requestContext = Context()
        }
        return requestContext;
    }

    fun declareContext(tenantID: String, tenantUrl: String = "", clientURL: String = "") {
        val requestContext = getContext();
        requestContext.tenantUrl = tenantUrl;
        requestContext.tenantID = tenantID;
        requestContext.clientURL = clientURL;
    }

}

class Context {
    var tenantID = TENANTID_DEFAULT_VALUE;
    var tenantUrl = "";
    var clientURL = "";
}
c
ktor uses thread pool, which means your thread local will return different values between calls.
j
yes but the "asContext" keep the context?
c
i am not sure that anything is kept. like, each time a new worker spawns, all plugins get reinitialized for it, so my guess is nope. but let me fetch docs.
also to test my theory you can log current thread name and send a barrage of calls to server. and see if context loss is correlated with different threads executing calls.
oh, and yeah, almost forgot
before
async
and after it you might be executing in different threads.
j
so it's not possible to create multitenant application with ktor? Maybe I missundertood, but with asContextElement, the ThreadLocal is supposed to be maintain in different thread? https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/as-context-element.html
d
You shouldn't mix thread local with coroutines.
But of course asContextElement should work 🙂
I think you always need to access the context as
threadLocalContext.asContextElement()
Lots of things called context in your example 🙂
j
Sure, too many "context" in my code. If we cannot use coroutine and thread local , how can create a multi-tenant API?
Feature of Ktor use threadLocalContext like https://ktor.io/servers/features/call-logging.html
131 Views