Fork me on GitHub
#pedestal
<
2023-05-26
>
Eduardo Lopes22:05:02

How can I achieve ""hot reloading"", or just loading, for interceptors running in REPL? I was following this http://pedestal.io/guides/live-repl#_starting_point to achieve routes reloading, but now I am struggling on interceptors. I tried using and clojure.tools.namespace.repl but I'm not understanding some things I will describe better on the thread.

Eduardo Lopes22:05:20

I first tried using clojure.tools.namespace.repl with a functions to restart my component system, using component.repl also:

(defn restart-system []
  (stop)
  (set-refresh-dirs "src/")
  (set-init get-system)
  (refresh)
  (start))
But changing an interceptor and restarting the system did not worked. The old code keeps it there.

Eduardo Lopes22:05:51

I managed to work adding using dev/watch before my http server is created and started. But for this work I need to manually load interceptor files and restart the system to modifications appear.

Eduardo Lopes22:05:32

Is there a better way for doing this? I don't know clojure so much to make an approach similar to interactive routing docs, but I'm wondering if it's possible and is the better way to do so.

phill22:05:33

Hot (or warm?) reloading is kind of orthogonal to Pedestal. Pedestal interceptors are a slightly resistant pattern because (like all well-managed run-time data) the interceptor chain is data held in closure by a long-running function, which reloading does not interrupt. A workaround is for your interceptors to delegate immediately to plain functions, which (require...:reload) redefines.

souenzzo14:05:16

just switch from ::http/routes (my-routes) to ::http/routes (fn [] (my-routes))

Eduardo Lopes18:05:29

@U2J4FRT2T for routing I followed the doc

::http/routes (if (= :prod env)
                               routes/routes
                               #(route/expand-routes (deref (var routes/routes)))) 
Changing code from interceptor is the trouble

souenzzo19:05:05

Where are you using the interceptors? Before or after the router? The interceptors before the router will only change after a server restart. It should not be a issue once they do not change a lot.

Eduardo Lopes19:05:41

(cond-> {:env          env
         ::http/routes (if (= :prod env)
                         routes/routes
                         #(route/expand-routes (deref (var routes/routes))))
         ::http/type   :jetty
         ::http/port   8890
         ::http/join?  false}
  true   http/default-interceptors
  ;;true   http/dev-interceptors
  true   interceptors/apply-interceptors
  true   http/create-server
  (not= env :test)   http/start
  true   ((partial assoc this :service)))
Something like this, interceptors/apply-interceptors is like this:
(defn conj-interceptor [service-map interceptor]
  (update service-map ::http/interceptors conj interceptor))

(defn apply-interceptors [service-map]
  (-> service-map
      (conj-interceptor authentication-interceptor)))

Eduardo Lopes19:05:56

I think I found what may be my problem, but not sure yet. I am using helper functions and import with alias but this is a warning inhttps://github.com/clojure/tools.namespace#warnings-for-helper-functions.

Eduardo Lopes19:05:40

The problem is that I was setting (set-refresh-dirs "src/") and when I used refresh I actually was not reloading my user namespace (under dev/user.clj). I could debug this by adding keys to my component system map and even after refresh they did not appear. I assumed that if they didn't appear is because my system map is not reloading, so my interceptors wouldn't reload either. 🙂

Eduardo Lopes12:05:53

@U0HG4EHMH using :reload worked exactly as expected, loading my required file "hot reloads" interceptor. Using it have any consequences on production builded code? Just to make sure I'm not using an workaround that is actually not recommended