Ah, that makes sense. Thanks for the thorough response!
On Thursday, April 16, 2015 at 7:00:16 AM UTC-5, Stuart Sierra wrote:
Hi Dan,
The key to understanding what's happening here is to
remember that `component/start` combines both dependency
ordering *and* dependency injection.
Your super system looks like this just after it is
constructed:
{:system {:foo {}, :bar {}},
:system2 {:baz {}, :qux {}}}
When you call `component/start` on this system, it
dispatches to `start-system`, which will build the
dependency graph to see that :system must be started before
:system2.
Next, start-system will call `component/start` on the
component at :system. That component is itself a SystemMap,
so it dispatches to `start-system`, which starts :foo and
:bar.
Now it's time to start :system2. start-system sees that
:system2 has a dependency (`component/using`) on :system. So
it will `assoc` :system into the component at :system2. Now
the system looks like this:
{:system {:foo {:started true},
:bar {:started true,
:foo {:started true}}},
:system2 {:baz {},
:qux {},
:system {:foo {:started true},
:bar {:started true,
:foo {:started true}
Notice that :system2 now contains a complete copy of
:system.
The rest should be obvious. Starting :system2 will start
:baz, :qux, *and* the nested copy of :system. So :system
gets started again. :foo and :bar were already started, but
`component/start` doesn't know that, so it starts them
again.
This is another reason I don't recommend nesting systems:
the behavior is not obvious unless you deeply understand the
model.
There are 2 ways to prevent the repeated starting of :foo
and :bar in this example.
1. Define a custom record for your super system
implementing component/Lifecycle in a way that knows how
to start the subsystems in the correct order without
`assoc`ing their dependencies.
2. Define the `start` and `stop` methods of :foo and :bar to
check if they have already been started before starting
them again. (i.e. make them idempotent)
-S
On Wednesday, April 15, 2015 at 7:52:42 PM UTC+1, Dan Kee wrote:
Sorry to resurrect an old thread with a somewhat tangential question
but...
I'm seeing strange behavior in nesting systems that I am hoping someone
can explain. I have two independent systems as components of a super
system, with an artificial dependency to attempt to enforce ordering of
starting them. When I start the super system, the first system starts,
then the second, then first system is started again and I don't understand
why. I've since realized much simpler, more obvious ways to accomplish
this, but I'd like to understand what I'm seeing.
Code (apologies for the macro, but it keeps things brief):
```
(ns component-debug
(:require [com.stuartsierra.component :as c]) )
(defn capitalize-symbol [s]
(symbol (clojure.string/capitalize (str s))) )
(defmacro make-example-component [name]
(let [capitalized-name (capitalize-symbol name)]
`(do
(defrecord ~capitalized-name []
c/Lifecycle
(start [~name]
(println (str Starting '~name))
(assoc ~name :started true) )
(stop [~name]
(println (str Stopping '~name))
(assoc ~name :started false) ) )
(defn ~(symbol (str new- name)) []
(~(symbol (str map- capitalized-name)) {}) ) ) ) )
(make-example-component foo)
(make-example-component bar)
(make-example-component baz)
(make-example-component qux)
(defn new-system []
(c/system-map
:foo (new-foo)
:bar (c/using
(new-bar)
[:foo