openvibers/openvibe.network Commits
Boot: seed the identity legacy map with one INSERT … SELECT per source (the per-row loop over 28k guests was 6.4 s of every boot) (#269)
Changes
@@@ -47,23 +47,20 @@ async function backfill(db) {* reported through /internal/link-account.*/async function seedLegacyMap(db) {-- const ins = db.prepare(`INSERT INTO identity_legacy_map (source_system, source_type, source_id, subject_id, verified_at)-- VALUES (?, ?, ?, ?, ov_now()) ON CONFLICT DO NOTHING`);++ // One INSERT … SELECT per source, not one INSERT per row: with 28k anonymous sessions the per-row loop was 6.4 s of++ // every boot (2026-10-17), and a restart's queued requests wait for the boot.++ const into = `INSERT INTO identity_legacy_map (source_system, source_type, source_id, subject_id, verified_at)`;let n = 0;await db.tx(async () => {-- for (const u of await db.prepare('SELECT id, subject_id, legacy_source, legacy_id FROM users WHERE subject_id IS NOT NULL').all()) {-- n += (await ins.run(SELF, 'user', String(u.id), u.subject_id)).changes;-- if (u.legacy_source && u.legacy_id != null) n += (await ins.run(String(u.legacy_source), 'user', String(u.legacy_id), u.subject_id)).changes;-- }-- for (const g of await db.prepare('SELECT id, subject_id FROM anon_users WHERE subject_id IS NOT NULL').all()) {-- n += (await ins.run(SELF, 'anon_user', String(g.id), g.subject_id)).changes;-- }++ const run = async (select) => { n += (await db.prepare(`${into} ${select} ON CONFLICT DO NOTHING`).run()).changes; };++ await run(`SELECT '${SELF}', 'user', CAST(id AS TEXT), subject_id, ov_now() FROM users WHERE subject_id IS NOT NULL`);++ await run(`SELECT CAST(legacy_source AS TEXT), 'user', CAST(legacy_id AS TEXT), subject_id, ov_now() FROM users++ WHERE subject_id IS NOT NULL AND legacy_source IS NOT NULL AND legacy_source <> '' AND legacy_id IS NOT NULL`);++ await run(`SELECT '${SELF}', 'anon_user', CAST(id AS TEXT), subject_id, ov_now() FROM anon_users WHERE subject_id IS NOT NULL`);// Sites that report their own local id through /internal/link-account (Live does). Rows written by// the OAuth exchange hold 'network:<id>' instead and say nothing about the site's ids.-- for (const l of await db.prepare(`SELECT la.service, la.service_user_id, u.subject_id FROM linked_accounts la JOIN users u ON u.id = la.user_id-- WHERE u.subject_id IS NOT NULL AND la.service_user_id NOT LIKE 'network:%'`).all()) {-- if (/^[a-z][a-z0-9-]{1,39}$/.test(l.service)) n += (await ins.run(l.service, 'user', String(l.service_user_id), l.subject_id)).changes;-- }++ await run(`SELECT la.service, 'user', CAST(la.service_user_id AS TEXT), u.subject_id, ov_now() FROM linked_accounts la JOIN users u ON u.id = la.user_id++ WHERE u.subject_id IS NOT NULL AND la.service_user_id NOT LIKE 'network:%' AND la.service ~ '^[a-z][a-z0-9-]{1,39}$'`);});return n;}@@@ -39,6 +39,11 @@ assert.strictEqual(alex.subject_id.slice(4, 14), contracts.ids.ulid(Date.parse('const before = alex.subject_id;console.log = () => {}; await subjects.ensureSchema(db); console.log = quiet; // boot again: nothing changesassert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 1').get()).subject_id, before, 'subject ids are stable across boots');++// The set-based seed: network users 1 and 2, Live's legacy 77, guest 5 and Live's reported 321; the OAuth exchange's++// 'network:1' says nothing about Tools' ids. A second boot adds nothing.++const mapRows = await db.prepare('SELECT source_system, source_type, source_id FROM identity_legacy_map ORDER BY 1, 2, 3').all();++assert.deepStrictEqual(mapRows.map((r) => `${r.source_system}:${r.source_type}:${r.source_id}`),++ ['live:user:321', 'live:user:77', 'network:anon_user:5', 'network:user:1', 'network:user:2']);// Seeded map: network self ids and the Live legacy id.assert.strictEqual((await subjects.resolve(db, { source_system: 'network', source_type: 'user', source_id: 1 })).subject.id, before);
@@ -47,23 +47,20 @@ async function backfill(db) {
@@ -47,23 +47,20 @@ async function backfill(db) {
* reported through /internal/link-account.
* reported through /internal/link-account.
*/
*/
async function seedLegacyMap(db) {
async function seedLegacyMap(db) {
- const ins = db.prepare(`INSERT INTO identity_legacy_map (source_system, source_type, source_id, subject_id, verified_at)
- VALUES (?, ?, ?, ?, ov_now()) ON CONFLICT DO NOTHING`);
+ // One INSERT … SELECT per source, not one INSERT per row: with 28k anonymous sessions the per-row loop was 6.4 s of
+ // every boot (2026-10-17), and a restart's queued requests wait for the boot.
+ const into = `INSERT INTO identity_legacy_map (source_system, source_type, source_id, subject_id, verified_at)`;
let n = 0;
let n = 0;
await db.tx(async () => {
await db.tx(async () => {
- for (const u of await db.prepare('SELECT id, subject_id, legacy_source, legacy_id FROM users WHERE subject_id IS NOT NULL').all()) {
- n += (await ins.run(SELF, 'user', String(u.id), u.subject_id)).changes;
- if (u.legacy_source && u.legacy_id != null) n += (await ins.run(String(u.legacy_source), 'user', String(u.legacy_id), u.subject_id)).changes;
- }
- for (const g of await db.prepare('SELECT id, subject_id FROM anon_users WHERE subject_id IS NOT NULL').all()) {
- n += (await ins.run(SELF, 'anon_user', String(g.id), g.subject_id)).changes;
- }
+ const run = async (select) => { n += (await db.prepare(`${into} ${select} ON CONFLICT DO NOTHING`).run()).changes; };
+ await run(`SELECT '${SELF}', 'user', CAST(id AS TEXT), subject_id, ov_now() FROM users WHERE subject_id IS NOT NULL`);
+ await run(`SELECT CAST(legacy_source AS TEXT), 'user', CAST(legacy_id AS TEXT), subject_id, ov_now() FROM users
+ WHERE subject_id IS NOT NULL AND legacy_source IS NOT NULL AND legacy_source <> '' AND legacy_id IS NOT NULL`);
+ await run(`SELECT '${SELF}', 'anon_user', CAST(id AS TEXT), subject_id, ov_now() FROM anon_users WHERE subject_id IS NOT NULL`);
// Sites that report their own local id through /internal/link-account (Live does). Rows written by
// Sites that report their own local id through /internal/link-account (Live does). Rows written by
// the OAuth exchange hold 'network:<id>' instead and say nothing about the site's ids.
// the OAuth exchange hold 'network:<id>' instead and say nothing about the site's ids.
- for (const l of await db.prepare(`SELECT la.service, la.service_user_id, u.subject_id FROM linked_accounts la JOIN users u ON u.id = la.user_id
- WHERE u.subject_id IS NOT NULL AND la.service_user_id NOT LIKE 'network:%'`).all()) {
- if (/^[a-z][a-z0-9-]{1,39}$/.test(l.service)) n += (await ins.run(l.service, 'user', String(l.service_user_id), l.subject_id)).changes;
- }
+ await run(`SELECT la.service, 'user', CAST(la.service_user_id AS TEXT), u.subject_id, ov_now() FROM linked_accounts la JOIN users u ON u.id = la.user_id
+ WHERE u.subject_id IS NOT NULL AND la.service_user_id NOT LIKE 'network:%' AND la.service ~ '^[a-z][a-z0-9-]{1,39}$'`);
});
});
return n;
return n;
}
}
@@ -39,6 +39,11 @@ assert.strictEqual(alex.subject_id.slice(4, 14), contracts.ids.ulid(Date.parse('
@@ -39,6 +39,11 @@ assert.strictEqual(alex.subject_id.slice(4, 14), contracts.ids.ulid(Date.parse('
const before = alex.subject_id;
const before = alex.subject_id;
console.log = () => {}; await subjects.ensureSchema(db); console.log = quiet; // boot again: nothing changes
console.log = () => {}; await subjects.ensureSchema(db); console.log = quiet; // boot again: nothing changes
assert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 1').get()).subject_id, before, 'subject ids are stable across boots');
assert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 1').get()).subject_id, before, 'subject ids are stable across boots');
+// The set-based seed: network users 1 and 2, Live's legacy 77, guest 5 and Live's reported 321; the OAuth exchange's
+// 'network:1' says nothing about Tools' ids. A second boot adds nothing.
+const mapRows = await db.prepare('SELECT source_system, source_type, source_id FROM identity_legacy_map ORDER BY 1, 2, 3').all();
+assert.deepStrictEqual(mapRows.map((r) => `${r.source_system}:${r.source_type}:${r.source_id}`),
+ ['live:user:321', 'live:user:77', 'network:anon_user:5', 'network:user:1', 'network:user:2']);
// Seeded map: network self ids and the Live legacy id.
// Seeded map: network self ids and the Live legacy id.
assert.strictEqual((await subjects.resolve(db, { source_system: 'network', source_type: 'user', source_id: 1 })).subject.id, before);
assert.strictEqual((await subjects.resolve(db, { source_system: 'network', source_type: 'user', source_id: 1 })).subject.id, before);