Fork me on GitHub
#off-topic
<
2020-10-10
>
emccue00:10:40

so just (.close borrowed-client) and nothing else?

emccue00:10:26

interestingly it all works if I just get rid of the while true

emccue00:10:42

;; ----------------------------------------------------------------------------
(defn create-pub-sub-listener
  "Creates a listener for pub-sub messages. Borrows a connection
  from the pool and doesn't return it until it is shut down."
  [pool]
  (let [client ^Jedis (.getResource pool)
        logic (proxy [JedisPubSub] []
                (onMessage [channel message]
                  (log/info :channel channel :message message)))
        executor ^ExecutorService (Executors/newSingleThreadExecutor)]
    (.submit
      executor
      (reify Runnable
        (run [_]
          (.subscribe client
                      logic
                      ^{:tag "[Ljava.lang.String;"}
                      (into-array String [post-notification-channel])))))
    {:borrowed-client client
     :pub-sub logic
     :executor executor}))

;; ----------------------------------------------------------------------------
(defn shutdown-pub-sub-listener!
  [pub-sub-listener]
  (let [{:keys [borrowed-client pub-sub executor]} pub-sub-listener]
    (.shutdownNow executor)
    (.unsubscribe pub-sub (into-array String [post-notification-channel]))
    (.close borrowed-client)))

emccue00:10:02

but this means that any tasks I do based on messages better not fail

emccue00:10:39

since it would stop consumption of messages

hiredman00:10:45

yeah, because the try/catch is inside the while it just creates the subscription again after you unsubscribe

emccue00:10:46

and I would have no way to know

emccue00:10:06

but I don't get a log of the exception when I unsubscribe

hiredman00:10:07

you can instead of (while true ), use an atom

emccue00:10:29

so it doesn't seem like anything is thrown

hiredman00:10:33

it is likely a InterruptedException or wrapped in one

emccue00:10:04

(while true
            (try
              (.subscribe client
                          logic
                          ^{:tag "[Ljava.lang.String;"}
                          (into-array String [post-notification-channel]))
              (catch InterruptedException e
                (throw e))
              (catch Exception e
                (log/error ::jedis-pub-sub-error e
                           ::action-to-take "retry subscription")))))))

emccue00:10:09

i wouldn't think so

emccue00:10:25

since in both cases there should be some observable side effect

hiredman00:10:27

it may not throw any exception

hiredman00:10:00

so (while @whatever ...

hiredman00:10:20

and (reset! whatever false) on shutdown

emccue00:10:36

yeah but subscribe is already an infinite loop within itself

hiredman00:10:55

the unsubscribe will stop it

hiredman00:10:05

and you will stop the while

emccue00:10:06

and even adding a log to the interupttedexception case doesn't show anything

hiredman00:10:18

add a log after the subscribe call

hiredman00:10:24

or in a finally

hiredman00:10:35

what is happening is the subscribe call isn't throwing

hiredman00:10:40

just returning

hiredman00:10:45

and you loop around

emccue00:10:49

ahhhh that makes sense

emccue00:10:54

yep thats whats happening

emccue00:10:12

yep that did the trick

emccue00:10:19

;; ----------------------------------------------------------------------------
(defn create-pub-sub-listener
  "Creates a listener for pub-sub messages. Borrows a connection
  from the pool and doesn't return it until it is shut down."
  [pool]
  (let [client ^Jedis (.getResource pool)
        logic (proxy [JedisPubSub] []
                (onMessage [channel message]
                  (log/info :channel channel :message message)))
        executor ^ExecutorService (Executors/newSingleThreadExecutor)]
    (.submit
      executor
      (reify Runnable
        (run [_]
          (let [completed (atom false)]
            (while (not @completed)
              (try
                (.subscribe client
                            logic
                            ^{:tag "[Ljava.lang.String;"}
                            (into-array String [post-notification-channel]))
                (catch Exception e
                  (log/error ::jedis-pub-sub-error e
                             ::action-to-take "retry subscription"))
                (finally
                  (reset! completed true))))))))
    {:borrowed-client client
     :pub-sub logic
     :executor executor}))

emccue00:10:35

still open question marks on the multi threaded behavior of calling unsubscribe

emccue00:10:55

but this should only happen in dev so it won't matter that much

emccue00:10:47

...hopefully

hiredman00:10:35

The finally there means the while loop will never loop

hiredman00:10:28

Also if you want the while there you likely need to move the get resource call inside the while, otherwise if the connection dies you'll never get a new one

emccue01:10:48

;; ----------------------------------------------------------------------------
(defn create-pub-sub-listener
  "Creates a listener for pub-sub messages. Borrows a connection
  from the pool and doesn't return it until it is shut down."
  [pool]
  (let [client ^Jedis (.getResource pool)
        logic (proxy [JedisPubSub] []
                (onMessage [channel message]
                  (log/info :channel channel :message message)))
        executor ^ExecutorService (Executors/newSingleThreadExecutor)]
    (.submit
      executor
      (reify Runnable
        (run [_]
          (let [completed (atom false)]
            (while (not @completed)
              (try
                (do (.subscribe client
                                logic
                                ^{:tag "[Ljava.lang.String;"}
                                (into-array String [post-notification-channel]))
                    (reset! completed true))
                (catch Exception e
                  (log/error ::jedis-pub-sub-error e
                             ::action-to-take "retry subscription"))))))))
    {:borrowed-client client
     :pub-sub logic
     :executor executor}))

emccue01:10:01

yep so if it exits normally, thats another thread calling unsubscribe

emccue01:10:10

otherwise its an exception

hiredman01:10:09

You still have the issue of that while loop continuing to try on a dead connection

emccue01:10:52

yeah, but there are only like 8 connections in the pool

emccue01:10:14

if i keep getting new ones i will mess up pushing other stuff to redis

emccue01:10:33

the real issue is visibility

emccue01:10:54

like, if i could know that i messed up handling and shut down and restart this component

emccue01:10:33

or if i had it in my dashboard (i'm using heroku for everything right now) then I could manually handle it

emccue01:10:52

idk - i just don't want to do this in a "bad" way

emccue01:10:06

maybe for this component i shouldn't touch the pool at all

emccue01:10:21

and just give it its own Jedis it recreates on failure

hiredman01:10:15

The pool isn't limited to the same N connections

hiredman01:10:46

I believe when you return a connection to it, it tests it and if it is bad it throws it away and adds a new one to the pool

emccue02:10:44

stateful things are always nightmares

hiredman02:10:20

the structure of this in our code at work looks something like this using core.async:

(async/go-loop []
  (async/alt!
    exit ([_] ...)
    (get-connection redis-pool) ([connection]
                                 (async/alt!
                                   (async/thread
                                     (try
                                       (.subscribe connection ...)
                                       (finally
                                         (close-connection connection)))) ([_] (recur))
                                   exit ([_]
                                         (.unsubscribe connection ...)
                                         (close-connection connection)
                                         ....)))))

borkdude13:10:30

Are there any libraries (e.g. on top of rewrite-clj) that can merge EDN files and write back to a file, while preserving order / whitespace / comments etc? Maybe I'll have to write my own, but just asking in case there's already one

👍 3
borkdude13:10:58

The use case: I want to write a package manager, but both the user and the package manager should be able to put stuff in an .edn file

borkdude13:10:07

Kind of like npm does this with package.json

borkdude13:10:54

I guess npm has it easier since comments aren't allowed in JSON, but still, it seems to preserve some of the user-defined order in maps

andy.fingerhut13:10:46

order is relatively easy to preserve, but only if you tweaked the reading code to use something like the ordered library https://github.com/clj-commons/ordered as long as you were then also careful about what operations you did on those maps so the ordering wasn't lost (e.g. select-keys I think always returns a built-in Clojure unordered map, so you would need to avoid using that, and/or write your own variant that returned the type of map it was given. The same goes for many other core functions on maps).

andy.fingerhut13:10:10

preserving white space and/or comments is a special purpose reader that only a few libraries implement, like rewrite-clj.

andy.fingerhut13:10:51

I suspect it would be less work to take a library that already preserved whitespace and comments, and add preservation of map/set order, than vice versa.

borkdude13:10:49

if I would drop the comments/whitespace requirement, ordered maps could work. not sure if I can read ordered maps from plain edn maps or if I'll have to represent them using an ordered coll in .edn with a reader tag, which is kind of ugly

andy.fingerhut13:10:11

I think it would be a fairly small change to something like tools.reader lib to make it return ordered maps/sets, rather than the core unordered maps/sets.

borkdude13:10:57

yeah, I think I could also support it in edamame (which is already built on top of tools.reader)

andy.fingerhut13:10:08

I haven't dug into rewrite-clj, but perhaps it already maintains order, but perhaps it does so by not returning regular Clojure data structures, but a custom one representing the sequence of 'things' in the input file?

borkdude13:10:18

that's correct

borkdude13:10:35

and writing merge, update-in, etc on top of that is a lot of work

andy.fingerhut13:10:30

Attempting to preserve white space/comments, and returning data that one can do regular Clojure data manipulation on, sounds like magic.

borkdude13:10:57

can be done, I think. Just a lot of work

andy.fingerhut13:10:09

or writing your own custom implementation of those data manipulation functions on new data structures that contain representations of the white space/comments?

dominicm14:10:40

@borkdude I tried this. It's called ednup. I used it in a couple of little things.

dominicm14:10:02

I suspect your deep rewrite-clj knowledge will give you a better implementation here

borkdude15:10:45

I guess this could become useful pretty quickly to update deps.edn files, fixing some stuff like unqualified symbols

dominicm15:10:10

I wrote it for deps, yah. I have an alternative tag resolver that preserves comments.

dominicm15:10:19

Something I handled btw, was detection for that thing people do where they align their keys. That was feedback I got a lot of :)

tio16:10:14

In one of my commits I accidently deleted a file; how do I tell git that I want this file? I read the instructions for `git ammend` — you basically choose a commit and git will rebase on top of it. However; I’m unsure how to add back a file. This is not an Clojure question; but I didn’t know where else to go;

phronmophobic16:10:04

I probably wouldn't rewrite history if you don't have to. I would probably do something like:

git checkout <commit-where-file-exists> -- <filepath>
and then commit as normal

💯 3
tio16:10:20

It’s 2 commits backwords

tio16:10:28

Will it keep all of my changes

andy.fingerhut16:10:46

If you want to be really really safe about it, you can make a second clone of your repository elsewhere, do 'git checkout <commit-where-file exists>' in that copy, copy the file you want into your working repository, and do 'git add <filename>' in your working repository. Then delete the other copy of the repository if you don't need it any more.

andy.fingerhut16:10:57

Crude, but effective, and harder to mess up your working repo's state

phronmophobic16:10:54

my suggestion works even if the file is two commits backwards and it will keep all your changes

andy.fingerhut16:10:56

I do not know off hand whether that technique will let you do 'git blame' on that file and see its entire history since it was first created, or not.

phronmophobic16:10:09

my guess is that it won't let you do git blame all the way backwards

Thomas Harned16:10:10

@U7RJTCH6J is giving you solid advice here. If you really want to really want to put your mind at ease, check out the chapter on branching in the pro git book. At the risk of oversimplifying, a branch is just a pointer to a commit. So by checking out an earlier commit, all you’re really doing is pointing to an earlier snapshot of your work. Nothing is being deleted or erased. https://git-scm.com/book/en/v2

phronmophobic16:10:26

the command I gave doesn't actually do a full checkout of the whole commit. it only gets the version of <filename> from that commit and stages it

phronmophobic16:10:22

git checkout is overloaded and can do a bunch of different things

tio16:10:45

So this git checkout 183ca537 -- apps/admin_web/lib/admin_web/templates/layout/_sidebar.html.eex

tio16:10:51

Does that look OK to you?

👍 3
phronmophobic16:10:06

for reference, the fancy way to fix your issue and preserve git blame is to use git interactive rebase, but I wouldn't recommend doing that unless you feel confident in your git fu

phronmophobic16:10:36

the git-scm book that @U01BTEYDC05 linked is really good and I would recommend reading it even if you don't use git

tio16:10:47

That worked, thank you!!! Saved me a lot of frustration and hours of work.

parrot 3
🎉 3
tio17:10:54

This deleted all of my work….

3
tio17:10:14

I did the commmand above

tio17:10:35

I did this and then git push forced

phronmophobic17:10:24

if you're the only one working on the repo, that should not have required a force push

phronmophobic17:10:30

since it only added a new commit

phronmophobic17:10:38

force pushing is almost never required

tio17:10:25

Fuck; ok thats my fault.

tio17:10:27

Thank you.

tio17:10:35

I’m not the only one on the repo;

phronmophobic17:10:50

well, the good news is that someone else likely has the commits that were overwritten and it's likely they can recover them

Vincent Cantin08:10:09

@U013HT66AG4 check "git reflog" on internet, you will get your previous branches back. They are still in your repository.

tio00:10:09

It wasn’t in gitrefleg. I had to find it in the dangling commits.

tio00:10:14

I used the git reset HARD command.

tio00:10:38

Anyway, thank you all for all your help. This was very informative for me. I will not use git commands without knowing exactly what they do 🙂

Thomas Harned01:10:20

Shooting yourself in the foot with git is a software engineering right of passage. This Summer I accidentally nuked all of my Intern’s work one week before he was to present it. I was up until midnight on a Friday reading git documentation. I was eventually able to recover it after about 5 tense hours. Likewise, I no longer copy/paste git commands without understanding what’s going on under the hood.

6
👆 6
tio06:10:05

What did you say to the intern when you realized what you did?

tio06:10:20

Were you calm and collected; did you tell your superior? That’s amzing story.

Thomas Harned21:10:36

Fortunately it happened late on a Friday so everyone was already signed off for the weekend. I sent an email that there was an “issue” I was working to resolve and left it at that. After I fixed it, I told them the whole story on Monday and everyone had a good laugh at my expense.

tio22:10:42

Ah. 🙂

tio22:10:45

Good story!