patrixr / strapi-middleware-cache

:electric_plug: A cache middleware for https://strapi.io
MIT License
290 stars 28 forks source link

Strapi-middleware-cache is not updating/busting the Redis cache accordingly on Strapi v3.0.6 #59

Closed vinod77 closed 3 years ago

vinod77 commented 3 years ago

Description: Strapi-middleware-cache is not updating/busting the Redis cache accordingly on Strapi v3.0.6.

Issue: We are using Strapi-middleware-cache plugin and updated our middleware.js accordingly. We are using Redis storage for caching collections. We want to bust/flush & update the cache accordingly for each update/create/delete any collection's entries.

But we found the Redis cache is getting bust/flush only with Redis server restart (each time we had to manually restart Redis).

We are following this documentation: https://strapi.io/blog/caching-in-strapi-strapi-middleware-cache

Please see our middleware.js code below:

module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      type: "redis",
      maxAge: 3600000,
      logs: true,
      populateContext: true,
      populateStrapiMiddleware: true,
      enableEtagSupport: true,
      models: ["cars", "tests"],
      redisConfig: {
        host: "127.0.0.1",
        port: 6379,
      },
    },
  },
});

And we updated the cars controller accordingly on this file: api/cars/controllers/cars.js

module.exports = {
  async index(ctx) {
    console.log("Inside cars controller--------");
    ctx.middleware.cache.store;
    await ctx.middleware.cache.bust({ model: "cars" });
  },
};

Actual Issue: Cache is not getting flush/bust automatically on create/update/delete operations. We found the Redis cache is getting bust/flush only with Redis server restart, each time when any entry is update/deleted/created, we had to manually restart Redis.

Expected: We want to bust/flush & update the cache accordingly for each update/create/delete operations for any collection's entries. We don't need to restart Redis server to flush/update the cache.

System: OS: MAC OS Catalina 10.15.7 Strapi: Strapi v3.0.6 strapi-middleware-cache: "^1.5.0" redis_version: v6.2.5

stafyniaksacha commented 3 years ago

Hello! Can you try on the beta branch here: https://github.com/patrixr/strapi-middleware-cache/tree/beta ? A lot of improvement have been made only on this branch, and we will fix on this branch if the problem persist!

vinod77 commented 3 years ago

Hi @stafyniaksacha Thanks for the quick response.

As you mentioned above, I just tried using "strapi-middleware-cache": "2.0.1-beta.2" on core Strapi app v3.0.6

But still the same issue. API is not returning the most recent updated records/content until I manually flush Redis cache on the Redis console like this: redis-cli FLUSHALL

I also added below code on the model: api/cars/models/cars.js as mentioned on this doc: https://snyk.io/advisor/npm-package/strapi-middleware-cache

module.exports = {
  lifecycles: {
    async beforeUpdate(params, data) {
      console.log("Inside cars model beforeUpdate---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
    async afterUpdate(params, data) {
      console.log("Inside cars model afterUpdate---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
    async beforeDelete(params, data) {
      console.log("Inside cars model beforeDelete---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
    async afterDelete(params, data) {
      console.log("Inside cars model afterDelete---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
    async beforeCreate(params, data) {
      console.log("Inside cars model beforeCreate---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
    async afterCreate(params, data) {
      console.log("Inside cars model afterCreate---");
      strapi.middleware.cache; // A direct access to the Cache API
    },
  },
};

But still no luck. You can test the same on core Strapi v3.0.6 app with strapi-middleware-cache using Redis server. Thanks!

vinod77 commented 3 years ago

HI @stafyniaksacha and team..

Looks this is working as expected (I am getting most recent updated/deleted/created content) by just passing hitpass: true, below is the config:

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      models: [
        {
          model: 'keywords',
          hitpass: true
        },
      ],
    },
  },
});

I didn't made any other changes.

Now when any CMS API is called, like this: http://localhost/keywords (local) I see below running server logs:

[2021-08-20T16:16:45.651Z] debug [cache] GET /keywords HITPASS
[2021-08-20T16:16:45.652Z] debug GET /keywords?status=Published (11 ms) 200

But I have 2 questions:

  1. How I can confirm that I am getting cached API response from Redis only (not from MongoDB)? Does this above server log says all that?
  2. How can i view the cached APIs content on redis-cli?
patrixr commented 3 years ago

Hi @vinod77

Regarding your questions

How I can confirm that I am getting cached API response from Redis only (not from MongoDB)? Does this above server log says all that?

The logs should tell you whether the data is coming from the cache (you should see 'HIT' in the logs). Alternatively, you can enable enableXCacheHeaders in the configuration, which will set an X-Cache: HIT header when delivered from cache

How can i view the cached APIs content on redis-cli

All entries on redis are prefixed with strapi-middleware-cache: that should allow you to find entries from the middleware.

Hope this helps !

vinod77 commented 3 years ago

Hi @patrixr Thanks for the response..

I am just using below configs in config/middleware.js with hitpass: true:

module.exports = ({ env }) => ({
  settings: {
    cache: {
        enabled: true,
        type: 'redis',
        logs: true,
        maxAge: 3600000,
        enableXCacheHeaders: true,
      models: [
        {
          model: 'keywords',
          hitpass: true
        },
      ],
    },
  },
});

And when this API (/keywords) is called, I see below server logs:

[2021-08-20T16:16:45.651Z] debug [cache] GET /keywords HITPASS
[2021-08-20T16:16:45.652Z] debug GET /keywords?status=Published (11 ms) 200

And in the Response Headers I see: x-cache: HITPASS

So I believe I am getting the cached API response from Redis only.. right? CC: @patrixr

patrixr commented 3 years ago

@vinod77 According to this config, hitpass: true would make it "skip" the cache (which is why you're seeing "HITPASS"), setting it to false would enable it all the time, you would then see "HIT"

I'm guessing the reason you need to set it to false, is because the default behaviours skips the cache when an auth header is found ass seen in the readme:

By default, cache is not looked up if Authorization or Cookie header are present. To dissable this behaviour add hitpass: false to the model cache configuration

You can customize event further with a function hitpass: (ctx) => true where ctx is the koa context of the request. Keep in mind that this function is executed before every recv requests.

To simplify terms, HITPASS == SKIP Relevant code here

Correct me if I'm wrong @stafyniaksacha

vinod77 commented 3 years ago

Hi @patrixr Thanks for your response.

Then the issue still exists. We are not getting the updated content from Redis when an entry is updated/created/deleted, which means strapi-middleware-cache is not getting bust/flush.

I made below changes on the core Strapi v306 app: Have these configs in config/middleware.js:

module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      type: "redis",
      populateStrapiMiddleware: true,
      enableEtagSupport: true,
      enableXCacheHeaders: true,
      models: [
        {
          model: "bikes",
          hitpass: false,
          routes: ["/bikes"],
          headers: ["accept-JavaScript"],
        },
      ],
      redisConfig: {
        host: "127.0.0.1",
        port: 6379,
      },
    },
  },
});

And have below code on api/bike/models/bike.js (I won't be having ctx available):

module.exports = {
  lifecycles: {
    beforeUpdate: async (params, data) => {
      strapi.middleware.cache;
    },
    afterUpdate: async (params, data) => {
      strapi.middleware.cache;
    },
    beforeCreate: async (params, data) => {
      strapi.middleware.cache;
    },
    afterCreate: async (params, data) => {
      strapi.middleware.cache;
    },
    beforeDelete: async (params, data) => {
      strapi.middleware.cache;
    },
    afterDelete: async (params, data) => {
      strapi.middleware.cache;
    },
  },
};

The response I am getting when /bikes API is called for first time (I think 1st time response is from the DB):

[2021-08-30T06:32:21.451Z] debug [cache] GET /bikes MISS
[2021-08-30T06:32:21.453Z] debug [cache] GET /bikes MISS
[2021-08-30T06:32:21.454Z] debug GET /bikes (13 ms) 200

When /bikes API is just reloaded/called again (I think I got the response from Redis cache here):

[2021-08-30T06:33:16.063Z] debug [cache] GET /bikes HIT
[2021-08-30T06:33:16.063Z] debug GET /bikes (1 ms) 304

Now when I call this API again after updating any entry --- I got the same response from Redis cache, it's not the updated response. The cache is not getting flush/bust:

I see the same logs:

[2021-08-30T06:34:46.371Z] debug [cache] GET /bikes HIT
[2021-08-30T06:34:46.371Z] debug GET /bikes (1 ms) 304

Note: I see the updated response only when I manually run FLUSHALL on the redis-cli

PLease let us know what we need to do to get the updated response from Redis by flushing the cache on each create/update/delete?

System: OS: MAC OS Catalina 10.15.7 Strapi: Strapi v3.0.6 strapi-middleware-cache: "^2.0.1-beta.2" redis_version: v6.2.5 Thanks!

CC: @patrixr , @stafyniaksacha, @derrickmehaffy

derrickmehaffy commented 3 years ago

I mentioned to @vinod77 on our community Discord that the issue might be coming from the very old version of Strapi (v3.0.6) and advised them to try upgrading the Strapi version or at least testing on a fresh project using the latest version of Strapi.

vinod77 commented 3 years ago

HI @derrickmehaffy Thanks for the update.

Finally we made it work on Strapi: v3.0.6 with strapi-middleware-cache: v1.5.0 Below are the configs we are using:

 cache: {
        enabled: true,
        type: 'redis',
        logs: true,
        maxAge: 86400000,
        enableXCacheHeaders: true,
        populateStrapiMiddleware: true,
        models: [
          'cars'
        ],
        redisConfig: {
          host: env('REDIS_HOST'),
          port: env('REDIS_PORT')
        }
      }

& on API/car/models/car.js:

module.exports = {
  lifecycles: {
    afterUpdate() {
      strapi.middleware.cache.bust({ model: 'cars' });
    },
    afterCreate() {
      strapi.middleware.cache.bust({ model: 'cars' });
    },
    afterDelete() {
      strapi.middleware.cache.bust({ model: 'cars' });
    }
  }
};

Thank you all for your support here. This issue can be closed now. CC: @derrickmehaffy, @patrixr, @stafyniaksacha

derrickmehaffy commented 3 years ago

Setting as closed then!

drashtikheni commented 2 years ago

I am getting TypeError: strapi.middleware.cache.bust is not a function afterUpdate: async (model) => { strapi.middleware.cache.bust({ model: "promotion" }); },