I am a little step away of a very simple database cache system with nodejs. I don't want to install a complete data handling solution like Redis, I just need to read and cache data and sometimes reload it from DB, without any handling feature, so I implemented this:
- In my cache module, for the first evaluation I query DB and return the result in module.exports
- So all following
require
for this module will return the cached value instead of querying the DB again >> I made it so far and I divided by 5 the consumed time. - Then I need a function to reload the module >> I failed there...
I feel like I am a very little step away to succeed, here is what I implemented (I run node.js v14.15.1, I simplified all code to make it more readable...):
Firstly I made the module to export the permissions by itself, it works, I got the list of permissions when I require the module:
/** FILE cached_permissions.js **/
const permissionModel = require("../models/permission.model")
getPermissionsAtInit = async () => {
return await permissionModel.find().exec()
}
module.exports = getPermissionsAtInit()
Then, the classic route file:
/** FILE permissions.route.js **/
// Permission service
const PermissionService = require("../services/permission.service")
// Routes to get permissions from DB
app.get("/api/permissions", async (req, res) => {
res.send(await PermissionService.getPermissionsFromDB()} )
// Route to get permission from cache
app.get("/api/cached-permissions", async (req, res) => {
res.send(await PermissionService.getCachedPermissions()})
// Route to reset permission cache
app.get("/api/reset-cache", async (req, res) => {
await PermissionService.resetCache()
res.send("cache reseted")})
Here is the permission service module
/** FILE permission.service.js */
// Module with cached permissions
const cachedPermissions = require("./cached_permissions.js")
// Module to query the db
const permissionModel = require("../models/permission.model")
class PermissionService {
static getPermissionsFromDB = async () => {
// Mongoose query to get data from DB
return await permissionModel.find().exec()
}
static getCachedPermissions = () => {
// Directly returns value from cached module
return cachedPermissions
}
static resetCache = async () => {
// firstly we delete the cached module
delete require.cache[require.resolve("./cached_permissions.js")]
// then we require again the module to force the re-cache
require("./cached_permissions.js")
}
}
module.exports = PermissionService
So I can run the following:
- http call /api/permissions >> returns the list of permissions from DB ~50ms
- http call /api/cached-permissions >> returns the same list of permissions ~10ms
- I manually update data in mongoDB
- 2nd http call to /api/permissions >> returned updated data
- 2nd http call to /api/cached-permissions >> returned first set of data
So for so good, this is the expected behavior
- Then http call to /api/reset-cache
- 3rd call to /api/cached-permissions >> still return not updated data
I know the /api/reset-cache is done because I console.log Object.keys(require.cache)
, and I can see it deleted after the delete require.cache[...]
and I can see it appear again after the new require("./cached_permissions.js")
.
But anyway, when I call again /api/cached-permissions, the value has not been updated. It feels like the const cachedPermissions = require("./cached_permissions.js")
from my file permission.service.js has been loaded only once when the server starts when the permissions.route.js file require the permission.service.js file, and not each time a route is called, even if I reload a specific module.
Do you know a way I could reload the module in already loaded file, or update the value of a module in an alreay loaded file or some other way to make this work? I feel like I am a very little step away of a very simple DB cache system...
CodePudding user response:
It feels like the
const cachedPermissions = require("./cached_permissions.js")
from my file permission.service.js has been loaded only once when the server starts, and not each time a route is called
Well that's exactly what your code is written to do. The cachedPermissions
is a const
ant in your permission.service.js module, and you never update that constant. If instead you would do
static getCachedPermissions = () => {
// Directly returns value from cached module
return require("./cached_permissions.js");
}
it would load the module (from the module cache, or evaluate the module code if fresh) on every request.
But really there's no reason to use the module cache for this. A simple variable in your permission service module would suffice:
/* permission.service.js */
const permissionModel = require("../models/permission.model"); // Module to query the db
async function getPermissionsFromDB() {
// Mongoose query to get data from DB
return await permissionModel.find().exec()
}
let cachedPermissions = getPermissionsFromDB();
module.exports.getPermissionsFromDB = getPermissionsFromDB;
module.exports.getCachedPermissions = () => cachedPermissions;
module.exports.resetCache = () => {
cachedPermissions = getPermissionsFromDB();
};