feat: improving performance on sources

This commit is contained in:
Chubby Granny Chaser
2025-10-14 17:42:59 +01:00
parent 311555386e
commit 41227b125e
3 changed files with 100 additions and 10 deletions

View File

@@ -1,7 +1,10 @@
import { registerEvent } from "../register-event";
import { downloadSourcesSublevel } from "@main/level";
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
import { HydraApi } from "@main/services";
import { importDownloadSourceToLocal } from "./helpers";
import {
importDownloadSourceToLocal,
invalidateDownloadSourcesCache,
} from "./helpers";
const addDownloadSource = async (
_event: Electron.IpcMainInvokeEvent,
@@ -12,6 +15,28 @@ const addDownloadSource = async (
throw new Error("Failed to import download source");
}
// Verify that repacks were actually written to the database (read-after-write)
// This ensures all async operations are complete before proceeding
let repackCount = 0;
const repackIds: number[] = [];
for await (const [, repack] of repacksSublevel.iterator()) {
if (repack.downloadSourceId === result.id) {
repackCount++;
repackIds.push(repack.id);
}
}
// Log for debugging - helps identify if repacks are being created
console.log(
`✅ Download source ${result.id} (${result.name}) created with ${repackCount} repacks`
);
console.log(
` Repack IDs: [${repackIds.slice(0, 5).join(", ")}${repackIds.length > 5 ? "..." : ""}]`
);
console.log(
` Object IDs: [${result.objectIds.slice(0, 5).join(", ")}${result.objectIds.length > 5 ? "..." : ""}]`
);
await HydraApi.post("/profile/download-sources", {
urls: [url],
});
@@ -24,6 +49,7 @@ const addDownloadSource = async (
{ needsAuth: false }
);
// Update the source with fingerprint
const updatedSource = await downloadSourcesSublevel.get(`${result.id}`);
if (updatedSource) {
await downloadSourcesSublevel.put(`${result.id}`, {
@@ -33,6 +59,33 @@ const addDownloadSource = async (
});
}
// Final verification: ensure the source with fingerprint is persisted
const finalSource = await downloadSourcesSublevel.get(`${result.id}`);
if (!finalSource || !finalSource.fingerprint) {
throw new Error("Failed to persist download source with fingerprint");
}
// Verify repacks still exist after fingerprint update
let finalRepackCount = 0;
for await (const [, repack] of repacksSublevel.iterator()) {
if (repack.downloadSourceId === result.id) {
finalRepackCount++;
}
}
if (finalRepackCount !== repackCount) {
console.warn(
`⚠️ Repack count mismatch! Before: ${repackCount}, After: ${finalRepackCount}`
);
} else {
console.log(
`✅ Final verification passed: ${finalRepackCount} repacks confirmed`
);
}
// Invalidate cache to ensure fresh data on next read
invalidateDownloadSourcesCache();
return {
...result,
fingerprint,

View File

@@ -282,6 +282,9 @@ export const importDownloadSourceToLocal = async (
steamGames
);
// Invalidate ID caches after creating new repacks to prevent ID collisions
invalidateIdCaches();
return {
...downloadSource,
objectIds,

View File

@@ -3,7 +3,7 @@ import axios, { AxiosError } from "axios";
import { z } from "zod";
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
import { DownloadSourceStatus } from "@shared";
import { invalidateDownloadSourcesCache } from "./helpers";
import { invalidateDownloadSourcesCache, invalidateIdCaches } from "./helpers";
const downloadSourceSchema = z.object({
name: z.string().max(255),
@@ -111,19 +111,52 @@ const addNewDownloads = async (
for (const download of downloads) {
const formattedTitle = formatRepackName(download.title);
const [firstLetter] = formattedTitle;
const games = steamGames[firstLetter] || [];
let gamesInSteam: FormattedSteamGame[] = [];
const gamesInSteam = games.filter((game) =>
formattedTitle.startsWith(game.formattedName)
);
// Only try to match if we have a valid formatted title
if (formattedTitle && formattedTitle.length > 0) {
const [firstLetter] = formattedTitle;
const games = steamGames[firstLetter] || [];
if (gamesInSteam.length === 0) continue;
// Try exact prefix match first
gamesInSteam = games.filter((game) =>
formattedTitle.startsWith(game.formattedName)
);
// If no exact prefix match, try contains match (more lenient)
if (gamesInSteam.length === 0) {
gamesInSteam = games.filter(
(game) =>
formattedTitle.includes(game.formattedName) ||
game.formattedName.includes(formattedTitle)
);
}
// If still no match, try checking all letters (not just first letter)
// This helps with repacks that use abbreviations or alternate naming
if (gamesInSteam.length === 0) {
for (const letter of Object.keys(steamGames)) {
const letterGames = steamGames[letter] || [];
const matches = letterGames.filter(
(game) =>
formattedTitle.includes(game.formattedName) ||
game.formattedName.includes(formattedTitle)
);
if (matches.length > 0) {
gamesInSteam = matches;
break;
}
}
}
}
// Add matched game IDs to source tracking
for (const game of gamesInSteam) {
objectIdsOnSource.add(String(game.id));
}
// Create the repack even if no games matched
// This ensures all repacks from sources are imported
const repack = {
id: nextRepackId++,
objectIds: gamesInSteam.map((game) => String(game.id)),
@@ -248,8 +281,9 @@ const syncDownloadSources = async (
}
}
// Invalidate cache after all sync operations complete
// Invalidate caches after all sync operations complete
invalidateDownloadSourcesCache();
invalidateIdCaches();
return newRepacksCount;
} catch (err) {