Skip to content

Commit 1c25d7e

Browse files
authored
Merge pull request #6650 from nextcloud/fix/y-websocket-with-full-updates
Send one full update from y-websocket
2 parents 9a0b4a4 + 511b304 commit 1c25d7e

15 files changed

Lines changed: 756 additions & 814 deletions

File tree

cypress/component/helpers/yjs.cy.js

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
*/
55

66
import * as Y from 'yjs'
7-
import { getDocumentState, getUpdateMessage, applyUpdateMessage } from '../../../src/helpers/yjs.js'
7+
import { getDocumentState, documentStateToStep, applyStep } from '../../../src/helpers/yjs.js'
88

99
describe('Yjs base64 wrapped with our helpers', function() {
10-
it('applies step in wrong order', function() {
10+
it('applies step generated from document state', function() {
1111
const source = new Y.Doc()
1212
const target = new Y.Doc()
1313
const sourceMap = source.getMap()
@@ -17,44 +17,26 @@ describe('Yjs base64 wrapped with our helpers', function() {
1717
// console.log('afterTransaction', tr)
1818
})
1919

20-
const state0 = getDocumentState(source)
21-
2220
// Add keyA to source and apply to target
2321
sourceMap.set('keyA', 'valueA')
2422

2523
const stateA = getDocumentState(source)
26-
const update0A = getUpdateMessage(source, state0)
27-
applyUpdateMessage(target, update0A)
24+
const step0A = documentStateToStep(stateA)
25+
applyStep(target, step0A)
2826
expect(targetMap.get('keyA')).to.be.eq('valueA')
2927

3028
// Add keyB to source, don't apply to target yet
3129
sourceMap.set('keyB', 'valueB')
3230
const stateB = getDocumentState(source)
33-
const updateAB = getUpdateMessage(source, stateA)
31+
const step0B = documentStateToStep(stateB)
3432

3533
// Add keyC to source, apply to target
3634
sourceMap.set('keyC', 'valueC')
37-
const updateBC = getUpdateMessage(source, stateB)
38-
applyUpdateMessage(target, updateBC)
39-
expect(targetMap.get('keyB')).to.be.eq(undefined)
40-
expect(targetMap.get('keyC')).to.be.eq(undefined)
4135

4236
// Apply keyB to target
43-
applyUpdateMessage(target, updateAB)
37+
applyStep(target, step0B)
4438
expect(targetMap.get('keyB')).to.be.eq('valueB')
45-
expect(targetMap.get('keyC')).to.be.eq('valueC')
46-
})
47-
48-
it('update message is empty if no additional state exists', function() {
49-
const source = new Y.Doc()
50-
const sourceMap = source.getMap()
51-
const state0 = getDocumentState(source)
52-
sourceMap.set('keyA', 'valueA')
53-
const stateA = getDocumentState(source)
54-
const update0A = getUpdateMessage(source, state0)
55-
const updateAA = getUpdateMessage(source, stateA)
56-
expect(update0A.length).to.be.eq(29)
57-
expect(updateAA).to.be.eq(undefined)
39+
expect(targetMap.get('keyC')).to.be.eq(undefined)
5840
})
5941

6042
})

cypress/e2e/api/SyncServiceProvider.spec.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { randUser } from '../../utils/index.js'
7+
import SessionApi from '../../../src/services/SessionApi.js'
78
import { SyncService } from '../../../src/services/SyncService.js'
89
import createSyncServiceProvider from '../../../src/services/SyncServiceProvider.js'
910
import { Doc } from 'yjs'
@@ -39,9 +40,11 @@ describe('Sync service provider', function() {
3940
*/
4041
function createProvider(ydoc) {
4142
const queue = []
43+
const api = new SessionApi()
4244
const syncService = new SyncService({
4345
serialize: () => 'Serialized',
4446
getDocumentState: () => null,
47+
api,
4548
})
4649
syncService.on('opened', () => syncService.startSync())
4750
return createSyncServiceProvider({

lib/Service/DocumentService.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ public function addStep(Document $document, Session $session, array $steps, int
208208
$documentId = $session->getDocumentId();
209209
$readOnly = $this->isReadOnly($this->getFileForSession($session, $shareToken), $shareToken);
210210
$stepsToInsert = [];
211-
$querySteps = [];
211+
$stepsIncludeQuery = false;
212+
$documentState = null;
212213
$newVersion = $version;
213214
foreach ($steps as $step) {
214215
$message = YjsMessage::fromBase64($step);
@@ -217,7 +218,7 @@ public function addStep(Document $document, Session $session, array $steps, int
217218
}
218219
// Filter out query steps as they would just trigger clients to send their steps again
219220
if ($message->getYjsMessageType() === YjsMessage::YJS_MESSAGE_SYNC && $message->getYjsSyncType() === YjsMessage::YJS_MESSAGE_SYNC_STEP1) {
220-
$querySteps[] = $step;
221+
$stepsIncludeQuery = true;
221222
} else {
222223
$stepsToInsert[] = $step;
223224
}
@@ -228,8 +229,24 @@ public function addStep(Document $document, Session $session, array $steps, int
228229
}
229230
$newVersion = $this->insertSteps($document, $session, $stepsToInsert);
230231
}
231-
// If there were any queries in the steps send the entire history
232-
$getStepsSinceVersion = count($querySteps) > 0 ? 0 : $version;
232+
233+
// By default send all steps the user has not received yet.
234+
$getStepsSinceVersion = $version;
235+
if ($stepsIncludeQuery) {
236+
$this->logger->debug('Loading document state for ' . $documentId);
237+
try {
238+
$stateFile = $this->getStateFile($documentId);
239+
$documentState = $stateFile->getContent();
240+
$this->logger->debug('Existing document, state file loaded ' . $documentId);
241+
// If there were any queries in the steps send all steps since last save.
242+
$getStepsSinceVersion = $document->getLastSavedVersion();
243+
} catch (NotFoundException $e) {
244+
$this->logger->debug('Existing document, but no state file found for ' . $documentId);
245+
// If there is no state file include all the steps.
246+
$getStepsSinceVersion = 0;
247+
}
248+
}
249+
233250
$allSteps = $this->getSteps($documentId, $getStepsSinceVersion);
234251
$stepsToReturn = [];
235252
foreach ($allSteps as $step) {
@@ -238,9 +255,11 @@ public function addStep(Document $document, Session $session, array $steps, int
238255
$stepsToReturn[] = $step;
239256
}
240257
}
258+
241259
return [
242260
'steps' => $stepsToReturn,
243-
'version' => $newVersion
261+
'version' => $newVersion,
262+
'documentState' => $documentState
244263
];
245264
}
246265

0 commit comments

Comments
 (0)