(def last-session-id (atom 0))
(defn next-session-id []
(swap! last-session-id inc))
(def sessions (atom {}))
(defn new-session [initial]
(let [session-id (next-session-id)]
(swap! sessions assoc session-id initial)
session-id))
(defn get-session [id]
(@sessions id))
Creamos una nueva sesión pasando un valor inicial a new-session, que obtiene una nueva ID de sesión y la agrega a las sesiones llamando a swap!. Recuper una sesión con get-session es una simple cuestión de buscarlo por su ID.
Si no vamos a aumentar continuamente la cantidad de memoria que usamos, necesitaremos alguna forma de eliminar las sesiones cuando ya no estén en uso. Nosotros podría hacer esto explícitamente (quizás con una función de eliminación de sesión), pero dado que estamos escribiendo un servicio web en el que no necesariamente podemos confiar en que los clientes limpien correctamente, vamos a implementar la caducidad de la sesión (expiry) en su lugar. Esto requiere un pequeño cambio en el código anterior:
(def sessions (atom {}))
(defn now []
(System/currentTimeMillis))
(defn new-session [initial]
(let [session-id (next-session-id)
session (assoc initial :last-referenced (atom (now)))]
(swap! sessions assoc session-id session)
session-id))
(defn get-session [id]
(let [session (@sessions id)]
(reset! (:last-referenced session) (now))
session))
Ahora que cada sesión tiene una entrada :last-referenced, podemos caducar sesiones verificando periódicamente si alguna no ha sido referenciada por más de un cierto período de tiempo:
(defn session-expiry-time []
(- (now) (* 10 60 1000)))
(defn expired? [session]
(< @(:last-referenced session) (session-expiry-time)))
(defn sweep-sessions []
(swap! sessions #(remove-vals % expired?)))
(def session-sweeper
(schedule {:min (range 0 60 5)} sweep-sessions))