store session values as individual values in store

so we don't have to write the whole object every time something changes
we'll use this to store the olm account
This commit is contained in:
Bruno Windels 2020-08-27 14:28:40 +02:00
parent 25f3dfbb75
commit 14b27f81fe
3 changed files with 61 additions and 34 deletions

View file

@ -24,7 +24,8 @@ export class Session {
constructor({storage, hsApi, sessionInfo}) { constructor({storage, hsApi, sessionInfo}) {
this._storage = storage; this._storage = storage;
this._hsApi = hsApi; this._hsApi = hsApi;
this._session = null; this._syncToken = null;
this._syncFilterId = null;
this._sessionInfo = sessionInfo; this._sessionInfo = sessionInfo;
this._rooms = new ObservableMap(); this._rooms = new ObservableMap();
this._sendScheduler = new SendScheduler({hsApi, backoff: new RateLimitingBackoff()}); this._sendScheduler = new SendScheduler({hsApi, backoff: new RateLimitingBackoff()});
@ -42,11 +43,12 @@ export class Session {
this._storage.storeNames.pendingEvents, this._storage.storeNames.pendingEvents,
]); ]);
// restore session object // restore session object
this._session = await txn.session.get(); const [syncToken, syncFilterId] = await Promise.all([
if (!this._session) { txn.session.get("syncToken"),
this._session = {}; txn.session.get("syncFilterId")
return; ]);
} this._syncToken = syncToken;
this._syncFilterId = syncFilterId;
const pendingEventsByRoomId = await this._getPendingEventsByRoom(txn); const pendingEventsByRoomId = await this._getPendingEventsByRoom(txn);
// load rooms // load rooms
const rooms = await txn.roomSummary.getAll(); const rooms = await txn.roomSummary.getAll();
@ -70,11 +72,9 @@ export class Session {
const txn = await this._storage.readWriteTxn([ const txn = await this._storage.readWriteTxn([
this._storage.storeNames.session this._storage.storeNames.session
]); ]);
const newSessionData = Object.assign({}, this._session, {serverVersions: lastVersionResponse}); txn.session.set("serverVersions", lastVersionResponse);
txn.session.set(newSessionData);
// TODO: what can we do if this throws? // TODO: what can we do if this throws?
await txn.complete(); await txn.complete();
this._session = newSessionData;
} }
this._sendScheduler.start(); this._sendScheduler.start();
@ -115,27 +115,28 @@ export class Session {
} }
writeSync(syncToken, syncFilterId, accountData, txn) { writeSync(syncToken, syncFilterId, accountData, txn) {
if (syncToken !== this._session.syncToken) { if (syncToken !== this._syncToken) {
// don't modify this._session because transaction might still fail // don't modify `this` because transaction might still fail
const newSessionData = Object.assign({}, this._session, {syncToken, syncFilterId}); txn.session.set("syncToken", syncToken);
txn.session.set(newSessionData); txn.session.set("syncFilterId", syncFilterId);
return newSessionData; return {syncToken, syncFilterId};
} }
} }
afterSync(newSessionData) { afterSync(changes) {
if (newSessionData) { if (changes) {
// sync transaction succeeded, modify object state now // sync transaction succeeded, modify object state now
this._session = newSessionData; this._syncToken = changes.syncToken;
this._syncFilterId = changes.syncFilterId;
} }
} }
get syncToken() { get syncToken() {
return this._session.syncToken; return this._syncToken;
} }
get syncFilterId() { get syncFilterId() {
return this._session.syncFilterId; return this._syncFilterId;
} }
get user() { get user() {
@ -149,8 +150,8 @@ export function tests() {
readTxn() { readTxn() {
return Promise.resolve({ return Promise.resolve({
session: { session: {
get() { get(key) {
return Promise.resolve(Object.assign({}, session)); return Promise.resolve(session[key]);
} }
}, },
pendingEvents: { pendingEvents: {
@ -176,18 +177,24 @@ export function tests() {
syncFilterId: 5, syncFilterId: 5,
}), sessionInfo: {userId: ""}}); }), sessionInfo: {userId: ""}});
await session.load(); await session.load();
let txnSetCalled = false; let syncTokenSet = false;
let syncFilterIdSet = false;
const syncTxn = { const syncTxn = {
session: { session: {
set({syncToken, syncFilterId}) { set(key, value) {
txnSetCalled = true; if (key === "syncToken") {
assert.equal(syncToken, "b"); assert.equal(value, "b");
assert.equal(syncFilterId, 6); syncTokenSet = true;
} else if (key === "syncFilterId") {
assert.equal(value, 6);
syncFilterIdSet = true;
}
} }
} }
}; };
const newSessionData = session.writeSync("b", 6, {}, syncTxn); const newSessionData = session.writeSync("b", 6, {}, syncTxn);
assert(txnSetCalled); assert(syncTokenSet);
assert(syncFilterIdSet);
assert.equal(session.syncToken, "a"); assert.equal(session.syncToken, "a");
assert.equal(session.syncFilterId, 5); assert.equal(session.syncFilterId, 5);
session.afterSync(newSessionData); session.afterSync(newSessionData);

View file

@ -1,12 +1,14 @@
import {iterateCursor} from "./utils.js"; import {iterateCursor, reqAsPromise} from "./utils.js";
import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js"; import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js";
import {RoomMemberStore} from "./stores/RoomMemberStore.js"; import {RoomMemberStore} from "./stores/RoomMemberStore.js";
import {SessionStore} from "./stores/SessionStore.js";
// FUNCTIONS SHOULD ONLY BE APPENDED!! // FUNCTIONS SHOULD ONLY BE APPENDED!!
// the index in the array is the database version // the index in the array is the database version
export const schema = [ export const schema = [
createInitialStores, createInitialStores,
createMemberStore, createMemberStore,
migrateSession,
]; ];
// TODO: how to deal with git merge conflicts of this array? // TODO: how to deal with git merge conflicts of this array?
@ -44,3 +46,21 @@ async function createMemberStore(db, txn) {
} }
}); });
} }
async function migrateSession(db, txn) {
const session = txn.objectStore("session");
try {
const PRE_MIGRATION_KEY = 1;
const entry = await reqAsPromise(session.get(PRE_MIGRATION_KEY));
if (entry) {
session.delete(PRE_MIGRATION_KEY);
const store = new SessionStore(session);
for (const [key, value] of Object.entries(entry.value)) {
store.set(key, value);
}
}
} catch (err) {
txn.abort();
console.error("could not migrate session", err.stack);
}
}

View file

@ -35,14 +35,14 @@ export class SessionStore {
this._sessionStore = sessionStore; this._sessionStore = sessionStore;
} }
async get() { async get(key) {
const session = await this._sessionStore.selectFirst(IDBKeyRange.only(1)); const entry = await this._sessionStore.get(key);
if (session) { if (entry) {
return session.value; return entry.value;
} }
} }
set(session) { set(key, value) {
return this._sessionStore.put({key: 1, value: session}); return this._sessionStore.put({key, value});
} }
} }