The usage of delay here is clever. I suggest as an addition, using
`force` instead of `deref` to disambiguate delay vs. atom (of course
if you take a few moments to think about it, swap! shouldn't return an
atom etc., but I think it becomes clearer with force).

On Mon, Jun 15, 2020 at 10:34 AM Ernesto Garcia <titogar...@gmail.com> wrote:
>
> Hi, it's a long time that this question was posted, but I have found it 
> interesting in the implementation of token refreshes.
>
> First of all, for service invocation, given a `revise-oauth-token` method, I 
> think this is good client code:
>
> (http/request
>   {:method :get
>    :url "https://example.com/";
>    :oauth-token (revise-oauth-token token-store)})
>
> If you find it too repetitive or fragile in your client code, you can make a 
> local function, but I wouldn't abstract the service invocation at a higher 
> layer.
>
> Regarding the implementation of the token store, we could initially think of 
> a synchronized store, like an atom, and `revise-oauth-token` would swap its 
> content when a refresh is required. This is inconvenient for multithreaded 
> clients, because there could be several refresh invocations going on 
> concurrently.
>
> In order to avoid concurrent refreshes, I propose to implement the token 
> store as an atom of promises. Implementation of `revise-oauth-token` would be:
>
> (defn revise-oauth-token [token-store]
>   (:access_token
>     @(swap! token-store
>        (fn [token-promise]
>          (if (token-needs-refresh? @token-promise (Instant/now))
>            (delay (refresh-oauth-token (:refresh_token @token-promise)))
>            token-promise)))))
>
> Note that using a delay avoids running `refresh-oauth-token` within the 
> `swap!` operation, as this operation may be run multiple times.
> Also note that `token-needs-refresh` takes an argument with the present time. 
> This keeps the function pure, which could help for unit testing, for example.
>
> There is an alternative implementation using `compare-and-set!` that avoids 
> checking `token-needs-refresh?` several times, but it is more complicated. I 
> have posted full sample code in a gist: 
> https://gist.github.com/titogarcia/4f09bcc5fa38fbdc1076954b9a99a8fc
>
> Remark: None of this refers to "functional programming" per se. Dealing with 
> state in a purely functional way involves using different constructs (like 
> possibly monads, for which you can find Clojure libraries if you are 
> interested), and best practices are still a topic of research. Clojure has 
> taken the pragmatic approach of making purely functional code easy to write, 
> but it doesn't reject the use of state, rather it provides well-behaved 
> primitives like vars, atoms, agents, etc.
>
> Ernesto
>
> --
> 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.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/clojure/ac79058b-2c31-4b9c-9cf3-e2de998eb8deo%40googlegroups.com.

-- 
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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/CAGokn9L_Od2ZN2LJAsYUfJ2G_hbLKkamkUxgFX2vTKySxpHQWg%40mail.gmail.com.

Reply via email to