Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
package {
|
|
|
|
|
|
|
|
import flash.events.TimerEvent;
|
|
|
|
import flash.external.ExternalInterface;
|
2014-06-11 21:17:18 +02:00
|
|
|
import flash.utils.Dictionary;
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
import flash.utils.Timer;
|
|
|
|
|
|
|
|
|
2014-06-07 20:34:19 +02:00
|
|
|
final public class AphlictClient extends Aphlict {
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The connection name for this client. This will be used for the
|
|
|
|
* @{class:LocalConnection} object.
|
|
|
|
*/
|
|
|
|
private var client:String;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The expiry timestamp for the @{class:AphlictMaster}. If this time is
|
|
|
|
* elapsed then the master will be assumed to be dead and another
|
|
|
|
* @{class:AphlictClient} will create a master.
|
|
|
|
*/
|
|
|
|
private var expiry:Number = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The interval at which to ping the @{class:AphlictMaster}.
|
|
|
|
*/
|
|
|
|
public static const INTERVAL:Number = 3000;
|
|
|
|
|
|
|
|
private var master:AphlictMaster;
|
|
|
|
private var timer:Timer;
|
|
|
|
|
|
|
|
private var remoteServer:String;
|
|
|
|
private var remotePort:Number;
|
|
|
|
|
|
|
|
|
|
|
|
public function AphlictClient() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
ExternalInterface.addCallback('connect', this.externalConnect);
|
|
|
|
ExternalInterface.call(
|
|
|
|
'JX.Stratcom.invoke',
|
|
|
|
'aphlict-component-ready',
|
|
|
|
null,
|
|
|
|
{});
|
|
|
|
}
|
|
|
|
|
2014-06-11 21:17:18 +02:00
|
|
|
public function externalConnect(
|
|
|
|
server:String,
|
|
|
|
port:Number,
|
|
|
|
subscriptions:Array):void {
|
|
|
|
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
this.externalInvoke('connect');
|
|
|
|
|
|
|
|
this.remoteServer = server;
|
|
|
|
this.remotePort = port;
|
|
|
|
|
|
|
|
this.client = AphlictClient.generateClientId();
|
|
|
|
this.recv.connect(this.client);
|
|
|
|
|
|
|
|
this.timer = new Timer(AphlictClient.INTERVAL);
|
|
|
|
this.timer.addEventListener(TimerEvent.TIMER, this.keepalive);
|
|
|
|
|
|
|
|
this.connectToMaster();
|
2014-06-11 21:17:18 +02:00
|
|
|
|
|
|
|
// Send subscriptions to master.
|
|
|
|
this.log('Sending subscriptions to master.');
|
|
|
|
this.send.send('aphlict_master', 'subscribe', this.client, subscriptions);
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a unique identifier that will be used to communicate with the
|
|
|
|
* @{class:AphlictMaster}.
|
|
|
|
*/
|
|
|
|
private static function generateClientId():String {
|
|
|
|
return 'aphlict_client_' + Math.round(Math.random() * 100000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new connection to the @{class:AphlictMaster}.
|
|
|
|
*
|
|
|
|
* If there is no current @{class:AphlictMaster} instance, then a new master
|
|
|
|
* will be created.
|
|
|
|
*/
|
|
|
|
private function connectToMaster():void {
|
|
|
|
this.timer.stop();
|
|
|
|
|
|
|
|
// Try to become the master.
|
|
|
|
try {
|
|
|
|
this.log('Attempting to become the master...');
|
|
|
|
this.master = new AphlictMaster(this.remoteServer, this.remotePort);
|
|
|
|
this.log('I am the master.');
|
2014-06-08 00:16:23 +02:00
|
|
|
} catch (err:ArgumentError) {
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
this.log('Cannot become the master... probably one already exists');
|
2014-06-08 00:16:23 +02:00
|
|
|
} catch (err:Error) {
|
|
|
|
this.error(err);
|
Modify the Aphlict client to use `LocalConnection`.
Summary:
Ref T4324. Currently, an Aphlict client (with a corresponding connection to the Aphlict Server) is created for every tab that a user has open. This significantly affects the scalability of Aphlict as a service. Instead, we can use `LocalConnection` instances to coordinate the communication of multiple Aphlict clients to the server.
Similar functionality existed prior to D2704, but was removed as the author was not able to get this functionality working as intended. It seems that the main issue with the initial attempt was the use of the `setTimeout` function, which seemed to be a blocking call which prevented messages from being received. I have instead used an event-based model using a `Timer` object.
Roughly this works as follows:
# The first instance will create an `AphlictClient` and an `AphlictMaster`. The `AphlictClient` will register itself with the `AphlictMaster` and will consequently be notified of incoming messages.
# The `AphlictClient` is then responsible for pinging the `AphlictMaster` at regular intervals. If the client does not ping the master in a given period of time, the master will assume that the client is dead and will remove the client from the pool.
# Similarly, the `AphlictMaster` is required to respond to pings with a "pong" response. The pong response lets the clients know that the `AphlictMaster` is still alive. If the clients do not receive a pong in a given period of time, then the clients will attempt to spawn a new master.
Test Plan: I have tested this on our Phabricator install with a few tabs opened and inspecting the console output. I will upload a screencast of my test results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: epriestley, Korvin
Maniphest Tasks: T4324
Differential Revision: https://secure.phabricator.com/D9327
2014-05-29 16:04:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.send.send('aphlict_master', 'register', this.client);
|
|
|
|
this.expiry = new Date().getTime() + (5 * AphlictClient.INTERVAL);
|
|
|
|
this.log('Registered client ' + this.client);
|
|
|
|
|
|
|
|
this.timer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a keepalive signal to the @{class:AphlictMaster}.
|
|
|
|
*
|
|
|
|
* If the connection to the master has expired (because the master has not
|
|
|
|
* sent a heartbeat signal), then a new connection to master will be
|
|
|
|
* created.
|
|
|
|
*/
|
|
|
|
private function keepalive(event:TimerEvent):void {
|
|
|
|
if (new Date().getTime() > this.expiry) {
|
|
|
|
this.connectToMaster();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.send.send('aphlict_master', 'ping', this.client);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is used to receive the heartbeat signal from the
|
|
|
|
* @{class:AphlictMaster}.
|
|
|
|
*/
|
|
|
|
public function pong():void {
|
|
|
|
this.expiry = new Date().getTime() + (2 * AphlictClient.INTERVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Receive a message from the Aphlict Server, via the
|
|
|
|
* @{class:AphlictMaster}.
|
|
|
|
*/
|
|
|
|
public function receiveMessage(msg:Object):void {
|
|
|
|
this.log('Received message.');
|
|
|
|
this.externalInvoke('receive', msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|