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

Merge pull request #711 from matrix-org/bwindels/clearidbmembersonleave

Remove lazy loaded members when leaving room
This commit is contained in:
Bruno Windels
2018-09-04 16:53:56 +02:00
committed by GitHub
8 changed files with 114 additions and 8 deletions

View File

@@ -443,6 +443,22 @@ RoomState.prototype.markOutOfBandMembersFailed = function() {
this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED; this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED;
}; };
/**
* Clears the loaded out-of-band members
*/
RoomState.prototype.clearOutOfBandMembers = function() {
let count = 0;
Object.keys(this.members).forEach((userId) => {
const member = this.members[userId];
if (member.isOutOfBand()) {
++count;
delete this.members[userId];
}
});
console.log(`LL: RoomState removed ${count} members...`);
this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED;
};
/** /**
* Sets the loaded out-of-band members. * Sets the loaded out-of-band members.
* @param {MatrixEvent[]} stateEvents array of membership state events * @param {MatrixEvent[]} stateEvents array of membership state events

View File

@@ -456,6 +456,30 @@ Room.prototype.loadMembersIfNeeded = function() {
return this._membersPromise; return this._membersPromise;
}; };
/**
* Removes the lazily loaded members from storage if needed
*/
Room.prototype.clearLoadedMembersIfNeeded = async function() {
if (this._opts.lazyLoadMembers && this._membersPromise) {
await this.loadMembersIfNeeded();
await this._client.store.clearOutOfBandMembers(this.roomId);
this.currentState.clearOutOfBandMembers();
this._membersPromise = null;
}
};
/**
* called when sync receives this room in the leave section
* to do cleanup after leaving a room. Possibly called multiple times.
*/
Room.prototype.onLeft = function() {
this.clearLoadedMembersIfNeeded().catch((err) => {
console.error(`error after clearing loaded members from ` +
`room ${this.roomId} after leaving`);
console.dir(err);
});
};
/** /**
* Reset the live timeline of all timelineSets, and start new ones. * Reset the live timeline of all timelineSets, and start new ones.
* *

View File

@@ -71,7 +71,7 @@ function selectQuery(store, keyRange, resultMapper) {
}); });
} }
function promiseifyTxn(txn) { function txnAsPromise(txn) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
txn.oncomplete = function(event) { txn.oncomplete = function(event) {
resolve(event); resolve(event);
@@ -82,7 +82,7 @@ function promiseifyTxn(txn) {
}); });
} }
function promiseifyRequest(req) { function reqAsEventPromise(req) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
req.onsuccess = function(event) { req.onsuccess = function(event) {
resolve(event); resolve(event);
@@ -93,6 +93,17 @@ function promiseifyRequest(req) {
}); });
} }
function reqAsPromise(req) {
return new Promise((resolve, reject) => {
req.onsuccess = () => resolve(req);
req.onerror = (err) => reject(err);
});
}
function reqAsCursorPromise(req) {
return reqAsEventPromise(req).then((event) => event.target.result);
}
/** /**
* Does the actual reading from and writing to the indexeddb * Does the actual reading from and writing to the indexeddb
* *
@@ -159,7 +170,7 @@ LocalIndexedDBStoreBackend.prototype = {
console.log( console.log(
`LocalIndexedDBStoreBackend.connect: awaiting connection...`, `LocalIndexedDBStoreBackend.connect: awaiting connection...`,
); );
return promiseifyRequest(req).then((ev) => { return reqAsEventPromise(req).then((ev) => {
console.log( console.log(
`LocalIndexedDBStoreBackend.connect: connected`, `LocalIndexedDBStoreBackend.connect: connected`,
); );
@@ -265,7 +276,7 @@ LocalIndexedDBStoreBackend.prototype = {
const tx = this.db.transaction(["oob_membership_events"], "readwrite"); const tx = this.db.transaction(["oob_membership_events"], "readwrite");
const store = tx.objectStore("oob_membership_events"); const store = tx.objectStore("oob_membership_events");
const eventPuts = membershipEvents.map((e) => { const eventPuts = membershipEvents.map((e) => {
const putPromise = promiseifyRequest(store.put(e)); const putPromise = reqAsEventPromise(store.put(e));
// ignoring the result makes sure we discard the IDB success event // ignoring the result makes sure we discard the IDB success event
// ASAP, and not create a potentially big array containing them // ASAP, and not create a potentially big array containing them
// unneccesarily later on by calling Promise.all. // unneccesarily later on by calling Promise.all.
@@ -281,7 +292,7 @@ LocalIndexedDBStoreBackend.prototype = {
oob_written: true, oob_written: true,
state_key: 0, state_key: 0,
}; };
const markerPut = promiseifyRequest(store.put(markerObject)); const markerPut = reqAsEventPromise(store.put(markerObject));
const allPuts = eventPuts.concat(markerPut); const allPuts = eventPuts.concat(markerPut);
// ignore the empty array Promise.all creates // ignore the empty array Promise.all creates
// as this method should just resolve // as this method should just resolve
@@ -292,6 +303,44 @@ LocalIndexedDBStoreBackend.prototype = {
}); });
}, },
clearOutOfBandMembers: async function(roomId) {
// the approach to delete all members for a room
// is to get the min and max state key from the index
// for that room, and then delete between those
// keys in the store.
// this should be way faster than deleting every member
// individually for a large room.
const readTx = this.db.transaction(
["oob_membership_events"],
"readonly");
const store = readTx.objectStore("oob_membership_events");
const roomIndex = store.index("room");
const roomRange = IDBKeyRange.only(roomId);
const minStateKeyProm = reqAsCursorPromise(
roomIndex.openKeyCursor(roomRange, "next"),
).then((cursor) => cursor && cursor.primaryKey[1]);
const maxStateKeyProm = reqAsCursorPromise(
roomIndex.openKeyCursor(roomRange, "prev"),
).then((cursor) => cursor && cursor.primaryKey[1]);
const [minStateKey, maxStateKey] = await Promise.all(
[minStateKeyProm, maxStateKeyProm]);
const writeTx = this.db.transaction(
["oob_membership_events"],
"readwrite");
const writeStore = writeTx.objectStore("oob_membership_events");
const membersKeyRange = IDBKeyRange.bound(
[roomId, minStateKey],
[roomId, maxStateKey],
);
console.log(`LL: Deleting all users + marker in storage for ` +
`room ${roomId}, with key range:`,
[roomId, minStateKey], [roomId, maxStateKey]);
await reqAsPromise(writeStore.delete(membersKeyRange));
},
/** /**
* Clear the entire database. This should be used when logging out of a client * Clear the entire database. This should be used when logging out of a client
* to prevent mixing data between accounts. * to prevent mixing data between accounts.
@@ -389,7 +438,7 @@ LocalIndexedDBStoreBackend.prototype = {
roomsData: roomsData, roomsData: roomsData,
groupsData: groupsData, groupsData: groupsData,
}); // put == UPSERT }); // put == UPSERT
return promiseifyTxn(txn); return txnAsPromise(txn);
}); });
}, },
@@ -406,7 +455,7 @@ LocalIndexedDBStoreBackend.prototype = {
for (let i = 0; i < accountData.length; i++) { for (let i = 0; i < accountData.length; i++) {
store.put(accountData[i]); // put == UPSERT store.put(accountData[i]); // put == UPSERT
} }
return promiseifyTxn(txn); return txnAsPromise(txn);
}); });
}, },
@@ -428,7 +477,7 @@ LocalIndexedDBStoreBackend.prototype = {
event: tuple[1], event: tuple[1],
}); // put == UPSERT }); // put == UPSERT
} }
return promiseifyTxn(txn); return txnAsPromise(txn);
}); });
}, },

View File

@@ -110,6 +110,10 @@ RemoteIndexedDBStoreBackend.prototype = {
return this._doCmd('setOutOfBandMembers', [roomId, membershipEvents]); return this._doCmd('setOutOfBandMembers', [roomId, membershipEvents]);
}, },
clearOutOfBandMembers: function(roomId) {
return this._doCmd('clearOutOfBandMembers', [roomId]);
},
/** /**
* Load all user presence events from the database. This is not cached. * Load all user presence events from the database. This is not cached.
* @return {Promise<Object[]>} A list of presence events in their raw form. * @return {Promise<Object[]>} A list of presence events in their raw form.

View File

@@ -95,6 +95,9 @@ class IndexedDBStoreWorker {
case 'getOutOfBandMembers': case 'getOutOfBandMembers':
prom = this.backend.getOutOfBandMembers(msg.args[0]); prom = this.backend.getOutOfBandMembers(msg.args[0]);
break; break;
case 'clearOutOfBandMembers':
prom = this.backend.clearOutOfBandMembers(msg.args[0]);
break;
case 'setOutOfBandMembers': case 'setOutOfBandMembers':
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]); prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
break; break;

View File

@@ -242,4 +242,8 @@ IndexedDBStore.prototype.setOutOfBandMembers = function(roomId, membershipEvents
return this.backend.setOutOfBandMembers(roomId, membershipEvents); return this.backend.setOutOfBandMembers(roomId, membershipEvents);
}; };
IndexedDBStore.prototype.clearOutOfBandMembers = function(roomId) {
return this.backend.clearOutOfBandMembers(roomId);
};
module.exports.IndexedDBStore = IndexedDBStore; module.exports.IndexedDBStore = IndexedDBStore;

View File

@@ -272,6 +272,10 @@ StubStore.prototype = {
setOutOfBandMembers: function() { setOutOfBandMembers: function() {
return Promise.resolve(); return Promise.resolve();
}, },
clearOutOfBandMembers: function() {
return Promise.resolve();
},
}; };
/** Stub Store class. */ /** Stub Store class. */

View File

@@ -1141,6 +1141,8 @@ SyncApi.prototype._processSyncResponse = async function(
accountDataEvents.forEach(function(e) { accountDataEvents.forEach(function(e) {
client.emit("event", e); client.emit("event", e);
}); });
room.onLeft();
}); });
// update the notification timeline, if appropriate. // update the notification timeline, if appropriate.