Skip to content

Commit 7470ec0

Browse files
committed
Further work on caching
1 parent 906ac6d commit 7470ec0

4 files changed

Lines changed: 104 additions & 50 deletions

File tree

backend/src/db/operations/edgeOperations.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
import { query, queryMany } from '../db';
22
import { generateEdgeId } from '../idGenerator';
33
import { DbEdge } from '../dbTypes';
4-
import { memoryCache } from '../../services/cacheService';
4+
import { memoryCache, withCache } from '../../services/cacheService';
5+
6+
7+
/*
8+
Cached functions
9+
*/
10+
11+
/**
12+
* Gets edges for a single graph with caching.
13+
*
14+
* @param graphId - The ID of the graph to get edges for.
15+
* @returns Promise resolving to an array of DbEdge objects.
16+
*/
17+
export async function getEdgesByGraphId(
18+
graphId: string
19+
): Promise<DbEdge[]> {
20+
const cacheKey = `graph:${graphId}:edges`;
21+
22+
return withCache(
23+
cacheKey,
24+
60 * 60 * 1000, // 1 hour cache
25+
async () => {
26+
return queryMany<DbEdge>(
27+
'SELECT * FROM edges WHERE graph_id = $1',
28+
[graphId]
29+
);
30+
}
31+
);
32+
}
33+
534

635

736
export async function getEdgesFromDb(graphIds: string[]): Promise<DbEdge[]> {
@@ -75,7 +104,7 @@ export async function updateGraphEdges(graphId: string, newEdges: { sourceId: st
75104
})));
76105
}
77106

78-
const cacheKeyPattern = `graph:${graphId}`;
107+
const cacheKeyPattern = `graph:${graphId}:edges`;
79108
memoryCache.deletePattern(cacheKeyPattern);
80109

81110
// Return all edges in the graph after the update

backend/src/db/operations/graphOperations.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { generateGraphId } from '../idGenerator';
22
import { Argument, Edge, Graph, GraphData } from '../../.shared/types';
33
import { getArgumentScores } from '../../analysis/argumentScoreHandler';
4-
import { getReactionCountsFromDb } from './reactionOperations';
4+
import { getReactionCounts, getUserReactionsByGraphId } from './reactionOperations';
55
import { getTimestamp } from '../getTimestamp';
6-
import { DbArgument, DbEdge, DbGraph, DbReaction } from '../dbTypes';
6+
import { DbGraph } from '../dbTypes';
77
import { query, queryMany, queryOne } from '../db';
88
import { memoryCache, withCache } from '../../services/cacheService';
99
import { getArgumentsByGraphId } from './argumentOperations';
10-
import { getEdgesFromDb } from './edgeOperations';
10+
import { getEdgesByGraphId } from './edgeOperations';
1111

1212
/*
1313
Cached functions
@@ -57,44 +57,37 @@ export async function getUserGraphs(userId: string): Promise<GraphData[]> {
5757
);
5858
}
5959

60-
61-
export async function getFullGraph(graphId: string, userId?: string): Promise<Graph> {
62-
const cacheKey = `graph:${graphId}${userId ? `:${userId}` : ''}`;
60+
export async function getGraphName(graphId: string): Promise<string> {
61+
const cacheKey = `graph-name:${graphId}`;
6362

6463
return withCache(
6564
cacheKey,
6665
60 * 60 * 1000, // 1 hour cache
67-
() => getFullGraphFromDb(graphId, userId)
66+
async () => {
67+
const result = await query('SELECT name FROM graphs WHERE id = $1', [graphId]);
68+
return result.rows[0].name;
69+
}
6870
);
6971
}
7072

71-
/*
72-
Heavy functions, meant to be used from cached or rarely used functions
73-
*/
74-
75-
async function getFullGraphFromDb(graphId: string, userId?: string): Promise<Graph> {
76-
console.log(`Loading graph into cache: ${graphId}`);
73+
export async function getFullGraph(graphId: string, userId?: string): Promise<Graph> {
74+
console.log(`Getting full graph ${graphId} for user ${userId}`);
7775
const startTime = performance.now();
7876

7977
const [
80-
{ rows: [{ name }] },
78+
name,
8179
argumentsResult,
8280
edgesResult,
8381
reactionCounts,
8482
argumentScores,
8583
userReactionsResult
8684
] = await Promise.all([
87-
query('SELECT name FROM graphs WHERE id = $1', [graphId]),
85+
getGraphName(graphId),
8886
getArgumentsByGraphId(graphId),
89-
getEdgesFromDb([graphId]),
90-
getReactionCountsFromDb(graphId),
87+
getEdgesByGraphId(graphId),
88+
getReactionCounts(graphId),
9189
getArgumentScores(graphId),
92-
userId ? queryMany<DbReaction>(
93-
`SELECT *
94-
FROM reactions
95-
WHERE user_id = $1 AND argument_id IN (SELECT id FROM arguments WHERE graph_id = $2)`,
96-
[userId, graphId]
97-
) : Promise.resolve([])
90+
userId ? getUserReactionsByGraphId(userId, graphId) : Promise.resolve([])
9891
]);
9992

10093
if (!name) {
@@ -133,7 +126,7 @@ async function getFullGraphFromDb(graphId: string, userId?: string): Promise<Gra
133126

134127
const endTime = performance.now();
135128
const duration = ((endTime - startTime) / 1000).toFixed(2);
136-
console.log(`Loaded graph "${name}" (${graphId}) in ${duration}s`);
129+
console.log(`Loaded graph "${name}" (${graphId}) for user ${userId || 'anonymous'} in ${duration}s`);
137130

138131
return {
139132
id: graphId,
@@ -143,6 +136,10 @@ async function getFullGraphFromDb(graphId: string, userId?: string): Promise<Gra
143136
} as Graph;
144137
}
145138

139+
/*
140+
Heavy functions, meant to be used from cached or rarely used functions
141+
*/
142+
146143
async function getGraphDataFromDb(graphIds: string[]): Promise<GraphData[]> {
147144
const placeholders = graphIds.map((_, i) => `$${i + 1}`).join(',');
148145
const result = await query(

backend/src/db/operations/reactionOperations.ts

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,56 @@ export async function getReactionsByGraphId(
2727
);
2828
}
2929

30+
export async function getUserReactionsByGraphId(
31+
userId: string,
32+
graphId: string
33+
): Promise<DbReaction[]> {
34+
const cacheKey = `user:${userId}:graph:${graphId}:reactions`;
35+
36+
return withCache(
37+
cacheKey,
38+
60 * 60 * 1000, // 1 hour cache
39+
async () => {
40+
return queryMany<DbReaction>(
41+
`SELECT *
42+
FROM reactions
43+
WHERE user_id = $1 AND argument_id IN (SELECT id FROM arguments WHERE graph_id = $2)`,
44+
[userId, graphId]
45+
);
46+
}
47+
);
48+
}
49+
50+
export async function getReactionCounts(
51+
graphId: string
52+
): Promise<Map<string, ReactionCounts>> {
53+
const cacheKey = `graph:${graphId}:reactions:counts`;
54+
55+
return withCache(
56+
cacheKey,
57+
60 * 60 * 1000, // 1 hour cache
58+
async () => {
59+
const reactionCountsResult = await query(
60+
`SELECT argument_id, type, COUNT(*) as count
61+
FROM reactions
62+
WHERE argument_id IN (SELECT id FROM arguments WHERE graph_id = $1)
63+
GROUP BY argument_id, type`,
64+
[graphId]
65+
);
66+
67+
const reactionCountsMap = new Map();
68+
reactionCountsResult.rows.forEach((row: any) => {
69+
if (!reactionCountsMap.has(row.argument_id)) {
70+
reactionCountsMap.set(row.argument_id, { agree: 0, disagree: 0, unclear: 0 });
71+
}
72+
reactionCountsMap.get(row.argument_id)[row.type] = parseInt(row.count);
73+
});
74+
75+
return reactionCountsMap;
76+
}
77+
);
78+
}
79+
3080
export interface ReactionForAnalysis {
3181
reactions: {
3282
userId: string;
@@ -96,28 +146,6 @@ export async function getReactionsForAnalysis(
96146
Heavy functions, meant to be used from cached or rarely used functions
97147
*/
98148

99-
export async function getReactionCountsFromDb(
100-
graphId: string
101-
): Promise<Map<string, ReactionCounts>> {
102-
const reactionCountsResult = await query(
103-
`SELECT argument_id, type, COUNT(*) as count
104-
FROM reactions
105-
WHERE argument_id IN (SELECT id FROM arguments WHERE graph_id = $1)
106-
GROUP BY argument_id, type`,
107-
[graphId]
108-
);
109-
110-
const reactionCountsMap = new Map();
111-
reactionCountsResult.rows.forEach((row: any) => {
112-
if (!reactionCountsMap.has(row.argument_id)) {
113-
reactionCountsMap.set(row.argument_id, { agree: 0, disagree: 0, unclear: 0 });
114-
}
115-
reactionCountsMap.get(row.argument_id)[row.type] = parseInt(row.count);
116-
});
117-
118-
return reactionCountsMap;
119-
}
120-
121149
export async function getSpecificUserArgumentReactionsFromDb(
122150
userArgumentPairs: { userId: string; argumentId: string }[]
123151
): Promise<DbReaction[]> {

backend/src/websocket/updateHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Server, Socket } from "socket.io";
22
import { getArgumentScores } from "../analysis/argumentScoreHandler";
3-
import { getReactionCountsFromDb } from "../db/operations/reactionOperations";
3+
import { getReactionCounts } from "../db/operations/reactionOperations";
44
import { Argument, Edge, ReactionCounts, Score, UserReaction } from "../.shared/types";
55

66
export const sendNewArgumentsUpdate = async (
@@ -24,7 +24,7 @@ export const sendGraphReactionsAndScoresUpdate = async (
2424
io: Server,
2525
graphId: string
2626
) => {
27-
const reactionCounts: Map<string, ReactionCounts> = await getReactionCountsFromDb(graphId);
27+
const reactionCounts: Map<string, ReactionCounts> = await getReactionCounts(graphId);
2828
const argumentScores: Map<string, Score> = await getArgumentScores(graphId);
2929
const graphReactionsRecord = Object.fromEntries(reactionCounts);
3030
const argumentScoresRecord = Object.fromEntries(argumentScores);

0 commit comments

Comments
 (0)