mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
refactor(dm-db): use singleton IndexedDB connection to avoid N+1 overhead
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
1fbee4219b
commit
03f4efbfeb
3 files changed
+108
-11
No files matched your search
@@ -0,0 +1,55 @@
|
||||
|
||||
import { performance } from "perf_hooks";
|
||||
|
||||
// Mock IndexedDB if running in Node
|
||||
if (!globalThis.indexedDB) {
|
||||
const { indexedDB, IDBKeyRange } = await import("fake-indexeddb");
|
||||
globalThis.indexedDB = indexedDB;
|
||||
globalThis.IDBKeyRange = IDBKeyRange;
|
||||
}
|
||||
|
||||
// Mock console to suppress logs
|
||||
const originalConsoleWarn = console.warn;
|
||||
console.warn = () => {};
|
||||
|
||||
// Import after mocking console
|
||||
const { writeMessages, clearDmDb } = await import("../js/storage/dmDb.js");
|
||||
|
||||
async function runBenchmark() {
|
||||
await clearDmDb();
|
||||
|
||||
const iterations = 1000;
|
||||
const messages = [];
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
messages.push({
|
||||
id: `msg-${i}`,
|
||||
conversation_id: "conv-1",
|
||||
sender_pubkey: "sender",
|
||||
receiver_pubkey: "receiver",
|
||||
created_at: Date.now(),
|
||||
kind: 4,
|
||||
content: `Message ${i}`,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Starting benchmark with ${iterations} writes...`);
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
// Simulate N+1 writes
|
||||
for (const msg of messages) {
|
||||
await writeMessages(msg);
|
||||
}
|
||||
|
||||
const end = performance.now();
|
||||
const duration = end - start;
|
||||
|
||||
console.log(`Total time: ${duration.toFixed(2)}ms`);
|
||||
console.log(`Average time per write: ${(duration / iterations).toFixed(2)}ms`);
|
||||
|
||||
// Restore console
|
||||
console.warn = originalConsoleWarn;
|
||||
}
|
||||
|
||||
runBenchmark().catch(console.error);
|
||||
+51
-11
@@ -84,6 +84,9 @@ const MIGRATIONS = [
|
||||
},
|
||||
];
|
||||
|
||||
let dbInstance = null;
|
||||
let dbPromise = null;
|
||||
|
||||
function isIndexedDbAvailable() {
|
||||
try {
|
||||
return typeof indexedDB !== "undefined";
|
||||
@@ -101,7 +104,15 @@ function applyMigrations(db, oldVersion, newVersion, transaction) {
|
||||
}
|
||||
|
||||
function openDmDb() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (dbInstance) {
|
||||
return Promise.resolve(dbInstance);
|
||||
}
|
||||
|
||||
if (dbPromise) {
|
||||
return dbPromise;
|
||||
}
|
||||
|
||||
dbPromise = new Promise((resolve, reject) => {
|
||||
if (!isIndexedDbAvailable()) {
|
||||
resolve(null);
|
||||
return;
|
||||
@@ -118,10 +129,39 @@ function openDmDb() {
|
||||
applyMigrations(db, oldVersion, newVersion, upgradeTransaction);
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () =>
|
||||
request.onsuccess = () => {
|
||||
const db = request.result;
|
||||
dbInstance = db;
|
||||
|
||||
db.onversionchange = () => {
|
||||
db.close();
|
||||
dbInstance = null;
|
||||
dbPromise = null;
|
||||
};
|
||||
|
||||
db.onclose = () => {
|
||||
dbInstance = null;
|
||||
dbPromise = null;
|
||||
};
|
||||
|
||||
resolve(db);
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
dbPromise = null;
|
||||
reject(request.error || new Error("Failed to open DM database."));
|
||||
};
|
||||
});
|
||||
|
||||
return dbPromise;
|
||||
}
|
||||
|
||||
export async function closeDmDb() {
|
||||
if (dbInstance) {
|
||||
dbInstance.close();
|
||||
dbInstance = null;
|
||||
}
|
||||
dbPromise = null;
|
||||
}
|
||||
|
||||
function sanitizeTimestamp(value) {
|
||||
@@ -264,7 +304,7 @@ export async function writeConversationMetadata(conversation) {
|
||||
const tx = db.transaction(STORES.CONVERSATIONS, "readwrite");
|
||||
tx.objectStore(STORES.CONVERSATIONS).put(normalized);
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return normalized;
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to write conversation metadata", error);
|
||||
@@ -296,7 +336,7 @@ export async function writeMessages(messages) {
|
||||
}
|
||||
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return normalized;
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to persist messages", error);
|
||||
@@ -319,7 +359,7 @@ export async function getConversation(conversationId) {
|
||||
const store = tx.objectStore(STORES.CONVERSATIONS);
|
||||
const result = await runRequest(store.get(conversationId.trim()));
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return result || null;
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to read conversation metadata", error);
|
||||
@@ -369,7 +409,7 @@ export async function listMessagesByConversation(conversationId, options = {}) {
|
||||
userLogger.warn("[dmDb] Failed to list messages", request.error);
|
||||
resolve(results);
|
||||
};
|
||||
}).finally(() => db.close());
|
||||
}); // removed finally db.close()
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to list messages", error);
|
||||
return [];
|
||||
@@ -429,7 +469,7 @@ export async function updateConversationFromMessage(message, options = {}) {
|
||||
|
||||
store.put(next);
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return next;
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to update conversation metadata", error);
|
||||
@@ -478,7 +518,7 @@ export async function updateConversationOpenState(
|
||||
|
||||
store.put(next);
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return next;
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to update conversation open state", error);
|
||||
@@ -512,7 +552,7 @@ export async function listConversations() {
|
||||
userLogger.warn("[dmDb] Failed to list conversations", request.error);
|
||||
resolve(results);
|
||||
};
|
||||
}).finally(() => db.close());
|
||||
}); // removed finally db.close()
|
||||
} catch (error) {
|
||||
userLogger.warn("[dmDb] Failed to list conversations", error);
|
||||
return [];
|
||||
@@ -542,7 +582,7 @@ export async function clearDmDb() {
|
||||
}
|
||||
|
||||
await finalizeTransaction(tx);
|
||||
db.close();
|
||||
// db.close(); // Keep connection open
|
||||
return true;
|
||||
} catch (error) {
|
||||
devLogger.warn("[dmDb] Failed to clear DM database", error);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
listMessagesByConversation,
|
||||
updateConversationFromMessage,
|
||||
writeMessages,
|
||||
closeDmDb,
|
||||
} from "../js/storage/dmDb.js";
|
||||
|
||||
const { indexedDB, IDBKeyRange } = await import("fake-indexeddb");
|
||||
@@ -25,6 +26,7 @@ async function deleteDatabase(name) {
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await closeDmDb();
|
||||
await deleteDatabase("bitvidDm");
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user