Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pouch db migration edge cases #9820

Merged
merged 1 commit into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pxtlib/browserutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,8 @@ namespace pxt.BrowserUtils {
private name: string,
private version: number,
private upgradeHandler?: IDBUpgradeHandler,
private quotaExceededHandler?: () => void) {
private quotaExceededHandler?: () => void,
private skipErrorLog = false) {
}

private throwIfNotOpened(): void {
Expand All @@ -845,6 +846,10 @@ namespace pxt.BrowserUtils {
}

private errorHandler(err: Error, op: string, reject: (err: Error) => void): void {
if (this.skipErrorLog) {
reject(err);
return;
}
console.error(new Error(`${this.name} IDBWrapper error for ${op}: ${err.message}`));
reject(err);
// special case for quota exceeded
Expand Down
66 changes: 60 additions & 6 deletions webapp/src/idbworkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,42 @@ async function performMigrationsAsync() {
return _migrationPromise;
}

const POUCH_OBJECT_STORE = "by-sequence";
const POUCH_DB_VERSION = 5;

async function checkIfPouchDbExistsAsync() {
// Unfortunately, there is no simple cross-browser way to check
// if an indexedDb already exists. This works by requesting the
// db with a version lower than the current version. If it
// throws an exception, then the db must already exist with the
// higher version. If it tries to upgrade, then the db doesn't
// exist. We abort the transaction to avoid poisoning pouchdb
// if anyone ever visits an old version of the editor.
let result = true;
try {
const db = new pxt.BrowserUtils.IDBWrapper("_pouch_pxt-" + pxt.storage.storageId(), 1, (e, r) => {
result = false;
r.transaction.abort();
}, null, true);
await db.openAsync();
}
catch (e) {
// This will always throw an exception
}

return result;
}

async function migratePouchAsync() {
const POUCH_OBJECT_STORE = "by-sequence";
const oldDb = new pxt.BrowserUtils.IDBWrapper("_pouch_pxt-" + pxt.storage.storageId(), 5, () => {});
if (!await checkIfPouchDbExistsAsync()) return;

const oldDb = new pxt.BrowserUtils.IDBWrapper("_pouch_pxt-" + pxt.storage.storageId(), POUCH_DB_VERSION, () => {});
await oldDb.openAsync();

const entries = await oldDb.getAllAsync<any>(POUCH_OBJECT_STORE);
const alreadyMigratedList = await getMigrationDbAsync();

for (const entry of entries) {
if (entry._migrated) continue;
// format is (prefix-)?tableName--id::rev
const docId: string = entry._doc_id_rev;

Expand Down Expand Up @@ -100,15 +128,18 @@ async function migratePouchAsync() {
continue;
}

if (await alreadyMigratedList.getAsync(table, id)) {
continue;
}

alreadyMigratedList.setAsync(table, { id });

const db = await getDbAsync(prefix)
const existing = await db.getAsync(table, id);

if (!existing) {
await db.setAsync(table, entry);
}

entry._migrated = true;
await oldDb.setAsync(POUCH_OBJECT_STORE, entry);
}
}

Expand Down Expand Up @@ -303,6 +334,29 @@ export async function copyProjectToLegacyEditorAsync(header: Header, script: pxt
} as StoredText);
}

async function getMigrationDbAsync() {
const idbDb = new pxt.BrowserUtils.IDBWrapper(`__pxt_idb_migration_${pxt.storage.storageId()}`, 1, (ev, r) => {
const db = r.result as IDBDatabase;
db.createObjectStore(TEXTS_TABLE, { keyPath: KEYPATH });
db.createObjectStore(HEADERS_TABLE, { keyPath: KEYPATH });
db.createObjectStore(SCRIPT_TABLE, { keyPath: KEYPATH });
db.createObjectStore(HOSTCACHE_TABLE, { keyPath: KEYPATH });
db.createObjectStore(GITHUB_TABLE, { keyPath: KEYPATH });
}, async () => {
await pxt.BrowserUtils.clearTranslationDbAsync();
await pxt.BrowserUtils.clearTutorialInfoDbAsync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we clearing the TutorialInfoDb here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. This is just to free up space if needed...

});

try {
await idbDb.openAsync();
} catch (e) {
pxt.reportException(e);
return Promise.reject(e);
}

return idbDb;
}

export function initGitHubDb() {
class GithubDb implements pxt.github.IGithubDb {
// in memory cache
Expand Down
Loading