Skip to content

Commit 6794cf5

Browse files
Throttle live (codesandbox#3722)
* throttle live based on connections * ops, forgot to change back * modified * refactor throttling * another iteration * presence and blocking instead, not working though * fix presence * fix typo * open up when saving * fix bug * ensure revision when ack * add as experiment * reset files with operation error * properly propagate errors * do not force experiment :D * final thing * rename stuff * comments * use 2s throttling * do not wait to be synchronized * wait for operation flush also on save clicked * fix comments from ives * even smoother
1 parent 6690fe4 commit 6794cf5

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

packages/app/src/app/overmind/effects/live/clients.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { TextOperation } from 'ot';
21
import { logBreadcrumb } from '@codesandbox/common/lib/utils/analytics/sentry';
32
import { Blocker, blocker } from 'app/utils/blocker';
3+
import { TextOperation } from 'ot';
44

55
import { OTClient, synchronized_ } from './ot/client';
66

packages/app/src/app/overmind/effects/live/index.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import _debug from '@codesandbox/common/lib/utils/debug';
1313
import { Blocker, blocker } from 'app/utils/blocker';
1414
import { camelizeKeys } from 'humps';
15-
import { TextOperation, SerializedTextOperation } from 'ot';
15+
import { SerializedTextOperation, TextOperation } from 'ot';
1616
import { Channel, Presence, Socket } from 'phoenix';
1717
import uuid from 'uuid';
1818

@@ -45,13 +45,16 @@ declare global {
4545
}
4646
}
4747

48+
const TIME_TO_THROTTLE_SOLO_MODE_SENDS = 2000;
49+
4850
class Live {
4951
private identifier = uuid.v4();
5052
private pendingMessages = new Map();
5153
private debug = _debug('cs:socket');
5254
private channel: Channel | null;
5355
private messageIndex = 0;
5456
private clients: ReturnType<typeof clientsFactory>;
57+
private awaitSendTimer: number;
5558
private socket: Socket;
5659
/*
5760
Since in "Solo mode" we want to batch up operations and other events later,
@@ -86,15 +89,32 @@ class Live {
8689
private connectionsCount = 0;
8790
private setAwaitSend() {
8891
this.awaitSend = blocker();
92+
clearTimeout(this.awaitSendTimer);
93+
this.awaitSendTimer = window.setTimeout(async () => {
94+
if (this.connectionsCount === 1) {
95+
// We await the currently resolved blocker before setting it back,
96+
// so that messages gets through
97+
await this.resolveAwaitSend();
98+
this.setAwaitSend();
99+
}
100+
}, TIME_TO_THROTTLE_SOLO_MODE_SENDS);
89101
}
90102

91103
private resolveAwaitSend() {
92104
if (!this.awaitSend) {
93-
return;
105+
return Promise.resolve();
94106
}
95107
const awaitSend = this.awaitSend;
96108
this.awaitSend = null;
97109
awaitSend.resolve();
110+
return awaitSend.promise;
111+
}
112+
113+
private async awaitSynchronizedModule(moduleShortid: string) {
114+
const client = this.clients.get(moduleShortid);
115+
if (client.awaitSynchronized) {
116+
await client.awaitSynchronized.promise;
117+
}
98118
}
99119

100120
private onSendOperation = async (
@@ -168,7 +188,7 @@ class Live {
168188

169189
const waitTime = 500 + 5000 * Math.random();
170190

171-
setTimeout(() => {
191+
window.setTimeout(() => {
172192
this.socket.connect();
173193
}, waitTime);
174194
}
@@ -224,9 +244,9 @@ class Live {
224244
const currentCount = this.connectionsCount;
225245

226246
this.connectionsCount = this.presence.list().length;
227-
if (currentCount >= 2 && this.connectionsCount < 2) {
247+
if (currentCount !== 1 && this.connectionsCount === 1) {
228248
this.setAwaitSend();
229-
} else if (currentCount < 2 && this.connectionsCount >= 2) {
249+
} else if (currentCount === 1 && this.connectionsCount > 1) {
230250
this.resolveAwaitSend();
231251
}
232252
});
@@ -315,13 +335,10 @@ class Live {
315335
*/
316336
if (this.isLiveBlockerExperiement() && this.awaitSend) {
317337
this.resolveAwaitSend();
318-
const client = this.clients.get(module.shortid);
319-
if (client.awaitSynchronized) {
320-
await client.awaitSynchronized.promise;
321-
}
338+
this.awaitSynchronizedModule(module.shortid);
322339
this.setAwaitSend();
323-
// Send the save message
324340
}
341+
// Send the save message
325342
}
326343

327344
sendModuleUpdate(module: Module) {
@@ -485,6 +502,12 @@ class Live {
485502
return this.clients.getAll();
486503
}
487504

505+
applyClient(moduleShortid: string, operation: SerializedTextOperation) {
506+
return this.clients
507+
.get(moduleShortid)
508+
.applyClient(TextOperation.fromJSON(operation));
509+
}
510+
488511
applyServer(moduleShortid: string, operation: SerializedTextOperation) {
489512
return this.clients
490513
.get(moduleShortid)

packages/app/src/app/overmind/namespaces/editor/actions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,10 @@ export const saveClicked: AsyncAction = withOwnedSandbox(
370370
state.editor.changedModuleShortids.includes(module.shortid)
371371
);
372372

373+
await Promise.all(
374+
changedModules.map(module => effects.live.saveModule(module))
375+
);
376+
373377
const updatedModules = await effects.api.saveModules(
374378
sandbox.id,
375379
changedModules

0 commit comments

Comments
 (0)