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.