-
-
Notifications
You must be signed in to change notification settings - Fork 160
Description
Hello all! :)
I have recently noticed a peculiar scenario in an API using Mnesia-backed Pow in production which I'm interested in reproducing to get to the bottom of it, but I've had no success thus far.
Due to reasons yet unknown some sessions are seemingly persisting in the cache even after their TTL has passed.
We have set up an authentication plug similar to the one in "How to use Pow in an API" tutorial and we are using Pow.Store.Backend.MnesiaCache as our cache_store_backend. Under normal circumstances, creating/renewing/deleting sessions via the plug works just as expected, as well as having them expire automatically. Even though the application is containerized, mnesia data is kept between versions in an attached persistent volume. I'm currently using this snippet to check active sessions for an user:
iex> config = [pow_config: [otp_app: :app], backend: Pow.Store.Backend.MnesiaCache]
iex> {:ok, user} = get_user(...)
iex> sessions = Pow.Store.CredentialsCache.sessions(config, user)
[...] # session_idsTesting with a newly-created user, this list behaves normally, having a session id inserted after signing in, replaced after renewal, and deleted after signing out or after 30 minutes. However, for some of the earlier users, this list has been steadily growing over the months, even reaching hundreds of session ids. Luckily, these sessions cannot be used for authentication, since Pow.Store.CredentialsCache.get/1 returns :not_found.
Reproducing these steps in CredentialsCache, the first call for Base.get returns the expected {user_key, metadata}, but the second one returns :not_found. Manually assembling the session_key ([struct, :user, id, :session, session_id]) I was also able to verify that the timestamps are also still present in the cache.
Next, I tried to inspect the current state of MnesiaCache as well using the code below:
iex> Process.whereis(Pow.Store.Backend.MnesiaCache) |> :sys.get_state()
%{
invalidators: %{
...
}
}And found that all of the stale sessions still had invalidators in the state, and the call to Process.read_timer/1 to each of their References returned false which suggests they should be expired. Then I went to check the deletion logic in MnesiaCache and tried to reproduce its steps as well. Right away it begins by fetching the key so I figured I'd get the same result by calling get/2:
iex> Pow.Store.Backend.MnesiaCache.get(config, hd(sessions))
:not_foundWhich according to table_get/2 means that the fetch has returned nil. This seemed off since the same call for Pow.Store.Base.get/3 yielded the {user, metadata} earlier, and it was at this point that I decided to reach out for help. 😅
What could be going off here? I suspect it has to do with sessions that persist between deploys, but I couldn't reproduce it consistently yet.
Any help will be much appreciated, thanks!