stay in catchup mode as long as there are device messages

this implements https://github.com/vector-im/element-web/issues/2782

it also implements 0 timeout for catchup, getting rid of the
catching up with your convo banner for 30s upon reconnection.
This commit is contained in:
Bruno Windels 2020-09-21 17:57:01 +02:00
parent 015c6b1c70
commit c9ee5a5db2
2 changed files with 36 additions and 8 deletions

View file

@ -389,11 +389,17 @@ export class Session {
} }
} }
async afterSyncCompleted() { async afterSyncCompleted(isCatchupSync) {
const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage);
const promises = [this._deviceMessageHandler.decryptPending(this.rooms)]; const promises = [this._deviceMessageHandler.decryptPending(this.rooms)];
if (needsToUploadOTKs) { // we don't start uploading one-time keys until we've caught up with
promises.push(this._e2eeAccount.uploadKeys(this._storage)); // to-device messages, to help us avoid throwing away one-time-keys that we
// are about to receive messages for
// (https://github.com/vector-im/riot-web/issues/2782).
if (!isCatchupSync) {
const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage);
if (needsToUploadOTKs) {
promises.push(this._e2eeAccount.uploadKeys(this._storage));
}
} }
// run key upload and decryption in parallel // run key upload and decryption in parallel
await Promise.all(promises); await Promise.all(promises);

View file

@ -98,11 +98,27 @@ export class Sync {
let roomStates; let roomStates;
try { try {
console.log(`starting sync request with since ${syncToken} ...`); console.log(`starting sync request with since ${syncToken} ...`);
const timeout = syncToken ? INCREMENTAL_TIMEOUT : undefined; // unless we are happily syncing already, we want the server to return
// as quickly as possible, even if there are no events queued. This
// serves two purposes:
//
// * When the connection dies, we want to know asap when it comes back,
// so that we can hide the error from the user. (We don't want to
// have to wait for an event or a timeout).
//
// * We want to know if the server has any to_device messages queued up
// for us. We do that by calling it with a zero timeout until it
// doesn't give us any more to_device messages.
const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0;
const syncResult = await this._syncRequest(syncToken, timeout); const syncResult = await this._syncRequest(syncToken, timeout);
syncToken = syncResult.syncToken; syncToken = syncResult.syncToken;
roomStates = syncResult.roomStates; roomStates = syncResult.roomStates;
this._status.set(SyncStatus.Syncing); // initial sync or catchup sync
if (this._status.get() !== SyncStatus.Syncing && syncResult.hadToDeviceMessages) {
this._status.set(SyncStatus.CatchupSync);
} else {
this._status.set(SyncStatus.Syncing);
}
} catch (err) { } catch (err) {
if (!(err instanceof AbortError)) { if (!(err instanceof AbortError)) {
console.warn("stopping sync because of error"); console.warn("stopping sync because of error");
@ -118,9 +134,10 @@ export class Sync {
} }
async _runAfterSyncCompleted(roomStates) { async _runAfterSyncCompleted(roomStates) {
const isCatchupSync = this._status.get() === SyncStatus.CatchupSync;
const sessionPromise = (async () => { const sessionPromise = (async () => {
try { try {
await this._session.afterSyncCompleted(); await this._session.afterSyncCompleted(isCatchupSync);
} catch (err) { } catch (err) {
console.error("error during session afterSyncCompleted, continuing", err.stack); console.error("error during session afterSyncCompleted, continuing", err.stack);
} }
@ -186,7 +203,12 @@ export class Sync {
rs.room.afterSync(rs.changes); rs.room.afterSync(rs.changes);
} }
return {syncToken, roomStates}; const toDeviceEvents = response.to_device?.events;
return {
syncToken,
roomStates,
hadToDeviceMessages: Array.isArray(toDeviceEvents) && toDeviceEvents.length > 0,
};
} }
async _openPrepareSyncTxn() { async _openPrepareSyncTxn() {