# Cache

![](https://github.com/sebelga/nsql-cache/raw/master/logo/logo.gif)

gstore-node integrates the [**nsql-cache**](https://github.com/sebelga/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](https://github.com/BryanDonovan/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](https://www.npmjs.com/package/lru-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](https://github.com/dabroek/node-cache-manager-redis-store) support it. If you provide [another store engine](https://github.com/BryanDonovan/node-cache-manager#store-engines) 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.

#### Default configuration

```javascript
// server.js (Application Bootstrap)
const { Gstore } = require('gstore-node');

// Default cache configuration
const gstore = new Gstore({ cache: true }); 

// Or by passing cache store(s) and configuration
const gstore = new Gstore({
    cache: {
        stores: [ ... ],
        config: { ... }
    },
});
```

This is the default configuration from nsql-cache.

```javascript
// ------------------------------
// Default cache stores
// ------------------------------

const defaultCacheStores = [
    {
        store: 'memory', // LRU cache
        max: 100, // maximum number of item in the cache
    },
];

// ------------------------------
// Default configuration
// ------------------------------

const defaultConfig = {
    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](https://github.com/sebelga/nsql-cache#api) 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.

```javascript
...
const redisStore = require('cache-manager-redis-store');

const cacheStores = [{
    store: 'memory',
    max: 200,
}, {
    store: redisStore,
    host: 'localhost', // default value
    port: 6379, // default value
    // ... any other configuration for Redis client
}];

const cacheConfig = {
    ttl: {
        memory: {
            keys: 10, // 10 seconds
            queries: 5, // 5 seconds
        },
        redis: {
            keys: 60 * 60,
            queries: 60 * 60
        },
    }
};

const gstore = new Gstore({
    cache: {
        stores: cacheStores,
        config: cacheConfig
    }
});

instances.set('default', gstore);
```

## Access the cache instance

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](https://github.com/sebelga/nsql-cache#api).

```javascript
// Anywhere in your Application
const { instances } = require('gstore-node');

const gstore = instances.get('default');
const { cache } = gstore;

// You can then call any method from nsql-cache
cache.get('somekey').then(...);
cache.set('someKey', { name: 'john' }).then(...);
```

## Advanced Cache for Queries

nsql-cache has an advanced cache for queries **when you provide a** ***Redis*** **store** (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 **k**ind **s**et) 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:

```javascript
const { instances } = require('gstore-node');

const gstore = instances.get('default');
const { cache } = gstore;

const Posts = require('./posts.model');
const Users = require('./users.model');

/**
 * Handler to fetch all the data for our Home Page
 */
const fetchHomeData = () => {
    // Check the cache first...
    cache.get('website:home').then(data => {
        if (data) {
            // in the cache... great!
            return data;
        }

        // Cache not found, query the data
        const queryPosts = Posts.query()
            .filter('category', 'tech')
            .limit(10)
            .order('publishedOn', { descending: true });

        const queryTopStories = Posts.query()
            .order('score', { descending: true })
            .limit(3);

        const queryProducts = Products.query().filter('featured', true);

        return Promise.all([queryPosts.run(), queryTopStories.run(), queryProducts.run()])
            .then(result => {
                // Build our data object
                const homeData = {
                    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.
                return cache.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.

```javascript
const { instances } = require('gstore-node');

const gstore = instances.get('default');
const transaction = gstore.transaction();

const User = require('./user.model');
const Post = require('../posts/post.model');

transaction.run()
    .then(() => {
        return User.get(123, null, null, transaction)
            .then((user) => {
                const post = new Post({ title: 'My new Blog Post', authorId: user.id });
                post.save(transaction);

                // We update the total of posts for this user
                user.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 deleted
                        const promise1 = 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 deleted
                        const promise2 = Post.clearCache();

                        return Promise.all([promise1, promise2]);
                    });
            });

    })
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sebloix.gitbook.io/gstore-node/cache-dataloader/cache.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
