Search code examples
scalaplayframeworkplayframework-2.0akkalong-polling

Implementing long polling in scala and play 2.0 with akka


I'm implementing long polling in Play 2.0 in potentially a distributed environment. The way I understand it is that when Play gets a request, it should suspend pending notification of an update then go to the db to fetch new data and repeat. I started looking at the chat example that Play 2.0 offers but it's in websocket. Furthermore it doesn't look like it's capable of being distributed. So I thought I will use Akka's event bus. I took the eventstream implementation and replicated my own with LookupClassification. However I'm stumped as to how I'm gonna get a message back (or for that matter, what should be the subscriber instead of ActorRef)?

EventStream implementation: https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/event/EventStream.scala


Solution

  • I am not sure that is what you are looking for, but there is quite a simple solution in the comet-clock sample, that you can adapt to use AKKA actors. It uses an infinite iframe instead of long polling. I have used an adapted version for a more complex application doing multiple DB calls and long computation in AKKA actors and it works fine.

      def enum = Action {
        //get your actor
        val myActorRef = Akka.system.actorOf(Props[TestActor]) 
    
       //do some query to your DB here. Promise.timeout is to simulate a blocking call
       def getDatabaseItem(id: Int): Promise[String] = { Promise.timeout("test", 10 milliseconds) } 
    
        //test iterator, you will want something smarter here
        val items1 = 1 to 10 toIterator
    
        // this is a very simple enumerator that takes ints from an existing iterator (for an http request parameters for instance) and do some computations
        def myEnum(it: Iterator[Int]): Enumerator[String] = Enumerator.fromCallback[String] { () =>
          if (!items1.hasNext)
            Promise.pure[Option[String]](None) //we are done with our computations
          else {
    
            // get the next int, query the database and compose the promise with a further query to the AKKA actor
            getDatabaseItem(items1.next).flatMap { dbValue =>
              implicit val timeout = new Timeout(10 milliseconds)
              val future = (myActorRef ? dbValue) mapTo manifest[String]
    
              // here we convert the AKKA actor to the right Promise[Option] output
              future.map(v => Some(v)).asPromise
            }
          }
        }
    
        // finally we stream the result to the infinite iframe. 
        // console.log is the javascript callback, you will want something more interesting.
        Ok.stream(myEnum(items1) &> Comet(callback = "console.log"))
      }
    

    Note that this fromCallback doesn't allow you to combine enumerators with "andThen", there is in the trunk version of play2 a generateM method that might be more appropriate if you want to use combinations.

    It's not long polling, but it works fine.