feat: add Redis caching to cospend API endpoints
All checks were successful
CI / update (push) Successful in 1m21s

Implement comprehensive caching for all cospend routes to improve performance:

Cache Implementation:
- Balance API: 30-minute TTL for user balances and global balances
- Debts API: 15-minute TTL for debt breakdown calculations
- Payments List: 10-minute TTL with pagination support
- Individual Payment: 30-minute TTL for payment details

Cache Invalidation:
- Created invalidateCospendCaches() helper function
- Invalidates user balances, debts, and payment lists on mutations
- Applied to payment create, update, and delete operations
- Applied to recurring payment execution (manual and cron)
This commit is contained in:
2026-01-13 19:45:09 +01:00
parent 041d415525
commit baa3f3e533
7 changed files with 163 additions and 29 deletions

View File

@@ -3,6 +3,7 @@ import { PaymentSplit } from '../../../../models/PaymentSplit';
import { Payment } from '../../../../models/Payment'; // Need to import Payment for populate to work
import { dbConnect } from '../../../../utils/db';
import { error, json } from '@sveltejs/kit';
import cache from '$lib/server/cache';
export const GET: RequestHandler = async ({ locals, url }) => {
const auth = await locals.auth();
@@ -14,9 +15,17 @@ export const GET: RequestHandler = async ({ locals, url }) => {
const includeAll = url.searchParams.get('all') === 'true';
await dbConnect();
try {
if (includeAll) {
// Try cache first for all balances
const cacheKey = 'cospend:balance:all';
const cached = await cache.get(cacheKey);
if (cached) {
return json(JSON.parse(cached));
}
const allSplits = await PaymentSplit.aggregate([
{
$group: {
@@ -44,14 +53,27 @@ export const GET: RequestHandler = async ({ locals, url }) => {
netBalance: 0
};
return json({
const result = {
currentUser: currentUserBalance,
allBalances: allSplits
});
};
// Cache for 30 minutes
await cache.set(cacheKey, JSON.stringify(result), 1800);
return json(result);
} else {
// Try cache first for individual user balance
const cacheKey = `cospend:balance:${username}`;
const cached = await cache.get(cacheKey);
if (cached) {
return json(JSON.parse(cached));
}
const userSplits = await PaymentSplit.find({ username }).lean();
// Calculate net balance: negative = you are owed money, positive = you owe money
const netBalance = userSplits.reduce((sum, split) => sum + split.amount, 0);
@@ -78,17 +100,22 @@ export const GET: RequestHandler = async ({ locals, url }) => {
paymentId: split.paymentId._id,
username: { $ne: username }
}).lean();
if (otherSplit) {
split.otherUser = otherSplit.username;
}
}
}
return json({
const result = {
netBalance,
recentSplits
});
};
// Cache for 30 minutes
await cache.set(cacheKey, JSON.stringify(result), 1800);
return json(result);
}
} catch (e) {