perf: add Server-Timing, split fitness bundle, tighten DB queries
- Add `timing` handle in hooks.server.ts emitting Server-Timing headers
and expose `locals.timing.mark/measure` for per-load instrumentation.
- Drop dead `getEnrichedExerciseById` fallback in fitness detail page —
server load already 404s via errorWithVerse, so the client no longer
pulls exercisedb-raw.json (~760KB) into the detail bundle.
- Add `{ createdBy: 1, nextExecutionDate: -1 }` index on RecurringPayment
for user-scoped list queries.
- Narrow populate projections in cospend/debts (title/date/category on
userSplits, _id only on allRelatedSplits) to cut payload + hydration.
- Parallelize today's sessions + WorkoutSchedule lookup in the nutrition
page load via Promise.all; add `.lean()` + `.select('templateId')` to
the lastScheduled query.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homepage",
|
||||
"version": "1.37.2",
|
||||
"version": "1.37.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Vendored
+4
@@ -13,6 +13,10 @@ declare global {
|
||||
interface Locals {
|
||||
auth(): Promise<Session | null>;
|
||||
session?: Session | null;
|
||||
timing: {
|
||||
mark(name: string, dur: number): void;
|
||||
measure<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
|
||||
};
|
||||
}
|
||||
// interface PageData {}
|
||||
interface PageState {
|
||||
|
||||
+27
-1
@@ -6,6 +6,31 @@ import { initializeScheduler } from "./lib/server/scheduler"
|
||||
import { dbConnect } from "./utils/db"
|
||||
import { errorWithVerse, getRandomVerse } from "$lib/server/errorQuote"
|
||||
|
||||
async function timing({ event, resolve }: Parameters<Handle>[0]) {
|
||||
const marks: Record<string, number> = {};
|
||||
event.locals.timing = {
|
||||
mark(name, dur) {
|
||||
marks[name] = (marks[name] ?? 0) + dur;
|
||||
},
|
||||
async measure(name, fn) {
|
||||
const t0 = performance.now();
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
this.mark(name, performance.now() - t0);
|
||||
}
|
||||
}
|
||||
};
|
||||
const t0 = performance.now();
|
||||
const response = await resolve(event);
|
||||
marks.total = performance.now() - t0;
|
||||
const header = Object.entries(marks)
|
||||
.map(([k, v]) => `${k};dur=${v.toFixed(1)}`)
|
||||
.join(', ');
|
||||
response.headers.set('Server-Timing', header);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Initialize database connection on server startup
|
||||
console.log('🚀 Server starting - initializing database connection...');
|
||||
await dbConnect().then(() => {
|
||||
@@ -19,7 +44,7 @@ await dbConnect().then(() => {
|
||||
});
|
||||
|
||||
async function authorization({ event, resolve }: Parameters<Handle>[0]) {
|
||||
const session = await event.locals.auth();
|
||||
const session = await event.locals.timing.measure('auth', () => event.locals.auth());
|
||||
event.locals.session = session;
|
||||
const { fetch, url } = event;
|
||||
|
||||
@@ -107,6 +132,7 @@ export const handleError: HandleServerError = async ({ error, event, status, mes
|
||||
};
|
||||
|
||||
export const handle: Handle = sequence(
|
||||
timing,
|
||||
auth.handle,
|
||||
authorization
|
||||
);
|
||||
|
||||
@@ -137,5 +137,6 @@ const RecurringPaymentSchema = new mongoose.Schema(
|
||||
|
||||
// Index for efficiently finding payments that need to be executed
|
||||
RecurringPaymentSchema.index({ nextExecutionDate: 1, isActive: 1 });
|
||||
RecurringPaymentSchema.index({ createdBy: 1, nextExecutionDate: -1 });
|
||||
|
||||
export const RecurringPayment = mongoose.model<IRecurringPayment>("RecurringPayment", RecurringPaymentSchema);
|
||||
@@ -31,7 +31,7 @@ export const GET: RequestHandler = async ({ locals }) => {
|
||||
try {
|
||||
// Get all splits for the current user
|
||||
const userSplits = await PaymentSplit.find({ username: currentUser })
|
||||
.populate('paymentId')
|
||||
.populate('paymentId', 'title date category _id')
|
||||
.lean();
|
||||
|
||||
// Get all other users who have splits with payments involving the current user
|
||||
@@ -40,7 +40,7 @@ export const GET: RequestHandler = async ({ locals }) => {
|
||||
paymentId: { $in: paymentIds } as any,
|
||||
username: { $ne: currentUser }
|
||||
})
|
||||
.populate('paymentId')
|
||||
.populate('paymentId', '_id')
|
||||
.lean();
|
||||
|
||||
// Group debts by user
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
import { getEnrichedExerciseById } from '$lib/data/exercisedb';
|
||||
|
||||
/** @param {string | undefined | null} type @param {'en'|'de'} lang */
|
||||
function exerciseTypeInfo(type, lang) {
|
||||
@@ -20,7 +19,6 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
import { localizeExercise, translateTerm } from '$lib/data/exercises';
|
||||
import { detectFitnessLang, fitnessSlugs, t } from '$lib/js/fitnessI18n';
|
||||
import { ChevronRight } from '@lucide/svelte';
|
||||
|
||||
@@ -54,7 +52,7 @@
|
||||
|
||||
let activeTab = $state('about');
|
||||
|
||||
const exercise = $derived(data.exercise ?? getEnrichedExerciseById($page.params.id, lang));
|
||||
const exercise = $derived(data.exercise);
|
||||
const typeInfo = $derived(exerciseTypeInfo(exercise?.exerciseType, lang));
|
||||
const similar = $derived(data.similar ?? []);
|
||||
const history = $derived(data.history?.history ?? []);
|
||||
|
||||
+8
-6
@@ -19,10 +19,13 @@ export const load: PageServerLoad = async ({ fetch, params, locals }) => {
|
||||
try {
|
||||
const user = await requireAuth(locals);
|
||||
await dbConnect();
|
||||
const sessions = await WorkoutSession.find({
|
||||
createdBy: user.nickname,
|
||||
startTime: { $gte: dayStart, $lte: dayEnd }
|
||||
}).select('kcalEstimate').lean();
|
||||
const [sessions, schedule] = await Promise.all([
|
||||
WorkoutSession.find({
|
||||
createdBy: user.nickname,
|
||||
startTime: { $gte: dayStart, $lte: dayEnd }
|
||||
}).select('kcalEstimate').lean(),
|
||||
WorkoutSchedule.findOne({ userId: user.nickname }).lean()
|
||||
]);
|
||||
let kcal = 0;
|
||||
for (const s of sessions) {
|
||||
if (s.kcalEstimate?.kcal) kcal += s.kcalEstimate.kcal;
|
||||
@@ -31,12 +34,11 @@ export const load: PageServerLoad = async ({ fetch, params, locals }) => {
|
||||
// If no exercise done today, project kcal from the next scheduled template
|
||||
let projected = null;
|
||||
if (kcal === 0) {
|
||||
const schedule = await WorkoutSchedule.findOne({ userId: user.nickname });
|
||||
if (schedule?.templateOrder?.length) {
|
||||
const lastScheduled = await WorkoutSession.findOne({
|
||||
createdBy: user.nickname,
|
||||
templateId: { $in: schedule.templateOrder }
|
||||
}).sort({ startTime: -1 });
|
||||
}).sort({ startTime: -1 }).select('templateId').lean();
|
||||
|
||||
let nextId;
|
||||
if (!lastScheduled?.templateId) {
|
||||
|
||||
Reference in New Issue
Block a user