I feel stupid, but I have not been able to track this down. 

The background is that I have Jenkins running on the server, and when 
triggered it pulls code from Github, compiles it, and then moves the final 
uberjar to the directory where I keep all the uberjars that run on this 
server. Then I have an app that should kill all currently running 
instances. Then Supervisord should step in and restart the instances, using 
the new uberjars for the new instances. 

All of this works, but I have been doing one bit by hand: "kill all 
currently running instances". I log in as root and run a script that kills 
the old instances. But just today I wrote a quick Clojure app to automate 
this for me (the whole app is 50 lines of code, so this is a very small 
app). 

To trigger the main action, I thought the app should set a watcher on the 
final directory where Jenkins moves the uberjars to. When Jenkins moves a 
new uberjar to the directory, the watch would be triggered and then it 
could run the "kill all currently running instances" script. However, I 
assumed that the ""kill all currently running instances"" script would be 
triggered once each time the uberjar was updated, but instead it seems to 
be triggered infinitely. Does a JVM app, or a Clojure app, change the dir 
its in, on launch? Or is the problem in my own code? I'm using Chris 
Zheng's excellent Hara library for the Watch. This is the main function of 
my app, which is called on startup: 

(defn establish-watchers []
  "The config contains a hashmap which has a 'watchers' key, which contains 
a vector that holds hashmaps with 2 keys, one pointing to the directory 
that should be watched and the other pointing to the command that should be 
run when the watch is triggered."
  (let [config (read-string (slurp "/etc/fiit.edn"))
        watchers (:watchers config)]
  (doseq [w watchers]
     (common-watch/add (clojure.java.io/file (:dir-to-watch w)) (keyword 
 (:dir-to-watch w))
           (fn [f k _ [cmd file]]
             (pprint/pprint (:out (sh/sh (:action-to-do w)))))
           {:types #{:create :modify}
            :recursive false
            :async false}))))

So if I log in as root and start the app like this:

/usr/bin/java -jar /home/jenkins/fiit/fiit-1-standalone.jar

Then at the terminal I see this, which I expect: 

[/home/jenkins/sarr]
[/home/jenkins/food_for_all]

Those are the 2 directories that it is watching. 

Then if I trigger Jenkins for the sarr app, Jenkins will pull the sarr code 
from Github, rebuild the sarr uberjar, and move the uberjar to 
/home/jenkins/sarr. 

All of that works just great. And then my app sees there has been a change 
in /home/jenkins/sarr, and so it calls the command to kill all existing 
instances of the sarr app. However, instead of doing this once, it starts 
doing so an infinite number of times. At the terminal I see: 

(sarr is a Java app, not a Clojure app)

"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \nroot     25593  0.0  0.1 5813460 19452 ?   
    Sl   20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\nroot     25595  0.0  0.1 5813460 19564 ?       Sl   
20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\n25593\n25595\n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n25694\n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \nroot     26239  0.0  0.1 5813460 19452 ?   
    Sl   20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\n26239\n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \nroot     26319  0.0  0.1 5811252 15484 ?   
    Sl   20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\n26319\n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"
"We will kill these processes: \n"


I have Supervisord set to run 3 instances of the sarr app, so you can see 
at first it it is killing these PIDs:

25593
25595
25694

Supervisord restarts these almost instantly, so you can see these processes 
are launched and instantly killed, all on the same line of output: 

"We will kill these processes: \nroot     26239  0.0  0.1 5813460 19452 ?   
    Sl   20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\n26239\n"

"We will kill these processes: \nroot     26319  0.0  0.1 5811252 15484 ?   
    Sl   20:28   0:00 /usr/bin/java -cp /home/jenkins/sarr/sarr.jar 
com.candle.sarr.Main\n26319\n"

This goes on for as long as I allow it (it takes Supervisord a long time to 
quit, because when I am developing new software, I set startretries=500, in 
Supervisord, exactly so I can experiment like this). 

I looked here to try to figure out what was going on: 

https://github.com/zcaudate/hara/blob/55b4eda688a4e616f03bf3740419a69b5c5dd658/src/hara/io/watch.clj

As near as I can tell, the main action happens here:

(defn run-watcher [watcher]
  (let [^java.nio.file.WatchKey wkey
        (.take ^java.nio.file.WatchService (:service watcher))]
    (doseq [^java.nio.file.WatchEvent event (.pollEvents wkey)
            :when (not= (.kind event)
                        StandardWatchEventKinds/OVERFLOW)]
      (let [kind (.kind event)
            ^java.nio.file.Path path (.watchable wkey)
            ^java.nio.file.Path context (.context event)
            ^java.nio.file.Path res-path (.resolve path context)
            ^java.io.File file (.toFile res-path)]
        (if (and (= kind StandardWatchEventKinds/ENTRY_CREATE)
                 (.isDirectory file)
                 (-> watcher :options :recursive))
          (register-sub-directory watcher (.getPath file)))
        (if (.isFile file)
          (process-event watcher kind file))))
    (.reset wkey)
    (recur watcher)))


Again, maybe I am being stupid, but it looks to me like the callback is 
called once per event triggered on that directory. So why would the 
callback seem to be called an infinite number of times? Does starting or 
stopping the app trigger a change in the directory (thus triggering the 
callback again)? 






-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to