feature/IO-2967-Better-Refetch-Handling - Bug fixes and hardening

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-09-27 14:52:38 -04:00
parent 03761bbb2a
commit d705f8211e
3 changed files with 64 additions and 48 deletions

View File

@@ -192,7 +192,7 @@ const main = async () => {
const server = http.createServer(app); const server = http.createServer(app);
const { pubClient, ioRedis } = await applySocketIO(server, app); const { pubClient, ioRedis } = await applySocketIO(server, app);
const api = applyRedisHelpers(pubClient, app); const api = applyRedisHelpers(pubClient, app, logger);
const ioHelpers = applyIOHelpers(app, api, ioRedis, logger); const ioHelpers = applyIOHelpers(app, api, ioRedis, logger);
// Legacy Socket Events // Legacy Socket Events
@@ -211,11 +211,9 @@ const main = async () => {
}; };
// Start server // Start server
try { main().catch((error) => {
main();
} catch (error) {
logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, { logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, {
error: error.message, error: error.message,
errorjson: JSON.stringify(error) errorjson: JSON.stringify(error)
}); });
} });

View File

@@ -1,48 +1,71 @@
const logger = require("./logger");
/** /**
* Apply Redis helper functions * Apply Redis helper functions
* @param pubClient * @param pubClient
* @param app * @param app
*/ */
const applyRedisHelpers = (pubClient, app) => { const applyRedisHelpers = (pubClient, app, logger) => {
// Store session data in Redis // Store session data in Redis
const setSessionData = async (socketId, key, value) => { const setSessionData = async (socketId, key, value) => {
await pubClient.hSet(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient try {
await pubClient.hSet(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient
} catch (error) {
logger.log(`Error Setting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
// Retrieve session data from Redis // Retrieve session data from Redis
const getSessionData = async (socketId, key) => { const getSessionData = async (socketId, key) => {
const data = await pubClient.hGet(`socket:${socketId}`, key); try {
return data ? JSON.parse(data) : null; const data = await pubClient.hGet(`socket:${socketId}`, key);
return data ? JSON.parse(data) : null;
} catch (error) {
logger.log(`Error Getting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
// Clear session data from Redis // Clear session data from Redis
const clearSessionData = async (socketId) => { const clearSessionData = async (socketId) => {
await pubClient.del(`socket:${socketId}`); try {
await pubClient.del(`socket:${socketId}`);
} catch (error) {
logger.log(`Error Clearing Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
// Store multiple session data in Redis // Store multiple session data in Redis
const setMultipleSessionData = async (socketId, keyValues) => { const setMultipleSessionData = async (socketId, keyValues) => {
// keyValues is expected to be an object { key1: value1, key2: value2, ... } try {
const entries = Object.entries(keyValues).map(([key, value]) => [key, JSON.stringify(value)]); // keyValues is expected to be an object { key1: value1, key2: value2, ... }
await pubClient.hSet(`socket:${socketId}`, ...entries.flat()); const entries = Object.entries(keyValues).map(([key, value]) => [key, JSON.stringify(value)]);
await pubClient.hSet(`socket:${socketId}`, ...entries.flat());
} catch (error) {
logger.log(`Error Setting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
// Retrieve multiple session data from Redis // Retrieve multiple session data from Redis
const getMultipleSessionData = async (socketId, keys) => { const getMultipleSessionData = async (socketId, keys) => {
const data = await pubClient.hmGet(`socket:${socketId}`, keys); try {
// Redis returns an object with null values for missing keys, so we parse the non-null ones const data = await pubClient.hmGet(`socket:${socketId}`, keys);
return Object.fromEntries(keys.map((key, index) => [key, data[index] ? JSON.parse(data[index]) : null])); // Redis returns an object with null values for missing keys, so we parse the non-null ones
return Object.fromEntries(keys.map((key, index) => [key, data[index] ? JSON.parse(data[index]) : null]));
} catch (error) {
logger.log(`Error Getting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
const setMultipleFromArraySessionData = async (socketId, keyValueArray) => { const setMultipleFromArraySessionData = async (socketId, keyValueArray) => {
// Use Redis multi/pipeline to batch the commands try {
const multi = pubClient.multi(); // Use Redis multi/pipeline to batch the commands
const multi = pubClient.multi();
keyValueArray.forEach(([key, value]) => { keyValueArray.forEach(([key, value]) => {
multi.hSet(`socket:${socketId}`, key, JSON.stringify(value)); multi.hSet(`socket:${socketId}`, key, JSON.stringify(value));
}); });
await multi.exec(); // Execute all queued commands
await multi.exec(); // Execute all queued commands } catch (error) {
logger.log(`Error Setting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
}; };
// Helper function to add an item to the end of the Redis list // Helper function to add an item to the end of the Redis list
@@ -50,7 +73,7 @@ const applyRedisHelpers = (pubClient, app) => {
try { try {
await pubClient.rPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); await pubClient.rPush(`socket:${socketId}:${key}`, JSON.stringify(newItem));
} catch (error) { } catch (error) {
console.error(`Error adding item to the end of the list for socket ${socketId}:`, error); logger.log(`Error adding item to the end of the list for socket ${socketId}: ${error}`, "ERROR", "redis");
} }
}; };
@@ -59,7 +82,7 @@ const applyRedisHelpers = (pubClient, app) => {
try { try {
await pubClient.lPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); await pubClient.lPush(`socket:${socketId}:${key}`, JSON.stringify(newItem));
} catch (error) { } catch (error) {
console.error(`Error adding item to the beginning of the list for socket ${socketId}:`, error); logger.log(`Error adding item to the beginning of the list for socket ${socketId}: ${error}`, "ERROR", "redis");
} }
}; };
@@ -68,33 +91,33 @@ const applyRedisHelpers = (pubClient, app) => {
try { try {
await pubClient.del(`socket:${socketId}:${key}`); await pubClient.del(`socket:${socketId}:${key}`);
} catch (error) { } catch (error) {
console.error(`Error clearing list for socket ${socketId}:`, error); logger.log(`Error clearing list for socket ${socketId}: ${error}`, "ERROR", "redis");
} }
}; };
// Add methods to manage room users // Add methods to manage room users
const addUserToRoom = async (bodyshopUUID, user) => { const addUserToRoom = async (room, user) => {
try { try {
await pubClient.sAdd(`bodyshopRoom:${bodyshopUUID}`, JSON.stringify(user)); await pubClient.sAdd(room, JSON.stringify(user));
} catch (err) { } catch (error) {
console.error(`Error adding user to room: ${bodyshopUUID}`); logger.log(`Error adding user to room ${room}: ${error}`, "ERROR", "redis");
} }
}; };
const removeUserFromRoom = async (bodyshopUUID, user) => { const removeUserFromRoom = async (room, user) => {
try { try {
await pubClient.sRem(`bodyshopRoom:${bodyshopUUID}`, JSON.stringify(user)); await pubClient.sRem(room, JSON.stringify(user));
} catch (err) { } catch (error) {
console.error(`Error remove user from room: ${bodyshopUUID}`); logger.log(`Error removing user to room ${room}: ${error}`, "ERROR", "redis");
} }
}; };
const getUsersInRoom = async (bodyshopUUID) => { const getUsersInRoom = async (room) => {
try { try {
const users = await pubClient.sMembers(`bodyshopRoom:${bodyshopUUID}`); const users = await pubClient.sMembers(room);
return users.map((user) => JSON.parse(user)); return users.map((user) => JSON.parse(user));
} catch (err) { } catch (error) {
console.error(`Error getUsersInRoom: ${bodyshopUUID}`); logger.log(`Error getting users in room ${room}: ${error}`, "ERROR", "redis");
} }
}; };

View File

@@ -33,8 +33,8 @@ const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRo
try { try {
const room = getBodyshopRoom(bodyshopUUID); const room = getBodyshopRoom(bodyshopUUID);
socket.join(room); socket.join(room);
await addUserToRoom(room, { uid: socket.user.uid, email: socket.user.email }); await addUserToRoom(room, { uid: socket.user.uid, email: socket.user.email, socket: socket.id });
createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${bodyshopUUID}`); createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${room}`);
// Notify all users in the room about the updated user list // Notify all users in the room about the updated user list
const usersInRoom = await getUsersInRoom(room); const usersInRoom = await getUsersInRoom(room);
@@ -79,13 +79,8 @@ const redisSocketEvents = (io, { addUserToRoom, getUsersInRoom, removeUserFromRo
// Get all rooms the socket is part of // Get all rooms the socket is part of
const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id); const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id);
for (const room of rooms) {
for (const bodyshopRoom of rooms) { await removeUserFromRoom(room, { uid: socket.user.uid, email: socket.user.email, socket: socket.id });
await removeUserFromRoom(bodyshopRoom, { uid: socket.user.uid, email: socket.user.email });
// Notify all users in the room about the updated user list
const usersInRoom = await getUsersInRoom(bodyshopRoom);
io.to(bodyshopRoom).emit("room-users-updated", usersInRoom);
} }
} catch (error) { } catch (error) {
createLogEvent(socket, "ERROR", `Error getting room: ${error}`); createLogEvent(socket, "ERROR", `Error getting room: ${error}`);