Skip to content
Snippets Groups Projects
Commit 5f1472db authored by Clément Pit-Claudel's avatar Clément Pit-Claudel
Browse files

server/gc: User time.Instant instead of milliseconds and add logging

parent a10df1aa
No related branches found
No related tags found
1 merge request!31server: Add a GarbageCollection
......@@ -3,26 +3,41 @@ package server.web
import scala.collection.concurrent
import scala.concurrent.duration.*
import scala.jdk.DurationConverters.*
import java.time.Instant
object GarbageCollector:
private val GC_TICK = 1.minutes
private val GC_CREATION_TIME_GRACE_PERIOD = 1.hour
private val GC_LAST_ACTIVITY_GRACE_PERIOD = 3.days
private val GC_TICK = 5.minutes
private val GC_CREATION_TIME_GRACE_PERIOD = 1.hour.toJava
private val GC_LAST_ACTIVITY_GRACE_PERIOD = 3.days.toJava
def garbageCollect(): Unit = // Not safe, needs lock or care; too slow?
val now = System.currentTimeMillis()
val lastActivityThreshold = now - GC_LAST_ACTIVITY_GRACE_PERIOD.toMillis
val creationTimeThreshold = now - GC_CREATION_TIME_GRACE_PERIOD.toMillis
def garbageCollect(): Unit =
val now = Instant.now()
val lastActivityThreshold = now.minus(GC_LAST_ACTIVITY_GRACE_PERIOD)
val creationTimeThreshold = now.minus(GC_CREATION_TIME_GRACE_PERIOD)
println(f"[gc] Collection started at $now")
println(f"[gc] Collection criteria:\n" +
f"[gc] Created before: $creationTimeThreshold\n" +
f"[gc] Last active before: $lastActivityThreshold")
WebServer.instances.snapshot()
.values.filter: inst =>
inst.lastActivity < collectInactiveBefore &&
inst.creationTime < collectNeverConnectedBefore
val lastActivity = inst.lastActivity
lastActivity.isBefore(lastActivityThreshold) &&
inst.creationTime.isBefore(creationTimeThreshold) && {
println(f"[gc] Marking ${inst.appInfo.id}/${inst.instanceId} for collection:\n" +
f"[gc] Activity: $lastActivity < $lastActivityThreshold\n" +
f"[gc] Creation: ${inst.creationTime} < $creationTimeThreshold")
true
}
.toList.foreach: inst =>
println(f"[gc] Stopping ${inst.appInfo.name}/${inst.instanceId}.")
println(f"[gc] Stopping ${inst.appInfo.id}/${inst.instanceId}.")
WebServer.shutdownApp(inst.instanceId)
println("[gc] Collection complete")
private val clock =
val scheduler = Clock.newScheduler()
Clock("gc", GC_TICK.toMillis)(using scheduler)(garbageCollect())
clock.start()
def start() = clock.start()
def shutdown() = clock.shutdown()
......@@ -3,7 +3,8 @@ package server
package web
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicReference
import java.time.Instant
import scala.collection.mutable
import scala.util.{Success, Failure, Try}
......@@ -58,11 +59,11 @@ private[web] abstract class ServerApp:
*/
def transition(clients: Seq[UserId])(userId: UserId, eventJs: ujson.Value): Try[PendingActions]
val creationTime = System.currentTimeMillis()
val creationTime = Instant.now
private var _lastActivity = AtomicLong(0)
def recordActivity() = lastActivity.set(System.currentTimeMillis())
def lastActivity = lastActivity.get()
private var _lastActivity = AtomicReference(Instant.MIN)
def recordActivity() = _lastActivity.set(Instant.now)
def lastActivity = _lastActivity.get()
private val channels =
mutable.Map[UserId, mutable.Set[cask.WsChannelActor]]().withDefault(_ => mutable.Set())
......
......@@ -23,8 +23,6 @@ private case class Wordlist(fname: String):
/** Contains the web server state and functionalities */
object WebServer:
GarbageCollector.start()
private val instanceIdsFileName = "eff_short_wordlist_1.txt" // Where to find the list of possible instance ids
private val instanceIdLength = 4 // How many words do we concatenate for the instance id
private val instanceIdWords = Wordlist(instanceIdsFileName)
......@@ -69,3 +67,5 @@ object WebServer:
instances.remove(instanceId).map: app =>
app.shutdown()
println(s"[${app.appInfo.id}][$instanceId] shut down")
GarbageCollector.start()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment