add more sync logging

This commit is contained in:
Bruno Windels 2021-02-17 18:45:04 +01:00
parent e14929bd4f
commit f321968ac3
8 changed files with 164 additions and 147 deletions

View file

@ -89,6 +89,7 @@ export class LogItem {
if (!filter.filter(this, children)) { if (!filter.filter(this, children)) {
return null; return null;
} }
// in (v)alues, (l)abel and (t)ype are also reserved.
const item = { const item = {
// (s)tart // (s)tart
s: this._start, s: this._start,

View file

@ -34,7 +34,7 @@ export class DeviceMessageHandler {
/** /**
* @return {bool} whether messages are waiting to be decrypted and `decryptPending` should be called. * @return {bool} whether messages are waiting to be decrypted and `decryptPending` should be called.
*/ */
async writeSync(toDeviceEvents, txn) { async writeSync(toDeviceEvents, txn, log) {
const encryptedEvents = toDeviceEvents.filter(e => e.type === "m.room.encrypted"); const encryptedEvents = toDeviceEvents.filter(e => e.type === "m.room.encrypted");
if (!encryptedEvents.length) { if (!encryptedEvents.length) {
return false; return false;
@ -53,14 +53,14 @@ export class DeviceMessageHandler {
* @param {[type]} txn [description] * @param {[type]} txn [description]
* @return {[type]} [description] * @return {[type]} [description]
*/ */
async _writeDecryptedEvents(olmResults, txn) { async _writeDecryptedEvents(olmResults, txn, log) {
const megOlmRoomKeysResults = olmResults.filter(r => { const megOlmRoomKeysResults = olmResults.filter(r => {
return r.event?.type === "m.room_key" && r.event.content?.algorithm === MEGOLM_ALGORITHM; return r.event?.type === "m.room_key" && r.event.content?.algorithm === MEGOLM_ALGORITHM;
}); });
let roomKeys; let roomKeys;
log.set("roomKeyCount", megOlmRoomKeysResults.length);
if (megOlmRoomKeysResults.length) { if (megOlmRoomKeysResults.length) {
console.log("new room keys", megOlmRoomKeysResults); roomKeys = await this._megolmDecryption.addRoomKeys(megOlmRoomKeysResults, txn, log);
roomKeys = await this._megolmDecryption.addRoomKeys(megOlmRoomKeysResults, txn);
} }
return {roomKeys}; return {roomKeys};
} }
@ -76,12 +76,13 @@ export class DeviceMessageHandler {
} }
// not safe to call multiple times without awaiting first call // not safe to call multiple times without awaiting first call
async decryptPending(rooms) { async decryptPending(rooms, log) {
if (!this._olmDecryption) { if (!this._olmDecryption) {
return; return;
} }
const readTxn = this._storage.readTxn([this._storage.storeNames.session]); const readTxn = this._storage.readTxn([this._storage.storeNames.session]);
const pendingEvents = await this._getPendingEvents(readTxn); const pendingEvents = await this._getPendingEvents(readTxn);
log.set("eventCount", pendingEvents.length);
if (pendingEvents.length === 0) { if (pendingEvents.length === 0) {
return; return;
} }
@ -89,7 +90,7 @@ export class DeviceMessageHandler {
const olmEvents = pendingEvents.filter(e => e.content?.algorithm === OLM_ALGORITHM); const olmEvents = pendingEvents.filter(e => e.content?.algorithm === OLM_ALGORITHM);
const decryptChanges = await this._olmDecryption.decryptAll(olmEvents); const decryptChanges = await this._olmDecryption.decryptAll(olmEvents);
for (const err of decryptChanges.errors) { for (const err of decryptChanges.errors) {
console.warn("decryption failed for event", err, err.event); log.child("decrypt_error").catch(err);
} }
const txn = this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
// both to remove the pending events and to modify the olm account // both to remove the pending events and to modify the olm account
@ -99,7 +100,7 @@ export class DeviceMessageHandler {
]); ]);
let changes; let changes;
try { try {
changes = await this._writeDecryptedEvents(decryptChanges.results, txn); changes = await this._writeDecryptedEvents(decryptChanges.results, txn, log);
decryptChanges.write(txn); decryptChanges.write(txn);
txn.session.remove(PENDING_ENCRYPTED_EVENTS); txn.session.remove(PENDING_ENCRYPTED_EVENTS);
} catch (err) { } catch (err) {

View file

@ -374,7 +374,7 @@ export class Session {
} }
/** @internal */ /** @internal */
async writeSync(syncResponse, syncFilterId, txn) { async writeSync(syncResponse, syncFilterId, txn, log) {
const changes = { const changes = {
syncInfo: null, syncInfo: null,
e2eeAccountChanges: null, e2eeAccountChanges: null,
@ -390,20 +390,20 @@ export class Session {
const deviceOneTimeKeysCount = syncResponse.device_one_time_keys_count; const deviceOneTimeKeysCount = syncResponse.device_one_time_keys_count;
if (this._e2eeAccount && deviceOneTimeKeysCount) { if (this._e2eeAccount && deviceOneTimeKeysCount) {
changes.e2eeAccountChanges = this._e2eeAccount.writeSync(deviceOneTimeKeysCount, txn); changes.e2eeAccountChanges = this._e2eeAccount.writeSync(deviceOneTimeKeysCount, txn, log);
} }
if (this._deviceTracker) { if (this._deviceTracker) {
const deviceLists = syncResponse.device_lists; const deviceLists = syncResponse.device_lists;
if (deviceLists) { if (deviceLists) {
await this._deviceTracker.writeDeviceChanges(deviceLists, txn); await log.wrap("deviceTracker", log => this._deviceTracker.writeDeviceChanges(deviceLists, txn, log));
} }
} }
const toDeviceEvents = syncResponse.to_device?.events; const toDeviceEvents = syncResponse.to_device?.events;
if (Array.isArray(toDeviceEvents)) { if (Array.isArray(toDeviceEvents)) {
changes.deviceMessageDecryptionPending = changes.deviceMessageDecryptionPending =
await this._deviceMessageHandler.writeSync(toDeviceEvents, txn); await log.wrap("deviceMsgs", log => this._deviceMessageHandler.writeSync(toDeviceEvents, txn, log));
} }
// store account data // store account data
@ -430,10 +430,10 @@ export class Session {
} }
/** @internal */ /** @internal */
async afterSyncCompleted(changes, isCatchupSync) { async afterSyncCompleted(changes, isCatchupSync, log) {
const promises = []; const promises = [];
if (changes.deviceMessageDecryptionPending) { if (changes.deviceMessageDecryptionPending) {
promises.push(this._deviceMessageHandler.decryptPending(this.rooms)); promises.push(log.wrap("decryptPending", log => this._deviceMessageHandler.decryptPending(this.rooms, log)));
} }
// we don't start uploading one-time keys until we've caught up with // we don't start uploading one-time keys until we've caught up with
// to-device messages, to help us avoid throwing away one-time-keys that we // to-device messages, to help us avoid throwing away one-time-keys that we
@ -442,7 +442,7 @@ export class Session {
if (!isCatchupSync) { if (!isCatchupSync) {
const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage); const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage);
if (needsToUploadOTKs) { if (needsToUploadOTKs) {
promises.push(this._e2eeAccount.uploadKeys(this._storage)); promises.push(log.wrap("uploadKeys", log => this._e2eeAccount.uploadKeys(this._storage, log)));
} }
} }
if (promises.length) { if (promises.length) {

View file

@ -90,79 +90,81 @@ export class Sync {
this._syncLoop(syncToken); this._syncLoop(syncToken);
} }
_createLogFilter(filter, log) {
if (log.duration >= 2000 || log.error || this._status.get() === SyncStatus.CatchupSync) {
return filter.minLevel(log.level.Detail);
} else {
return filter.minLevel(log.level.Info);
}
}
async _syncLoop(syncToken) { async _syncLoop(syncToken) {
// if syncToken is falsy, it will first do an initial sync ... // if syncToken is falsy, it will first do an initial sync ...
while(this._status.get() !== SyncStatus.Stopped) { while(this._status.get() !== SyncStatus.Stopped) {
let roomStates; let roomStates;
let sessionChanges; let sessionChanges;
try { let wasCatchup = this._status.get() === SyncStatus.CatchupSync;
console.log(`starting sync request with since ${syncToken} ...`); await this._logger.run("sync", async log => {
// unless we are happily syncing already, we want the server to return log.set("token", syncToken);
// as quickly as possible, even if there are no events queued. This log.set("status", this._status.get());
// serves two purposes: try {
// // unless we are happily syncing already, we want the server to return
// * When the connection dies, we want to know asap when it comes back, // as quickly as possible, even if there are no events queued. This
// so that we can hide the error from the user. (We don't want to // serves two purposes:
// have to wait for an event or a timeout). //
// // * When the connection dies, we want to know asap when it comes back,
// * We want to know if the server has any to_device messages queued up // so that we can hide the error from the user. (We don't want to
// for us. We do that by calling it with a zero timeout until it // have to wait for an event or a timeout).
// doesn't give us any more to_device messages. //
const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0; // * We want to know if the server has any to_device messages queued up
const syncResult = await this._logger.run("sync", // for us. We do that by calling it with a zero timeout until it
log => this._syncRequest(syncToken, timeout, log), // doesn't give us any more to_device messages.
this._logger.level.Info, const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0;
this._createLogFilter.bind(this) const syncResult = await this._syncRequest(syncToken, timeout, log);
); syncToken = syncResult.syncToken;
syncToken = syncResult.syncToken; roomStates = syncResult.roomStates;
roomStates = syncResult.roomStates; sessionChanges = syncResult.sessionChanges;
sessionChanges = syncResult.sessionChanges; // initial sync or catchup sync
// initial sync or catchup sync if (this._status.get() !== SyncStatus.Syncing && syncResult.hadToDeviceMessages) {
if (this._status.get() !== SyncStatus.Syncing && syncResult.hadToDeviceMessages) { this._status.set(SyncStatus.CatchupSync);
this._status.set(SyncStatus.CatchupSync); } else {
this._status.set(SyncStatus.Syncing);
}
} catch (err) {
// retry same request on timeout
if (err.name === "ConnectionError" && err.isTimeout) {
// don't run afterSyncCompleted
return;
}
this._error = err;
if (err.name !== "AbortError") {
// sync wasn't asked to stop, but is stopping
// because of the error.
log.error = err;
log.logLevel = log.level.Fatal;
}
log.set("stopping", true);
this._status.set(SyncStatus.Stopped);
}
if (this._status.get() !== SyncStatus.Stopped) {
// TODO: if we're not going to run this phase in parallel with the next
// sync request (because this causes OTKs to be uploaded twice)
// should we move this inside _syncRequest?
// Alternatively, we can try to fix the OTK upload issue while still
// running in parallel.
await log.wrap("afterSyncCompleted", log => this._runAfterSyncCompleted(sessionChanges, roomStates, log));
}
},
this._logger.level.Info,
(filter, log) => {
if (log.duration >= 2000 || log.error || wasCatchup) {
return filter.minLevel(log.level.Detail);
} else { } else {
this._status.set(SyncStatus.Syncing); return filter.minLevel(log.level.Info);
} }
} catch (err) { });
// retry same request on timeout
if (err.name === "ConnectionError" && err.isTimeout) {
// don't run afterSyncCompleted
continue;
}
this._error = err;
if (err.name !== "AbortError") {
console.warn("stopping sync because of error");
console.error(err);
}
this._status.set(SyncStatus.Stopped);
}
if (this._status.get() !== SyncStatus.Stopped) {
// TODO: if we're not going to run this phase in parallel with the next
// sync request (because this causes OTKs to be uploaded twice)
// should we move this inside _syncRequest?
// Alternatively, we can try to fix the OTK upload issue while still
// running in parallel.
await this._runAfterSyncCompleted(sessionChanges, roomStates);
}
} }
} }
async _runAfterSyncCompleted(sessionChanges, roomStates) { async _runAfterSyncCompleted(sessionChanges, roomStates, log) {
const isCatchupSync = this._status.get() === SyncStatus.CatchupSync; const isCatchupSync = this._status.get() === SyncStatus.CatchupSync;
const sessionPromise = (async () => { const sessionPromise = (async () => {
try { try {
await this._session.afterSyncCompleted(sessionChanges, isCatchupSync); await log.wrap("session", log => this._session.afterSyncCompleted(sessionChanges, isCatchupSync, log));
} catch (err) { } catch (err) {} // error is logged, but don't fail sessionPromise
console.error("error during session afterSyncCompleted, continuing", err.stack);
}
})(); })();
const roomsNeedingAfterSyncCompleted = roomStates.filter(rs => { const roomsNeedingAfterSyncCompleted = roomStates.filter(rs => {
@ -170,10 +172,8 @@ export class Sync {
}); });
const roomsPromises = roomsNeedingAfterSyncCompleted.map(async rs => { const roomsPromises = roomsNeedingAfterSyncCompleted.map(async rs => {
try { try {
await rs.room.afterSyncCompleted(rs.changes); await log.wrap("room", log => rs.room.afterSyncCompleted(rs.changes, log));
} catch (err) { } catch (err) {} // error is logged, but don't fail roomsPromises
console.error(`error during room ${rs.room.id} afterSyncCompleted, continuing`, err.stack);
}
}); });
// run everything in parallel, // run everything in parallel,
// we don't want to delay the next sync too much // we don't want to delay the next sync too much
@ -185,7 +185,7 @@ export class Sync {
async _syncRequest(syncToken, timeout, log) { async _syncRequest(syncToken, timeout, log) {
let {syncFilterId} = this._session; let {syncFilterId} = this._session;
if (typeof syncFilterId !== "string") { if (typeof syncFilterId !== "string") {
this._currentRequest = this._hsApi.createFilter(this._session.user.id, {room: {state: {lazy_load_members: true}}}); this._currentRequest = this._hsApi.createFilter(this._session.user.id, {room: {state: {lazy_load_members: true}}}, {log});
syncFilterId = (await this._currentRequest.response()).filter_id; syncFilterId = (await this._currentRequest.response()).filter_id;
} }
const totalRequestTimeout = timeout + (80 * 1000); // same as riot-web, don't get stuck on wedged long requests const totalRequestTimeout = timeout + (80 * 1000); // same as riot-web, don't get stuck on wedged long requests
@ -193,47 +193,46 @@ export class Sync {
const response = await this._currentRequest.response(); const response = await this._currentRequest.response();
const isInitialSync = !syncToken; const isInitialSync = !syncToken;
syncToken = response.next_batch;
log.set("syncToken", syncToken);
log.set("status", this._status.get());
const roomStates = this._parseRoomsResponse(response.rooms, isInitialSync); const roomStates = this._parseRoomsResponse(response.rooms, isInitialSync);
await log.wrap("prepare rooms", log => this._prepareRooms(roomStates, log)); log.set("roomCount", roomStates.length);
await log.wrap("prepare", log => this._prepareRooms(roomStates, log));
let sessionChanges; let sessionChanges;
const syncTxn = this._openSyncTxn();
try { await log.wrap("write", async log => {
sessionChanges = await log.wrap("session.writeSync", log => this._session.writeSync(response, syncFilterId, syncTxn, log)); const syncTxn = this._openSyncTxn();
await Promise.all(roomStates.map(async rs => {
rs.changes = await log.wrap("room.writeSync", log => rs.room.writeSync(
rs.roomResponse, isInitialSync, rs.preparation, syncTxn, log));
}));
} catch(err) {
// avoid corrupting state by only
// storing the sync up till the point
// the exception occurred
try { try {
syncTxn.abort(); sessionChanges = await log.wrap("session", log => this._session.writeSync(response, syncFilterId, syncTxn, log), log.level.Detail);
} catch (abortErr) { await Promise.all(roomStates.map(async rs => {
console.error("Could not abort sync transaction, the sync response was probably only partially written and may have put storage in a inconsistent state.", abortErr); rs.changes = await log.wrap("room", log => rs.room.writeSync(
rs.roomResponse, isInitialSync, rs.preparation, syncTxn, log), log.level.Detail);
}));
} catch(err) {
// avoid corrupting state by only
// storing the sync up till the point
// the exception occurred
try {
syncTxn.abort();
} catch (abortErr) {
log.set("couldNotAbortTxn", true);
}
throw err;
} }
throw err;
}
try {
await syncTxn.complete(); await syncTxn.complete();
console.info("syncTxn committed!!"); });
} catch (err) {
console.error("unable to commit sync tranaction"); log.wrap("after", log => {
throw err; log.wrap("session", log => this._session.afterSync(sessionChanges, log), log.level.Detail);
} // emit room related events after txn has been closed
this._session.afterSync(sessionChanges); for(let rs of roomStates) {
// emit room related events after txn has been closed log.wrap("room", log => rs.room.afterSync(rs.changes, log), log.level.Detail);
for(let rs of roomStates) { }
rs.room.afterSync(rs.changes); });
}
const toDeviceEvents = response.to_device?.events; const toDeviceEvents = response.to_device?.events;
return { return {
syncToken, syncToken: response.next_batch,
roomStates, roomStates,
sessionChanges, sessionChanges,
hadToDeviceMessages: Array.isArray(toDeviceEvents) && toDeviceEvents.length > 0, hadToDeviceMessages: Array.isArray(toDeviceEvents) && toDeviceEvents.length > 0,
@ -250,11 +249,11 @@ export class Sync {
async _prepareRooms(roomStates, log) { async _prepareRooms(roomStates, log) {
const prepareTxn = this._openPrepareSyncTxn(); const prepareTxn = this._openPrepareSyncTxn();
await Promise.all(roomStates.map(async rs => { await Promise.all(roomStates.map(async rs => {
rs.preparation = await log.wrap("room.prepareSync", log => rs.room.prepareSync(rs.roomResponse, rs.membership, prepareTxn, log)); rs.preparation = await log.wrap("room", log => rs.room.prepareSync(rs.roomResponse, rs.membership, prepareTxn, log), log.level.Detail);
})); }));
// This is needed for safari to not throw TransactionInactiveErrors on the syncTxn. See docs/INDEXEDDB.md // This is needed for safari to not throw TransactionInactiveErrors on the syncTxn. See docs/INDEXEDDB.md
await prepareTxn.complete(); await prepareTxn.complete();
await Promise.all(roomStates.map(rs => rs.room.afterPrepareSync(rs.preparation))); await Promise.all(roomStates.map(rs => rs.room.afterPrepareSync(rs.preparation, log)));
} }
_openSyncTxn() { _openSyncTxn() {

View file

@ -80,7 +80,7 @@ export class Account {
return this._identityKeys; return this._identityKeys;
} }
async uploadKeys(storage) { async uploadKeys(storage, log) {
const oneTimeKeys = JSON.parse(this._account.one_time_keys()); const oneTimeKeys = JSON.parse(this._account.one_time_keys());
// only one algorithm supported by olm atm, so hardcode its name // only one algorithm supported by olm atm, so hardcode its name
const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519); const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);
@ -93,8 +93,9 @@ export class Account {
if (oneTimeKeysEntries.length) { if (oneTimeKeysEntries.length) {
payload.one_time_keys = this._oneTimeKeysPayload(oneTimeKeysEntries); payload.one_time_keys = this._oneTimeKeysPayload(oneTimeKeysEntries);
} }
const response = await this._hsApi.uploadKeys(payload).response(); const response = await this._hsApi.uploadKeys(payload, {log}).response();
this._serverOTKCount = response?.one_time_key_counts?.signed_curve25519; this._serverOTKCount = response?.one_time_key_counts?.signed_curve25519;
log.set("serverOTKCount", this._serverOTKCount);
// TODO: should we not modify this in the txn like we do elsewhere? // TODO: should we not modify this in the txn like we do elsewhere?
// we'd have to pickle and unpickle the account to clone it though ... // we'd have to pickle and unpickle the account to clone it though ...
// and the upload has succeed at this point, so in-memory would be correct // and the upload has succeed at this point, so in-memory would be correct
@ -173,11 +174,12 @@ export class Account {
txn.session.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey)); txn.session.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));
} }
writeSync(deviceOneTimeKeysCount, txn) { writeSync(deviceOneTimeKeysCount, txn, log) {
// we only upload signed_curve25519 otks // we only upload signed_curve25519 otks
const otkCount = deviceOneTimeKeysCount.signed_curve25519 || 0; const otkCount = deviceOneTimeKeysCount.signed_curve25519 || 0;
if (Number.isSafeInteger(otkCount) && otkCount !== this._serverOTKCount) { if (Number.isSafeInteger(otkCount) && otkCount !== this._serverOTKCount) {
txn.session.set(SERVER_OTK_COUNT_SESSION_KEY, otkCount); txn.session.set(SERVER_OTK_COUNT_SESSION_KEY, otkCount);
log.set("otkCount", otkCount);
return otkCount; return otkCount;
} }
} }

View file

@ -337,7 +337,7 @@ export class RoomEncryption {
return id; return id;
} }
async flushPendingRoomKeyShares(hsApi, operations = null) { async flushPendingRoomKeyShares(hsApi, operations, log) {
// this has to be reentrant as it can be called from Room.start while still running // this has to be reentrant as it can be called from Room.start while still running
if (this._isFlushingRoomKeyShares) { if (this._isFlushingRoomKeyShares) {
return; return;

View file

@ -122,33 +122,40 @@ export class Decryption {
* @param {[type]} txn a storage transaction with read/write on inboundGroupSessions * @param {[type]} txn a storage transaction with read/write on inboundGroupSessions
* @return {Promise<Array<MegolmInboundSessionDescription>>} an array with the newly added sessions * @return {Promise<Array<MegolmInboundSessionDescription>>} an array with the newly added sessions
*/ */
async addRoomKeys(decryptionResults, txn) { async addRoomKeys(decryptionResults, txn, log) {
const newSessions = []; const newSessions = [];
for (const {senderCurve25519Key: senderKey, event, claimedEd25519Key} of decryptionResults) { for (const {senderCurve25519Key: senderKey, event, claimedEd25519Key} of decryptionResults) {
const roomId = event.content?.["room_id"]; await log.wrap("room_key", async log => {
const sessionId = event.content?.["session_id"]; const roomId = event.content?.["room_id"];
const sessionKey = event.content?.["session_key"]; const sessionId = event.content?.["session_id"];
const sessionKey = event.content?.["session_key"];
if ( log.set("roomId", roomId);
typeof roomId !== "string" || log.set("sessionId", sessionId);
typeof sessionId !== "string" ||
typeof senderKey !== "string" ||
typeof sessionKey !== "string"
) {
return;
}
const session = new this._olm.InboundGroupSession(); if (
try { typeof roomId !== "string" ||
session.create(sessionKey); typeof sessionId !== "string" ||
const sessionEntry = await this._writeInboundSession( typeof senderKey !== "string" ||
session, roomId, senderKey, claimedEd25519Key, sessionId, txn); typeof sessionKey !== "string"
if (sessionEntry) { ) {
newSessions.push(sessionEntry); log.logLevel = log.level.Warn;
log.set("invalid", true);
return;
} }
} finally {
session.free(); const session = new this._olm.InboundGroupSession();
} try {
session.create(sessionKey);
const sessionEntry = await this._writeInboundSession(
session, roomId, senderKey, claimedEd25519Key, sessionId, txn);
if (sessionEntry) {
newSessions.push(sessionEntry);
}
} finally {
session.free();
}
}, log.level.Detail);
} }
// this will be passed to the Room in notifyRoomKeys // this will be passed to the Room in notifyRoomKeys
return newSessions; return newSessions;

View file

@ -176,11 +176,12 @@ export class Room extends EventEmitter {
} }
async prepareSync(roomResponse, membership, txn, log) { async prepareSync(roomResponse, membership, txn, log) {
log.set("roomId", this.id); log.set("id", this.id);
const summaryChanges = this._summary.data.applySyncResponse(roomResponse, membership) const summaryChanges = this._summary.data.applySyncResponse(roomResponse, membership)
let roomEncryption = this._roomEncryption; let roomEncryption = this._roomEncryption;
// encryption is enabled in this sync // encryption is enabled in this sync
if (!roomEncryption && summaryChanges.encryption) { if (!roomEncryption && summaryChanges.encryption) {
log.set("enableEncryption", true);
roomEncryption = this._createRoomEncryption(this, summaryChanges.encryption); roomEncryption = this._createRoomEncryption(this, summaryChanges.encryption);
} }
@ -204,16 +205,19 @@ export class Room extends EventEmitter {
}; };
} }
async afterPrepareSync(preparation) { async afterPrepareSync(preparation, parentLog) {
if (preparation.decryptPreparation) { if (preparation.decryptPreparation) {
preparation.decryptChanges = await preparation.decryptPreparation.decrypt(); await parentLog.wrap("afterPrepareSync decrypt", async log => {
preparation.decryptPreparation = null; log.set("id", this.id);
preparation.decryptChanges = await preparation.decryptPreparation.decrypt();
preparation.decryptPreparation = null;
});
} }
} }
/** @package */ /** @package */
async writeSync(roomResponse, isInitialSync, {summaryChanges, decryptChanges, roomEncryption}, txn, log) { async writeSync(roomResponse, isInitialSync, {summaryChanges, decryptChanges, roomEncryption}, txn, log) {
log.set("roomId", this.id); log.set("id", this.id);
const {entries, newLiveKey, memberChanges} = const {entries, newLiveKey, memberChanges} =
await this._syncWriter.writeSync(roomResponse, txn); await this._syncWriter.writeSync(roomResponse, txn);
if (decryptChanges) { if (decryptChanges) {
@ -259,7 +263,8 @@ export class Room extends EventEmitter {
* Called with the changes returned from `writeSync` to apply them and emit changes. * Called with the changes returned from `writeSync` to apply them and emit changes.
* No storage or network operations should be done here. * No storage or network operations should be done here.
*/ */
afterSync({summaryChanges, newTimelineEntries, newLiveKey, removedPendingEvents, memberChanges, heroChanges, roomEncryption}) { afterSync({summaryChanges, newTimelineEntries, newLiveKey, removedPendingEvents, memberChanges, heroChanges, roomEncryption}, log) {
log.set("id", this.id);
this._syncWriter.afterSync(newLiveKey); this._syncWriter.afterSync(newLiveKey);
this._setEncryption(roomEncryption); this._setEncryption(roomEncryption);
if (memberChanges.size) { if (memberChanges.size) {
@ -310,9 +315,11 @@ export class Room extends EventEmitter {
* Can be used to do longer running operations that resulted from the last sync, * Can be used to do longer running operations that resulted from the last sync,
* like network operations. * like network operations.
*/ */
async afterSyncCompleted() { async afterSyncCompleted(changes, log) {
log.set("id", this.id);
if (this._roomEncryption) { if (this._roomEncryption) {
await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi); // TODO: pass log to flushPendingRoomKeyShares once we also have a logger in `start`
await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, null);
} }
} }