Show and log progress during the SQLCipher migration

pull/1/head
Scott Nonnenberg 7 years ago
parent 6b78f2582b
commit 1d7987108b

@ -157,6 +157,17 @@
"description": "description":
"Message shown on the loading screen while we are doing application optimizations" "Message shown on the loading screen while we are doing application optimizations"
}, },
"migratingToSQLCipher": {
"message": "Optimizing messages... $status$ complete.",
"description":
"Message shown on the loading screen while we are doing application optimizations",
"placeholders": {
"status": {
"content": "$1",
"example": "45/200"
}
}
},
"chooseDirectory": { "chooseDirectory": {
"message": "Choose folder", "message": "Choose folder",
"description": "Button to allow the user to find a folder on disk" "description": "Button to allow the user to find a folder on disk"

@ -319,11 +319,33 @@
await upgradeMessages(); await upgradeMessages();
const db = await Whisper.Database.open(); const db = await Whisper.Database.open();
const totalMessages = await MessageDataMigrator.getNumMessages({
connection: db,
});
function showMigrationStatus(current) {
const status = `${current}/${totalMessages}`;
Views.Initialization.setMessage(
window.i18n('migratingToSQLCipher', [status])
);
}
if (totalMessages) {
window.log.info(`About to migrate ${totalMessages} messages`);
showMigrationStatus(0);
await window.Signal.migrateToSQL({ await window.Signal.migrateToSQL({
db, db,
clearStores: Whisper.Database.clearStores, clearStores: Whisper.Database.clearStores,
handleDOMException: Whisper.Database.handleDOMException, handleDOMException: Whisper.Database.handleDOMException,
countCallback: count => {
window.log.info(`Migration: ${count} messages complete`);
showMigrationStatus(count);
},
}); });
}
Views.Initialization.setMessage(window.i18n('loading'));
// Note: We are not invoking the second set of IndexedDB migrations because it is // Note: We are not invoking the second set of IndexedDB migrations because it is
// likely that any future migrations will simply extracting things from IndexedDB. // likely that any future migrations will simply extracting things from IndexedDB.

@ -155,7 +155,6 @@
return; return;
} }
window.log.info('Merging updated message into collection');
existing.merge(message.attributes); existing.merge(message.attributes);
}; };

@ -134,7 +134,7 @@ exports.dangerouslyProcessAllWithoutIndex = async ({
// NOTE: Even if we make this async using `then`, requesting `count` on an // NOTE: Even if we make this async using `then`, requesting `count` on an
// IndexedDB store blocks all subsequent transactions, so we might as well // IndexedDB store blocks all subsequent transactions, so we might as well
// explicitly wait for it here: // explicitly wait for it here:
const numTotalMessages = await _getNumMessages({ connection }); const numTotalMessages = await exports.getNumMessages({ connection });
const migrationStartTime = Date.now(); const migrationStartTime = Date.now();
let numCumulativeMessagesProcessed = 0; let numCumulativeMessagesProcessed = 0;
@ -366,7 +366,7 @@ const _dangerouslyFetchMessagesRequiringSchemaUpgradeWithoutIndex = ({
}); });
}; };
const _getNumMessages = async ({ connection } = {}) => { exports.getNumMessages = async ({ connection } = {}) => {
if (!isObject(connection)) { if (!isObject(connection)) {
throw new TypeError("'connection' is required"); throw new TypeError("'connection' is required");
} }

@ -10,6 +10,8 @@ const {
const { const {
getMessageExportLastIndex, getMessageExportLastIndex,
setMessageExportLastIndex, setMessageExportLastIndex,
getMessageExportCount,
setMessageExportCount,
getUnprocessedExportLastIndex, getUnprocessedExportLastIndex,
setUnprocessedExportLastIndex, setUnprocessedExportLastIndex,
} = require('./settings'); } = require('./settings');
@ -18,7 +20,12 @@ module.exports = {
migrateToSQL, migrateToSQL,
}; };
async function migrateToSQL({ db, clearStores, handleDOMException }) { async function migrateToSQL({
db,
clearStores,
handleDOMException,
countCallback,
}) {
if (!db) { if (!db) {
throw new Error('Need db for IndexedDB connection!'); throw new Error('Need db for IndexedDB connection!');
} }
@ -31,7 +38,10 @@ async function migrateToSQL({ db, clearStores, handleDOMException }) {
window.log.info('migrateToSQL: start'); window.log.info('migrateToSQL: start');
let lastIndex = await getMessageExportLastIndex(db); let [lastIndex, doneSoFar] = await Promise.all([
getMessageExportLastIndex(db),
getMessageExportCount(db),
]);
let complete = false; let complete = false;
while (!complete) { while (!complete) {
@ -48,7 +58,16 @@ async function migrateToSQL({ db, clearStores, handleDOMException }) {
({ complete, lastIndex } = status); ({ complete, lastIndex } = status);
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await setMessageExportLastIndex(db, lastIndex); await Promise.all([
setMessageExportCount(db, doneSoFar),
setMessageExportLastIndex(db, lastIndex),
]);
const { count } = status;
doneSoFar += count;
if (countCallback) {
countCallback(doneSoFar);
}
} }
window.log.info('migrateToSQL: migrate of messages complete'); window.log.info('migrateToSQL: migrate of messages complete');
@ -85,7 +104,7 @@ async function migrateStoreToSQLite({
storeName, storeName,
handleDOMException, handleDOMException,
lastIndex = null, lastIndex = null,
batchSize = 20, batchSize = 50,
}) { }) {
if (!db) { if (!db) {
throw new Error('Need db for IndexedDB connection!'); throw new Error('Need db for IndexedDB connection!');

@ -4,6 +4,7 @@ const ITEMS_STORE_NAME = 'items';
const LAST_PROCESSED_INDEX_KEY = 'attachmentMigration_lastProcessedIndex'; const LAST_PROCESSED_INDEX_KEY = 'attachmentMigration_lastProcessedIndex';
const IS_MIGRATION_COMPLETE_KEY = 'attachmentMigration_isComplete'; const IS_MIGRATION_COMPLETE_KEY = 'attachmentMigration_isComplete';
const MESSAGE_LAST_INDEX_KEY = 'sqlMigration_messageLastIndex'; const MESSAGE_LAST_INDEX_KEY = 'sqlMigration_messageLastIndex';
const MESSAGE_COUNT_KEY = 'sqlMigration_messageCount';
const UNPROCESSED_LAST_INDEX_KEY = 'sqlMigration_unprocessedLastIndex'; const UNPROCESSED_LAST_INDEX_KEY = 'sqlMigration_unprocessedLastIndex';
// Public API // Public API
@ -25,6 +26,10 @@ exports.getMessageExportLastIndex = connection =>
exports._getItem(connection, MESSAGE_LAST_INDEX_KEY); exports._getItem(connection, MESSAGE_LAST_INDEX_KEY);
exports.setMessageExportLastIndex = (connection, lastIndex) => exports.setMessageExportLastIndex = (connection, lastIndex) =>
exports._setItem(connection, MESSAGE_LAST_INDEX_KEY, lastIndex); exports._setItem(connection, MESSAGE_LAST_INDEX_KEY, lastIndex);
exports.getMessageExportCount = connection =>
exports._getItem(connection, MESSAGE_COUNT_KEY);
exports.setMessageExportCount = (connection, count) =>
exports._setItem(connection, MESSAGE_COUNT_KEY, count);
exports.getUnprocessedExportLastIndex = connection => exports.getUnprocessedExportLastIndex = connection =>
exports._getItem(connection, UNPROCESSED_LAST_INDEX_KEY); exports._getItem(connection, UNPROCESSED_LAST_INDEX_KEY);

@ -2,26 +2,44 @@
/* global i18n: false */ /* global i18n: false */
const OPTIMIZATION_MESSAGE_DISPLAY_THRESHOLD = 1000; // milliseconds const DISPLAY_THRESHOLD = 3000; // milliseconds
const SELECTOR = '.app-loading-screen .message';
const setMessage = () => { let timeout;
const message = document.querySelector('.app-loading-screen .message'); let targetString;
let didTimeout = false;
const clear = () => {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
};
const setMessage = loadingText => {
const message = document.querySelector(SELECTOR);
if (!message) { if (!message) {
return () => {}; return clear;
}
targetString = loadingText || i18n('optimizingApplication');
message.innerText = didTimeout ? targetString : i18n('loading');
if (timeout) {
return clear;
} }
message.innerText = i18n('loading');
const optimizingMessageTimeoutId = setTimeout(() => { timeout = setTimeout(() => {
const innerMessage = document.querySelector('.app-loading-screen .message'); didTimeout = true;
const innerMessage = document.querySelector(SELECTOR);
if (!innerMessage) { if (!innerMessage) {
return; return;
} }
innerMessage.innerText = i18n('optimizingApplication'); innerMessage.innerText = targetString;
}, OPTIMIZATION_MESSAGE_DISPLAY_THRESHOLD); }, DISPLAY_THRESHOLD);
return () => { return clear;
clearTimeout(optimizingMessageTimeoutId);
};
}; };
module.exports = { module.exports = {

Loading…
Cancel
Save