Thanks for sharing the various options you guys are using, much appreciated.
I'm thinking I'm going to go with a function returning a map of configs (potentially memoized) with the option to overwrite certain keys from a separate map for testing etc. Should be good enough for my use cases. On Wed, Sep 11, 2013 at 9:12 AM, Softaddicts <lprefonta...@softaddicts.ca>wrote: > Euh... our local config would require loading a top level map with more > than > twenty keys and a dozen levels deep of maps. > > I am not sure I would like to carry a local copy in all workers. > > Freezing the app somehow when a config change occurs is mandatory. > Each node in the cluster runs at least a dozen workers and not all the > nodes > run the same set of workers. We cannot allow a worker to run with the old > config in place while others pick up the changes. Mismatches in resources > between collaboration workers would create a huge chaos. > > Allowing some workers to continue processing would require knowing which > ones > are impacted by a change and which ones can safely continue w/o picking up > the change. This requires a tool to analyze dependencies at runtime > when a config change occurs. > > This may be a future enhancement but up to now the value of such an > enhancement has been very low. A config change does not stall the app > for a significant amount of time. > > Luc P. > > > > 2013/9/11 Softaddicts <lprefonta...@softaddicts.ca>: > > > We load configuration data once from zookeeper and conceal it in a > name space. > > > Changing the context is quite simple, we reload resources in this name > space > > > using a minimal list of properties which says where the configuration > data should be > > > pulled from in zookeeper. > > > > > > This is doable from the REPL at any time. No other name space keeps > > > this stuff in vars. Any external resource is pulled at runtime from the > > > configuration name space where they are cached except for some very > short lived > > > structures (requests to storage, locks, queues, channels, ... for the > duration of > > > the request). > > > > > > Test configuration data is also contained in zookeeper. Tests > > > pull from this configuration tree the configuration they need. > > > > > > Part of the test stubbing is kept in this configuration. > > > > > > No mutations occur in the configuration data from zookeeper during the > "normal" > > > app life cycle. We allow config changes while the app is running but > in a special > > > context of its life cycle. Namely the app has to enter a state that > allows it to > > > reconfigured itself. This means that workers have been stopped, .... > > > > > > We can test a new configuration without mutating the previous one in > zookeeper > > > using a versioning scheme. > > > > > > This requires some discipline enforced by the configuration name space > API. > > > So far it's been a charm to work with. > > > > Sure, and adding a binding call providing the same config as the one > > found in root would allow for the whole thread to consistently read > > the same value. > > > > > > > > Luc P. > > > > > >> As far as possible, I think it is best to try and minimise mutable > global > > >> state (like mutable configuration data) and implicit context (like > dynamic > > >> vars). My preferred approach is to pass the configuration data (as a > value) > > >> to a higher order function that constructs the configurable object / > > >> function appropriately. I regard this is more of a "functional" > style. So > > >> you might do something like: > > >> > > >> (def my-ring-application (create-application config-map)) > > >> > > >> Once constructed, the ring application is fully configured and > doesn't need > > >> any extra parameters, i.e. it works just like a regular ring > application. > > >> You can treat the ring application as being effectively immutable. If > you > > >> want to reconfigure, then just create a new one! > > >> > > >> This approach has several advantages: > > >> - It's highly composable. Your components can easily be plugged > together, > > >> since they aren't bound to any external configuration state. > > >> - You can perform some significant optimisations in the construction > phase > > >> (depending on the nature of the configuration you might be able to > > >> eliminate validation checks, optimise the size of buffers etc.) > > >> - It's highly testable. Just create as many differently-configured > > >> instances as you like. > > >> > > >> The main downside, of course, is that you need to be thoughtful and > do a > > >> bit more work in designing the constructor function. But I think > that's a > > >> worthwhile activity - it can often lead to a better design overall. > > >> > > >> Note that this technique can apply to much more than web > applications. You > > >> can even use it to construct objects that themselves contain mutable > state. > > >> For example, I use this method to construct the entire GUI + running > game > > >> instance for my little Clojure roguelike game "Alchemy". There is no > > >> mutable global state at all - you can launch several totally > independent > > >> game instances from the same REPL. If you are interested, see the > "launch" > > >> code at the bottom of this file: > > >> > https://github.com/mikera/alchemy/blob/master/src/main/clojure/mikera/alchemy/main.clj > > >> > > >> On Tuesday, 10 September 2013 15:19:35 UTC+8, Alexandr Kurilin wrote: > > >> > > > >> > I'm trying to determine how to best deal with the concept of > globals in > > >> > Clojure. Say I have a map of configuration values for my Ring app, > populate > > >> > at app startup from disk or env, and I need to reference the > contents of > > >> > this map from all over the project. Assuming MVC, models and > controllers > > >> > all would be interested in its contents. I just want to clarify > that the > > >> > question is not so much about "configuration" as it is about > dealing with > > >> > state that many different components in an application might be all > > >> > interested in. This is an issue that seems to arise very often. > > >> > > > >> > I'm seeing a couple of options here: > > >> > > > >> > - Pass the configs map along with each Ring request with some > > >> > middleware, and then down every subsequent function call that > might > > >> > eventually need it. It's a bit of a hassle since now you're > passing more > > >> > data, potentially increasing the # of parameters, or forcing you > to use a > > >> > parameter map everywhere where you might have passed along just 1 > > >> > primitive. Nonetheless, this is as pure as it gets. > > >> > - Def the configs map in a namespace somewhere and refer to it > from > > >> > wherever, just like global state. The trick is now that now > you're no > > >> > longer certain what e.g. your model functions are expecting > there to be in > > >> > the system and testing becomes trickier. Now you have to either > lein test > > >> > with a separate set of configurations (which doesn't actually > give you much > > >> > per-test granularity) or use with-redefs everywhere to make sure > the right > > >> > test config state is being used. > > >> > - Something in the middle where perhaps you agree to never > directly > > >> > reference the configs map from your models (again, thinking MVC > here) and > > >> > instead only ever access it from controllers, and pass the > necessary > > >> > options along down to the model functions. This way you can test > both > > >> > controllers and models in isolation in purity. > > >> > > > >> > Ultimately I want to contain the chaos of having to know internal > > >> > implementation details of my functions at different layers and want > them to > > >> > be easily testable in isolation. It does seem like the first option > is the > > >> > most straightforward, but I'd be curious to find out what those of > you who > > >> > have deal with the problem for a while would recommend. Purity all > the way, > > >> > or are there patterns that can give you the best of both worlds? Or > what > > >> > else? > > >> > > > >> > > > >> > > > >> > > >> -- > > >> -- > > >> 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/groups/opt_out. > > >> > > > -- > > > Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my > ipad! > > > > > > -- > > > -- > > > 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/groups/opt_out. > > > > -- > > -- > > 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/groups/opt_out. > > > -- > Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my ipad! > > -- > -- > 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/groups/opt_out. > -- Alexandr Kurilin - Front Row Education (www.frontrowed.com) 206.687.8740 | a...@kurilin.net @alex_kurilin -- -- 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/groups/opt_out.