Skip to content

Commit 1ee6be3

Browse files
throttle live based on connections (codesandbox#3664)
* 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 * Update packages/app/src/app/overmind/namespaces/live/actions.ts * Update packages/app/src/app/overmind/namespaces/live/actions.ts Co-authored-by: Ives van Hoorne <[email protected]>
1 parent fb6c381 commit 1ee6be3

File tree

14 files changed

+374
-177
lines changed

14 files changed

+374
-177
lines changed

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

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { logBreadcrumb } from '@codesandbox/common/lib/utils/analytics/sentry';
2-
import { OTClient } from './ot/client';
2+
import { Blocker, blocker } from 'app/utils/blocker';
3+
4+
import { OTClient, synchronized_ } from './ot/client';
35

46
export type SendOperation = (
57
moduleShortid: string,
@@ -9,21 +11,13 @@ export type SendOperation = (
911

1012
export type ApplyOperation = (moduleShortid: string, operation: any) => void;
1113

12-
function operationToElixir(ot) {
13-
return ot.map(op => {
14-
if (typeof op === 'number') {
15-
if (op < 0) {
16-
return { d: -op };
17-
}
18-
19-
return op;
20-
}
21-
22-
return { i: op };
23-
});
24-
}
25-
2614
class CodeSandboxOTClient extends OTClient {
15+
/*
16+
We need to be able to wait for a client to go intro synchronized
17+
state. The reason is that we want to send a "save" event when the
18+
client is synchronized
19+
*/
20+
public awaitSynchronized: Blocker<void> | null;
2721
moduleShortid: string;
2822
onSendOperation: (revision: number, operation: any) => Promise<unknown>;
2923
onApplyOperation: (operation: any) => void;
@@ -41,8 +35,15 @@ class CodeSandboxOTClient extends OTClient {
4135
}
4236

4337
sendOperation(revision, operation) {
44-
this.onSendOperation(revision, operationToElixir(operation.toJSON())).then(
45-
() => {
38+
// Whenever we send an operation we enable the blocker
39+
// that lets us wait for its resolvment when moving back
40+
// to synchronized state
41+
if (!this.awaitSynchronized) {
42+
this.awaitSynchronized = blocker();
43+
}
44+
45+
this.onSendOperation(revision, operation)
46+
.then(() => {
4647
logBreadcrumb({
4748
type: 'ot',
4849
message: `Acknowledging ${JSON.stringify({
@@ -51,10 +52,19 @@ class CodeSandboxOTClient extends OTClient {
5152
operation,
5253
})}`,
5354
});
54-
55-
this.serverAck();
56-
}
57-
);
55+
// We only acknowledge valig revisions
56+
if (this.revision === revision) {
57+
this.serverAck();
58+
}
59+
})
60+
.catch(error => {
61+
// If an operation errors on the server we will reject
62+
// the blocker, as an action might be waiting for it to resolve,
63+
// creating a user friendly error related to trying to save
64+
if (this.awaitSynchronized) {
65+
this.awaitSynchronized.reject(error);
66+
}
67+
});
5868
}
5969

6070
applyOperation(operation) {
@@ -64,6 +74,13 @@ class CodeSandboxOTClient extends OTClient {
6474
serverAck() {
6575
try {
6676
super.serverAck();
77+
78+
// If we are back in synchronized state we resolve the blocker
79+
if (this.state === synchronized_ && this.awaitSynchronized) {
80+
const awaitSynchronized = this.awaitSynchronized;
81+
this.awaitSynchronized = null;
82+
awaitSynchronized.resolve();
83+
}
6784
} catch (e) {
6885
// Undo the revision increment again
6986
super.revision--;
@@ -96,6 +113,7 @@ export default (
96113
): CodeSandboxOTClient;
97114
create(moduleShortid: string, revision: number): CodeSandboxOTClient;
98115
clear(): void;
116+
reset(moduleShortid: string, revision: number): void;
99117
} => {
100118
const modules = new Map<string, CodeSandboxOTClient>();
101119

@@ -126,6 +144,10 @@ export default (
126144

127145
return client;
128146
},
147+
reset(moduleShortid, revision) {
148+
modules.delete(moduleShortid);
149+
this.create(moduleShortid, revision);
150+
},
129151
clear() {
130152
modules.clear();
131153
},

0 commit comments

Comments
 (0)