目次

Version 1, last updated by Peter Robinett at Nov 28 01:17 UTC

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.”