On 3 September 2015 at 00:03, <j...@signafire.com> wrote:
>
> The HTTP connection may be closed at any time by the server; if that
> happens, the app should persistently attempt to reconnect using an
> exponential back-off pattern. In addition, if the app goes thirty seconds
> without receiving any data, it should close the connection and try to
> reconnect. It's not clear to me how to best express these "retry"
> requirements in the component lifecycle.
>

It seems to me that retrying connections is orthogonal to the component
lifecycle. Don't fall into the trap of thinking that start and stop are
your only tools. They only determine what should happen when your system
begins, and what happens when it ends.

So what *does* happen when your system starts? Well, from your description:

I'm writing an app that consumes events from a streaming HTTP connection
> and writes those events to a message queue


It seems to me that a naïve implementation of this would be:

(loop []
  (when-let [event (read-event! stream)]
    (deliver! queue event)
    (recur)

So we block until we can read an event, and once we have the event we write
it to the message queue.

>From your description, it seems as if the event stream needs to maintain an
open HTTP connection, but we expect to need to reconnect for various
reasons. The connection will therefore change over the system's runtime, so
we need a mutable reference:

(defrecord HttpEventStream [url]
  component/Lifecycle
  (start [component]
    (if (:conn component)
      component
      (assoc component :conn (volatile! (open-connection url))))
  (stop [component]
    (if-let [conn (:conn component)]
      (do (locking conn
            (close-connection @conn)
            (vreset! conn nil))
          (dissoc component :conn))
      component)))

(defn read-event! [{:keys [conn url]}]
  (when conn
    (locking conn
      (when @conn
        (loop []
          (if (closed? @conn)
            (do (vswap! conn (open-connection url)) (recur))
            (read-line-from-body! @conn)))))

Because we're dealing with I/O, instead of using an atom I'm using a
volatile and locks, as we explicitly don't want two threads accessing the
same connection at once.

Note that I'm also making sure that read-event! can handle being given a
component that's been stopped.

If you want the connection to reconnect if there has been too much
inactivity, then you'll need to maintain another reference that holds a
timestamp of when an event was last read.

- James

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