Sessions
This page is a work in progress. Please add to it and ask on the mailing list if you have session-related questions that are not answered here.
Lift is makes extensive use of state, and thus sessions, but you are still given lots of control over how this works.
“Sessions will be cleaned up by the container after some configurable idle time, usually 20-30 mins. Idle means no requests come in for that session. Note that by default Lift might send down javascript for GCing functions which will keep a session live while the browser is viewing that page.” – Ross Mellgren on the mailing list
David Pollak: “I’ve got a fair amount of experience dealing with sessions in Lift. I’ve distilled much of what I’ve learned into code in examples/example
. Here’s code to periodically dump session info:”
object SessionInfoDumper extends LiftActor with Loggable {
private var lastTime = millis
private def cyclePeriod = 1 minute
import net.liftweb.example.lib.SessionChecker
protected def messageHandler =
{
case SessionWatcherInfo(sessions) =>
if ((millis - cyclePeriod) > lastTime) {
lastTime = millis
val rt = Runtime.getRuntime
rt.gc
RuntimeStats.lastUpdate = timeNow
RuntimeStats.totalMem = rt.totalMemory
RuntimeStats.freeMem = rt.freeMemory
RuntimeStats.sessions = sessions.size
val percent = (RuntimeStats.freeMem * 100L) / RuntimeStats.totalMem
// get more aggressive about purging if we're
// at less than 35% free memory
if (percent < 35L) {
SessionChecker.killWhen /= 2L
if (SessionChecker.killWhen < 5000L)
SessionChecker.killWhen = 5000L
SessionChecker.killCnt *= 2
} else {
SessionChecker.killWhen *= 2L
if (SessionChecker.killWhen >
SessionChecker.defaultKillWhen)
SessionChecker.killWhen = SessionChecker.defaultKillWhen
val newKillCnt = SessionChecker.killCnt / 2
if (newKillCnt > 0) SessionChecker.killCnt = newKillCnt
}
val dateStr: String = timeNow.toString
logger.info("[MEMDEBUG] At " + dateStr + " Number of open
sessions: " + sessions.size)
logger.info("[MEMDEBUG] Free Memory: " +
pretty(RuntimeStats.freeMem))
logger.info("[MEMDEBUG] Total Memory: " +
pretty(RuntimeStats.totalMem))
logger.info("[MEMDEBUG] Kill Interval: " +
(SessionChecker.killWhen / 1000L))
logger.info("[MEMDEBUG] Kill Count: " + (SessionChecker.killCnt))
}
}
private def pretty(in: Long): String =
if (in > 1000L) pretty(in / 1000L) + "," + (in % 1000L)
else in.toString
}
and:
object SessionChecker extends Function2[Map[String, SessionInfo],
SessionInfo => Unit, Unit] with
Logger
{
def defaultKillWhen = 180000L
// how long do we wait to kill single browsers
@volatile var killWhen = defaultKillWhen
@volatile var killCnt = 1
def apply(sessions: Map[String, SessionInfo],
destroyer: SessionInfo => Unit): Unit = {
val cutoff = millis - 180000L
sessions.foreach {
case (name, si @ SessionInfo(session, agent, _, cnt, lastAccess)) =>
if (cnt <= killCnt && lastAccess < cutoff) {
info("Purging "+agent)
destroyer(si)
}
}
}
}
And to enable the functionality in Boot.scala
:
SessionMaster.sessionCheckFuncs = SessionMaster.sessionCheckFuncs ::: List(SessionChecker)
SessionMaster.sessionWatchers = SessionInfoDumper :: SessionMaster.sessionWatchers
“The code sheds sessions based on some rules. The rules are tuned every minute based on available memory and memory trends. Basically, the code protects against creating lots of sessions during a search engine spider of the site. Google will create 20 sessions per second and those sessions need to get shed quickly. I use the code on http://demo.liftweb.net/. The last time the site went down was when I upgraded the site to run against Scala 2.8.0.RC7.”
Comments are disabled for this space. In order to enable comments, Messages tool must be added to project.
You can add Messages tool from Tools section on the Admin tab.