It looks like Lacinia will always execute all the resolvers in a different thread than the caller, even if no async is ever into play - is there a reason for this other than implementation simplicity? It doesn't play well with ThreadLocals, and I can't see a clear way to inject some ThreadLocals into the executing thread...
Ah, I see that Lacinia 1.2 alpha provides an executor option that will be useful - trying this out
It works just fine:
(defn lacinia-executor
"An Executor that will carefully copy the Log4J2 context into the running thread,
clearing it out before/after execution. This will copy Log4J2 context variables
into the ThreadLocal of the executing resolvers, helping with logging and Sentry reports."
[]
(let [queue (LinkedBlockingQueue.)
*thread-id (atom 0)
^ThreadFactory factory (reify ThreadFactory
(^Thread newThread [_ ^Runnable runnable]
(Thread. runnable
(format "GraphQL Executor #%d" (swap! *thread-id inc)))))
executor (proxy [ThreadPoolExecutor]
[(int 0) (int 10)
1 TimeUnit/SECONDS
queue
factory]
(execute [^Runnable r]
(let [ctx (org.apache.logging.log4j.ThreadContext/getImmutableContext)
r' (fn []
(org.apache.logging.log4j.ThreadContext/putAll ctx)
(.run r))]
(proxy-super execute r')))
(beforeExecute [^Thread t ^Runnable e]
(org.apache.logging.log4j.ThreadContext/clearMap)
(proxy-super beforeExecute t e))
(afterExecute [^Thread t ^Runnable e]
(proxy-super afterExecute t e)
(org.apache.logging.log4j.ThreadContext/clearMap)))]
executor))
The rationale behind always executing the query off-thread was to provide a way to apply an execution timeout.
Ah, thanks for the clarification.