pathom

:pathom: https://github.com/wilkerlucio/pathom/ & https://pathom3.wsscode.com & https://roamresearch.com/#/app/wsscode
Janos 2021-06-25T04:56:09.133200Z

I see, thanks for the info, we'll check using reader2 would solve the problem as a quick fix, if not then we could migrate to pathom3 (which we planned to do anyway)

dehli 2021-06-25T16:43:35.137300Z

Hi, I’ve got some strange behavior that I’m trying to trace down in pathom2. I’ve got a resolver that calls out to the parser from within it. To narrow down the issue I’ve started removing keys that are in the subquery to figure out what’s causing the misbehavior. I was able to reduce the query all the way to the following and it still returns unexpectedly. To me it seems like this should be impossible. Is there any ideas for how I can further debug?

;; Subquery within the resolver
(parser env [{[:curriculum/uuid "my-uuid"] [:curriculum/uuid]}])

;; Response from above subquery
{[:curriculum/uuid "my-uuid"] {:curriculum/uuid ::p.core/not-found}}

wilkerlucio 2021-06-27T13:54:56.138800Z

I tried to replicate the issue in a small setup, but the results looks as expected:

(pc/defresolver inner-call [{:keys [parser] :as env} _]
  {::pc/output [:inner-result]}
  {:inner-result (parser env [{[:foo 1] [:foo]}])})

;; define a list will our resolvers
(def my-resolvers [inner-call])

;; setup for a given connect system
(def parser
  (p/parser
    {::p/env     {::p/reader               [p/map-reader
                                            pc/reader2
                                            pc/open-ident-reader
                                            p/env-placeholder-reader]
                  ::p/placeholder-prefixes #{">"}}
     ::p/mutate  pc/mutate
     ::p/plugins [(pc/connect-plugin {::pc/register my-resolvers})
                  p/error-handler-plugin
                  p/request-cache-plugin
                  p/trace-plugin]}))

(comment
  (parser {} [:inner-result]))

dehli 2021-06-28T12:00:22.139200Z

thanks! it normally works as expected. it’s just when i have many resolvers nested that I get this strange behavior

dehli 2021-06-28T12:00:38.139400Z

if I can’t trace it down, i’ll try to come up with a reproducible example

dehli 2021-06-28T15:03:32.139800Z

Looks like I was able to reproduce the bug in a smaller project. I think it has to do with parallel-parser or parallel-reader b/c when I switch to using the regular reader / parser the bug goes away. Here’s the example project I setup. https://github.com/dehli/pathom2-bug/blob/main/src/dev/dehli/pathom_bug.cljs

wilkerlucio 2021-06-28T16:15:11.140100Z

yeah, looks off, started an issue for it https://github.com/wilkerlucio/pathom/issues/198

wilkerlucio 2021-06-28T16:19:07.140400Z

ok, I did some investigation, and I think I got it

wilkerlucio 2021-06-28T16:21:30.140600Z

the problem is that in the parallel case the entity gets modified upon each request

dehli 2021-06-28T16:25:35.140800Z

Awesome! That’s great news 🙂 Thanks!

wilkerlucio 2021-06-28T16:27:32.141Z

just replied on the issue with details, please let me know if the solution proposed there works for you

dehli 2021-06-28T16:40:52.141200Z

Thanks! I think that would work. I was initially using placeholders, and the bug only started to surface as I moved to idents. Is this intended behavior for the parallel-parser?

wilkerlucio 2021-06-28T16:42:32.141400Z

using parser inside parser has some of this repercusions, its kinda expected

👍 1
wilkerlucio 2021-06-28T16:42:50.141600Z

if you are using it too much, would be nice to see why, ,and if it could be avoied

wilkerlucio 2021-06-28T16:43:25.141800Z

but there cases (specially on pathom 2) that its just the only way (a hack way for optional and/or nested inputs)

dehli 2021-06-28T16:50:25.142Z

cool! we don’t use them too often (almost always it’s due to nested inputs). pathom3 will fix that, so we are excited to make the switch 🙂

👍 1
dehli 2021-06-29T18:24:54.142400Z

Hello again! I’m still seeing similar behavior (even when using the (with-entity) function you had suggested. I’ve also tried clearing out ::p/request-cache and I’m still getting a subset of my query returned in my nested parser call. Is there some other key in env that I could look at to debug? If not, I can try to find another reproducible example.

dehli 2021-06-30T17:19:12.144Z

I’ve got another example showing the bug I’m seeing. For this one, I am using the with-entity function. I’ll keep playing around with it to try to better figure out what’s going on.

(ns dev.dehli.pathom-bug
  (:require [clojure.core.async :refer [go <! <!!]]
            [com.wsscode.pathom.core :as p]
            [com.wsscode.pathom.connect :as pc]
            [com.wsscode.pathom.parser :as pp]))

(defn with-entity [env ent]
  (assoc env ::p/entity (atom ent)))

(pc/defresolver one
  [{id :id}]
  {:one (str "one " id)})

(pc/defresolver state
  [{:keys [parser] :as env} {id :id}]
  {::pc/output [:state]}
  (go
    {:state (<! (parser (with-entity env {:id id}) [:one]))}))

(def parser
  (p/parallel-parser
    {::p/env     {::p/reader               [p/map-reader
                                            pc/parallel-reader
                                            pc/open-ident-reader
                                            p/env-placeholder-reader]
                  ::p/placeholder-prefixes #{">"}
                  ::pp/external-wait-ignore-timeout 100}
     ::p/plugins [(pc/connect-plugin {::pc/register [state
                                                     one]})
                  p/error-handler-plugin
                  p/trace-plugin]}))

(comment
  (<!! (parser {} [{[:id "foo"] [:one :state]}]))

  ;; Returns
  ;; {[:id "foo"] {:one "one foo" :state {}}}
  )

👀 1
dehli 2021-06-30T19:48:29.144500Z

It seems to be returning empty state b/c it’s reaching the ::pp/external-wait-ignore-timeout

dehli 2021-06-30T20:42:48.144900Z

I think I was able to fix the bug by adding ::pp/processing-recheck-timer

dehli 2021-06-30T20:42:59.145100Z

although it does seem like a bandaid rather than a full solution

wilkerlucio 2021-06-30T20:43:26.145300Z

yeah, I'm taking a deeper look, it doesn't look very good

wilkerlucio 2021-06-30T20:43:50.145500Z

the issue is that the parallel processor has all these "tricks" add for performance tuning over time

wilkerlucio 2021-06-30T20:44:17.145700Z

and these examples are exposing their sensitivity for recursive calls

dehli 2021-06-30T20:47:24.145900Z

I see. That lines up with what I’m seeing on my end as well. Our recently added resolvers utilize recursion which must be why we’re starting to see some unexpected behavior.

wilkerlucio 2021-06-30T20:51:47.146100Z

did you recently changed to parallel parser? or recently add recursive calls like this?

dehli 2021-06-30T20:53:06.146400Z

we’ve been using parallel parser but recently added recursive calls like this

wilkerlucio 2021-06-30T20:56:55.146600Z

and the reason you use the parallel parser is related to long attribute dependencies? or because of processing many entities in parallel? ou maybe both?

dehli 2021-06-30T20:58:32.146800Z

we’re using the parallel parser for the second reason (needing to execute many entities in parallel)

dehli 2021-06-30T20:58:44.147Z

i guess the first as well

dehli 2021-06-30T20:59:14.147200Z

we saw pretty good performance improvements when we switched to parallel

wilkerlucio 2021-06-30T20:59:54.147400Z

gotcha, we can try to hack a bit on the parallel and "reset" the things that need reset on the recursive call, we may be able to make this work for now

dehli 2021-06-30T21:01:53.147600Z

ya, i was looking through the env map to try and figure out what could be reset to give it a “fresh” view

dehli 2021-06-30T21:03:52.147800Z

additionally the bandaid might be enough as well until we make the switch to pathom3. it seems that the parallel parser can be implemented much simpler with the new architecture

wilkerlucio 2021-06-30T21:06:08.148Z

yes, parallel processing in pathom 3 should be much simpler

1
wilkerlucio 2021-06-30T21:06:25.148200Z

I'm thinking of doing something in pathom 2 that I did for pathom 3 regarding env

wilkerlucio 2021-06-30T21:06:44.148400Z

there I just save a "backup" version of the env on entry, so you can restore to it

wilkerlucio 2021-06-30T21:06:56.148600Z

but may not be enough, because of some mutable state things, like active-paths

wilkerlucio 2021-06-30T21:11:58.148900Z

ok, I find something that works with your example:

wilkerlucio 2021-06-30T21:11:59.149100Z

(defn with-entity [env ent]
  (assoc env ::p/entity (atom ent)))

(pc/defresolver one
  [{id :id}]
  {:one (str "one " id)})

(defn reset-env [env]
  (assoc env
    :com.wsscode.pathom.parser/active-paths (atom #{})
    :com.wsscode.pathom.parser/waiting #{}
    :com.wsscode.pathom.parser/key-watchers (atom {})
    :com.wsscode.pathom.core/entity-path-cache (atom {})))

(pc/defresolver state
  [{:keys [parser] :as env} {id :id}]
  {::pc/output [:state]}
  (go
    {:state (<! (parser (-> env
                            (reset-env)
                            (with-entity {:id id})) [:one]))}))

(def parser
  (p/parallel-parser
    {::p/env     {::p/reader                        [p/map-reader
                                                     pc/parallel-reader
                                                     pc/open-ident-reader
                                                     p/env-placeholder-reader]
                  ::p/placeholder-prefixes          #{">"}
                  ::pp/external-wait-ignore-timeout 100}
     ::p/plugins [(pc/connect-plugin {::pc/register [state
                                                     one]})
                  p/error-handler-plugin
                  p/trace-plugin]}))

wilkerlucio 2021-06-30T21:12:25.149300Z

I can make reset-env part of the Pathom API, so we can move it with the library (and if we find more things to reset, an update will fix)

dehli 2021-06-30T21:13:58.149600Z

awesome! ya, i’ll give that a go. I have a quick question about the processing-recheck-timer how does it know it’s “stuck”? does that just mean the resolver is still executing? Trying to determine what’s a good value so that I don’t retry database calls, etc.

wilkerlucio 2021-06-30T21:18:33.149800Z

Im reading again on the code to remember what thats about

wilkerlucio 2021-06-30T21:21:48.150Z

for reasons I don't remember now, the parser may get stuck in some part of the process, this is probably due to the channel madness going on in the orchestration there, looking at the code, its a kinda of "timeout" case, the parallel parser will walk all children of the AST, and try to make sure every attribute is "started" (not every single attribute will trigger a process, for each process pathom knows all "expected" attributes, and will skip those during the scan in case a previous attribute ended up hitting it in the process)

wilkerlucio 2021-06-30T21:22:07.150200Z

so after start everything, it waits for some response, to merge as they come

wilkerlucio 2021-06-30T21:22:54.150400Z

but if it is stuck, that will not finish, this is where the process-recheck-timer enters, it its like a timeout case for this step, and if the code reaches it, it will re-trigger the process for the attributes that still pending

wilkerlucio 2021-06-30T21:23:04.150600Z

makes sense?

dehli 2021-06-30T21:24:52.150800Z

ya, that makes sense! thanks so much! i’ve also gotten your reset-env function added to our subquery function so i think that will fix the issues we’ve run into. will test it out and let you know!

wilkerlucio 2021-06-30T21:25:43.151Z

a little polishing on the api:

wilkerlucio 2021-06-30T21:25:44.151200Z

(defn with-entity [env ent]
  (assoc env ::p/entity (atom ent)))

(pc/defresolver one
  [{id :id}]
  {:one (str "one " id)})

(defn reset-env [env]
  (assoc env
    :com.wsscode.pathom.parser/active-paths (atom #{})
    :com.wsscode.pathom.parser/waiting #{}
    :com.wsscode.pathom.parser/key-watchers (atom {})
    :com.wsscode.pathom.core/entity-path-cache (atom {})))

(defn call-parser [{:keys [parser] :as env} entity tx]
  (parser
    (-> (reset-env env) (with-entity entity))
    tx))

(pc/defresolver state
  [env {id :id}]
  {::pc/output [:state]}
  (go
    {:state (<! (call-parser env {:id id} [:one]))}))

💯 1
dehli 2021-07-01T14:41:01.152200Z

Thanks again for all your help! Have deployed with the changes and the unexpected behavior is now gone 🙂

🎉 1
wilkerlucio 2021-07-01T14:55:49.152500Z

great to hear! 🙂 later I'll put the reset-env at the library, so you can use from it

🎉 3
wilkerlucio 2021-06-25T17:14:21.138200Z

looks strange indeed, any chance a plugin may be doing something?

dehli 2021-06-25T17:45:35.138400Z

Unfortunately not. I’ve removed all plugins (other than pc/connect-plugin)and I’m still seeing that behavior.

dehli 2021-06-25T17:47:08.138600Z

I’m going to try to remove references to placeholders in my test and then remove the placeholder reader to see if that’s related