Fork me on GitHub
#component
<
2022-07-04
>
wilkerlucio18:07:09

hello, is there a way to measure how much time each component is taking to start? I'm trying to debug why some project here takes so long to start, and having the time for each specific component would be super helpful. the hard part of my case is that most components are part of external libraries, so changing their code to measure is painful

1
wilkerlucio18:07:01

so far I got around by forking component and adding my measurement code there, but would be nice a way that doesn't require hacking the library

hiredman19:07:31

What do you mean by start?

wilkerlucio19:07:50

how long the start fn (from Component/start) takes to run, for each component

wilkerlucio19:07:04

this is what I did on my hack:

wilkerlucio19:07:06

(defn- try-action [component system key f args]
  (try (println "starting" component)
       (time
         (apply f component args))
       (catch #?(:clj Throwable :cljs :default) t
         (throw (ex-info (str "Error in component " key
                           " in system " (platform/type-name system)
                           " calling " f)
                  {:reason     ::component-function-threw-exception
                   :function   f
                   :system-key key
                   :component  component
                   :system     system}
                  t)))))

wilkerlucio19:07:33

(the hack is back because this fn might be called during stop as well, but just a dirty hack to get it working for my case)

hiredman19:07:25

Sure, but I mean, take a step back, of your application is slow to start it is very unlikely that component start time meaningfully contributes to that

wilkerlucio19:07:51

it actually is, from my debug I could find a single component taking 97s to start

wilkerlucio19:07:13

from the 120s total

hiredman19:07:29

That is crazy

hiredman19:07:00

It is possible to do arbitrary stuff in start, but that doesn't mean you should

wilkerlucio19:07:38

well, the slow ones are things that depend on external data, so they have to download things from the internet, it happens the perpetrator is doing a lot of downloading

hiredman19:07:44

Oof, horrendous

wilkerlucio19:07:30

so, this debug allowed me to pinpoint that component, and that's the gist of why I want something like that

hiredman19:07:46

Sure, well the component library has a function I forget the name of that basically maps whatever over the components in a system

hiredman19:07:07

Starting a system is using that function together with the start function

hiredman19:07:37

So you just use that function with a function that wraps start with time

hiredman19:07:20

(update-system s (keys s) (fn [c] (prn (type c)) (time (start c)))) or something

hiredman19:07:21

But once you identify the components that are fetching things over the internet when starting, absolutely ditch them

wilkerlucio19:07:33

its not that easy, because the components might implement other protocols other than the component one (and most on them in my case does) so wrapping that instance while forwarding every protocol is complicated

wilkerlucio19:07:57

I think the ideal situation would be have sort of plugins/hooks support on component itself, so we could react to things like start/stop

hiredman19:07:28

There is no wrapping

hiredman19:07:05

You just use your own call using update-system like above in place of calling start on the whole system

hiredman19:07:58

Er start-system

wilkerlucio20:07:25

ah, makes sense, afk now, but will try whenI get back :)

wilkerlucio21:07:12

ok, this works:

(defn start-measured [component]
  (println "starting" component)
  (time (component/start component)))

(comment
  (time
    (let [sys (system-config)]
      (component/update-system sys (keys sys) #'start-measured))))