NAME
Mojo::ATProto::OAuth::SessionStore::SQLite - SQLite-backed session store for Mojo::ATProto::OAuth
SYNOPSIS
my $oauth = Mojo::ATProto::OAuth->new(..., store => [SQLite => 'file:/var/lib/myapp/oauth.db']);
# opt out of cross-process session locking (read the caveats below first)
$oauth->store->lock_sessions(0);
DESCRIPTION
Implements the full store interface described in "THE STORE INTERFACE" in Mojo::ATProto::OAuth on top of Mojo::SQLite. Constructor arguments are passed straight to Mojo::SQLite->new; the schema is created and migrated automatically.
Session locking
lock_session/lock_session_p (which "refresh_tokens" in Mojo::ATProto::OAuth runs under) take a lease: a row in the session_locks table naming the holder and an expiry time. A caller that finds a live lease held by someone else polls every "lock_poll_interval" seconds until it's released, giving up with lock_session: timed out waiting for session lock after "lock_timeout" seconds. The async form waits on timers, so the event loop keeps running while it waits. A lease left behind by a crashed process expires after "lock_timeout" seconds and is then taken over.
No SQLite transaction or database lock is held while the lease is held, so the lock never blocks any other reads or writes to the database - only other lock_session callers for the same session.
Caveats of leaving it on (the default):
A caller waiting for the lock waits in steps of "lock_poll_interval", so a concurrent refresh finishes up to that much later than it otherwise would. Each poll is a small write to the database.
"lock_timeout" doubles as the lease length. It must be longer than a refresh can take - the default of 30 seconds covers Mojo::ATProto::OAuth's default 10-second request timeout with its one DPoP-nonce retry. If you raise the user agent's timeout, raise this too; otherwise a slow refresh can outlive its lease and a second caller can refresh concurrently.
The synchronous
lock_sessionblocks (sleeps) while it waits. Within one process, a synchronous call waiting on a lease held by a still-pendinglock_session_pfor the same session can't make progress, and times out.
Setting "lock_sessions" to false skips all of this: the code just gets a fresh read of the session, with no lock. Only do this if no session is ever used by more than one process, or by more than one in-flight _p call, at a time. Otherwise two callers can present the same refresh token; the auth server rejects the second with invalid_grant, and an ATProto auth server may revoke the whole session in response. "refresh_tokens" in Mojo::ATProto::OAuth recovers from that invalid_grant only if the other caller's refresh has already been saved by the time it re-reads the session.
ATTRIBUTES
sqlite
The underlying Mojo::SQLite instance.
lock_sessions
Whether lock_session/lock_session_p actually lock. Defaults to 1. See "Session locking" before turning this off.
lock_timeout
Seconds to wait for a session lock before giving up, and the length of the lease once taken. Defaults to 30.
lock_poll_interval
Seconds between attempts to take a session lock held by someone else. Defaults to 0.1.
SEE ALSO
Mojo::ATProto::OAuth, Mojo::ATProto::OAuth::SessionStore::Pg