From a08ecc3de5b889431786339e8f572dae85055857 Mon Sep 17 00:00:00 2001 From: Putin Date: Thu, 3 Oct 2019 10:09:44 -0400 Subject: [PATCH 1/3] performance optimization based on ets table --- lib/goth/token_store.ex | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/goth/token_store.ex b/lib/goth/token_store.ex index 8351777..a3713dc 100644 --- a/lib/goth/token_store.ex +++ b/lib/goth/token_store.ex @@ -10,11 +10,14 @@ defmodule Goth.TokenStore do use GenServer alias Goth.Token + @ets_table :goth_token_store + def start_link do GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end def init(state) do + :ets.new(@ets_table, [:named_table, :set, {:read_concurrency, true}]) {:ok, state} end @@ -58,12 +61,22 @@ defmodule Goth.TokenStore do def find(scope, sub) when is_binary(scope), do: find({:default, scope}, sub) def find({account, scope}, sub) do - GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + case :ets.lookup(@ets_table, {account, scope, sub}) do + [{{^account, ^scope, ^sub}, token}] -> + case {:ok, token} |> filter_expired(:os.system_time(:seconds)) do + :error -> GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + response -> response + end + + _ -> + GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + end end # when we store a token, we should refresh it later def handle_call({:store, {account, scope, sub}, token}, _from, state) do # this is a race condition when inserting an expired (or about to expire) token... + :ets.insert(@ets_table, {account, scope, sub}, token) pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, {account, scope, sub}, token)} end From 22b17f7e5a99a88d5f78dab7c2cb1688350cf84f Mon Sep 17 00:00:00 2001 From: Putin Date: Thu, 3 Oct 2019 10:17:03 -0400 Subject: [PATCH 2/3] magic number to module attribute --- lib/goth/token.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/goth/token.ex b/lib/goth/token.ex index 74fbb23..356d7ec 100644 --- a/lib/goth/token.ex +++ b/lib/goth/token.ex @@ -53,6 +53,9 @@ defmodule Goth.Token do defstruct [:token, :type, :scope, :sub, :expires, :account] + # time in seconds before token will be auto renewed + @timeout 120 + @doc """ Get a `%Goth.Token{}` for a particular `scope`. `scope` can be a single scope or multiple scopes joined by a space. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/googlescopes) for all available scopes. @@ -137,13 +140,13 @@ defmodule Goth.Token do def queue_for_refresh(%__MODULE__{} = token) do diff = token.expires - :os.system_time(:seconds) - if diff < 10 do + if diff < @timeout do # just do it immediately Task.async(fn -> __MODULE__.refresh!(token) end) else - :timer.apply_after((diff - 10) * 1000, __MODULE__, :refresh!, [token]) + :timer.apply_after((diff - @timeout) * 1000, __MODULE__, :refresh!, [token]) end end From d639aa20b8fc95d307b5e3b544f08f1b8daacb43 Mon Sep 17 00:00:00 2001 From: Putin Date: Thu, 3 Oct 2019 12:43:01 -0400 Subject: [PATCH 3/3] fixed ets insert --- lib/goth/token_store.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/goth/token_store.ex b/lib/goth/token_store.ex index a3713dc..0a1cdd0 100644 --- a/lib/goth/token_store.ex +++ b/lib/goth/token_store.ex @@ -76,7 +76,7 @@ defmodule Goth.TokenStore do # when we store a token, we should refresh it later def handle_call({:store, {account, scope, sub}, token}, _from, state) do # this is a race condition when inserting an expired (or about to expire) token... - :ets.insert(@ets_table, {account, scope, sub}, token) + :ets.insert(@ets_table, {{account, scope, sub}, token}) pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, {account, scope, sub}, token)} end