Skip to content

Conversation

@maxweber
Copy link
Contributor

Thanks a lot for creating babashka/process , it is awesome and helps us a lot 🥳

We found this edge case due to an OutOfMemory issue on one of our production servers. It only occurs after several days. We call process/shell three times per second. Below a suggestion for a potential fix.

(.addShutdownHook hook))
(if-before-jdk8
nil ;; throwing an exception here would commit breakage, therefore accept the memory leak for jdk8
(-> (.onExit proc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there is also a :exit-fn?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I tested this, it works because you can call .onExit + .thenRun multiple times.

user=> (require '[babashka.process :as p])
nil
user=> (let [p (:proc (p/process "sleep" "1"))] (-> (.onExit p) (.thenRun (fn [] (prn :foo)))) (-> (.onExit p) (.thenRun (fn [] (prn :bar)))))
#object[java.util.concurrent.CompletableFuture 0x77049094 "java.util.concurrent.CompletableFuture@77049094[Not completed]"]
user=> :bar
:foo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxweber Can I ask you what kind of stuff you were doing in the shutdown hook which caused the memory build-up?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only downside to this PR is that the shutdown hook won't be executed if the process finishes earlier than the exit of the parent process, but I guess this is fine since the shutdown hook is mostly there for killing the process and its children.

Copy link
Contributor

@borkdude borkdude Mar 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, one corner case: if you previously had :shutdown p/destroy-tree then the PR behavior might result in not having the children killed if the process exits before the parent process?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxweber Yes, I think that would be reasonably, although I don't know exactly what would happen if you call p/destroy-tree in :exit-fn but we could try to find out :)

What I was wondering: did you use any custom :shutdown hook or just p/destroy(-tree)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems to just work. So adding (shutdown res) after removing the shutdown hook would work I think to ensure it's still called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@borkdude I'm using p/destroy(-tree) indirectly via the defaults of process/shell. In our code I just set :shutdown nil to fix the memory leak for the moment :)

Adding (shutdown res) after the hook removal sounds good 👍 One thought, maybe starting the hook Thread instead would be safer? If someone does something 'fancy' in the hook that blocks very long or just has an error that causes an exception usually only during the shutdown of a long running app?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Also please update the docstring to document this new behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@borkdude as discussed the :shutdown hook is now executed as soon as the child process ends and the docstring has been extended.

@borkdude borkdude merged commit 792467b into babashka:master Mar 31, 2025
17 checks passed
@borkdude
Copy link
Contributor

@maxweber Do you use this library from the JVM and do you need a new release?

@borkdude
Copy link
Contributor

Already released a new version 0.6.23

@maxweber
Copy link
Contributor Author

maxweber commented Apr 1, 2025

Awesome 🥳 Thanks a lot.

@maxweber maxweber deleted the fix-shutdown-hook-memory-leak branch May 12, 2025 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants