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] ) ) )                                         
                                                              
(defn new-system2 []                                          
  (c/system-map                                               
   :baz (new-baz)                                             
   :qux (c/using                                              
         (new-qux)                                            
         [:baz] ) ) )                                         
                                                              
(defn new-super-system []                                     
  (c/system-map                                               
   :system (new-system)                                       
   :system2 (c/using                                          
             (new-system2)                                    
             [:system] ) ) )                                  
                                                              
(defn -main [& args]                                          
  (c/start (new-super-system)) )                              
```

Output:

```
Starting foo
Starting bar
Starting baz
Starting qux
Starting foo
Starting bar
```

Thank you!

--Dan

On Wednesday, March 18, 2015 at 8:51:17 AM UTC-5, platon...@gmail.com wrote:
>
> A possible implementation for the idea expressed in the previous post - 
> https://github.com/stuartsierra/component/pull/25
>
>
> On Wednesday, March 18, 2015 at 2:41:46 PM UTC+1, platon...@gmail.com 
> wrote:
>>
>> I've also been investigating the nested system approach/problem.
>>
>> The primary use case that I have is composing subsystems which are mostly 
>> independent modules using a higher order system to run in one process. The 
>> modules themselves can be easily extracted into separate applications thus 
>> becoming their own top level systems which makes it desirable to keep their 
>> system maps intact. 
>>
>> Components inside modules might depend on the *whole* modules, not their 
>> constituent parts. This allows to have modules call each other through the 
>> API's in-process as well as being easily replaced by remote endpoints when 
>> separated into multiple processes.
>>
>> This mostly works except for the components depending on other 
>> modules/systems, e.g.:
>>
>> (require '[com.stuartsierra.component :as cmp])
>>
>> (defrecord X [x started] 
>>    cmp/Lifecycle 
>>    (start [this] (if started (println "Already started " x) (println 
>> "Starting " x " " this)) (assoc this :started true)) 
>>    (stop [this] (println "Stopping " x " " this) this))
>>
>> (def sys1 (cmp/system-map :x (cmp/using (X. "depends on dep" nil) 
>> [:dep])))
>> (def sys2 (cmp/system-map :y (cmp/using (X. "depends on sys1" nil) 
>> [:sys1])))
>> (def hsys (cmp/system-map :sys1 (cmp/using sys1 [:dep]), :sys2 (cmp/using 
>> sys2 [:sys1]) :dep (X. "dependency" nil)))
>>
>> (cmp/start hsys)
>>
>> Starting  dependency   #user.X{:x dependency, :started nil}
>> Already started  dependency
>> Starting  depends on dep   #user.X{:x depends on dep, :started nil, :dep 
>> #user.X{:x dependency, :started true}}
>>
>> clojure.lang.ExceptionInfo: Error in component :sys2 in system 
>> com.stuartsierra.component.SystemMap calling 
>> #'com.stuartsierra.component/start
>> clojure.lang.ExceptionInfo: Missing dependency :dep of 
>> clojure.lang.Keyword expected in system at :dep
>>
>> This happens because of the following:
>> 1. Dependency :*dep* of *sys1* is started in *hsys*
>> 2. *sys1* is started (:*dep* is started again, so the start/stop should 
>> be idempotent)
>> 3. Dependency :*sys1* of *sys2* is started in *hsys*
>> 4. :*sys1* cannot be started as it depends on :*dep* which isn't present 
>> in *sys2*
>>
>> This scenario could be supported by the Component library in several ways:
>>
>> 1. introduce an IdempotentLifecycle protocol which will be respected by 
>> the Component library. Implement the protocol for the SystemMap. 
>> IdempotentLifecycles will not be started or stopped for the second time, 
>> also their dependencies will not be updated if they are already started.
>> 2. do not fail if a component already has a dependency under the 
>> specified key. This is a hack compared to the first solution, but I might 
>> go with it in the short term.
>>
>> Stuart, what do you think about that? Would you consider a PR 
>> implementing the first proposal?
>>
>> On Wednesday, March 18, 2015 at 10:18:36 AM UTC+1, Stuart Sierra wrote:
>>>
>>>
>>> On Tue, Mar 17, 2015 at 5:47 PM, James Gatannah <james.g...@gmail.com> 
>>> wrote:
>>>
>>>> FWIW, we've been using something that smells an awful lot like nested
>>>> systems for months now. I never realized we weren't supposed to.
>>>>
>>>
>>>
>>> It's not that nested systems *never* work, but from what I've seen they 
>>> cause more complications than they're worth. The 'component' model doesn't 
>>> forbid it, but it does not support dependencies between components in 
>>> different "subsystems."
>>>
>>> I've found it easier to keep system maps "flat" and use namespaced 
>>> keywords to distinguish "subsystem" groups, even in large systems with 30+ 
>>> components.
>>>
>>> –S
>>>
>>>

-- 
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