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

SAM-71 migrations (with libp2p migration) #52

Merged
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
4 changes: 4 additions & 0 deletions packages/gateway-client/src/worker/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ClientMessageType } from '../worker-messaging';
import { SamizdAppDevTools } from './devtools';
import { logger } from './logging';
import messenger from './messenger';
import { runMigrations } from './migrations';
import { P2pClient } from './p2p-client';
import { overrideFetch } from './p2p-fetch/override-fetch';
import status from './status';
Expand All @@ -14,6 +15,9 @@ export default async () => {
// create our bootstrap logger
const log = logger.getLogger('worker/bootstrap');

// run migrations before anything else
await runMigrations();

// setup event handlers
self.addEventListener('online', () => log.debug('<<<<online'));
self.addEventListener('offline', () => log.debug('<<<<offline'));
Expand Down
7 changes: 7 additions & 0 deletions packages/gateway-client/src/worker/migrations.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runMigrations } from './migrations';

describe('migrations', () => {
it('should run without error', async () => {
await runMigrations();
});
});
100 changes: 100 additions & 0 deletions packages/gateway-client/src/worker/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import localforage from 'localforage';

import { logger } from './logging';

const log = logger.getLogger('worker/migrations');

type Migration = () => Promise<void>;

const migrations = new Map<string, Migration>();

const registerMigration = (id: string, migration: Migration) => {
migrations.set(id, migration);
};

const migrationState = new Set<string>();

const loadCache = async () => {
const cached = await localforage.getItem<string>('migration:state');
if (cached) {
JSON.parse(cached).forEach((id: string) => migrationState.add(id));
}
};

const dumpCache = async () => {
await localforage.setItem(
'migration:state',
JSON.stringify(Array.from(migrationState))
);
};

export const runMigrations = async () => {
// load migration data
await loadCache();

log.info('Running migrations...');

// loop migrations
for (const [id, migration] of migrations.entries()) {
// if this migration has already been executed
if (migrationState.has(id)) {
log.trace('Skipping already run migration: ', id);
// skip it
continue;
}

// else, this migration has not yet been executed, so execute it
try {
log.info('Running migration: ', id);
await migration();
} catch (e) {
log.error(
`Migration ${id} has failed (it will be re-run on next worker execution): `,
e
);
continue;
}

// if the migration was successful, add it to the cache
migrationState.add(id);
}

log.info('Finished running migrations.');

// save migration data
await dumpCache();
};

// Migrate from libp2p.bootstrap to new bootstrap-list
registerMigration('0f8edbd5-6aa0-492e-9c3a-b419752dfdb1', async () => {
// check for libp2p.bootstrap key
const oldBootstrap = await localforage.getItem<string[]>(
'libp2p.bootstrap'
);
// if we didn't find it
if (!oldBootstrap) {
// no need to run the migration
return;
} // else, we need to migrate it

// attempt to get the new bootstrap list,
// or create a new one if it doesn't exist
const newBootstrap =
(await localforage.getItem<string>('p2p:bootstrap-list')) ?? '[]';
const newBootstrapList = JSON.parse(newBootstrap) as Record<
string,
unknown
>[];
// append the old bootstrap value to the new bootstrap list
newBootstrapList.push({
address: oldBootstrap,
});
// save the new bootstrap list
await localforage.setItem(
'p2p:bootstrap-list',
JSON.stringify(newBootstrapList)
);

// we won't remove the old bootstrap key
// in case it is needed after the migration
});
18 changes: 12 additions & 6 deletions packages/gateway-client/src/worker/p2p-client/bootstrap-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class BootstrapAddress {

toJson(): Record<string, unknown> {
return {
address: this.address,
lastSeen: this.lastSeen,
latency: this.latency,
address: this.address ?? '',
lastSeen: this.lastSeen ?? Date.now(),
latency: this.latency ?? Infinity,
};
}

Expand Down Expand Up @@ -184,9 +184,15 @@ export class BootstrapList extends Bootstrap {
this.log.debug('Loaded cached bootstrap list: ', cacheList);
// parse the cached list
await Promise.all(
cacheList.map((address: Record<string, unknown>) =>
this.addAddress(BootstrapAddress.fromJson(address))
)
cacheList.map((address: Record<string, unknown>) => {
let parsedAddress: string | BootstrapAddress = '';
try {
parsedAddress = BootstrapAddress.fromJson(address);
} catch (e) {
this.log.warn('Invalid address in cache: ', address, e);
}
return this.addAddress(parsedAddress);
})
);
}

Expand Down