2
votes

My app has a long running task (anywhere from 5 minutes to 2 hours) that I can start through an admin panel.

I need a good way to know whether the task is currently running or not, so I can display the status on the admin panel and to prevent the task from being started twice.

Currently my code looks like this (simplified):

object TaskMonitor extends Controller {

var isRunning = false

// Displays the task status on the admin panel
def status = Action {
    Ok.chunked(running &> Comet(callback = "parent.running"))
}

// Check task status every 100 ms
lazy val running: Enumerator[String] = {
    Enumerator.generateM {
        Promise.timeout(Some(isRunning.toString), 100 milliseconds)
    }
}

    // start the task, but only if it's not already running
def startTask = Action {
    if (!isRunning) {
        isRunning = true

        val f = scala.concurrent.Future { Task.start }

        f onComplete {
            case _ => isRunning = false
        }
    }
    Ok
}
}

Obviously this is has all kinds of issues, mainly the fact that I have unsynchronized mutable state (isRunning variable) in my controller.

What would be the correct way to go about what I want to achieve ?

1

1 Answers

1
votes

You're right, you have unsynchronized mutable state. Is it really a problem? I mean this is your admin right? How many concurrent 'startTask' are you gonna send?

The recommended way to handle this in Play! is to use an Akka actor - you don't need any extra dependency, Play! includes Akka already.

Create an actor to hold your isRunning value; in startTask you can send a message to the actor to start the task (the actor will check if the task is not already running). To send the current status of the task you can query the actor for the value of isRunning.

This will give you a protected mutable state. Your choice to decide if it's really worth the trouble.