open storage transactions synchronously

this (almost) makes it work in some browsers that otherwise
have throw a TransactionInactiveError on the first operation
you try to do on a store.
This commit is contained in:
Bruno Windels 2020-09-25 16:42:41 +02:00
parent 7d271bb35e
commit 561879dc6b
16 changed files with 48 additions and 48 deletions

View file

@ -80,7 +80,7 @@ export class DeviceMessageHandler {
if (!this._olmDecryption) { if (!this._olmDecryption) {
return; return;
} }
const readTxn = await 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);
if (pendingEvents.length === 0) { if (pendingEvents.length === 0) {
return; return;
@ -91,7 +91,7 @@ export class DeviceMessageHandler {
for (const err of decryptChanges.errors) { for (const err of decryptChanges.errors) {
console.warn("decryption failed for event", err, err.event); console.warn("decryption failed for event", err, err.event);
} }
const txn = await 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
this._storage.storeNames.session, this._storage.storeNames.session,
this._storage.storeNames.olmSessions, this._storage.storeNames.olmSessions,

View file

@ -164,13 +164,13 @@ export class Session {
} }
const key = await ssssKeyFromCredential(type, credential, this._storage, this._cryptoDriver, this._olm); const key = await ssssKeyFromCredential(type, credential, this._storage, this._cryptoDriver, this._olm);
// and create session backup, which needs to read from accountData // and create session backup, which needs to read from accountData
const readTxn = await this._storage.readTxn([ const readTxn = this._storage.readTxn([
this._storage.storeNames.accountData, this._storage.storeNames.accountData,
]); ]);
await this._createSessionBackup(key, readTxn); await this._createSessionBackup(key, readTxn);
// only after having read a secret, write the key // only after having read a secret, write the key
// as we only find out if it was good if the MAC verification succeeds // as we only find out if it was good if the MAC verification succeeds
const writeTxn = await this._storage.readWriteTxn([ const writeTxn = this._storage.readWriteTxn([
this._storage.storeNames.session, this._storage.storeNames.session,
]); ]);
try { try {
@ -217,7 +217,7 @@ export class Session {
await this._e2eeAccount.uploadKeys(this._storage); await this._e2eeAccount.uploadKeys(this._storage);
await this._deviceMessageHandler.decryptPending(this.rooms); await this._deviceMessageHandler.decryptPending(this.rooms);
const txn = await this._storage.readTxn([ const txn = this._storage.readTxn([
this._storage.storeNames.session, this._storage.storeNames.session,
this._storage.storeNames.accountData, this._storage.storeNames.accountData,
]); ]);
@ -231,7 +231,7 @@ export class Session {
} }
async load() { async load() {
const txn = await this._storage.readTxn([ const txn = this._storage.readTxn([
this._storage.storeNames.session, this._storage.storeNames.session,
this._storage.storeNames.roomSummary, this._storage.storeNames.roomSummary,
this._storage.storeNames.roomMembers, this._storage.storeNames.roomMembers,
@ -276,7 +276,7 @@ export class Session {
async start(lastVersionResponse) { async start(lastVersionResponse) {
if (lastVersionResponse) { if (lastVersionResponse) {
// store /versions response // store /versions response
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.session this._storage.storeNames.session
]); ]);
txn.session.set("serverVersions", lastVersionResponse); txn.session.set("serverVersions", lastVersionResponse);
@ -284,7 +284,7 @@ export class Session {
await txn.complete(); await txn.complete();
} }
const opsTxn = await this._storage.readWriteTxn([ const opsTxn = this._storage.readWriteTxn([
this._storage.storeNames.operations this._storage.storeNames.operations
]); ]);
const operations = await opsTxn.operations.getAll(); const operations = await opsTxn.operations.getAll();

View file

@ -184,7 +184,7 @@ export class Sync {
const roomStates = this._parseRoomsResponse(response.rooms, isInitialSync); const roomStates = this._parseRoomsResponse(response.rooms, isInitialSync);
await this._prepareRooms(roomStates); await this._prepareRooms(roomStates);
let sessionChanges; let sessionChanges;
const syncTxn = await this._openSyncTxn(); const syncTxn = this._openSyncTxn();
try { try {
await Promise.all(roomStates.map(async rs => { await Promise.all(roomStates.map(async rs => {
console.log(` * applying sync response to room ${rs.room.id} ...`); console.log(` * applying sync response to room ${rs.room.id} ...`);
@ -221,24 +221,24 @@ export class Sync {
}; };
} }
async _openPrepareSyncTxn() { _openPrepareSyncTxn() {
const storeNames = this._storage.storeNames; const storeNames = this._storage.storeNames;
return await this._storage.readTxn([ return this._storage.readTxn([
storeNames.inboundGroupSessions, storeNames.inboundGroupSessions,
]); ]);
} }
async _prepareRooms(roomStates) { async _prepareRooms(roomStates) {
const prepareTxn = await this._openPrepareSyncTxn(); const prepareTxn = this._openPrepareSyncTxn();
await Promise.all(roomStates.map(async rs => { await Promise.all(roomStates.map(async rs => {
rs.preparation = await rs.room.prepareSync(rs.roomResponse, rs.membership, prepareTxn); rs.preparation = await rs.room.prepareSync(rs.roomResponse, rs.membership, prepareTxn);
})); }));
await Promise.all(roomStates.map(rs => rs.room.afterPrepareSync(rs.preparation))); await Promise.all(roomStates.map(rs => rs.room.afterPrepareSync(rs.preparation)));
} }
async _openSyncTxn() { _openSyncTxn() {
const storeNames = this._storage.storeNames; const storeNames = this._storage.storeNames;
return await this._storage.readWriteTxn([ return this._storage.readWriteTxn([
storeNames.session, storeNames.session,
storeNames.roomSummary, storeNames.roomSummary,
storeNames.roomState, storeNames.roomState,

View file

@ -45,7 +45,7 @@ export class Account {
} }
const pickledAccount = account.pickle(pickleKey); const pickledAccount = account.pickle(pickleKey);
const areDeviceKeysUploaded = false; const areDeviceKeysUploaded = false;
const txn = await storage.readWriteTxn([ const txn = storage.readWriteTxn([
storage.storeNames.session storage.storeNames.session
]); ]);
try { try {
@ -212,7 +212,7 @@ export class Account {
} }
async _updateSessionStorage(storage, callback) { async _updateSessionStorage(storage, callback) {
const txn = await storage.readWriteTxn([ const txn = storage.readWriteTxn([
storage.storeNames.session storage.storeNames.session
]); ]);
try { try {

View file

@ -68,7 +68,7 @@ export class DeviceTracker {
} }
const memberList = await room.loadMemberList(); const memberList = await room.loadMemberList();
try { try {
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.roomSummary, this._storage.storeNames.roomSummary,
this._storage.storeNames.userIdentities, this._storage.storeNames.userIdentities,
]); ]);
@ -149,7 +149,7 @@ export class DeviceTracker {
}).response(); }).response();
const verifiedKeysPerUser = this._filterVerifiedDeviceKeys(deviceKeyResponse["device_keys"]); const verifiedKeysPerUser = this._filterVerifiedDeviceKeys(deviceKeyResponse["device_keys"]);
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.userIdentities, this._storage.storeNames.userIdentities,
this._storage.storeNames.deviceIdentities, this._storage.storeNames.deviceIdentities,
]); ]);
@ -252,7 +252,7 @@ export class DeviceTracker {
* @return {[type]} [description] * @return {[type]} [description]
*/ */
async devicesForTrackedRoom(roomId, hsApi) { async devicesForTrackedRoom(roomId, hsApi) {
const txn = await this._storage.readTxn([ const txn = this._storage.readTxn([
this._storage.storeNames.roomMembers, this._storage.storeNames.roomMembers,
this._storage.storeNames.userIdentities, this._storage.storeNames.userIdentities,
]); ]);
@ -268,7 +268,7 @@ export class DeviceTracker {
} }
async devicesForRoomMembers(roomId, userIds, hsApi) { async devicesForRoomMembers(roomId, userIds, hsApi) {
const txn = await this._storage.readTxn([ const txn = this._storage.readTxn([
this._storage.storeNames.userIdentities, this._storage.storeNames.userIdentities,
]); ]);
return await this._devicesForUserIds(roomId, userIds, txn, hsApi); return await this._devicesForUserIds(roomId, userIds, txn, hsApi);
@ -298,7 +298,7 @@ export class DeviceTracker {
queriedDevices = await this._queryKeys(outdatedIdentities.map(i => i.userId), hsApi); queriedDevices = await this._queryKeys(outdatedIdentities.map(i => i.userId), hsApi);
} }
const deviceTxn = await this._storage.readTxn([ const deviceTxn = this._storage.readTxn([
this._storage.storeNames.deviceIdentities, this._storage.storeNames.deviceIdentities,
]); ]);
const devicesPerUser = await Promise.all(upToDateIdentities.map(identity => { const devicesPerUser = await Promise.all(upToDateIdentities.map(identity => {

View file

@ -183,7 +183,7 @@ export class RoomEncryption {
console.warn("Got session key back from backup with different sender key, ignoring", {session, senderKey}); console.warn("Got session key back from backup with different sender key, ignoring", {session, senderKey});
return; return;
} }
const txn = await this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]); const txn = this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]);
let roomKey; let roomKey;
try { try {
roomKey = await this._megolmDecryption.addRoomKeyFromBackup( roomKey = await this._megolmDecryption.addRoomKeyFromBackup(
@ -273,7 +273,7 @@ export class RoomEncryption {
const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set())); const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set()));
// store operation for room key share, in case we don't finish here // store operation for room key share, in case we don't finish here
const writeOpTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]); const writeOpTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
let operationId; let operationId;
try { try {
operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn); operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn);
@ -290,7 +290,7 @@ export class RoomEncryption {
await this._sendRoomKey(roomKeyMessage, devices, hsApi); await this._sendRoomKey(roomKeyMessage, devices, hsApi);
// remove the operation // remove the operation
const removeOpTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]); const removeOpTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
try { try {
removeOpTxn.operations.remove(operationId); removeOpTxn.operations.remove(operationId);
} catch (err) { } catch (err) {
@ -329,7 +329,7 @@ export class RoomEncryption {
this._isFlushingRoomKeyShares = true; this._isFlushingRoomKeyShares = true;
try { try {
if (!operations) { if (!operations) {
const txn = await this._storage.readTxn([this._storage.storeNames.operations]); const txn = this._storage.readTxn([this._storage.storeNames.operations]);
operations = await txn.operations.getAllByTypeAndScope("share_room_key", this._room.id); operations = await txn.operations.getAllByTypeAndScope("share_room_key", this._room.id);
} }
for (const operation of operations) { for (const operation of operations) {
@ -339,7 +339,7 @@ export class RoomEncryption {
} }
const devices = await this._deviceTracker.devicesForRoomMembers(this._room.id, operation.userIds, hsApi); const devices = await this._deviceTracker.devicesForRoomMembers(this._room.id, operation.userIds, hsApi);
await this._sendRoomKey(operation.roomKeyMessage, devices, hsApi); await this._sendRoomKey(operation.roomKeyMessage, devices, hsApi);
const removeTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]); const removeTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
try { try {
removeTxn.operations.remove(operation.id); removeTxn.operations.remove(operation.id);
} catch (err) { } catch (err) {

View file

@ -54,7 +54,7 @@ export class Encryption {
async encrypt(roomId, type, content, encryptionParams) { async encrypt(roomId, type, content, encryptionParams) {
let session = new this._olm.OutboundGroupSession(); let session = new this._olm.OutboundGroupSession();
try { try {
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.inboundGroupSessions, this._storage.storeNames.inboundGroupSessions,
this._storage.storeNames.outboundGroupSessions, this._storage.storeNames.outboundGroupSessions,
]); ]);

View file

@ -67,7 +67,7 @@ export class Decryption {
return this._senderKeyLock.takeLock(senderKey); return this._senderKeyLock.takeLock(senderKey);
})); }));
try { try {
const readSessionsTxn = await this._storage.readTxn([this._storage.storeNames.olmSessions]); const readSessionsTxn = this._storage.readTxn([this._storage.storeNames.olmSessions]);
// decrypt events for different sender keys in parallel // decrypt events for different sender keys in parallel
const senderKeyOperations = await Promise.all(Array.from(eventsPerSenderKey.entries()).map(([senderKey, events]) => { const senderKeyOperations = await Promise.all(Array.from(eventsPerSenderKey.entries()).map(([senderKey, events]) => {
return this._decryptAllForSenderKey(senderKey, events, timestamp, readSessionsTxn); return this._decryptAllForSenderKey(senderKey, events, timestamp, readSessionsTxn);

View file

@ -98,7 +98,7 @@ export class Encryption {
} }
async _findExistingSessions(devices) { async _findExistingSessions(devices) {
const txn = await this._storage.readTxn([this._storage.storeNames.olmSessions]); const txn = this._storage.readTxn([this._storage.storeNames.olmSessions]);
const sessionIdsForDevice = await Promise.all(devices.map(async device => { const sessionIdsForDevice = await Promise.all(devices.map(async device => {
return await txn.olmSessions.getSessionIds(device.curve25519Key); return await txn.olmSessions.getSessionIds(device.curve25519Key);
})); }));
@ -213,7 +213,7 @@ export class Encryption {
} }
async _loadSessions(encryptionTargets) { async _loadSessions(encryptionTargets) {
const txn = await this._storage.readTxn([this._storage.storeNames.olmSessions]); const txn = this._storage.readTxn([this._storage.storeNames.olmSessions]);
// given we run loading in parallel, there might still be some // given we run loading in parallel, there might still be some
// storage requests that will finish later once one has failed. // storage requests that will finish later once one has failed.
// those should not allocate a session anymore. // those should not allocate a session anymore.
@ -239,7 +239,7 @@ export class Encryption {
} }
async _storeSessions(encryptionTargets, timestamp) { async _storeSessions(encryptionTargets, timestamp) {
const txn = await this._storage.readWriteTxn([this._storage.storeNames.olmSessions]); const txn = this._storage.readWriteTxn([this._storage.storeNames.olmSessions]);
try { try {
for (const target of encryptionTargets) { for (const target of encryptionTargets) {
const sessionEntry = createSessionEntry( const sessionEntry = createSessionEntry(

View file

@ -82,7 +82,7 @@ export class Room extends EventEmitter {
let retryEntries; let retryEntries;
if (retryEventIds) { if (retryEventIds) {
retryEntries = []; retryEntries = [];
txn = await this._storage.readTxn(stores); txn = this._storage.readTxn(stores);
for (const eventId of retryEventIds) { for (const eventId of retryEventIds) {
const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId); const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId);
if (storageEntry) { if (storageEntry) {
@ -99,7 +99,7 @@ export class Room extends EventEmitter {
// check we have not already decrypted the most recent event in the room // check we have not already decrypted the most recent event in the room
// otherwise we know that the messages for this room key will not update the room summary // otherwise we know that the messages for this room key will not update the room summary
if (!sinceEventKey || !sinceEventKey.equals(this._syncWriter.lastMessageKey)) { if (!sinceEventKey || !sinceEventKey.equals(this._syncWriter.lastMessageKey)) {
txn = await this._storage.readTxn(stores.concat(this._storage.storeNames.timelineFragments)); txn = this._storage.readTxn(stores.concat(this._storage.storeNames.timelineFragments));
const candidateEntries = await this._readRetryDecryptCandidateEntries(sinceEventKey, txn); const candidateEntries = await this._readRetryDecryptCandidateEntries(sinceEventKey, txn);
retryEntries = this._roomEncryption.findAndCacheEntriesForRoomKey(roomKey, candidateEntries); retryEntries = this._roomEncryption.findAndCacheEntriesForRoomKey(roomKey, candidateEntries);
} }
@ -138,7 +138,7 @@ export class Room extends EventEmitter {
_decryptEntries(source, entries, inboundSessionTxn = null) { _decryptEntries(source, entries, inboundSessionTxn = null) {
const request = new DecryptionRequest(async r => { const request = new DecryptionRequest(async r => {
if (!inboundSessionTxn) { if (!inboundSessionTxn) {
inboundSessionTxn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]); inboundSessionTxn = this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
} }
if (r.cancelled) return; if (r.cancelled) return;
const events = entries.filter(entry => { const events = entries.filter(entry => {
@ -155,7 +155,7 @@ export class Room extends EventEmitter {
// read to fetch devices if timeline is open // read to fetch devices if timeline is open
stores.push(this._storage.storeNames.deviceIdentities); stores.push(this._storage.storeNames.deviceIdentities);
} }
const writeTxn = await this._storage.readWriteTxn(stores); const writeTxn = this._storage.readWriteTxn(stores);
let decryption; let decryption;
try { try {
decryption = await changes.write(writeTxn); decryption = await changes.write(writeTxn);
@ -387,7 +387,7 @@ export class Room extends EventEmitter {
} }
}).response(); }).response();
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.pendingEvents, this._storage.storeNames.pendingEvents,
this._storage.storeNames.timelineEvents, this._storage.storeNames.timelineEvents,
this._storage.storeNames.timelineFragments, this._storage.storeNames.timelineFragments,
@ -490,7 +490,7 @@ export class Room extends EventEmitter {
async _getLastEventId() { async _getLastEventId() {
const lastKey = this._syncWriter.lastMessageKey; const lastKey = this._syncWriter.lastMessageKey;
if (lastKey) { if (lastKey) {
const txn = await this._storage.readTxn([ const txn = this._storage.readTxn([
this._storage.storeNames.timelineEvents, this._storage.storeNames.timelineEvents,
]); ]);
const eventEntry = await txn.timelineEvents.get(this._roomId, lastKey); const eventEntry = await txn.timelineEvents.get(this._roomId, lastKey);
@ -511,7 +511,7 @@ export class Room extends EventEmitter {
async clearUnread() { async clearUnread() {
if (this.isUnread || this.notificationCount) { if (this.isUnread || this.notificationCount) {
const txn = await this._storage.readWriteTxn([ const txn = this._storage.readWriteTxn([
this._storage.storeNames.roomSummary, this._storage.storeNames.roomSummary,
]); ]);
let data; let data;

View file

@ -272,7 +272,7 @@ export class RoomSummary {
if (data === this._data) { if (data === this._data) {
return false; return false;
} }
const txn = await storage.readWriteTxn([ const txn = storage.readWriteTxn([
storage.storeNames.roomSummary, storage.storeNames.roomSummary,
]); ]);
try { try {

View file

@ -18,7 +18,7 @@ limitations under the License.
import {RoomMember} from "./RoomMember.js"; import {RoomMember} from "./RoomMember.js";
async function loadMembers({roomId, storage}) { async function loadMembers({roomId, storage}) {
const txn = await storage.readTxn([ const txn = storage.readTxn([
storage.storeNames.roomMembers, storage.storeNames.roomMembers,
]); ]);
const memberDatas = await txn.roomMembers.getAll(roomId); const memberDatas = await txn.roomMembers.getAll(roomId);
@ -33,7 +33,7 @@ async function fetchMembers({summary, syncToken, roomId, hsApi, storage, setChan
const memberResponse = await hsApi.members(roomId, {at: syncToken}).response(); const memberResponse = await hsApi.members(roomId, {at: syncToken}).response();
const txn = await storage.readWriteTxn([ const txn = storage.readWriteTxn([
storage.storeNames.roomSummary, storage.storeNames.roomSummary,
storage.storeNames.roomMembers, storage.storeNames.roomMembers,
]); ]);

View file

@ -130,7 +130,7 @@ export class SendQueue {
} }
async _tryUpdateEvent(pendingEvent) { async _tryUpdateEvent(pendingEvent) {
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]); const txn = this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
console.log("_tryUpdateEvent: got txn"); console.log("_tryUpdateEvent: got txn");
try { try {
// pendingEvent might have been removed already here // pendingEvent might have been removed already here
@ -152,7 +152,7 @@ export class SendQueue {
async _createAndStoreEvent(eventType, content) { async _createAndStoreEvent(eventType, content) {
console.log("_createAndStoreEvent"); console.log("_createAndStoreEvent");
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]); const txn = this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
let pendingEvent; let pendingEvent;
try { try {
const pendingEventsStore = txn.pendingEvents; const pendingEventsStore = txn.pendingEvents;

View file

@ -108,14 +108,14 @@ export class TimelineReader {
readFrom(eventKey, direction, amount) { readFrom(eventKey, direction, amount) {
return new ReaderRequest(async r => { return new ReaderRequest(async r => {
const txn = await this._openTxn(); const txn = this._openTxn();
return await this._readFrom(eventKey, direction, amount, r, txn); return await this._readFrom(eventKey, direction, amount, r, txn);
}); });
} }
readFromEnd(amount) { readFromEnd(amount) {
return new ReaderRequest(async r => { return new ReaderRequest(async r => {
const txn = await this._openTxn(); const txn = this._openTxn();
const liveFragment = await txn.timelineFragments.liveFragment(this._roomId); const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);
let entries; let entries;
// room hasn't been synced yet // room hasn't been synced yet

View file

@ -19,7 +19,7 @@ import {keyFromPassphrase} from "./passphrase.js";
import {keyFromRecoveryKey} from "./recoveryKey.js"; import {keyFromRecoveryKey} from "./recoveryKey.js";
async function readDefaultKeyDescription(storage) { async function readDefaultKeyDescription(storage) {
const txn = await storage.readTxn([ const txn = storage.readTxn([
storage.storeNames.accountData storage.storeNames.accountData
]); ]);
const defaultKeyEvent = await txn.accountData.get("m.secret_storage.default_key"); const defaultKeyEvent = await txn.accountData.get("m.secret_storage.default_key");

View file

@ -34,7 +34,7 @@ export class Storage {
} }
} }
async readTxn(storeNames) { readTxn(storeNames) {
this._validateStoreNames(storeNames); this._validateStoreNames(storeNames);
try { try {
const txn = this._db.transaction(storeNames, "readonly"); const txn = this._db.transaction(storeNames, "readonly");
@ -44,7 +44,7 @@ export class Storage {
} }
} }
async readWriteTxn(storeNames) { readWriteTxn(storeNames) {
this._validateStoreNames(storeNames); this._validateStoreNames(storeNames);
try { try {
const txn = this._db.transaction(storeNames, "readwrite"); const txn = this._db.transaction(storeNames, "readwrite");