Fork me on GitHub
#pedestal
<
2021-08-20
>
bringe21:08:55

Hello. I'm wondering if I'm misunderstanding something about pedestal's async handling in interceptors. The below code returns nil, rather than a context map.

(def async-example
  (interceptor
   {:name :async-example
    :enter (fn [context]
             (go
               (assoc context :async-result "some result")))}))

(chain/execute {} [async-example])
If I remove the go block, I get the context map I expect. I wonder if it's something related to using execute , though looking in pedestal's source, it seems execute does perform the async handling if a channel is returned. https://github.com/pedestal/pedestal/blob/6b518f1403132e0474daa5edaf08b1de1df267b5/interceptor/src/io/pedestal/interceptor/chain.clj#L173 Is something not working as it should, or am I doing something incorrectly for this to work?

2
isak23:08:58

Actually not sure that matters, it seems like your example is supposed to work

bringe23:08:11

Yeah, it looks like should. :thinking_face:

bringe00:08:44

I think it has something to do with the repl and threading, though I'm not sure what exactly is happening. If I change the body of execute in the pedestal source to this:

([context]
   (let [context (some-> context
                         begin
                         enter-all
                         terminate
                         leave-all
                         end)]
     (prn "context" context)
     (if-let [ex (::error context)]
       (throw ex)
       (do
         (prn "returning:" context)
         context))))
... then run my example, I get these logs and result:
"context" nil
"returning:" nil
"context" {:async-result "some result"}
"returning:" {:async-result "some result"}
nil

bringe00:08:30

I need to go afk for now, but thanks for taking a look earlier @U08JKUHA9.

souenzzo12:08:04

(chain/execute
  {}
  [(interceptor/interceptor
     {:name  :leave
      :leave (fn [{:keys [response]
                   :as   context}]
               (prn [:response response])
               context)})
   (interceptor/interceptor
     {:name  :async-example
      :enter (fn [context]
               (async/go
                 (assoc context :response {:status 202})))})])
ret => nil
stdout => [:response {:status 202}]

ddeaguiar21:08:36

From the http://pedestal.io/reference/interceptors#_interceptor_return_values and as demonstrated ^^: > Any interceptor downstream of an asynchronous interceptor will be executed in the `core.async` thread pool.

isak01:08:26

Understandable that it can't be synchronous, but wouldn't it make sense to return the result asynchronously in some way, like is done for the synchronous path?

lispers-anonymous13:08:19

I believe this behavior is expected. https://github.com/pedestal/pedestal/blob/6b518f1403132e0474daa5edaf08b1de1df267b5/interceptor/src/io/pedestal/interceptor/chain.clj#L110-L117 The docs call out that if a channel is received instead of a context it will return immediately with a nil.

lispers-anonymous13:08:24

Pedestal itself doesn't need the context when everything has finished executing. One of the last interceptors that will be executed is responsible for writing the response to the response output stream. Once that is done there is really nothing left to do. https://github.com/pedestal/pedestal/blob/6b518f1403132e0474daa5edaf08b1de1df267b5/service/src/io/pedestal/http/impl/servlet_interceptor.clj#L288 https://github.com/pedestal/pedestal/blob/6b518f1403132e0474daa5edaf08b1de1df267b5/service/src/io/pedestal/http/impl/servlet_interceptor.clj#L218

bringe15:08:35

Thanks for the replies. @U0FL657GR Yes, that's the part of the docs I linked earlier in the thread. I suppose I just didn't fully understand the implications.