tonsky / clj-reload

Smarter way to reload Clojure code
MIT License
213 stars 5 forks source link

don't unload clojure.core? or other solutions maybe... #13

Closed jasonjckn closed 4 months ago

jasonjckn commented 4 months ago

is it necessary unload clojure.core, and if so how can I prevent it

The reason I ask, is i'm getting a malli schema error

Execution error at jdk.internal.reflect.DirectConstructorHandleAccessor/newInstance (DirectConstructorHandleAccessor.java:62).
clojure.lang.ExceptionInfo: :malli.core/invalid-schema {:type :malli.core/invalid-schema, :message :malli.core/invalid-schema, :data {:schema clojure.core$int_QMARK_@f346510, :form clojure.core$int_QMARK_@f346510}}
Syntax error macroexpanding at (spec.clj:57:84).
:malli.core/invalid-schema

This is happening because malli allows you to register function objects as schema keys, (e.g. (m/validate int? 3), and by default it registers a schema with key clojure.core/int? and value [:fn int?]. The issue arises if clojure.core gets reloaded but not my namespace that constructs the registry, and implicitly depends on clojure.core. e.g.

(defonce registry* (atom (malli.core/default-schemas))) 

eventually the registry becomes stale in seems, because it's storing an older definition of int? from before the clojure.core namespace reload.

I confirmed clojure.core is getting reloaded, and it correlates with my exception triggering,

(alter-var-root (intern 'clojure.core 'before-ns-unload)
                (fn [old]
                  (fn before-ns-unload []
                    (infof "%s unloading %n..." "clojure.core")
                    (printf "%s unloading %n..." "clojure.core")
                    (flush))))
image

And the exact conditions where clojure.core gets reloaded, I don't yet understand, i can't always reproduce it, it seems if I leave a REPL open for 10 minutes with no activity that causes clojure.core to get unloaded, as far as I can tell.

I tried to do this, to stop the unloading, but it didn't work.

(alter-meta! (find-ns 'clojure.core) assoc :clj-reload/no-reload true)
(clj-reload.core/init
      {:no-reload `#{~'user
                     ~'dev
                     clojure.core
                     }
       :dirs      (clj-reload.util/classpath-dirs)})
jasonjckn commented 4 months ago

the magnitude of the number of "unloading clojure.core" is strange too

image
jasonjckn commented 4 months ago

i missed the defonce semantics, changing my registry* from defonce to def seems to solve my original problem, TBD. Still don't understand why clojure.core is unloaded so much, but anyways that was tangential.

tonsky commented 4 months ago

clojure.core shouldn’t get reloaded. Unless you have it as a file on classpath, and you modify that file?

Can you share the project by any chance? I’d love to look into it

jasonjckn commented 4 months ago

@tonsky so sorry, it's super corporate source code, 100kLOC, can't share it :(

If you have large code bases that you actively work on, it might be worth adding

(alter-var-root (intern 'clojure.core 'before-ns-unload)
                  (fn [old]
                    (fn before-ns-unload []
                      (infof "%s unloading %n..." "clojure.core")
                      (printf "%s unloading %n..." "clojure.core")
                      (flush))))

to see if you ever repro.

tonsky commented 4 months ago

It prints what namespaces are loaded/unloaded anyways, but no, I haven’t seen it yet. Are you sure you don’t have Clojure sources on the classpath?