Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

L2l #1

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
54cdceb
Created L2L Channel Branch for lively.sync
Jan 18, 2017
53382b5
Changed format of l2lchannel to mimic that of the local sync channel
m-hemmings Jan 18, 2017
41ff294
Removed unit tests for old back channel
m-hemmings Jan 19, 2017
ccbb70e
Added distinct L2LClients to the channels and unit tests to make sure…
m-hemmings Jan 20, 2017
963906e
Corrected L2LClients to only create if not present, preventing double…
m-hemmings Jan 24, 2017
b784ddd
Added support for checking active sessions of a channel's master to s…
m-hemmings Jan 24, 2017
44c12b2
Updated goOffline function of l2lchannel to correctly check for maste…
m-hemmings Jan 24, 2017
fed0fc4
Clients will now join metachannel for status checking when the channe…
m-hemmings Jan 24, 2017
9d730ce
updated unit tests to include testing for 2 channels sharing a single…
m-hemmings Jan 24, 2017
098aa62
added test client for checking channel statuses in backchannel tests
m-hemmings Jan 24, 2017
58f8ae4
Added registration of supplied receive methods to channel client and …
m-hemmings Jan 24, 2017
7227b50
bug fixes, as per comments from robert on pull request
m-hemmings Jan 25, 2017
a7043bf
more bug fixes for variables not needed
m-hemmings Jan 25, 2017
e6c8acc
Changed l2l channel to inherit properties of local channel instead of…
m-hemmings Jan 25, 2017
f9b3382
Moved online logic to the establish method which calls the appropriat…
m-hemmings Jan 25, 2017
ad87b88
abstracted call for send from deliver to doSend to allow for differen…
m-hemmings Jan 26, 2017
4e34871
Added test to determine if channel has been brought online to allow f…
m-hemmings Jan 27, 2017
cf33cc0
Cleanup and preparation for integration in place of local channel
m-hemmings Jan 27, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion channel.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { string, num, promise, fun } from "lively.lang";

var debug = false;

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Expand All @@ -26,6 +25,7 @@ export class Channel {
this.lifetime = 100;
this._watchdogProcess = null
this.goOnline();
// this.l2lchannel = L2LChannel.create(L2LClient.default())
}

toString() {
Expand Down
1 change: 1 addition & 0 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export class Client {
metaChannel && metaChannel.goOffline();
var master = (opChannel || metaChannel).senderRecvrB;
master.removeConnection(con);
console.log(con.metaChannel)
con.metaChannel = null;
con.opChannel = null;
}
Expand Down
128 changes: 128 additions & 0 deletions l2lchannel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import L2LClient from "lively.2lively/client.js";
import { string, num, promise, fun } from "lively.lang";

export class Channel {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to copy the channel implementation from channel.js? Does subclassing work? Or just configuring the original channel with a variable backend? (l2l vs local)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved with inheritance

constructor(senderRecvrA, onReceivedMethodA, senderRecvrB, onReceivedMethodB){
if (!senderRecvrA) throw new Error("no sender / receiver a!");
if (!senderRecvrB) throw new Error("no sender / receiver b!");
if (typeof senderRecvrA[onReceivedMethodA] !== "function") throw new Error(`sender a has no receive method ${onReceivedMethodA}!`);
if (typeof senderRecvrB[onReceivedMethodB] !== "function") throw new Error(`sender b has no receive method ${onReceivedMethodB}!`);
this.senderRecvrA = senderRecvrA;
this.senderRecvrA.l2lclient ? {} : this.senderRecvrA.l2lclient = Channel.makeL2LClient()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this structure? Why not simply an if instead of a tertiary with an emtpy object?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected with ifs

this.onReceivedMethodA = onReceivedMethodA;
this.onReceivedMethodB = onReceivedMethodB;
this.senderRecvrB = senderRecvrB;
this.senderRecvrB.l2lclient ? {} : this.senderRecvrB.l2lclient = Channel.makeL2LClient()
this.queueAtoB = [];
this.queueBtoA = [];
this.delayAtoB = 0;
this.delayBtoA = 0;
this.online = false;
this.lifetime = 100;
// this._watchdogProcess = null
this.goOnline();
}

static makeL2LClient(hostname,port, namespace,io){
var client = L2LClient.forceNew({})
return client
}

registerReceive(client,method){
var l2l = client.l2lclient;
l2l.addService("lively.sync", (tracker, msg, ackFn, sender) => {
method(msg.data)
});
}

async getSessions(senderA,senderB,ackFn){
var l2lA = senderA.l2lclient,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you define l2lA but it is never used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

l2lB = senderB.l2lclient
var returnVal = 'incomplete';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is returnVal? It is never used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

l2lB.sendTo(l2lB.trackerId,'listRoom',{roomName: l2lB.socketId.split('#')[1]},(a) => {ackFn(a)})
}
toString() {
return `<channel ${this.senderRecvrA}.${this.onReceivedMethodA} – ${this.senderRecvrB}.${this.onReceivedMethodB}>`
}

isOnline() { return this.online; }

goOffline() {
console.log(this)
this.online = false;

var l2lA = this.senderRecvrA.l2lclient,
l2lB = this.senderRecvrB.l2lclient
if(!l2lA){ console.log('l2lA Missing');return''}
if(!l2lB){ console.log('l2lB Missing');return''}
l2lA.sendTo(l2lA.trackerId,'leaveRoom',{roomName: l2lB.socketId.split('#')[1]})

if (this.senderRecvrA && this.senderRecvrA.l2lclient) {
this.senderRecvrA.l2lclient.remove()
console.log('senderRecvrA disconnected')

} else {
console.log('senderRecvrA not disconnected');
}

if (this.senderRecvrB && this.senderRecvrB.l2lclient) {
this.getSessions(this.senderRecvrA,this.senderRecvrB,(a)=> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use async/awai as much as possible. It makes code much more readable than callback style

if(a.data.length <= 1){
l2lB.sendTo(l2lB.trackerId,'leaveRoom',{roomName: l2lB.socketId.split('#')[1]})
l2lB.remove();
} else {
console.log('senderRecvrB not disconnected');
}
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the procedure for cleaning up l2lA and l2lB be the same? it seems strange that you have to query for getSessions to disconnect b?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because I need to make sure it's the last thing in the room before it disconnects, since it's the master.


}

async goOnline() {
var l2lA = this.senderRecvrA.l2lclient,
l2lB = this.senderRecvrB.l2lclient
await l2lA.whenRegistered(300)
await l2lB.whenRegistered(300)

this.registerReceive(this.senderRecvrA,this.senderRecvrA.receiveOpsFromMaster)
this.registerReceive(this.senderRecvrB,this.senderRecvrB.receiveOpsFromClient)

l2lA.sendTo(l2lA.trackerId,'joinRoom',{roomName: l2lB.socketId.split('#')[1]})
l2lB.sendTo(l2lB.trackerId,'joinRoom',{roomName: l2lB.socketId.split('#')[1]})
this.online = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to wait for acknowledgment for joining the rooms to be sure we are really only? In other words, what happens when joinRoom fails? I cannot find error handling somewhere

this.watchdogProcess();
}

watchdogProcess() {
if (!this.isOnline() || this._watchdogProcess) return;

this._watchdogProcess = setTimeout(() => {
this._watchdogProcess = null;
if (this.queueAtoB.length) this.send([], this.senderRecvrA);
else if (this.queueBtoA.length) this.send([], this.senderRecvrB);
else return;
}, 800 + num.random(50));
}

isEmpty() {
return !this.queueBtoA.length && !this.queueAtoB.length;
}

waitForDelivery() {
}

componentsForSender(sender) {
}

send(content, sender) {


return this.deliver(sender);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"deliver" was used in the local channel to implement the details of send, here it is empty so a send actually does nothing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had just emptied it with the intention of filling it with code particular to the l2l version, so this should be resolved with inheritance now

}

deliver(sender) {
}



}
96 changes: 95 additions & 1 deletion tests/sync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { disconnect, disconnectAll, connect } from "lively.bindings";
import { buildTestWorld, destroyTestWorld } from "./helper.js";
import { Client } from "../client.js";
import { Master } from "../master.js";

import { Channel as L2LChannel } from "../l2lchannel.js";
import L2LClient from "lively.2lively/client.js";
import L2LTracker from "lively.2lively/tracker.js";

// System.decanonicalize("mocha-es6", "http://localhost:9011/lively.sync/tests/sync-test.js")
// var env1, env2, env3,
Expand Down Expand Up @@ -85,6 +87,98 @@ describe("messaging between master and client", () => {

});



describe("lively2lively backchannel tests", function() {
this.timeout(1*1000)
before(async() =>{
testingClient = await L2LClient.forceNew({});
await testingClient.whenRegistered(300);
})
after(async() => {
await testingClient.remove();
})
beforeEach(async () => setup(2));
afterEach(async () => teardown());

it("ensure clients have l2l clients", async () => {

var {world1, masterWorld, client1, master} = state;
var testChannel = new L2LChannel(client1, "receiveOpsFromMaster", master, "receiveOpsFromClient")

expect((testChannel.senderRecvrA.l2lclient) && (testChannel.senderRecvrA.l2lclient instanceof L2LClient)).equals(true,'client A not L2LClient')
expect((testChannel.senderRecvrB.l2lclient) && (testChannel.senderRecvrB.l2lclient instanceof L2LClient)).equals(true,'client B not L2LClient')

await testChannel.senderRecvrA.l2lclient.whenRegistered(300)
await testChannel.senderRecvrB.l2lclient.whenRegistered(300)

window.testChannel = testChannel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clean up debug code? Leaving globals and such around might lead to strange effects

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

console.log(testChannel);
window.testChannel = testChannel;

testChannel.goOffline();
})

it('ensure clients can share a master', async () => {
// return
var {world1, masterWorld, client1, client2, master} = state;
var testChannel = new L2LChannel(client1, "receiveOpsFromMaster", master, "receiveOpsFromClient")
window.testChannel = testChannel
expect((testChannel.senderRecvrA.l2lclient) && (testChannel.senderRecvrA.l2lclient instanceof L2LClient)).equals(true,'client A not L2LClient')
expect((testChannel.senderRecvrB.l2lclient) && (testChannel.senderRecvrB.l2lclient instanceof L2LClient)).equals(true,'client B not L2LClient')

await testChannel.senderRecvrA.l2lclient.whenRegistered(300)
await testChannel.senderRecvrB.l2lclient.whenRegistered(300)

var channelId = testChannel.senderRecvrB.l2lclient.socketId.split('#')[1]

var testChannel2 = new L2LChannel(client2, "receiveOpsFromMaster", master, "receiveOpsFromClient")
window.testChannel2 = testChannel2
expect((testChannel2.senderRecvrA.l2lclient) && (testChannel2.senderRecvrA.l2lclient instanceof L2LClient)).equals(true,'client A not L2LClient')
expect((testChannel2.senderRecvrB.l2lclient) && (testChannel2.senderRecvrB.l2lclient instanceof L2LClient)).equals(true,'client B not L2LClient')

await testChannel2.senderRecvrA.l2lclient.whenRegistered(300)
await testChannel2.senderRecvrB.l2lclient.whenRegistered(300)

expect(testChannel2.senderRecvrB === testChannel.senderRecvrB).equals(true,'Clients have different masters')

testChannel.goOffline();
testChannel2.goOffline();
})

it('ensure clients can communicate across channel', async () => {
var {world1, masterWorld, client1, master} = state;
var client1Buffer = []
var masterBuffer = []
var payload = "Test for lively.sync messaging"
var ack = 'received'
function masterCheck(item){
masterBuffer.push(item)
}
function ackFn(item){
client1Buffer.push(item)
client1.l2lclient.sendTo(master.l2lclient.id,'lively.sync',{payload: ack})
}
client1.receiveOpsFromMaster = ackFn;
master.receiveOpsFromClient = masterCheck;

var testChannel = new L2LChannel(client1, "receiveOpsFromMaster", master, "receiveOpsFromClient")
await testChannel.senderRecvrA.l2lclient.whenRegistered(300)
await testChannel.senderRecvrB.l2lclient.whenRegistered(300)

// testChannel.registerReceive(client1,client1.receiveOpsFromMaster)
// testChannel.registerReceive(master,master.receiveOpsFromClient)
master.l2lclient.sendTo(client1.l2lclient.id,'lively.sync',{payload: payload})
await promise.waitFor(200, () => client1Buffer.length >= 1);
await promise.waitFor(200, () => masterBuffer.length >= 1);
testChannel.goOffline();
expect(client1Buffer[0].payload).equals(payload,'payload not correct')
expect(masterBuffer[0].payload).equals(ack,'payload not correct')
})

})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of those tests actually runs lively.sync. How would the tests ""messaging between master and client - single op" and "syncing master with two clients - simple prop" look like when using l2l?



describe("syncing master with two clients", function() {

// this.timeout(5*1000);
Expand Down