Loading package.json +0 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "tsx -r dotenv/config src/index.ts" }, "keywords": [], Loading src/lib/postgres.ts +13 −3 Original line number Diff line number Diff line Loading @@ -17,10 +17,20 @@ export const query = { [end] ); }, pixelCovering: (x: number, y: number, date: Date) => { /** Batch: get all pixel history for a set of (x,y) positions, ordered by createdAt ASC per position. * Used to avoid N+1 queries when resolving covering pixels for deleted pixels. */ batchPixelHistory: (positions: { x: number; y: number }[]) => { if (positions.length === 0) return Promise.resolve({ rows: [] as DBPixel[] }); const params: number[] = []; const placeholders = positions .map((p) => { params.push(p.x, p.y); return `($${params.length - 1}::int, $${params.length}::int)`; }) .join(", "); return client.query<DBPixel>( `SELECT * FROM "Pixel" WHERE x = $1 AND y = $2 AND "deletedAt" IS NULL AND "createdAt" < $3 ORDER BY "createdAt" DESC LIMIT 1`, [x, y, date] `SELECT * FROM "Pixel" WHERE (x, y) IN (${placeholders}) ORDER BY x, y, "createdAt" ASC`, params ); }, }; Loading src/render_worker.ts +82 −22 Original line number Diff line number Diff line Loading @@ -158,44 +158,104 @@ const getFilenameForFrameID = (frameId: number) => "0".repeat(Math.max(0, 5 - (frameId + "").length)) + frameId; const getPixelsAsLog = async (start: number, end: number) => { let pixels: Pixel[] = []; const dbpixels = ( await query.pixelsWithinTimeframe(new Date(start), new Date(end)) ).rows; for (const dbpixel of dbpixels) { let pixel: Pixel = { x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: 0, hex: "", }; // Separate created pixels (new placements) from deleted pixels (removals) const deletedPixels: DBPixel[] = []; for (const dbpixel of dbpixels) { if ( dbpixel.createdAt.getTime() >= start && dbpixel.createdAt.getTime() < end ) { // this was created within the timeframe pixel.hex = dbpixel.color; pixel.date = dbpixel.createdAt.getTime(); // created within timeframe — handled below } else if (dbpixel.deletedAt) { deletedPixels.push(dbpixel); } else { if (dbpixel.deletedAt) { // this is a deleted pixel that was in the timeframe log("received pixel but not deleted or in timeframe", dbpixel); } } const coveredPixel: DBPixel | undefined = ( await query.pixelCovering(dbpixel.x, dbpixel.y, dbpixel.createdAt) ).rows[0]; // Batch-resolve covering pixels for all deleted pixels in ONE query // instead of N individual queries let coveringColorCache = new Map<string, string>(); if (deletedPixels.length > 0) { // Collect unique (x, y) positions from all deleted pixels const seen = new Set<string>(); const positions: { x: number; y: number }[] = []; for (const dp of deletedPixels) { const key = dp.x + "," + dp.y; if (!seen.has(key)) { seen.add(key); positions.push({ x: dp.x, y: dp.y }); } } pixel.hex = coveredPixel?.color || "ffffff"; pixel.date = dbpixel.deletedAt.getTime(); } else { log("received pixel but not deleted or in timeframe", dbpixel); // Get ALL pixel history at these positions in one batch query const allPixels = (await query.batchPixelHistory(positions)).rows; // Group history by (x, y), already ordered by createdAt ASC from SQL const historyByPosition = new Map<string, DBPixel[]>(); for (const p of allPixels) { const key = p.x + "," + p.y; let list = historyByPosition.get(key); if (!list) { list = []; historyByPosition.set(key, list); } list.push(p); } // For each deleted pixel, walk backwards through the position's history // to find the pixel that was active just before this one was placed for (const dp of deletedPixels) { const key = dp.x + "," + dp.y; const history = historyByPosition.get(key); if (!history) { coveringColorCache.set(key + ":" + dp.createdAt.getTime(), "ffffff"); continue; } // History is ordered createdAt ASC; walk backwards to find // the latest pixel created before dp was placed let covering = "ffffff"; for (let i = history.length - 1; i >= 0; i--) { if (history[i].createdAt.getTime() < dp.createdAt.getTime()) { covering = history[i].color; break; } } coveringColorCache.set(key + ":" + dp.createdAt.getTime(), covering); } } pixels.push(pixel); // Build event list preserving original chronological order const pixels: Pixel[] = []; for (const dbpixel of dbpixels) { if ( dbpixel.createdAt.getTime() >= start && dbpixel.createdAt.getTime() < end ) { // this was created within the timeframe pixels.push({ x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: dbpixel.createdAt.getTime(), hex: dbpixel.color, }); } else if (dbpixel.deletedAt) { // this is a deleted pixel that was in the timeframe const cacheKey = dbpixel.x + "," + dbpixel.y + ":" + dbpixel.createdAt.getTime(); pixels.push({ x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: dbpixel.deletedAt.getTime(), hex: coveringColorCache.get(cacheKey) || "ffffff", }); } } return pixels; Loading Loading
package.json +0 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "tsx -r dotenv/config src/index.ts" }, "keywords": [], Loading
src/lib/postgres.ts +13 −3 Original line number Diff line number Diff line Loading @@ -17,10 +17,20 @@ export const query = { [end] ); }, pixelCovering: (x: number, y: number, date: Date) => { /** Batch: get all pixel history for a set of (x,y) positions, ordered by createdAt ASC per position. * Used to avoid N+1 queries when resolving covering pixels for deleted pixels. */ batchPixelHistory: (positions: { x: number; y: number }[]) => { if (positions.length === 0) return Promise.resolve({ rows: [] as DBPixel[] }); const params: number[] = []; const placeholders = positions .map((p) => { params.push(p.x, p.y); return `($${params.length - 1}::int, $${params.length}::int)`; }) .join(", "); return client.query<DBPixel>( `SELECT * FROM "Pixel" WHERE x = $1 AND y = $2 AND "deletedAt" IS NULL AND "createdAt" < $3 ORDER BY "createdAt" DESC LIMIT 1`, [x, y, date] `SELECT * FROM "Pixel" WHERE (x, y) IN (${placeholders}) ORDER BY x, y, "createdAt" ASC`, params ); }, }; Loading
src/render_worker.ts +82 −22 Original line number Diff line number Diff line Loading @@ -158,44 +158,104 @@ const getFilenameForFrameID = (frameId: number) => "0".repeat(Math.max(0, 5 - (frameId + "").length)) + frameId; const getPixelsAsLog = async (start: number, end: number) => { let pixels: Pixel[] = []; const dbpixels = ( await query.pixelsWithinTimeframe(new Date(start), new Date(end)) ).rows; for (const dbpixel of dbpixels) { let pixel: Pixel = { x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: 0, hex: "", }; // Separate created pixels (new placements) from deleted pixels (removals) const deletedPixels: DBPixel[] = []; for (const dbpixel of dbpixels) { if ( dbpixel.createdAt.getTime() >= start && dbpixel.createdAt.getTime() < end ) { // this was created within the timeframe pixel.hex = dbpixel.color; pixel.date = dbpixel.createdAt.getTime(); // created within timeframe — handled below } else if (dbpixel.deletedAt) { deletedPixels.push(dbpixel); } else { if (dbpixel.deletedAt) { // this is a deleted pixel that was in the timeframe log("received pixel but not deleted or in timeframe", dbpixel); } } const coveredPixel: DBPixel | undefined = ( await query.pixelCovering(dbpixel.x, dbpixel.y, dbpixel.createdAt) ).rows[0]; // Batch-resolve covering pixels for all deleted pixels in ONE query // instead of N individual queries let coveringColorCache = new Map<string, string>(); if (deletedPixels.length > 0) { // Collect unique (x, y) positions from all deleted pixels const seen = new Set<string>(); const positions: { x: number; y: number }[] = []; for (const dp of deletedPixels) { const key = dp.x + "," + dp.y; if (!seen.has(key)) { seen.add(key); positions.push({ x: dp.x, y: dp.y }); } } pixel.hex = coveredPixel?.color || "ffffff"; pixel.date = dbpixel.deletedAt.getTime(); } else { log("received pixel but not deleted or in timeframe", dbpixel); // Get ALL pixel history at these positions in one batch query const allPixels = (await query.batchPixelHistory(positions)).rows; // Group history by (x, y), already ordered by createdAt ASC from SQL const historyByPosition = new Map<string, DBPixel[]>(); for (const p of allPixels) { const key = p.x + "," + p.y; let list = historyByPosition.get(key); if (!list) { list = []; historyByPosition.set(key, list); } list.push(p); } // For each deleted pixel, walk backwards through the position's history // to find the pixel that was active just before this one was placed for (const dp of deletedPixels) { const key = dp.x + "," + dp.y; const history = historyByPosition.get(key); if (!history) { coveringColorCache.set(key + ":" + dp.createdAt.getTime(), "ffffff"); continue; } // History is ordered createdAt ASC; walk backwards to find // the latest pixel created before dp was placed let covering = "ffffff"; for (let i = history.length - 1; i >= 0; i--) { if (history[i].createdAt.getTime() < dp.createdAt.getTime()) { covering = history[i].color; break; } } coveringColorCache.set(key + ":" + dp.createdAt.getTime(), covering); } } pixels.push(pixel); // Build event list preserving original chronological order const pixels: Pixel[] = []; for (const dbpixel of dbpixels) { if ( dbpixel.createdAt.getTime() >= start && dbpixel.createdAt.getTime() < end ) { // this was created within the timeframe pixels.push({ x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: dbpixel.createdAt.getTime(), hex: dbpixel.color, }); } else if (dbpixel.deletedAt) { // this is a deleted pixel that was in the timeframe const cacheKey = dbpixel.x + "," + dbpixel.y + ":" + dbpixel.createdAt.getTime(); pixels.push({ x: dbpixel.x, y: dbpixel.y, who: dbpixel.userId, date: dbpixel.deletedAt.getTime(), hex: coveringColorCache.get(cacheKey) || "ffffff", }); } } return pixels; Loading