Fork me on GitHub
#fulcro
<
2020-06-12
>
zilti14:06:10

The book says > When you pass tempids as a parameter to a mutation, that value will be captured and not rewritten. Thus your ok-action and result-action env will contain a :tempid→realid map that can be used to translate those values. But how else am I supposed to pass the data to the mutation?

tony.kay14:06:49

where does it say you can’t pass the data to the mutation?

tony.kay14:06:56

it just tells you how to fix it

zilti14:06:16

Nowhere, but how else can I pass it so that this rewrite takes place?

tony.kay14:06:50

I’m not understanding what you’re not understanding 😜

tony.kay14:06:19

there is a map, in env that goes from the tempid you passed, to the real id that was returned from the server

tony.kay14:06:31

if you have a tempid, then you can look it up

tony.kay14:06:35

so, what’s the issue?

zilti14:06:45

The book describes that Fulcro will rewrite the tempids of the data sent to the mutation, unless the data is handed to the mutation using an argument

zilti15:06:02

But using an argument is, as I see it, the only way?

tony.kay15:06:46

ok, so first, the book could be out of date: tempid rewriting happens against state and transaction queues. So, the only tempids that cannot be rewritten are those that are captured in runtime closures

tony.kay15:06:29

but the book is telling you that it is handing you the remapping “map”…so you can fix these runtime closures yourself, if need be

tony.kay15:06:34

so where’s the problem?

zilti15:06:06

Well I wanted to chain two mutations in a transaction (to see what would happen) and I expected it to re-write the tempid before running the second mutation, but it didn't. So I wondered what I'd have to do in such a case to have it happen automatically, or if I am misunderstanding something here.

tony.kay15:06:26

(defmutation f [params]
  (ok-action [{:keys [tempid->realid]}]
    (let [fixed-params (reduce-kv (fn [p k v] (assoc p k (if (tempid? v) (tempid->realid v v) v)) {} params)]
      (comp/transact ... [(new-mutation fixed-params)])))

tony.kay15:06:43

Mutations are multimethods. They get dispatched exactly once. The sections of the mutations become lambdas in a map in the runtime that are generated during that initial dispatch.

tony.kay15:06:43

I don’t remember, off the top of my head, why I don’t rewrite the tempids in params, but if that is the way it is working and written in the book, then there was probably some limitation on functional closure that was too hard to work around. Do you understand the solution?

tony.kay15:06:30

there’s a helper function somewhere that can use that map on an arbitrary data structure so you don’t have to write the reduce yourself

zilti15:06:47

Yes, I do understand this solution, this one was clear. I was wondering about the Warning, because I don't get in which case the automatic replacement actually does happen. Just made me curious, that's all

tony.kay15:06:22

tempid/resolve-tempids

tony.kay15:06:07

the auto-replacement happens against the entire app database, and everything in all transaction queues (including pending things in network queues)

tony.kay15:06:59

so, if there was a mutation you’d queued with a tempid that was waiting in line behind a mutation that rewrote IDs, it would actually resolve it in the network params of that one

zilti15:06:24

So I tried it with a

(comp/transact! this [(mutation-a (fs/dirty-fields props false {:new-entity? true}))
                      (mutation-b (fs/dirty-fields props false {:new-entity? true}))])
And I expected the tempids to be replaced by the time the data arrives on the server for mutation-b but they were not

tony.kay15:06:25

so perhaps this is the source of the confusion: I cannot rewrite data in functional closures. The things in the queues are not closures…they’re literal data. So all that can be rewritten

tony.kay15:06:43

were a and b goign to the same server?

tony.kay15:06:58

then how could I do that???? They will be one txn, as you submitted it

tony.kay15:06:16

you’re responsible for handling tempids in an atomic unit on a server

zilti15:06:29

I assumed first a would be sent to the server, then when the result got back it would rewrite the tempids and then send b to the server

tony.kay15:06:39

that’s not what a transaction is

tony.kay15:06:57

the only time Fulcro splits a txn is when it has to

tony.kay15:06:07

(or when you use the legacy ptransact!)

tony.kay15:06:23

two mutations to the same server in the same txn are an attempt, by you, to execute an atomic grouping…you have to enforce that yourself on the server, but Fulcro is allowing you to express that desire

zilti15:06:30

That makes sense that they get sent "simultaneously"

tony.kay15:06:09

The default txn processing will guarantee that a single submission will only be split if more than one server is involved, but that groupings to those servers will be maximal (i.e. everything that should go to a given server will go as a unit)

zilti15:06:12

So I guess I tried to use that rewrite feature in a context it wasn't meant for

tony.kay15:06:51

It is meant for exactly that…you’re just assuming Fulcro can do some magic in the middle of your atomic processing on the server, which it cannot

zilti15:06:52

Thanks for the clarifications 🙂

zilti15:06:10

Yes, I expected it to make a "round trip" back to the client in between

tony.kay15:06:16

you’ll have to carry along your rewrites in your parsing env on the server if you want that ability

zilti15:06:42

So to speak I expected only the client knows a and b belong to a transaction

tony.kay15:06:00

If you want that, you can use ptransact! , which has that semantic

tony.kay15:06:24

but it is completely pessimistic…not even the optimistic of b would run until a is done

tony.kay15:06:11

Currently the tx processing will not combine unrelated transations…so running two invocations of transact! should get the behavior you expected.

tony.kay15:06:49

default tx processing is sequential, and non-combining

zilti15:06:33

"Currently the tx processing will not combine unrelated transations" is that an implementation detail, or something that is by design? (Would make sense if it is by design)

tony.kay15:06:44

The tx processing is pluggable. The default implementation attempts to do things in a way that will “make sense” given the distributed systems environemtn of an SPA.

tony.kay15:06:40

for example, reads and writes that are submitted while you hold the js thread (i.e. you call load load load transact transact) will be reordered so that the writes go first. Loads and transacts might come from arbitrary code composition, and one way to cleanly reason about such composition is for these combinations to assume you don’t want “stale reads”…that is to say, if a single code path (possibly composed of black box calls) asked to both change and read things, then you probably don’t want to do the reads before those submitted changes have taken effect.

tony.kay15:06:27

txn vector as a “hint” for an atomic unit is another useful guarantee as is sequential network operation (which you can opt out of via parallel option)

tony.kay15:06:21

F2 had read combining for efficiency, but I have not reimplemented that for F3. it is also possible to do batching, though things like tempid handling gets a little tricky, as you’ve noticed

JAtkins20:06:51

I'm using RAD routing for my app, and I'm thinking about using the route predicate for simple permissions. Works well for the most part -- except on initial load. I haven't figured out where the code for the intial decode of the url string happens. Where should I be looking?