Fork me on GitHub
#fulcro
<
2018-01-16
>
tony.kay04:01:57

@levitanong B will see A’s result IF A claims to be remote at the time of the call

tony.kay04:01:40

technically B will always see A’s result of optimistic update…it’s the networking that defers if detected

tony.kay04:01:54

it’s a little confusing, I know

levitanong04:01:57

@tony.kay Yeah, it’s the networking layers that I’m finding problematic. What I need is for A’s networking layer to finish its job and merge novelty back into state (via ok-callback) before B starts, so that B sees the novelty that A introduced

levitanong04:01:50

For now, I’ve made a workaround to make the reconciler available in the networking layer to call transact! after the ok-callback

tony.kay04:01:51

then A has to claim it is remote when called for any reason

levitanong04:01:12

@tony.kay Oddly enough, it actually is.

levitanong04:01:22

Is there a special behavior for load-action?

tony.kay04:01:25

then it should defer…how are you calling it?

tony.kay04:01:58

you include (remote-load env) in the remote side to indicate you want to process the load queue

levitanong04:01:15

(defmutation a
  [{:keys [foo]}]
  (action [{:keys [state] :as env}]
          (swap! state assoc :something nil)
          (df/load-action env :asdf nil
                          {:remote  :google-places}))
  (remote [env] (df/remote-load env)))

tony.kay04:01:21

you’re hitting remote :google-places

tony.kay04:01:43

you have to tell it the correct remote so it can wait on the correct queue I think

levitanong04:01:16

ah, I had assumed that setting a :remote would make it override the (remote ...)

tony.kay04:01:21

(defmutation a … (google-places [env] (…))

levitanong04:01:39

in the event of multiple load-actions within a mutation, what should be done though?

levitanong04:01:47

multiple calls to remote-load?

tony.kay04:01:49

yeah, this isn’t ideal…so, any remote on a mutation will trigger queue processing

tony.kay04:01:20

so technically you can get away with what you’re doing in most cases; however, with ptransact!, it needs to figure out which network a tx is going to wait on

tony.kay04:01:36

and it cannot tell that from the load queue, because you could have queued a bunch of stuff…

levitanong04:01:47

right, because at call-time, it wouldn’t be able to see the action

tony.kay04:01:24

but if you’re claiming :remote, it should actually hang

levitanong04:01:55

hmm. i’m still getting the same result

tony.kay04:01:58

as in: it should defer the second thing, but since :remote doesn’t act, it should defer B until some future time

tony.kay04:01:04

so, there is a function in prim called pessimistic-transaction->transaction. It convers a tx to a pessimistic one. ptransact! is nothing more than (transact! this (p->t tx))

tony.kay04:01:17

pessimisim is a transform on a function

tony.kay04:01:29

you can run that function on your tx and see what it tries to defer

tony.kay04:01:36

(or you can look in fulcro inspect at the tx list)

tony.kay04:01:05

you should see :deferred entries

levitanong04:01:50

@tony.kay this is weird. If I don’t set the :remote key on the load-action configuration, it tries to get from remote, rather than the correct remote that I specify inside the defmutation.

levitanong04:01:07

should both be set and match?

tony.kay04:01:13

both set and match

tony.kay04:01:25

the load queue needs the entry to say where it is loading from

tony.kay04:01:42

the remote side is technically for sending a mutation…but with load-action you’re morphing it to a read

tony.kay04:01:55

so the remote-load call just triggers queue

tony.kay04:01:08

BUT ptransact looks at it to figure out deferral

levitanong04:01:50

Okay, I will now take a look at fulcro-inspect

levitanong04:01:54

And it would decide to defer if any remote for that mutation is “true”?

tony.kay04:01:20

it should nest them as deep as needed

tony.kay04:01:28

shows a test that is trying out the transform…you can see the deferred nesting

levitanong04:01:41

oooh, this is cool. So this is something I can actually test in the REPL!

tony.kay04:01:42

ptransact! is new…still working out the kinks. good to have someone trying it out on something complicated

levitanong04:01:24

that which must be deferred is deferred. hmm.

levitanong04:01:52

Can i just say that I love that fulcro inspect arranges the keys alphabetically. It’s hell looking for the right key in devtools

tony.kay04:01:59

@mandor2017 OK…found it. Ugly one. There was a bug in application initialization order, and the alternate branch inits were getting stomped. 2.1.1-SNAPSHOT is on clojars and it should fix it. I’ll probably patch it slightly more. It’s a bit ugly in that code segment.

levitanong04:01:28

Is fulcro-inspect supposed to show the effects of a remote merge? Because all I’m seeing is local mutations

tony.kay04:01:02

when remotes return, they are merges

tony.kay04:01:41

merge! is what is called by the callback of networking. Everything appears as a sequence of transactions local to the database.

levitanong04:01:47

I have a feeling i’m not doing remotes right…

tony.kay04:01:32

normal remotes have simple rules: you must call the ok OR error callback once and only once for each network thing you’re asked to do.

tony.kay04:01:43

when it is complete

tony.kay04:01:17

you can also implement an updating remote that gets an extra callback (update) that can be called any number of times to update progress

levitanong04:01:55

yeah, this is what i’m aware of.

levitanong04:01:42

and the intended behavior is, the deferred transaction will only run after the ok-callback is called, yes?

levitanong05:01:34

is there any possible explanation for the deferring not happening despite the system saying it is?

tony.kay05:01:07

I’m sure there could be

tony.kay05:01:18

you added a load action to the queue?

levitanong05:01:53

where does it say in fulcro-inspect?

tony.kay05:01:25

well, that is in your app state

tony.kay05:01:26

the load queue

tony.kay05:01:58

so, let me think

tony.kay05:01:03

I’m trying to remember the implementation

levitanong05:01:37

it’s transient though, isn’t it?

tony.kay05:01:42

It leverages a few things: 1. Mutations are queued before loads. 2. Loads are sequenced. 3. The deferred-transaction itself is a mutation that queues up a load.

tony.kay05:01:21

So, it is expecting that you’re doing a mutation in step 1..so that a write is split off and sent

tony.kay05:01:27

are you only doing reads?

levitanong05:01:19

nope, I’m messing with the app state a bit in A

tony.kay05:01:27

remote mutation, I mean

tony.kay05:01:33

remote mutation is what it blocks on

levitanong05:01:55

there we go.

levitanong05:01:01

yes, i’m only doing a load

tony.kay05:01:03

so, this isn’t what you want

levitanong05:01:04

no remote mutation

tony.kay05:01:58

and here’s where I’ve sort of muddied the lines

levitanong05:01:21

I guess I could do my data fetch via mutations. In fact, that’s what it was before I decided to experiment with load

tony.kay05:01:47

the model is supposed to be that mutations are writes. I’ve allowed reads from writes. But, I’ve also done load combining..because reads should be…well, reads.

tony.kay05:01:43

The model from the UI layer is that the potential ugliness of a read is hidden at the remote layer. This problem of “I want to read, then look at the result, then read again” isn’t really in the model

tony.kay05:01:01

because when you’re using the model as designed it isn’t needed…it’s necessitated by legacy APIs, though

levitanong05:01:49

Or even just third-party APIs, right?

tony.kay05:01:59

yeah, sorry, that’s what I meant

tony.kay05:01:39

the idea is to eliminate this aspect of REST, that you have to hit different URLs to gather data.

levitanong05:01:05

So is the idiom then, to have a server you control, and have it do all the third-party stuff for you?

tony.kay05:01:09

the model we’re shooting for here is that a single request can encapsulate what is needed, so the server can make those connections for you.

tony.kay05:01:18

not necessarily.

tony.kay05:01:26

you can do it at the client network layer in this case

tony.kay05:01:06

I’m debating whether this continual need justifies bending the model, though

levitanong05:01:23

could you expound on the client network layer?

levitanong05:01:37

isn’t this the various remotes?

tony.kay05:01:15

no, within the XHR layer..

tony.kay05:01:41

make one abstract load that you run from the UI. When your client network code sees it, make your 3 XHR calls to gather up the answer.

tony.kay05:01:46

call OK once

tony.kay05:01:29

you can even use a post-mutation on that load to handle the possible different results

tony.kay05:01:24

then you’re back in the model

tony.kay05:01:48

you’re back to callback hell…but there’s no avoiding it in this case..at least isolate it to a low layer

levitanong05:01:52

but then you wouldn’t have access to the state

tony.kay05:01:02

why do you need state?

tony.kay05:01:19

you have the incoming request (which can encode parameters), and you own the network comm

tony.kay05:01:56

(load this ... {:params ...data-you-need-in-network...})

levitanong05:01:13

i have an origin and a destination. I can get the coordinates for either one, but when I have both, compute the best route.

tony.kay05:01:29

sure, so those are parameters to the load

tony.kay05:01:48

and it looks much better from the application layer

tony.kay05:01:38

(load :google/route nil {:params {:origin o :dest d} :post-mutation 'decode-result-in-google-route)

levitanong05:01:56

that would take care of the state issue

levitanong05:01:23

now I’ve split my remotes into just the google remote, and some other service

levitanong05:01:46

to do this, it would seem I’d have to fold the other service into the google remote

tony.kay05:01:03

oh, I see..you’re pulling the coords from another API?

levitanong05:01:13

i’m pulling the coords from google

levitanong05:01:16

pulling routing from another API

tony.kay05:01:28

yes, I’d fold them together

tony.kay05:01:54

heck, for that matter you could just put them all under on :remote including your API, and treat those as “virtual” requests to the normal remote

tony.kay05:01:14

code the REST stuff into low-level functions for separation

levitanong05:01:20

lol, and then have some dispatching a-la parser?

levitanong05:01:52

true, true. So at that point, architecturally, what would be the use for having multiple remotes?

tony.kay05:01:25

well, if you had a lot of different things you did against, say, google, then it might make sense from an organizational sense

tony.kay05:01:38

or if you have microservices of your own, all talking EDN

tony.kay05:01:41

lots of reasons

levitanong05:01:05

ok, so Where It Makes Sense™ 😛

tony.kay05:01:11

exactly 😜

levitanong05:01:38

Alright, this has been eye-opening. Thanks, @tony.kay! I’m so glad you took the time to help me out.

levitanong05:01:45

you are most generous

tony.kay05:01:16

Sure. Look at the client networking as a place you talk to servers (duh), but take that to the extreme of it can do any amount of interfacing (or even act like the server).

tony.kay05:01:32

I use that in the book to completely fake the server within the client

tony.kay05:01:41

and you’re welcome

tony.kay05:01:58

sorry you’ve spent so much time beating your head against a wall on this one problem

tony.kay05:01:18

I need to improve docs on ptransact, obviously

levitanong05:01:58

Oh, no worries. I got a lot of architecture learnings from this anyhow 😄

levitanong05:01:42

ciao! I hope you have a great evening.

tony.kay05:01:49

thanks. You too.

levitanong13:01:14

Hi all, I’m encountering some odd behavior wherein sometimes a load marker works properly, and sometimes it just goes to :ready and never to :loading. (but still finishing the job) Has anyone experienced this behavior before?

wilkerlucio16:01:41

@levitanong when you have many items enqueued, the enqueued items will be :ready, then :loading, if you look the history with inspect, can you see the :loading at any point?

wilkerlucio16:01:51

maybe it's just too fast and you are not seeing the loading view?

levitanong16:01:41

naw, there have been instances where it stays :ready for quite a while

wilkerlucio16:01:16

humm, but it can be there for a while, if it's waiting for a previous network to end

wilkerlucio16:01:40

are you using the old markers or the new ones?

levitanong16:01:55

there are new ones? lol

levitanong16:01:09

:marker :something-something

levitanong16:01:24

and you have to use df/marker-table?

levitanong16:01:11

I guess it’s entirely possible that it stays :ready for a long time, waiting for something to finish, then suddenly goes nuts and finishes loading before the UI can react. XD

wilkerlucio16:01:08

yeah, I think that's whats happening

levitanong16:01:13

yeah i’ve been reading that

levitanong16:01:23

btw, nice work on the fulcro inspector! really useful.

levitanong16:01:45

though i’ve noticed that when i scrub the timeline, this error shows up in the console: Component GlobalInspector threw an exception while rendering Error: No protocol method Mult.tap* defined for type null:

wilkerlucio16:01:39

weird, I do nothing with core.async, this looks like something with the fulcro remote queue

wilkerlucio16:01:09

but may be triggered by something on inspect, if you can get a small case to reproduce I can take a look