Fork me on GitHub
#core-async
<
2020-01-26
>
jlmr16:01:21

Hi, I’m struggling with a bug. I’ve tried to make a minimal example of the code. When running this code I can see from the tap> that sometimes cache resets to an empty map {}. Anyone have an idea what I’m doing wrong?

(let [in (chan)
      tags [:some :list :of :tags]
      f some-function]

  (go-loop [cache {}]
    (let [{:keys [tag payload]} (<! in)
          new-cache (assoc cache tag payload)
          result (apply f (for [tag tags] (get new-cache tag)))]

      (tap> {:cache cache
             :new-cache new-cache
             :result result})

      (recur new-cache)))
  in)

hiredman16:01:17

Have you restarted your repl recently? My guess is you have some other code running, maybe an earlier version of that loop

jlmr16:01:27

@hiredman have restarted many times since finding this bug. Pretty sure that’s not the problem

hiredman16:01:39

It kind of has to be. What did does that tap result look like that is making you thing cache is becoming {} ?

hiredman16:01:40

Are you seeing {} in that tap output and concluding that cache has become {} when in fact that would be {:cache {} :new-cache ... :new-result ...} ?

jlmr16:01:30

@hiredman yes but also :new-cache seems to reset

jlmr16:01:43

Or am I misinterpreting something?

hiredman16:01:21

Are you sure you are looking at the correct file? I've seen people edit one copy and run another. Are you sure you are saving the file after edits?

jlmr17:01:08

What effect would saving the file have?

hiredman17:01:32

You can put an (assert nil) at the top of the loop to determine if that code is actually being run, and an (assert (not-empty? new-cache)) later on in the loop to determine if that loop is the source of those tap messages

hiredman17:01:43

I dunno how you are running all this

hiredman17:01:33

If you are telling your editor to eval the file, it may load the file from disk instead of the contents of the unsaved buffer

jlmr17:01:26

I’m using chlorine and it’s command to evaluate a specific form. But I will try your suggestions to make sure I’m looking at the right code.

hiredman17:01:36

The assert nil will cause an error if the code you are looking at is actually run, then you can remove it, and then the asset not empty will fire if new cache ever does become empty in that code

hiredman17:01:54

Are you sure you have actually restarted your repl?

hiredman17:01:39

Does chlorine just keep a repl around and opening and closing the repl window or whatever just reconnects you to it?

jlmr17:01:09

It connects to a running socket REPL.

jlmr17:01:36

The repl keeps going even if you close Atom. It’s started in a terminal window

jlmr17:01:26

Could be that my minimal example doesn’t show enough to pinpoint the problem

hiredman17:01:30

I didn't catch that that isn't what is actually breaking

hiredman17:01:13

If you actual code is building the cache with something more complicated then assoc the problem is likely there

hiredman17:01:49

Is it an actual cache with ttls? Then becoming empty has to be expected behavior

jlmr17:01:53

Nope that part is the same as the actual code. The assoc I mean

jlmr17:01:21

It’s just a regular map that I associate into

jlmr17:01:42

The only thing thats missing from my example is that i put (`>!`) result on some channels

hiredman17:01:55

In the example code, cache only grows in size, which is kind of atypical for a cache, is there any code at all that removes or expires entries

hiredman17:01:39

I guess is doesn't grow beyond a constant size, the number of tags, in the example

jlmr17:01:45

The number of tags is indeed constant. It just takes the most recent value with the tag, assocs it into the cache and reruns the function f using all the values in the cache.

jlmr17:01:00

And the result is put on some channels

hiredman17:01:29

I can't debug it without more information, either more detail of the real code, or the results of the asserts I suggested. I strongly recommend finding your invariants like new cache is always greater than or equal to cache in size, and asserting it in as many places as possible in code you are debugging

hiredman17:01:32

Because what you are looking for is the first place that invariant is violated

jlmr17:01:38

Will try some more things tomorrow and check back here if needed. Thanks for the tips!