Fork me on GitHub
Eric Dvorsak16:02:20

Is there a canonical way to purge the cache of a particular resolver from within another? my use case is that I have a mutation that does a query with the parser from the env, this uses resolver A, but the mutation also does some inserts and the initial query needs to return the result of resolver A, but not the cached one from the inner query, rather the updated one after the mutation


Could you please provide some examples of input and output and what you’re expecting?

Eric Dvorsak19:02:02

(ns com.wsscode.pathom3.test
  (:require  [clojure.test :as t]
             [com.wsscode.pathom3.format.shape-descriptor :as pfsd]
             [com.wsscode.pathom3.connect.operation :as pco]
             [com.wsscode.pathom3.connect.indexes :as pci]
             [com.wsscode.pathom3.connect.planner :as pcp]
             [edn-query-language.core :as eql]
             [com.wsscode.pathom3.interface.eql :as p.eql]
             [com.wsscode.pathom3.interface.async.eql :as p.a.eql]))

(def counter (atom 0))

(pco/defresolver get-count
  [env _]
  {::pco/output [:counter]}
  {:counter @counter})

(pco/defmutation inc-count
  [{:keys [parser] :as env} _]
  (println :counter-before (parser env [:counter]))
  (swap! counter inc)

(def env (-> (pci/register
             (assoc :com.wsscode.pathom3.error/lenient-mode? true)))

(def parser
  (let [process (p.eql/boundary-interface env)]
    (fn [env tx]
      (process (assoc env :parser process) {:pathom/ast (eql/query->ast tx)}))))

(defn test-case []
  (let [query [{`(inc-count) [:counter]}]]
    (parser {} query))

Eric Dvorsak19:02:18

com.wsscode.pathom3.test> (test-case)
:counter-before {:counter 0}
#:com.wsscode.pathom3.test{inc-count {:counter 0}}
com.wsscode.pathom3.test> @counter
com.wsscode.pathom3.test> (parser env [:counter])
{:counter 1}

Eric Dvorsak19:02:28

if you remove the query in the mutation and reset the counter:

Eric Dvorsak19:02:29

com.wsscode.pathom3.test> (test-case)
#:com.wsscode.pathom3.test{inc-count {:counter 1}}
com.wsscode.pathom3.test> @counter


afaik. Pathom3 will execute mutations first so their results are available to resolvers after. Calling the println in the mutations does seem to cache the result ::pco/cache? = false is something you can do in the resolver to prevent the cached result

Eric Dvorsak19:02:04

with pco/cache? false on the counter resolver:

com.wsscode.pathom3.test> (parser env [:counter {`(inc-count) [:counter]}])
:counter-before {:counter 0}
{:counter 1, com.wsscode.pathom3.test/inc-count {:counter 1}}

Eric Dvorsak19:02:36

but considering the resolver is expensive and used in other situations, ideally I'm looking for a solution to only reset the cache in that particular mutation


@U03K8V573EC it’s relatively easy to clean a cache, im afk now to get the exact name, but at runner namespace, there is some resolver-cache keyword, and that lives in the env


its an atom, you can have a look at it, you will see the keys are tuples, you can dissoc the thing you want removed from the cache


another option, that may be a bit cleaner, is to configure that resolver (or even a set of resolvers) to use a separate cache store


doing so, they will have their own atom, this way you can just clean that cache (reseting the atom to an empty map)


If you don’t run the query in the mutation, pathom will execute the mutation first and the resolvers after and you get the incremented value.

Eric Dvorsak19:02:34

@U3XCG2GBZ this is just a repro case, I need to run the query in the mutation for my use case since I'm using the results in the mutation

Eric Dvorsak19:02:28

@U066U8JQJ having a separate atom that can be reset sounds like a good idea.


Depending on what you’re doing, you might need to consider any transactional issues. If two requests are run simultaneously, they could both read a state of 0 and then both increment to 1 and write/return that. When you would probably intend on the result being 2

Eric Dvorsak18:02:19

Fulcro wraps mutation with a plugin to turn exceptions into data:

(letfn [(wrap-mutate-exceptions [mutate]
          (fn [env ast]
              (mutate env ast)
              (catch Throwable e
                (log/errorf e "Mutation %s failed." (:key ast))
                ;; FIXME: Need a bit more work on returning errors that are handled globally.
                ;; Probably should just propagate exceptions out, so the client sees a server error
                ;; Pathom 2 compatible message so UI can detect the problem
                {:com.wsscode.pathom.core/errors [{:message (ex-message e)
                                                   :data    (ex-data e)}]}))))]
  (p.plugin/defplugin rewrite-mutation-exceptions {::pcr/wrap-mutate wrap-mutate-exceptions}))
One issue I encountered is if my query asks for attributes for the mutation eg
[{'(my-mutation {:paramA 1 :paramB 2) [:sum]}]
then I don't have the error in the result, instead just an empty map {my-mutation {}} Is the only way to get the error to explicitly ask for the attribute :com.wsscode.pathom.core/errors?


:com.wsscode.pathom3.format.eql/map-select-include might help

Jakub Holý (HolyJak)16:02:31

> I don’t have the error in the result Where, in Fulcro or in the actual response (look into the browser Network tab and parse the transit response). If it is missing from Fulcro but present in the actual response then yes, the problem is that F. strips out everything you do not ask for. At one point RAD app did add it automatically (but not anymore, likely b/c it now needs to support both Pathom 2 and 3 and the key has changed)

Eric Dvorsak19:02:01

@U3XCG2GBZ solution did the trick, it wasn't even for Fulcro in my case I was doing the pathom3 query in an endpoint for a legacy app

👍 2

I’m not understanding EQL, I have a couple of resolvers like this:

{::pco/output  [{:x/as [:x/a]}]}

{::pco/input   [:x/a]
 ::pco/output  [:x/bs]}
What query would give me each :x/a with it’s corresponding :x/bs?


[{:x/as [:x/a :x/bs]}]

💯 2

this should work too

[{:x/as [:x/bs]}]


oh, thanks, that worked.

🎉 2

{:x/as {:x/bs :something}}
{:x/as [{:x/bs :something}]}
You can expect data to come back like that depending on the relationship respectively


Not sure what you mean, sorry. Could you send some examples?


is there a way to not mention :x/as in the above query? I just want :x/a :x/bs , which again i suppose i can just use clojure to “toss” the :x/as at root. Maybe that’s more what “smart maps” are for?