Fork me on GitHub
#graphql
<
2022-11-25
>
orestis08:11:39

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...

orestis08:11:01

Ah, I see that Lacinia 1.2 alpha provides an executor option that will be useful - trying this out

orestis08:11:24

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))

👍 2
hlship21:11:11

The rationale behind always executing the query off-thread was to provide a way to apply an execution timeout.

orestis06:11:25

Ah, thanks for the clarification.