I'm using mount in a webapp server, and I'm finding that, when I reload a namespace, it automatically restarts contained defstates. Would it be possible to automatically restart all dependent defstates as well?
@titogarcia mount uses compiler to figure out state dependencies via namespace refferals (i.e. :require)
while it has a “https://github.com/tolitius/mount/issues/12#issuecomment-167150505” built by a clojure compiler walking through these namespaces, this are not logical component dependencies
there are a couple of thoughts for the above 👆 question:
1. why would you need to restart other states when restarting a web server? (i.e. it very well may be the case, but it would be helpful to learn about your use case)
2. you could choose to not restart the web server when a namespace is reloaded / recompiled by https://github.com/tolitius/hubble/blob/master/src/clj/hubble/server.clj#L51 {:on-reload :noop} meta. this way you can just do (restart) only when needed vs. when something is reloaded
3. since mount allows to you access partial systems, you can create a function that restart certain states that you might neeed, and does not restart others that are logically dependent but don’t need to be restarted, in fact are better not restarted in many cases (i.e. kafka consumers, etc..)
> Isn’t that an incidental design decision with states it usually is not most of the problems with state are really context dependent and don’t generalize well outside of the context stateless functions / objects / etc.. are super great and generalize well statefull things do not that was the reason I wanted to learn about what states are there in the context of the problem you are trying to solve > the underlying HTTP server connection which is a state, not your web-server not exactly a pool of threads / file handlers / connections is a state. a web server has only a few APIs: i.e. start / stop / a few more, which is a gateway into that state
> the global vars argument is an old one
Don't know what you're referring to here. I'm not making an argument about global vars. I'm asking what you find a good practice to using states.
> no, it is always best to pass a state in vs. refer to it directly
> easier to test, evolve, reason about
But you need to refer to it at least once in order to use it. Any suggestions on what function(s) should depend on a state?
> https://github.com/tolitius/hubble/blob/master/src/clj/hubble/server.clj#L31-L42 is an example of a handler that takes in a state vs. references it internally
Yeah, I see routes depending directly on the config state. I was thinking of doing something similar. With mount you can avoid threading state through initialization functions.
Anyway, we're deviating from the original topic: Wouldn't it make sense to have a way to restart upstream dependencies when restarting a state? For example, if you change config and restart it, the web-server would be restarted for running with the new config.
Isn't that an incidental design decision, where composing state gives another state as a result? I mean, in the same way, you could say that your web-server is not a state, it is actually the underlying HTTP server connection which is a state, not your web-server. (I'm simplifying for the sake of the argument, your web-server will surely contain other states).
Hi, thank you for your response! > why would you need to restart other states when restarting a web server? I'm referring to the other way round. Let's say I have these defstates that depend on each other: db <- ring-handler <- web-server If ring-handler is restarted, it makes sense to restart the web-server to take the new ring-handler.
why would a ring handler be a state? a state is usually something low level that has to do with resources such as I/O, thread pools: web server, data source, connection pool, broker consumer, etc.
Good point. I'm modeling it as a state because it makes use of the DB, which is a another state. Thus, when initializing the ring-handler, you depend on having initialized the DB.
yea, in general I would recommend to have as few states as possible. stateless functions, components are much easier to reason about and evolve. per your description, a ring handler would not be a state
My ring-handler is a compound state in the same way the web-server is, and it uses other states, like db. But the functions that ring-handler delegates to don't rely on states directly, they rather expect those as parameters.
Even if I define states as global vars (the mount way), I can easily control what code directly relies on those.
the difference between the ring handler and the web server is that the web server is a library that has certain API exposed vs. a ring handler is an application specific, custom component that does not really need to be a state. it may use a state / take a state / work with a state (i.e. data source), but it should not be a state
I fail to see your reasoning. I choose the level I want to manage states. mount allows me to start a state without taking care of their internal dependencies.
It is quite the same for me in my example whether the web-server state starts by starting the db state and then creates a ring-handler from it, or whether it starts a ring-handler state that will depend on the db state. It is an implementation detail with no further implications.
In the same way you could say that a configuration map should not be a state, but rather the opened file that contains the configuration would a state. It's only that providing a ready-to-use configuration map is more convenient.
my only point is that state needs to be contained at the lowest level: decoupling is great. in case of a web server, its API is the lowest level that makes sense to use to start and stop that server that does wrap some low level resources in case of a ring handler there is no good reason to make it a state since it couples the changes in the handler / its functions / evolution / modularization with the underlying state: a database for example if the handler dealt with several stateful components: a data source, a thread pool, and a cache it would make sense for this handler not care how these stateful components started or stopped, and make 3 states instead: • a data source • a thread pool • and a cache
> it would make sense for this handler not care how these stateful components started or stopped, and make 3 states instead
How would these 3 states be used by a particular route handler, would their global vars be referenced directly? Do you have an example application where you show what you mean here?
> mount uses compiler to figure out state dependencies via namespace refferals (i.e. :require)
> while it has a “https://github.com/tolitius/mount/issues/12#issuecomment-167150505” built by a clojure compiler walking through these namespaces, this are not logical component dependencies
I would like to understand this better. What would be the difference between "state dependencies" and "logical component dependencies"?
the global vars argument is an old one any function is a global var any namespace is a global var any database is a global var …. mount states are no different, they just don’t cover up the fact that a database is a global var now to your question: > would they be referenced directly? no, it is always best to pass a state in vs. refer to it directly easier to test, evolve, reason about > Do you have an example application where you show what you mean here? https://github.com/tolitius/hubble/blob/master/src/clj/hubble/server.clj#L31-L42 is an example of a handler that takes in a state vs. references it internally > “state dependencies” and “logical component dependencies” state dependencies are the ones a state can not be created without for example a web server https://github.com/tolitius/hubble/blob/master/src/clj/hubble/server.clj#L52 state logical dependencies are other states that logically depend on this state. for example a web server state may depend on redis to be started to make sure that web requests can be faster i.e. vs. going to the database directly