from polling to real time: scala, akka, and websockets from scratch
TRANSCRIPT
![Page 1: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/1.jpg)
Letgo chat From polling to real timeScala, Akka, and WebSockets from scratch
@SergiGP @GVico46
@JavierCane#scbcn16 - Software Craftsmanship Barcelona 2016
![Page 2: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/2.jpg)
![Page 3: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/3.jpg)
@JavierCane@SergiGP
Welcome!
@GVico46
![Page 4: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/4.jpg)
![Page 5: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/5.jpg)
Contents
Context(not Bounded)
Legacy
Getting started
Pain Points
From PHP to Scala
![Page 6: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/6.jpg)
1. Context (not Bounded)
![Page 7: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/7.jpg)
![Page 8: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/8.jpg)
![Page 9: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/9.jpg)
![Page 10: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/10.jpg)
![Page 11: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/11.jpg)
App downloads
Messages sent monthly growth
Messages sent every day
30M
20 - 40%
~4M
![Page 12: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/12.jpg)
Context (not Bounded) Where do we come
● Mobile first
◕ Internal REST API ● Startup with less than 2 years
◕ Externalize services (Parse, Kahuna…) ● Funding: $200M
◕ Ads in TV in USA and Turkey
![Page 13: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/13.jpg)
2. Legacy
![Page 14: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/14.jpg)
Legacy
● PHP ● No test ● New Features
![Page 15: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/15.jpg)
Legacy REST API in PHP
Do I have new messages? No
And now?
And now?
And now?
And now?
No
No!!
NO!!
😑 🔫 💣
![Page 16: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/16.jpg)
Legacy No test
● Rebuild a system without tests => 🦄💩💣💀
● Coupled system => Acceptance tests
◕ Learning what the system does
◕ Find existing weird behaviors
![Page 17: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/17.jpg)
Background: Given there are test users: | user_object_id | user_name | user_token | | 19fd3160-8643-11e6-ae22-56b6b6499611 | seller | sellerToken | | 120291b2-8643-11e6-ae22-56b6b6499611 | buyer | buyerToken | And user "seller" has a product with: | id | objectId | | 120291b2-8643-11e6-ae22-56b6b6499611 | SuperProductId | Scenario: A user can get messages from another user associated to product Given user "seller" has a conversation related to product "SuperProductId" with user "buyer" When user "seller" asks for messages related to product "SuperProductId" from user "buyer" Then the response status code should be 200 And the response should be in JSON And the JSON should be valid according to the schema "messages.schema"
Acceptance test with Behat
![Page 18: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/18.jpg)
Legacy Taking advantage of backwards compatibility
Leaving The Monolith thanks to #EventSourcing @ #scpna
![Page 19: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/19.jpg)
Legacy New Features
● Product always want more features ● Negotiation:
◕ Archive conversations
◕ Mute interlocutor
◕ Stickers
![Page 20: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/20.jpg)
3. Getting started
![Page 21: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/21.jpg)
Getting started
● Why and how to switch to Scala ● Scala and Akka crash course ● Takeaways
![Page 22: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/22.jpg)
Why and how to switch to Scala
![Page 23: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/23.jpg)
We want a WhatsApp inside
Letgo
![Page 24: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/24.jpg)
LOL
I’ve payed $22 Billion for WhatsApp
![Page 25: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/25.jpg)
Getting started Why and how to switch to Scala
● Realtime (WebSockets) ● Akka ● Scale!
Why How
● Learning a lot ● External consultancy ● Akka :) ● Backwards Compatible
![Page 26: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/26.jpg)
Scala quick start
![Page 27: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/27.jpg)
Getting started Scala quick start
● Case classes ● Functional ● Optionals ● Futures ● OOP
![Page 28: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/28.jpg)
class User { private $id; private $name; public function __construct(Uuid $id, string $name) { $this!→id = $id; $this!→name = $name; } public function id() : Uuid { return $this!→id; } public function name() : string
Case Class
![Page 29: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/29.jpg)
{ return $this!→id; } public function name() : string { return $this!→name; } public function setId(Uuid $id) : self { return new static($id, $this!→name); } public function setName(string $name) : self { return new static($this!→id, $name); }}
Case Class
![Page 30: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/30.jpg)
rafa.name = "Santi"val santi = rafa.copy(name = "Santi")println(santi.name) #$ Santi
val rafa = User(UUID.randomUUID(), "Rafa")println(rafa.name) #$ Rafa
case class User(id: UUID, name: String)
Case classes
Does not compile
Usage
Immutability
![Page 31: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/31.jpg)
val users = List( User(UUID.randomUUID(), "Rafa"), User(UUID.randomUUID(), "Santi"), User(UUID.randomUUID(), "Jaime"), User(UUID.randomUUID(), "Diyan"))
Functional
Mutable state
val names = users.map(user %& user.name)val names = users.map(_.name)
List[String] names = new ArrayList();for (User user: users) { names.add(user.name)}
Procedural
![Page 32: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/32.jpg)
Option
Option[A]
Some(x: A) None
![Page 33: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/33.jpg)
def searchUser(id: UUID): Option[User] = { #$ …search user in database (blocking) Some(rafa)}
Option
searchUser(userId) match { case Some(user) %& #$ do stuff case None %& #$ user not found}
Usage (pattern matching)
![Page 34: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/34.jpg)
Option usage (functional)
searchUser(userId) match { case Some(user) %& #$ do stuff case None %& #$ user not found}
searchUser(userId).map { userFound %& #$ do stuff}
![Page 35: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/35.jpg)
Futures
def searchUser(id: UUID): Future[Option[User]] = { Future { Thread.sleep(1000) Some(rafa) } }
![Page 36: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/36.jpg)
Futures usage
searchUser(userId).onComplete { case Success(Some(user)) %& #$ do stuff case Success(None) %& #$ user not found case Failure(exception) %& #$ future has crashed}searchUser(userId).map { case Some(user) %& #$ do stuff case None %& #$ user not found}
searchUser(userId).map(_.map(_.name))
![Page 37: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/37.jpg)
trait UserRepository { def search(id: UUID): Future[Option[User]]}trait ConsoleLogger { def warning(message: String) = { println(message) }}
OOP - DIP
![Page 38: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/38.jpg)
OOP - DIP
class MysqlUserRepository extends UserRepository with ConsoleLogger { def search(id: UUID): Future[Option[User]] = { #$ implementation warning("user not found") Future(Some(rafa)) }}
![Page 39: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/39.jpg)
OOP - Companion object
object UserId { def random: UserId = { UserId(UUID.randomUUID()) }}case class UserId(id: UUID)
val userId = UserId.randomprintln(userId.id) case class User(id: UserId, name: String)
Usage
![Page 40: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/40.jpg)
Akka (actor model)
![Page 41: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/41.jpg)
Scala quick start Akka (actor model)
● Concept ● Introductory examples ● Chat actors architecture
![Page 42: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/42.jpg)
Scala quick start Akka (actor model) - Concept
● Mailbox (1 each time) ● receive to handle incoming messages ● ActorRef ● Tell or ask methods to interact with the ActorRef ● Location transparency
![Page 43: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/43.jpg)
final class ConnectionActor extends Actor { }
object ConnectionActor { def props: Props = Props(new ConnectionActor)}
Building our first actor
Instantiationval connection: ActorRef = context.actorOf(ConnectionActor.props)
![Page 44: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/44.jpg)
object ConnectionActor { def props: Props = Props(new ConnectionActor)}
final class ConnectionActor extends Actor {
override def receive: Receive = { case PingQuery %& } }
Building our first actor
Instantiationval connection: ActorRef = context.actorOf(ConnectionActor.props)
![Page 45: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/45.jpg)
final class ConnectionActor(webSocket: ActorRef) extends Actor { override def receive: Receive = { case PingQuery %& webSocket ! PongResponse } }
Tell (Fire & forget)
object ConnectionActor { def props(webSocket: ActorRef): Props = Props(new ConnectionActor(webSocket)) }
Building our first actor
Instantiationval connection: ActorRef = context.actorOf(ConnectionActor.props(webSocket))
![Page 46: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/46.jpg)
case class ConnectionActorState( lastRequestSentAt: Option[DateTime]) { def requestSent: ConnectionActorState = copy(lastRequestSentAt = Some(DateTime.now))}
Dealing with state
![Page 47: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/47.jpg)
case class ConnectionActorState( lastRequestSentAt: Option[DateTime]) { def requestSent: ConnectionActorState = copy(lastRequestSentAt = Some(DateTime.now))}
final class ConnectionActor(webSocket: ActorRef) extends Actor { var state = ConnectionActorState(lastRequestSentAt = None) override def receive: Receive = { case PingQuery(requestId) %& state = state.requestSent webSocket.actorRef ! PongResponse }
Dealing with state
State model
Akka: 1 message at a time (no race conditions)
![Page 48: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/48.jpg)
final class ConnectionActor(webSocket: ActorRef) extends Actor { var state = ConnectionActorState(lastRequestSentAt = None) override def preStart(): Unit = { context.system.scheduler.schedule( initialDelay = 1.minute, interval = 1.minute, receiver = self, message = CheckWebSocketTimeout ) } override def receive: Receive = { case PingQuery(requestId) %& state = state.requestSent
Lifecycle
![Page 49: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/49.jpg)
override def preStart(): Unit = { context.system.scheduler.schedule( initialDelay = 1.minute, interval = 1.minute, receiver = self, message = CheckWebSocketTimeout ) } override def receive: Receive = { case PingQuery(requestId) %& state = state.requestSent webSocket ! PongResponse() case CheckWebSocketTimeout %& if (state.hasBeenIdleFor(5.minutes)) { self ! PoisonPill } }}
Lifecycle
![Page 50: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/50.jpg)
override def receive: Receive = { case PingQuery %& Future { Thread.sleep(1000) sender() ! PongResponse }}
Akka and Futures - SHIT HAPPENS
sender() could have changed
![Page 51: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/51.jpg)
Be careful dealing with futures - sender()
override def receive: Receive = { case PingQuery %& Future { Thread.sleep(1000) PongResponse }.pipeTo(sender())}
sender() outside Future
Same happens with self
![Page 52: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/52.jpg)
Chat actors architecture
TalkerS
ConversationSJ
TalkerJ
ConnectionS1 ConnectionJ1
![Page 53: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/53.jpg)
Maintains consistency between 2 talkers : 1 conversation
Kill connections if shit happens
Chat actors architecture
TalkerS
ConversationSJ
ConnectionS1CS2CS3
Connection Supervisor
Talker Provider
Conversation Provider
Maintains consistency between N connections : 1 talker
“Singleton” actor
Non “singleton” actor
![Page 54: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/54.jpg)
Takeaways
![Page 55: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/55.jpg)
Scala quick start Takeaways
● Neophyte guide for scala ● Raúl Raja ● Akka Concurrency book ● Scala for the impatient ● Lightbend webinars ● Lightbend activator
![Page 56: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/56.jpg)
4. Pain Points
![Page 57: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/57.jpg)
Pain Points
● MaxScale ● Slick ● Deploy ● Dependency Injection ● Sync between chats
![Page 58: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/58.jpg)
Chat protocol
SERVER TO CLIENT
Events
Commands
Queries
ACK
RESPONSE
Events
CLIENT TO SERVER
![Page 59: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/59.jpg)
SERVER TO CLIENTCommands
typing_started
typing_stopped
interlocutor_typing_started
interlocutor_typing_stopped interlocutor_message_sent
interlocutor_reception_confirmed
interlocutor_read_confirmed
Events
Queries
Events
fetch_conversations
fetch_conversation_details
fetch_messages fetch_messages_newer_than_id
fetch_messages_older_than_id
confirm_reception
confirm_read
archive_conversations
unarchive_conversations
CLIENT TO SERVER
Chat protocol
authenticate
create_conversation
send_message
![Page 60: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/60.jpg)
DB initial import
![Page 61: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/61.jpg)
DB initial import
![Page 62: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/62.jpg)
Legacy events
workerN
Legacy events
worker…
Legacy events
worker2
Scaling domain events workers
Legacy events
worker1
Auto scaling supervisor actor
SQS
![Page 63: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/63.jpg)
Scaling domain events workers
Legacy events
worker1
Legacy events
worker2
Legacy events
worker…
Legacy events
workerN
Auto scaling supervisor actor
SQS
![Page 64: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/64.jpg)
5. From PHP to Scala
![Page 65: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/65.jpg)
From PHP to Scala
● Language community ● Composer vs SBT
◕ Semantic Versioning (scalaz, play…) ● Developer eXperience
◕ Not descriptive errors
◕ Scala and IntelliJ ● Learning Curve ● Loving and hating the compiler ● Another set of problems
![Page 66: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/66.jpg)
Questions?
Thanks!Contact
@JavierCane@SergiGP@GVico46
![Page 67: From polling to real time: Scala, Akka, and Websockets from scratch](https://reader034.vdocuments.net/reader034/viewer/2022042605/58e4e4031a28ab87378b4a3d/html5/thumbnails/67.jpg)
Credits
● Presentation base template by SlidesCarnival ● Graphics generated using draw.io