1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00

Add debug logs to encryption prep, take 3

This continues adding more logs to work out the root cause of
https://github.com/vector-im/element-web/issues/16194.

Somehow, we're getting stuck while sharing keys with new sessions.
This commit is contained in:
J. Ryan Stinnett
2021-02-22 18:17:05 +00:00
parent a8b9d8e3ae
commit 8d14dc9ee3
3 changed files with 65 additions and 20 deletions

View File

@@ -22,7 +22,7 @@ limitations under the License.
* @module crypto/algorithms/megolm * @module crypto/algorithms/megolm
*/ */
import {logger} from '../../logger'; import {getPrefixedLogger, logger} from '../../logger';
import * as utils from "../../utils"; import * as utils from "../../utils";
import {polyfillSuper} from "../../utils"; import {polyfillSuper} from "../../utils";
import * as olmlib from "../olmlib"; import * as olmlib from "../olmlib";
@@ -271,7 +271,7 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
logger.debug(`Shared keys with existing Olm sessions in ${this._roomId}`); logger.debug(`Shared keys with existing Olm sessions in ${this._roomId}`);
})(), })(),
(async () => { (async () => {
logger.debug(`Sharing keys with new Olm sessions in ${this._roomId}`); logger.debug(`Sharing keys (start phase 1) with new Olm sessions in ${this._roomId}`);
const errorDevices = []; const errorDevices = [];
// meanwhile, establish olm sessions for devices that we don't // meanwhile, establish olm sessions for devices that we don't
@@ -285,6 +285,7 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
session, key, payload, devicesWithoutSession, errorDevices, session, key, payload, devicesWithoutSession, errorDevices,
singleOlmCreationPhase ? 10000 : 2000, failedServers, singleOlmCreationPhase ? 10000 : 2000, failedServers,
); );
logger.debug(`Shared keys (end phase 1) with new Olm sessions in ${this._roomId}`);
if (!singleOlmCreationPhase && (Date.now() - start < 10000)) { if (!singleOlmCreationPhase && (Date.now() - start < 10000)) {
// perform the second phase of olm session creation if requested, // perform the second phase of olm session creation if requested,
@@ -313,21 +314,24 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
} }
} }
logger.debug(`Sharing keys (start phase 2) with new Olm sessions in ${this._roomId}`);
await this._shareKeyWithDevices( await this._shareKeyWithDevices(
session, key, payload, retryDevices, failedDevices, 30000, session, key, payload, retryDevices, failedDevices, 30000,
); );
logger.debug(`Shared keys (end phase 2) with new Olm sessions in ${this._roomId}`);
await this._notifyFailedOlmDevices(session, key, failedDevices); await this._notifyFailedOlmDevices(session, key, failedDevices);
})(); })();
} else { } else {
await this._notifyFailedOlmDevices(session, key, errorDevices); await this._notifyFailedOlmDevices(session, key, errorDevices);
} }
logger.debug(`Shared keys with new Olm sessions in ${this._roomId}`); logger.debug(`Shared keys (all phases done) with new Olm sessions in ${this._roomId}`);
})(), })(),
(async () => { (async () => {
logger.debug(`Notifying blocked devices in ${this._roomId}`); logger.debug(`Notifying blocked devices in ${this._roomId}`);
// also, notify blocked devices that they're blocked // also, notify blocked devices that they're blocked
const blockedMap = {}; const blockedMap = {};
let blockedCount = 0;
for (const [userId, userBlockedDevices] of Object.entries(blocked)) { for (const [userId, userBlockedDevices] of Object.entries(blocked)) {
for (const [deviceId, device] of Object.entries(userBlockedDevices)) { for (const [deviceId, device] of Object.entries(userBlockedDevices)) {
if ( if (
@@ -335,13 +339,14 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
session.blockedDevicesNotified[userId][deviceId] === undefined session.blockedDevicesNotified[userId][deviceId] === undefined
) { ) {
blockedMap[userId] = blockedMap[userId] || {}; blockedMap[userId] = blockedMap[userId] || {};
blockedMap[userId][deviceId] = {device}; blockedMap[userId][deviceId] = { device };
blockedCount++;
} }
} }
} }
await this._notifyBlockedDevices(session, blockedMap); await this._notifyBlockedDevices(session, blockedMap);
logger.debug(`Notified blocked devices in ${this._roomId}`); logger.debug(`Notified ${blockedCount} blocked devices in ${this._roomId}`);
})(), })(),
]); ]);
}; };
@@ -728,13 +733,18 @@ MegolmEncryption.prototype.reshareKeyWithDevice = async function(
MegolmEncryption.prototype._shareKeyWithDevices = async function( MegolmEncryption.prototype._shareKeyWithDevices = async function(
session, key, payload, devicesByUser, errorDevices, otkTimeout, failedServers, session, key, payload, devicesByUser, errorDevices, otkTimeout, failedServers,
) { ) {
logger.debug(`Ensuring Olm sessions for devices in ${this._roomId}`);
const devicemap = await olmlib.ensureOlmSessionsForDevices( const devicemap = await olmlib.ensureOlmSessionsForDevices(
this._olmDevice, this._baseApis, devicesByUser, otkTimeout, failedServers, this._olmDevice, this._baseApis, devicesByUser, otkTimeout, failedServers,
getPrefixedLogger(`[${this._roomId}]`),
); );
logger.debug(`Ensured Olm sessions for devices in ${this._roomId}`);
this._getDevicesWithoutSessions(devicemap, devicesByUser, errorDevices); this._getDevicesWithoutSessions(devicemap, devicesByUser, errorDevices);
logger.debug(`Sharing keys with Olm sessions in ${this._roomId}`);
await this._shareKeyWithOlmSessions(session, key, payload, devicemap); await this._shareKeyWithOlmSessions(session, key, payload, devicemap);
logger.debug(`Shared keys with Olm sessions in ${this._roomId}`);
}; };
MegolmEncryption.prototype._shareKeyWithOlmSessions = async function( MegolmEncryption.prototype._shareKeyWithOlmSessions = async function(
@@ -772,6 +782,11 @@ MegolmEncryption.prototype._shareKeyWithOlmSessions = async function(
MegolmEncryption.prototype._notifyFailedOlmDevices = async function( MegolmEncryption.prototype._notifyFailedOlmDevices = async function(
session, key, failedDevices, session, key, failedDevices,
) { ) {
logger.debug(
`Notifying ${failedDevices.length} devices we failed to ` +
`create Olm sessions in ${this._roomId}`,
);
// mark the devices that failed as "handled" because we don't want to try // mark the devices that failed as "handled" because we don't want to try
// to claim a one-time-key for dead devices on every message. // to claim a one-time-key for dead devices on every message.
for (const {userId, deviceInfo} of failedDevices) { for (const {userId, deviceInfo} of failedDevices) {
@@ -786,6 +801,10 @@ MegolmEncryption.prototype._notifyFailedOlmDevices = async function(
await this._olmDevice.filterOutNotifiedErrorDevices( await this._olmDevice.filterOutNotifiedErrorDevices(
failedDevices, failedDevices,
); );
logger.debug(
`Filtered down to ${filteredFailedDevices.length} error devices ` +
`in ${this._roomId}`,
);
const blockedMap = {}; const blockedMap = {};
for (const {userId, deviceInfo} of filteredFailedDevices) { for (const {userId, deviceInfo} of filteredFailedDevices) {
blockedMap[userId] = blockedMap[userId] || {}; blockedMap[userId] = blockedMap[userId] || {};
@@ -803,6 +822,10 @@ MegolmEncryption.prototype._notifyFailedOlmDevices = async function(
// send the notifications // send the notifications
await this._notifyBlockedDevices(session, blockedMap); await this._notifyBlockedDevices(session, blockedMap);
logger.debug(
`Notified ${filteredFailedDevices.length} devices we failed to ` +
`create Olm sessions in ${this._roomId}`,
);
}; };
/** /**

View File

@@ -183,18 +183,24 @@ export async function getExistingOlmSessions(
* @param {Array} [failedServers] An array to fill with remote servers that * @param {Array} [failedServers] An array to fill with remote servers that
* failed to respond to one-time-key requests. * failed to respond to one-time-key requests.
* *
* @param {Object} [log] A possibly customised log
*
* @return {Promise} resolves once the sessions are complete, to * @return {Promise} resolves once the sessions are complete, to
* an Object mapping from userId to deviceId to * an Object mapping from userId to deviceId to
* {@link module:crypto~OlmSessionResult} * {@link module:crypto~OlmSessionResult}
*/ */
export async function ensureOlmSessionsForDevices( export async function ensureOlmSessionsForDevices(
olmDevice, baseApis, devicesByUser, force, otkTimeout, failedServers, olmDevice, baseApis, devicesByUser, force, otkTimeout, failedServers, log,
) { ) {
if (typeof force === "number") { if (typeof force === "number") {
log = failedServers;
failedServers = otkTimeout; failedServers = otkTimeout;
otkTimeout = force; otkTimeout = force;
force = false; force = false;
} }
if (!log) {
log = logger;
}
const devicesWithoutSession = [ const devicesWithoutSession = [
// [userId, deviceId], ... // [userId, deviceId], ...
@@ -216,7 +222,7 @@ export async function ensureOlmSessionsForDevices(
// new chain when this side has an active sender chain. // new chain when this side has an active sender chain.
// If you see this message being logged in the wild, we should find // If you see this message being logged in the wild, we should find
// the thing that is trying to send Olm messages to itself and fix it. // the thing that is trying to send Olm messages to itself and fix it.
logger.info("Attempted to start session with ourself! Ignoring"); log.info("Attempted to start session with ourself! Ignoring");
// We must fill in the section in the return value though, as callers // We must fill in the section in the return value though, as callers
// expect it to be there. // expect it to be there.
result[userId][deviceId] = { result[userId][deviceId] = {
@@ -258,9 +264,9 @@ export async function ensureOlmSessionsForDevices(
} }
if (sessionId === null || force) { if (sessionId === null || force) {
if (force) { if (force) {
logger.info("Forcing new Olm session for " + userId + ":" + deviceId); log.info(`Forcing new Olm session for ${userId}:${deviceId}`);
} else { } else {
logger.info("Making new Olm session for " + userId + ":" + deviceId); log.info(`Making new Olm session for ${userId}:${deviceId}`);
} }
devicesWithoutSession.push([userId, deviceId]); devicesWithoutSession.push([userId, deviceId]);
} }
@@ -284,19 +290,19 @@ export async function ensureOlmSessionsForDevices(
// cause we're seeing in practice. // cause we're seeing in practice.
// See also https://github.com/vector-im/element-web/issues/16194 // See also https://github.com/vector-im/element-web/issues/16194
const otkTimeoutLogger = setTimeout(() => { const otkTimeoutLogger = setTimeout(() => {
logger.error(`Homeserver never replied while claiming ${taskDetail}`); log.error(`Homeserver never replied while claiming ${taskDetail}`);
}, otkTimeout); }, otkTimeout);
try { try {
logger.debug(`Claiming ${taskDetail}`); log.debug(`Claiming ${taskDetail}`);
res = await baseApis.claimOneTimeKeys( res = await baseApis.claimOneTimeKeys(
devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout, devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout,
); );
logger.debug(`Claimed ${taskDetail}`); log.debug(`Claimed ${taskDetail}`);
} catch (e) { } catch (e) {
for (const resolver of Object.values(resolveSession)) { for (const resolver of Object.values(resolveSession)) {
resolver.resolve(); resolver.resolve();
} }
logger.log(`Failed to claim ${taskDetail}`, e, devicesWithoutSession); log.log(`Failed to claim ${taskDetail}`, e, devicesWithoutSession);
throw e; throw e;
} finally { } finally {
clearTimeout(otkTimeoutLogger); clearTimeout(otkTimeoutLogger);
@@ -336,9 +342,10 @@ export async function ensureOlmSessionsForDevices(
} }
if (!oneTimeKey) { if (!oneTimeKey) {
const msg = "No one-time keys (alg=" + oneTimeKeyAlgorithm + log.warn(
") for device " + userId + ":" + deviceId; `No one-time keys (alg=${oneTimeKeyAlgorithm}) ` +
logger.warn(msg); `for device ${userId}:${deviceId}`,
);
if (resolveSession[key]) { if (resolveSession[key]) {
resolveSession[key].resolve(); resolveSession[key].resolve();
} }
@@ -364,9 +371,9 @@ export async function ensureOlmSessionsForDevices(
} }
taskDetail = `Olm sessions for ${promises.length} devices`; taskDetail = `Olm sessions for ${promises.length} devices`;
logger.debug(`Starting ${taskDetail}`); log.debug(`Starting ${taskDetail}`);
await Promise.all(promises); await Promise.all(promises);
logger.debug(`Started ${taskDetail}`); log.debug(`Started ${taskDetail}`);
return result; return result;
} }

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2018 André Jaenisch Copyright 2018 André Jaenisch
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ limitations under the License.
* @module logger * @module logger
*/ */
import log from "loglevel"; import log, { Logger } from "loglevel";
// This is to demonstrate, that you can use any namespace you want. // This is to demonstrate, that you can use any namespace you want.
// Namespaces allow you to turn on/off the logging for specific parts of the // Namespaces allow you to turn on/off the logging for specific parts of the
@@ -36,6 +36,11 @@ const DEFAULT_NAMESPACE = "matrix";
// when logging so we always get the current value of console methods. // when logging so we always get the current value of console methods.
log.methodFactory = function(methodName, logLevel, loggerName) { log.methodFactory = function(methodName, logLevel, loggerName) {
return function(...args) { return function(...args) {
/* eslint-disable babel/no-invalid-this */
if (this.prefix) {
args.unshift(this.prefix);
}
/* eslint-enable babel/no-invalid-this */
const supportedByConsole = methodName === "error" || const supportedByConsole = methodName === "error" ||
methodName === "warn" || methodName === "warn" ||
methodName === "trace" || methodName === "trace" ||
@@ -57,3 +62,13 @@ log.methodFactory = function(methodName, logLevel, loggerName) {
export const logger = log.getLogger(DEFAULT_NAMESPACE); export const logger = log.getLogger(DEFAULT_NAMESPACE);
logger.setLevel(log.levels.DEBUG); logger.setLevel(log.levels.DEBUG);
interface PrefixedLogger extends Logger {
prefix?: any;
}
export function getPrefixedLogger(prefix): PrefixedLogger {
const prefixLogger: PrefixedLogger = log.getLogger(`${DEFAULT_NAMESPACE}-${prefix}`);
prefixLogger.prefix = prefix;
prefixLogger.setLevel(log.levels.DEBUG);
return prefixLogger;
}