-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache Mongo-DB calls (in-memory and/or Redis) #926
base: master
Are you sure you want to change the base?
Conversation
Did you test it with iotagents in HA environments? Is there a really improvement there? |
Last time I checked stress testing throughput for The degree of improvement will depend on how you set up mongo-db - I guess a sharded-environment with many read-only replicas would alleviate the situation somewhat, but why rely on end-users being knowledgeable enough to architect their way out of a problem (Hint: plenty of people seem to use my tutorial I agree this needs proper benchmarking. Maybe you could stress test yourself or share a common HA benchmark configuration? |
Cache policies do have an impact in HA scenarios. We always deploy IoTAgents in HA (Active-Active) clusters. Any provided solution must be fully tested and studied in HA environments. It's not a matter of benchmarking, it's a matter of accessibility and consistency (ACID). Take into account that full accessibility and consistency is expected among the Agent-Cluster (so if you deploy a device, the device must available and updated through the whole cluster). |
const Device = require('../../model/Device'); | ||
const async = require('async'); | ||
const cacheManager = require('cache-manager'); | ||
const memoryCache = cacheManager.caching({store: 'memory', max: 1000, ttl: 10/*seconds*/}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic numbers. MUST be config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition, I think the whole possibilty of not using cache (e.g. something like cache: true|false
in configuration) should be provided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed 9471f42 and subsequent pushes.
IoT Agent A
IoT Agent B
|
So it depends on how you want the system to fall over. Do I want it to fail because it can't handle the throughput or do I want it to fail because it takes around 10 seconds for the data to settle? |
- Ensure memoryCache is reset/busted on each test config - Amend test config to use memoryCache to test additional path.
@mrutid @fgalan - PR updated, no magic numbers and an off-switch added.
|
@@ -22,7 +22,7 @@ | |||
*/ | |||
'use strict'; | |||
|
|||
var iotAgentLib = require('../../../lib/fiware-iotagent-lib'), | |||
var iotAgentLib = require('../../../lib/fiware-iotagent-lib'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var iotAgentLib = require('../../../lib/fiware-iotagent-lib'), | |
var iotAgentLib = require('../../../lib/fiware-iotagent-lib'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A MongoDB cache for IOTAs can be an interesting feature. However, let me clarify that for us the only scenario that makes sense in real-world utilization scenarios is number 3 (cluster of several IOTAgent nodes sharing the same DB doing provisions along the time, not only an initial one). Numbers 1 and 2 are not realistic. Taking this into account, the following requirements should be covered by a cache implementation as the one suggested in this PR:
Side-note: Maybe we can provide one shared cache for groups, and several distinct one for devices. As far as in node.js RAM is a hard-limited resource. The size of the device’s cache belonging to a group should be part of the configuration (maybe in terms of how many devices will be stored). |
probably, you need something like redis, in memory cache and ha, don't work well together |
The cache mechanism has an option to connect to Redis. I'll look at this when I have time. I guess the final architecture will look a bit like this:
Updated and magic numbers removed ✅ but more config will be needed for the Redis switches✅
Easier to do this once the basic architecture is agreed. ✅
The config could point to separate Redis instances.✅ |
@mapedraza @mrutid @fgalan - Getting back after the Christmas break, is there any progress on this? Is there anything else that needs to be done from my side? |
Hello Jason, thank you for your contribution! The pull request needs to be checked, but we still need some time to review it in deep. We will let you know when we review it. |
Finally, I could check this PR with the team. As you know, in a production environment, adding another component means adding more complexity to the platform operation, so the performance improvement has to be really clear. Mongo-DB is very fast in key accesses and with in-memory datasets and all three cases (Mongo-DB, Redis, and MemCache) are penalized with similar network accesses (Redis and MC we are also adding extra writes that do not exist in the base scenario). It is more complex and it is not clear to me, without tests, that it significantly improves the current scenario (with a well configured Mongo). Could you provide any figure or comparison between both scenarios? Moreover, to have at the same time Redis and MongoDB, as they cover the same architectural place (External shared state) adds architectural redundancy. Also, Mongo-DB is needed, as some other GEs needs it with use cases that cannot be covered with Redis (i.e. geo-queries done by CB are addressable in MongoDB but not in Redis) When @mrutid was talking about consistency, was enough to make it configurable for each config group. Each scenario may require or not the cache, with and specific cache policy (for instance, cache timeout), and also @fgalan also mentioned, segmentation, allowing each provision group to have a different cache slice isolated to prevent a single user monopolize all the system cache A good approach to have in mind using an in-memory cache, which is a real difference compared to Redis, Mongo-DB, and MemCache, is Orion Subscription Cache. The difference regarding Orion cache is that instead of having the same policy for all (as Orion does) in IOTAs we should have specific policies for each config group. |
Two points -
The Redis cache is just doing the same job it already does when used with QuantumLeap for example (see #926 (comment)). A cache is optional there too. The reason for picking Redis is that cache-manager supports it - I guess someone could look at adding a custom Orion cache support if they wanted. |
Relevant StackOverflow discussion of architectural differences between Redis and Mongo: https://stackoverflow.com/questions/5252577/how-much-faster-is-redis-than-mongodb . Summary is Redis should be faster on Read provided that the data lies on a single machine. The data held in a cache shouldn't be too large for that role. |
After discussions with @mapedraza I'm going to split this into smaller chunks for easier review and consumption. |
Assume that caching is not used by default and then additively use caching. This is necessary for backwards compatibility and tenant slicing.
# Conflicts: # doc/api.md
Mongo-DB access is slow. This PR minimizes the need to make database calls, by caching the last 1000 device calls and 100 group calls in a refreshable cache. This in turn reduces network traffic and increases maximum throughput.
Multiple Caching strategies are accepted - either memCache, Redis or both. All parameters are settable as config or Docker ENV variables.
Adding
dontCache=true
as part of any Device or Group configuration will ensure that the data is never cached and therefore always retrieved from the MongoDB database.Note that because all cacheable queries are potentially received from multiple databases it follows that they must be retrieved as JSON Objects not Mongoose Documents. Therefore the lean option has been enabled on the relevant read queries.
As the mongoose docs state:
so there should be a significant improvement even when not using a cache as well.