3

I have a Play 2.x app up and running on Heroku with a single web dyno.

On startup, an Akka actor is triggered which itself schedules future jobs (e.g. sending push notifications).

object Global extends GlobalSettings {

  override def onStart(app:Application) {
    val actor = Akka.system.actorOf(Props[SomeActor])
    Akka.system.scheduler.scheduleOnce(0 seconds, actor, None)
  }
}

This works fine with one web dyno but I am curious to know what happens if I turn up the number of web dynos. Will onStart be executed twice with two web dynos?

Would be great if Global really works globally and onStart is only executed once, independently of the number of web dynos. If not, multiple dynos have to somehow agree on one dyno responsible for doing the job.

Did anybody run into a similar issue?

Andreas
  • 33
  • 2

2 Answers2

5

If you run two web dynos, your global will be executed twice. Global is global to the process. When you scale your web process, you are running two processes. You have a couple options:

  • Use a different process (aka a singleton process) to run your global. The nice thing about Play is that you can have multiple GlobalSettings implementations. When you start your process, you specify the global you want to use with -Dapplication.global=YourSecondGlobal. In your procfile, then, you would have singleton: target/start -Dhttp.port=${PORT} ${JAVA_OPTS} -Dapplication.global=YourSecondGlobal. Start your web processes and singleton process and make sure singleton is scaled to 1.
  • Use a distributed semaphor to obtain a lock. Each process will then race to obtain a lock -- the one that wins will proceed and the others will fail. If you're using Postgres (as many people do on Heroku), an advisory lock is a good choice.
Naaman Newbold
  • 3,324
  • 1
  • 21
  • 12
  • Thanks Naaman for the good feedback! Didn't know about the `singleton` in ProcFiles. Will give it a try. – Andreas Mar 08 '13 at 08:42
  • @Naaman Don't you lose a web dyno with this solution? Since singleton couldn't handle HTTP requests (since only dynos with `web` as names do) – Mik378 Mar 05 '14 at 13:16
  • @Mik378 no, singleton acts as a worker. Perhaps it's confusing that the example includes -Dhttp.port, when maybe it doesn't need to (haven't tested to see if it's necessary or not). – Naaman Newbold Mar 05 '14 at 21:23
  • @Naaman Imagine an initial configuration with two bought **web** dynos. The current Procfile would be: `web: target/start -Dhttp.port=${PORT} ${JAVA_OPTS}`. Now, if I'm appending this line to my Procfile: `singleton: target/start -Dhttp.port=${PORT} ${JAVA_OPTS} -Dapplication.global=YourSecondGlobal` and scaling the process to tie 1 dyno max with `heroku ps:scale singleton=1`, I would end with how many **web** dynos? Still 2 + 1 worker (the singleton)? or one would be replaced by the singleton process that is a **worker**, leading to 1 web and 1 worker? – Mik378 Mar 05 '14 at 21:49
  • 1
    You'd have two web processes and one singleton process. You can test it on a separate app and see the processes by typing `heroku ps`. – Naaman Newbold Mar 06 '14 at 17:36
2

You can also get dyno name at runtime:

String dyno = System.getenv("DYNO");

so doing a check like this may also work:

if(dyno.equals("web.1")) {

}
Ömer Faruk Gül
  • 965
  • 1
  • 10
  • 22