defmodule Mongo.Session.ServerSession do
@moduledoc """
This module represents the server-side session. There are three fields:
* `last_use` - The timestamp for the last use of this server session
* `txn_num` - The current transaction number
* `session_id` - The session id of this server session
When a transaction is active, all operations in that transaction
use the same transaction number.
Transaction number is also used outside of transactions for
retryable writes. In this case, each write operation has its own
transaction number, but retries of a write operation use the same
transaction number as the first write (which is how the server
knows that subsequent writes are retries and should be ignored if
the first write succeeded on the server but was not read by the
client, for example).
"""
alias Mongo.Session.ServerSession
@type t :: %__MODULE__{
last_use: integer,
txn_num: non_neg_integer,
session_id: BSON.Binary.t()
}
defstruct last_use: 0, txn_num: 0, session_id: nil
@doc """
Create a new server session.
"""
@spec new() :: ServerSession.t()
def new() do
%ServerSession{session_id: Mongo.uuid(), last_use: System.monotonic_time(:second)}
end
@doc """
Update the last_use attribute of the server session to now.
"""
@spec set_last_use(ServerSession.t()) :: ServerSession.t()
def set_last_use(%ServerSession{} = session) do
%ServerSession{session | last_use: System.monotonic_time(:second)}
end
@doc """
Increment the current transaction number and return the new value.
"""
@spec next_txn_num(ServerSession.t()) :: ServerSession.t()
def next_txn_num(%ServerSession{:txn_num => txn_num} = session) do
%ServerSession{session | txn_num: txn_num + 1}
end
@doc """
Return true, if the server session will time out. In this case the session
can be removed from the queue.
"""
@spec about_to_expire?(ServerSession.t(), integer) :: boolean
@compile {:inline, about_to_expire?: 2}
def about_to_expire?(%ServerSession{:last_use => last_use}, logical_session_timeout) do
System.monotonic_time(:second) - last_use >= logical_session_timeout
end
defimpl Inspect, for: ServerSession do
def inspect(%ServerSession{last_use: last_use, txn_num: txn, session_id: session_id}, _opts) do
"#ServerSession(" <> inspect(DateTime.from_unix(last_use)) <> ", " <> to_string(txn) <> ", session_id: " <> inspect(session_id) <> ")"
end
end
end