Sporadically I will see errors in our Datomic Ion applications where a protocol method is not defined. Here's an example:
"No implementation of method: :v0 of protocol: #'st.interval.protocol/Interval found for class: org.threeten.extra.LocalDateRange"
The method v0 is defined for LocalDateRange and, based on the call stack, I cannot understand how the code that provokes this exception can be invoked before the v0 implementation is defined for LocalDateRange. It's just not possible. I'm left theorizing about perhaps closing over the v0 function invocation before the v0 implementation is defined for LocalDateRange (not really possible, right?) or other kinds of race conditions perhaps exacerbated by how Datomic Ions load my code in production.
This error seems to happen in our lambda handlers shortly after a deploy. None of our developers can provoke the error locally.
Any ideas? More details in the ๐งต .is something reloading the defprotocol? if defprotocol is reloaded it creates a new instance of the protocol that would not be extended by other things that already exist in memory / does not inherit the historical extensions
to debug you could add a top-level log statement to the same namespace as the defprotocol. oftentimes i'll put defprotocol in a dedicated namespace to prevent unintentional reloads
I'm going to try that approach now.
I need to get some clarification on a couple of points, @rutledgepaulv, if you can help...
How can it be possible to "reload" the defprotocol when it exists in exactly one namespace? Assuming one is not forcing the reload of a namespace, Clojure's namespace loading mechanisms should prevent the namespace from being reloaded which in turn should prevent the defprotocol from being reloaded.
(I don't have any :reload directives anywhere in my codebase)
agree, unless you have some middleware or something that forces reloads of a namespace from time to time (like popular ring dev middleware) although that should only do it when the file timestamp changes iirc
Nothing like that...
just suggesting that "reloading the defprotocol" might be the cause and not necessarily "the extension never happened"
But, I think you are on the right track, which is why I asked the question in this channel....
I wonder (@jaret) if Datomic Cloud's mechanism for loading the namespaces that hold the whitelisted lambdas and allows might be working in parallel or without the protection of Clojure's nromal loading.
if you want you could also wrap the defprotocol with a defonce just to rule it out
From what I see in the defmacro of defprotocol, there is already a defonce in place.
it does defonce the var but then it mutates it on each load i think, is easy to recreate if you force reload and then try to call a protocol method on an object that was reified with the old version
... but a lot of other stateful stuff as well.
Yeah.
an outer defonce skips all the stateful stuff the next time
the other thing i was going to say that i've seen (years ago) is AOT not playing well with defprotocol where it generates one version at AOT time and then another version at runtime and loading orders start to matter as far as which version you get
Regarding AOT, since my code runs in Datomic Ions, that part of the deploy is not under my control anyways, so I sure hope that's not the cause. I'm going to put something in the ns to alert if it is being reloaded. I suppose I could see if defonce'd var has been interned....
The lambda handler's ion entry point is a fn in a namespace where there is, effectively, this ns declaration:
(ns my.ion-entry-points
(:require [my.ns-with-defprotocol-of-interval]
[my.ns-with-extension-of-ldr-to-interval]))
(defn lambda-entry
[x]
(let [ldr (f x)]
(v0 ldr) ; provokes exception
...))
Where v0 is a method of the Interval protocol.