gstore-node integrates the nsql-cache manager. This means that the cache management (add, remove, invalidate) is done for you. Just initialize gstore-node with the cache turned on and call it a day.
nsql-cache uses underneath the powerful node-cache-manager. This means that you can have multiple cache stores with different TTL settings in each one. It also means that you get a LRU memory cache instance out of the box that will give a boost to your application right away.
Important: Since v2.7.0 of node-cache-manager it is possible to set/get and delete multiple keys at once from the cache stores. Although this feature is available in the manager, you still need to provide a store that supports it. At the time of this writing, only the memory store and the node-cache-manager-redis-store support it. If you provide another store engine that does not support mget or mset you will still be able to use the cache but you won't be able to fetch multiple keys (batch) from the Datastore and cache them.
Activate the cache
You activate the cache by passing a configuration object during the gstore initialization. You can also just pass true and the default cache configuration will be used.
This is the default configuration from nsql-cache.
// ------------------------------// Default cache stores// ------------------------------constdefaultCacheStores= [ { store:'memory',// LRU cache max:100,// maximum number of item in the cache },];// ------------------------------// Default configuration// ------------------------------constdefaultConfig= { ttl: { keys:60*10,// 10 minutes queries:5,// 5 seconds// the setting below is only when there are multiple cache stores memory: { keys:60*5,// 5 minutes queries:5, }, redis: { keys:60*60*24,// 1 day queries:0,// infinite }, }, cachePrefix: { keys:'gck:',// Gstore Cache Key queries:'gcq:',// Gstore Cache Query }, hashCacheKeys:true,// turn the cache "ON" globally.// If you set it to false, you will have to activate it// on each request global:true,};
Refer to the nsql-cache for a detailed explanation of the configuration properties.
Multi stores example
To change the configuration you only need to provide what needs to be overriden.
You can access at any time the underlying gstore-cache instance and call its API. If you need to cache custom data (other the than keys or queries managed by gstore-node), just call the set/mset/get/mset/del methods directly on the cache instance.
For more information on the API refer to the nsql-cache documentation.
// Anywhere in your Applicationconst { instances } =require('gstore-node');constgstore=instances.get('default');const { cache } = gstore;// You can then call any method from nsql-cachecache.get('somekey').then(...);cache.set('someKey', { name:'john' }).then(...);
Advanced Cache for Queries
nsql-cache has an advanced cache for queries when you provide aRedisstore (either as single store or in a multi-store). nsql-cache detects the Kind of the entities on which the query is run to not only cache the response of the Query but also keep a reference to the Query in a Redis Set. It creates one Set by Entity Kind.
This means that you can have an infinite TTL (0) for the queries on the Redis store. Each time an entity is added/updated/deleted gstore-node will automatically remove all the queries references from the Entity Kind Set and invalidate the cache of those queries.
Useful methods
All the cache management of nsql-cache is done for you by gstore-node. There is though one useful method that you might need:
gstore.cache.queries.kset(key, data, entityKinds)
In case you have a complex aggregation of data that comes from multiple queries, cache.queries.kset() lets you save its data in the cache and link Entiy Kinds to it. Later, if any Entity Kind passed here is added/updated or deleted, gstore will invalidate the cache for this data.
kset() (for kind set) takes 3 arguments:
key : a custom cache key you want to give to this data
data: the data to cache
entityKinds: one or multiple entityKinds related to the data
Let see it with an example:
const { instances } =require('gstore-node');constgstore=instances.get('default');const { cache } = gstore;constPosts=require('./posts.model');constUsers=require('./users.model');/** * Handler to fetch all the data for our Home Page */constfetchHomeData= () => {// Check the cache first...cache.get('website:home').then(data => {if (data) {// in the cache... great!return data; }// Cache not found, query the dataconstqueryPosts=Posts.query().filter('category','tech').limit(10).order('publishedOn', { descending:true });constqueryTopStories=Posts.query().order('score', { descending:true }).limit(3);constqueryProducts=Products.query().filter('featured',true);returnPromise.all([queryPosts.run(),queryTopStories.run(),queryProducts.run()]).then(result => {// Build our data objectconsthomeData= { posts: result[0], topStories: result[1], products: result[2], };// We save the result of the 3 queries to the cache ("website:home" key)// and link the data to the "Posts" & "Products" Entity Kinds.// We can now safely keep the cache infinitely, gstore will take care// of clearing the cache when a "Posts" or "Products" is added/edited or deleted.returncache.queries.kset('website:home', homeData, ['Posts','Products']); }); });};
Transactions
For the most part you don't have to worry about clearing the cache as gstore-node automatically does it for you each time you add/edit or delete an entity.
It cannot do it for you though when you are updating entities inside a transaction as it does not know if the transaction succeeded or not. So you will have to clear the cache manually after a transaction succeeds.
Let see it with an example.
const { instances } =require('gstore-node');constgstore=instances.get('default');consttransaction=gstore.transaction();constUser=require('./user.model');constPost=require('../posts/post.model');transaction.run().then(() => {returnUser.get(123,null,null, transaction).then((user) => {constpost=newPost({ title:'My new Blog Post', authorId:user.id });post.save(transaction);// We update the total of posts for this useruser.totalPosts +=1;user.save(transaction);transaction.commit().then(() => {// Transaction successful... we need to clear the cache now// 1. Delete the cache for "User" Entity Kind// ---> both the key passed **and** the queries linked to "User" will be deletedconstpromise1=User.clearCache(User.key(123));// 2. Delete the cache for "Post". Here no key is passed, we just want// to make sure that all query data cached linked to "Post" is deletedconstpromise2=Post.clearCache();returnPromise.all([promise1, promise2]); }); }); })