I'm currently using Integrant for starting some TestContainers for some automated tests. During a test, I would want to turn off one of the containers for some seconds, then turn it on again. What would be the way to achieve this?
I have tried to suspend! the key, but resumeing will start extra containers for keys that were already running.
> Currently you'd need to do this manually by passing the same set of keys to both suspend! and resume.
Only that resume will halt keys in the system that are not passed to it.
Ah, right! I'd forgotten. I believe that's so it behaves the same way as init.
Adjusting resume so that it knows about which keys were suspended sounds like a reasonable update, but I'll give it a little thought first.
In this case, I'd access the system map directly, assuming that suspending and resuming the container key can be done in isolation. Something like this, perhaps:
(defn suspend-container-key [system key]
(ig/suspend-key! key (get system key)))
(defn resume-container-key [system key]
(let [config (-> system meta ::ig/origin)
value (get config key)]
(update system #(ig/resume-key key value value %))))Alternatively, if you have a .start and .stop on your container you could do it:
(defn stop-container-key [system key]
(.stop (get system key)))
(defn start-container-key [system key]
(.start (get system key)))
I see, Integrant doesn't keep track of whether a key has been halted/suspended. So it doesn't know what keys are actually reusable, right?
Kinda right. Suspend/resume are methods designed to allow keys in development to reuse resources. When you implement these methods, you're telling Integrant that it can reuse resources under circumstances you control.
For example, if you have a database connection pool, and you know that the configuration is unchanged, there's no need to shut it down - you can reuse it without issue. Similarly, if you have a web server, and you know that only the handler function has changed, you can update the handler without restarting the web server. Otherwise, you can fall back to the default halt/init.
The purpose of this is to both speed up restarts in a development environment, and to prevent interruptions caused by closing connections. It's not designed as a way of partially stopping/starting a configuration.
That said, there's no reason we couldn't record which keys are suspended in the metadata of the system, and then to only resume those specific keys when we resume. Currently you'd need to do this manually by passing the same set of keys to both suspend! and resume.
Yeah, actually .stopping and .starting works. Don't know why I was under the impression that a TestContainer wasn't restartable.
Thanks for your response!
However, shouldn't resume not start again keys that are already been started? Its docs says:
integrant.core/resume
[config system]
[config system keys]
Turn a config map into a system map, reusing resources from an existing
system when it's possible to do so.Only if you explicitly write suspend-key! and resume-key to re-use resources. It wasn't designed with partial restarts in mind; rather, suspend/resume are for things like keeping the server and other connections open between restarts.
Yes, I noticed that when looking into the library code.
Suspending falls back to halting when suspend-key! is not defined, which makes suspend-key! kind of optional. Couldn't resume behave the same way when suspend-key! is not defined for a key, i.e. reuse already init'ed resources? The current behavior seems to get us onto unexpected/inconsistent system state.
You mean in the case that suspend! is applied only to a subset of keys?
In any case... does it make a difference?
If a key has been halted, it needs to be re-initiated, so can't be re-used. Only keys with suspend-key! implementations can potentially be re-used. Or keys that weren't included in the suspend! call in the first place. I guess I could record the subset of keys passed to suspend! and only trigger these for resume, if resume doesn't get keys of its own.