On Fri, Nov 27, 2009 at 7:48 PM, André Thieme
<splendidl...@googlemail.com>wrote:

> Let‘s say we have the functions A, B, C, D, E, F and G.
> A is calling B, B is calling C, C is calling D, and so on.
> Now a request R1 comes in, function A is called and this chain
> continues to,
> say, E.
> Now a reload happens. Some functions A-G are changed. B now may expect
> C
> to return not a value v1, but instead a vector of the values [v1 v2].
> Even if the signatures didn‘t change and no exception would be thrown,
> it
> could destroy consistency of the data anyway.
> What I want is that any request R1, R2, R3, ... will continue to use
> the
> chain A-G.
> A reload should happen atomically. All functions should load and eval
> in
> the background and only become callable if everything was reloaded.
> All new incoming requests will now go through the new chain A-G (maybe
> some
> new functions came in and it is now A-Z, or others were removed so
> that it
> will be A-Z but without D and E) while at the same time R1 continues
> with
> the old set of functions.
> That way each user of (my) web services would get a constintent
> answer.
>

There is one way to achieve something like that already, though you have to
explicitly set up certain functions to be replaceable in this transactional
manner:

...

(def B (ref (fn [args] body)))

(def A (ref (fn [args] body ... (@B foo bar) ...)))

(defn something-that-calls-A [args]
  (dosync
    (@A some-stuff)
    etc.))

(defn replace-A-and-B [new-A-fn new-B-fn]
  (dosync
    (ref-set A new-A-fn)
    (ref-set B new-B-fn)))

During the something-that-calls-A dosync everything will see a snapshot of
the "ref world" in which no replace-A-and-B transaction is half-completed,
so a particular compatible pair of functions will be seen.

Related means could also be used, e.g.

(def fn-set (atom {:B (fn [fn-set other-args] body)
                   :A (fn [fn-set other-args]
                       body ... ((:B fn-set) fn-set foo bar) ...)}))

(defn session-start [params]
  (let [fns @fn-set]
    ((:A fns) fns params)))

(defn replace-fn-set [fn-map]
  (reset! fn-set fn-map))

Each session or whatever holds a reference to a map of the functions to
call, and the functions pass the map around and call the functions through
the map. The map, being a Clojure map, is immutable, so a session is
guaranteed to always use a consistent group of functions. Changing the
function map in the global atom changes the map used by new sessions,
without affecting pre-existing ones.

If each session or whatever is associated with its own thread, you can also
go with

(def *fn-map* nil)

(def fn-set (atom {:B (fn [args] body)
                   :A (fn [args]
                        body ... ((:B *fn-map*) foo bar) ...)}))

(defn session-start [params]
  (binding [*fn-map* @fn-set]
    ((:A *fn-map*) params)))

(defn replace-fn-set [fn-map]
  (reset! fn-set fn-map))

which avoids the ugliness of passing the session's function map as a
parameter all over the place. You can even dispense with the keyword lookups
with a bit more effort:

(def *A* nil)

(def *B* nil)

(def fn-set (atom {:B (fn [args] body)
                   :A (fn [args]
                        body ... (*B* foo bar) ...)}))

(defn session-start [params]
  (let [fn-map @fn-set]
    (binding [*A* (:A fn-map)
              *B* (:B fn-map)]
      (*A* params))))

(defn replace-fn-set [fn-map]
  (reset! fn-set fn-map))

This version uses separate thread-local bindings for the individual
functions, which can be called like normal Clojure functions (because they
ARE normal Clojure functions). Each session binds all of them based on a
single dereference of the fn-set atom, so again has a consistent set of
functions, which replace-fn-set will change for subsequent (but not
preexisting) sessions.

I'm sure you can dream up more ways to achieve similar results. (A cookie if
you can think up one using agents.)

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

Reply via email to