Vamos a crear un servicio web que maneje múltiples transcripciones introduciendo el concepto de una sesión. Cada sesión tiene un identificador numérico único, que se genera de la siguiente manera:
(def last-session-id (atom 0))
(defn next-session-id []
(swap! last-session-id inc))
Esto usa un átomo, last-session-id , que se incrementa cada vez que queremos una nueva ID de sesión. Como resultado, cada vez que se llama a next-session-id, devuelve un número uno más alto que el anterior:
server.core=> (in-ns 'server.session)
#<Namespace server.session>
server.session=> (next-session-id)
1
server.session=> (next-session-id)
2
server.session=> (next-session-id)
3
Vamos a hacer un seguimiento de las sesiones activas con otro átomo llamado sesiones que contiene un mapa de ID de sesión y valores de sesión:
(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))
Hemos agregado una función llamada now que devuelve la hora actual. Cuando new-session crea una sesión, agrega una entrada :last-referenced a la sesión, otro átomo que contiene la hora actual. Esto se actualiza con reset! cada vez que get-session accede a la sesión.
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))
Esto utiliza la biblioteca Schejulure para crear un barrido de sesiones, que se ejecuten una vez cada cinco minutos. Cada vez que se ejecuta, elimina cualquier sesión que haya expirado.