Skip to content

Commit

Permalink
v1.9.0 SNAPSHOT
Browse files Browse the repository at this point in the history
v1.9.0 is a significant, non-breaking upgrade focused on addressing a
couple minor issues that were recently identified (#201 and #230). While
in the code, also took the opportunity to refactor some implementation
details.

- @ptaoussanis

This is a squashed commit that includes:

- Extend ref example to incl. a pair of buttons to excercise async push
  features.

- Drop (experimental) flexi packer.

- [#161] Clojure-side Transit optimizations
  Specifically:
    1. Now cache read-handlers and write-handlers.
       Can't tell if this actually realistically helps perf since I've
       got no handler maps to test against; feedback welcome.

    2. Now cache (thread-local) writer (allows baos reuse).
       Doesn't seem to actually realistically help perf from what I can
       tell. Might nix this later since this does add some complexity
       to the impl.

- [#201] Add support for more flexible conn-type upgrade/downgrade
  Initial downgrade strategy here is simple, may or may not turn out to be
  useful; still need to check. Point was to get a more flexible base that
  we can build on in future.

- [#230] Server-side ping to help GC non-terminating WebSocket conns.

  If a WebSocket connection is closed without normal termination (e.g. as
  caused by sudden loss of power, airplane mode, etc.) - it could hang
  around in conns_ for an extended period of time until the underlying TCP
  connection was identified as dead.

  This mods Sente's keep-alive ping from client->server to server->client,
  allowing the server to identify (and auto-gc) dead but abnormally
  terminated WebSocket connections.

  Big thanks to @altV for helping to catch + diagnose this issue.

  This change should also help to suppress possible "idle connection"
  Heroku warnings, Ref. https://goo.gl/zLR0Gk

- [#150 #159] Allow server to gc lp conns

  Using the infrastructure in place for #230, have decided to now
  initiate Ajax timeouts from the server side instead of the client side.

  As with #230, this'll help the underlying http server gc any abnormal
  connections.

  In particular, this should (?) resolve #159 (for Immutant) w/o the need
  for the earlier Immutant-side workaround introduced for that issue.
  • Loading branch information
ptaoussanis committed Jun 9, 2016
1 parent ff1847f commit e34d7f8
Show file tree
Hide file tree
Showing 8 changed files with 887 additions and 765 deletions.
20 changes: 9 additions & 11 deletions example-project/project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject com.taoensso.examples/sente "1.8.2-alpha1"
(defproject com.taoensso.examples/sente "1.9.0-SNAPSHOT"
:description "Sente, reference web-app example project"
:url "https://github.com/ptaoussanis/sente"
:license {:name "Eclipse Public License"
Expand All @@ -10,23 +10,21 @@
*assert* true}

:dependencies
[;; [org.clojure/clojure "1.7.0"]
[org.clojure/clojure "1.8.0"]

[org.clojure/clojurescript "1.7.170"]
[[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.36"]
[org.clojure/core.async "0.2.374"]
[org.clojure/tools.nrepl "0.2.12"] ; Optional, for Cider

[com.taoensso/sente "1.8.2-alpha1"] ; <--- Sente
[com.taoensso/sente "1.9.0-SNAPSHOT"] ; <--- Sente
[com.taoensso/timbre "4.3.1"]

;;; ---> Choose (uncomment) a supported web server <---
[http-kit "2.2.0-alpha1"]
;; [org.immutant/web "2.1.0"] ; v2.1+ recommended
[http-kit "2.2.0-alpha2"]
;; [org.immutant/web "2.1.4"]
;; [nginx-clojure/nginx-clojure-embed "0.4.2"] ; Needs v0.4.2+

[ring "1.4.0"]
[ring/ring-defaults "0.2.0"] ; Includes `ring-anti-forgery`, etc.
[ring "1.5.0"]
[ring/ring-defaults "0.2.1"] ; Includes `ring-anti-forgery`, etc.
;; [ring-anti-forgery "1.0.0"]

[compojure "1.5.0"] ; Or routing lib of your choice
Expand All @@ -42,7 +40,7 @@
[lein-ancient "0.6.10"]
[com.cemerick/austin "0.1.6"]
[lein-cljsbuild "1.1.3"]
[cider/cider-nrepl "0.11.0"] ; Optional, for use with Emacs
[cider/cider-nrepl "0.12.0"] ; Optional, for use with Emacs
]

:cljsbuild
Expand Down
20 changes: 19 additions & 1 deletion example-project/src/example/client.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

;; Serializtion format, must use same val for client + server:
packer :edn ; Default packer, a good choice in most cases
;; (sente-transit/get-flexi-packer :edn) ; Experimental, needs Transit dep
;; (sente-transit/get-transit-packer) ; Needs Transit dep

{:keys [chsk ch-recv send-fn state]}
(sente/make-channel-socket-client!
Expand Down Expand Up @@ -109,6 +109,24 @@
(chsk-send! [:example/button2 {:had-a-callback? "indeed"}] 5000
(fn [cb-reply] (->output! "Callback reply: %s" cb-reply))))))

(when-let [target-el (.getElementById js/document "btn3")]
(.addEventListener target-el "click"
(fn [ev]
(->output! "Button 3 was clicked (will ask server to test rapid async push)")
(chsk-send! [:example/test-rapid-push]))))

(when-let [target-el (.getElementById js/document "btn4")]
(.addEventListener target-el "click"
(fn [ev]
(->output! "Button 4 was clicked (will toggle async broadcast loop)")
(chsk-send! [:example/toggle-broadcast] 5000
(fn [cb-reply]
(when (cb-success? cb-reply)
(let [loop-enabled? cb-reply]
(if loop-enabled?
(->output! "Async broadcast loop now enabled")
(->output! "Async broadcast loop now disabled")))))))))

(when-let [target-el (.getElementById js/document "btn-login")]
(.addEventListener target-el "click"
(fn [ev]
Expand Down
91 changes: 56 additions & 35 deletions example-project/src/example/server.clj
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

(let [;; Serializtion format, must use same val for client + server:
packer :edn ; Default packer, a good choice in most cases
;; (sente-transit/get-flexi-packer :edn) ; Experimental, needs Transit dep
;; (sente-transit/get-transit-packer) ; Needs Transit dep

{:keys [ch-recv send-fn ajax-post-fn ajax-get-or-ws-handshake-fn
connected-uids]}
Expand All @@ -69,6 +69,12 @@
(def connected-uids connected-uids) ; Watchable, read-only atom
)

;; We can watch this atom for changes if we like
(add-watch connected-uids :connected-uids
(fn [_ _ old new]
(when (not= old new)
(infof "Connected uids change: %s" new))))

;;;; Ring handlers

(defn landing-pg-handler [ring-req]
Expand All @@ -77,8 +83,12 @@
[:p "An Ajax/WebSocket" [:strong " (random choice!)"] " has been configured for this example"]
[:hr]
[:p [:strong "Step 1: "] " try hitting the buttons:"]
[:button#btn1 {:type "button"} "chsk-send! (w/o reply)"]
[:button#btn2 {:type "button"} "chsk-send! (with reply)"]
[:p
[:button#btn1 {:type "button"} "chsk-send! (w/o reply)"]
[:button#btn2 {:type "button"} "chsk-send! (with reply)"]]
[:p
[:button#btn3 {:type "button"} "Test rapid server>user async pushes"]
[:button#btn4 {:type "button"} "Toggle server>user async broadcast push loop"]]
;;
[:p [:strong "Step 2: "] " observe std-out (for server output) and below (for client output):"]
[:textarea#output {:style "width: 100%; height: 200px;"}]
Expand Down Expand Up @@ -122,6 +132,41 @@
(ring.middleware.defaults/wrap-defaults
ring-routes ring.middleware.defaults/site-defaults))

;;;; Some server>user async push examples

(defn test-fast-server>user-pushes
"Quickly pushes 100 events to all connected users. Note that this'll be
fast+reliable even over Ajax!"
[]
(doseq [uid (:any @connected-uids)]
(doseq [i (range 100)]
(chsk-send! uid [:fast-push/is-fast (str "hello " i "!!")]))))

(comment (test-fast-server>user-pushes))

(def broadcast-enabled?_ (atom true))

(defn start-example-broadcaster!
"As an example of server>user async pushes, setup a loop to broadcast an
event to all connected users every 10 seconds"
[]
(let [broadcast!
(fn [i]
(let [uids (:any @connected-uids)]
(debugf "Broadcasting server>user: %s uids" (count uids))
(doseq [uid uids]
(chsk-send! uid
[:some/broadcast
{:what-is-this "An async broadcast pushed from server"
:how-often "Every 10 seconds"
:to-whom uid
:i i}]))))]

(go-loop [i 0]
(<! (async/timeout 10000))
(when @broadcast-enabled?_ (broadcast! i))
(recur (inc i)))))

;;;; Sente event handlers

(defmulti -event-msg-handler
Expand All @@ -145,6 +190,14 @@
(when ?reply-fn
(?reply-fn {:umatched-event-as-echoed-from-from-server event}))))

(defmethod -event-msg-handler :example/test-rapid-push
[ev-msg] (test-fast-server>user-pushes))

(defmethod -event-msg-handler :example/toggle-broadcast
[{:as ev-msg :keys [?reply-fn]}]
(let [loop-enabled? (swap! broadcast-enabled?_ not)]
(?reply-fn loop-enabled?)))

;; TODO Add your (defmethod -event-msg-handler <event-id> [ev-msg] <body>)s here...

;;;; Sente event router (our `event-msg-handler` loop)
Expand All @@ -157,38 +210,6 @@
(sente/start-server-chsk-router!
ch-chsk event-msg-handler)))

;;;; Some server>user async push examples

(defn start-example-broadcaster!
"As an example of server>user async pushes, setup a loop to broadcast an
event to all connected users every 10 seconds"
[]
(let [broadcast!
(fn [i]
(debugf "Broadcasting server>user: %s" @connected-uids)
(doseq [uid (:any @connected-uids)]
(chsk-send! uid
[:some/broadcast
{:what-is-this "An async broadcast pushed from server"
:how-often "Every 10 seconds"
:to-whom uid
:i i}])))]

(go-loop [i 0]
(<! (async/timeout 10000))
(broadcast! i)
(recur (inc i)))))

(defn test-fast-server>user-pushes
"Quickly pushes 100 events to all connected users. Note that this'll be
fast+reliable even over Ajax!"
[]
(doseq [uid (:any @connected-uids)]
(doseq [i (range 100)]
(chsk-send! uid [:fast-push/is-fast (str "hello " i "!!")]))))

(comment (test-fast-server>user-pushes))

;;;; Init stuff

(defonce web-server_ (atom nil)) ; {:server _ :port _ :stop-fn (fn [])}
Expand Down
25 changes: 12 additions & 13 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject com.taoensso/sente "1.8.2-alpha1"
(defproject com.taoensso/sente "1.9.0-SNAPSHOT"
:author "Peter Taoussanis <https://www.taoensso.com>"
:description "Realtime web comms for Clojure/Script"
:url "https://github.com/ptaoussanis/sente"
Expand All @@ -13,7 +13,7 @@
:dependencies
[[org.clojure/clojure "1.5.1"]
[org.clojure/core.async "0.2.374"]
[com.taoensso/encore "2.49.0"]
[com.taoensso/encore "2.53.1"]
[org.clojure/tools.reader "0.10.0"]
[com.taoensso/timbre "4.3.1"]]

Expand All @@ -24,18 +24,19 @@
:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
:1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]}
:1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0-alpha5"]]}
:test {:dependencies [[com.cognitect/transit-clj "0.8.285"]
[com.cognitect/transit-cljs "0.8.237"]
[org.clojure/test.check "0.9.0"]]
:plugins []}

:provided {:dependencies [[org.clojure/clojurescript "1.7.170"]]}
:provided {:dependencies [[org.clojure/clojurescript "1.9.36"]]}

:dev
[:1.7 :test
[:1.9 :test :server-jvm
{:dependencies
[[http-kit "2.2.0-alpha1"]
[org.immutant/web "2.1.3"]
[[http-kit "2.2.0-alpha2"]
[org.immutant/web "2.1.4"]
[nginx-clojure "0.4.4"]]
:plugins
[;;; These must be in :dev, Ref. https://github.com/lynaghk/cljx/issues/47:
Expand All @@ -46,7 +47,7 @@
[lein-ancient "0.6.10"]
;; [com.cemerick/austin "0.1.4"]
[com.cemerick/clojurescript.test "0.3.3"]
[lein-codox "0.9.4"]]}]}
[lein-codox "0.9.5"]]}]}

:cljx
{:builds
Expand All @@ -69,19 +70,17 @@
{:language :clojure ; [:clojure :clojurescript] ; No support?
:source-paths ["target/classes"]
:source-uri
{#"target/classes"
"https://github.com/ptaoussanis/sente/blob/master/src/{classpath}x#L{line}"
#".*"
"https://github.com/ptaoussanis/sente/blob/master/{filepath}#L{line}"}}
{#"target/classes" "https://github.com/ptaoussanis/sente/blob/master/src/{classpath}x#L{line}"
#".*" "https://github.com/ptaoussanis/sente/blob/master/{filepath}#L{line}"}}

:aliases
{"test-all" ["do" "clean," "cljx" "once,"
"with-profile" "+1.6:+1.7:+1.8" "test,"
"with-profile" "+1.9:+1.8:+1.7:+1.6" "test,"
;; "with-profile" "+test" "cljsbuild" "test"
]
"build-once" ["do" "cljx" "once," "cljsbuild" "once"]
"deploy-lib" ["do" "build-once," "deploy" "clojars," "install"]
"start-dev" ["with-profile" "+server-jvm" "repl" ":headless"]}
"start-dev" ["with-profile" "+dev" "repl" ":headless"]}

:repositories
{"sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"})
Loading

0 comments on commit e34d7f8

Please sign in to comment.