Skip to content

Commit

Permalink
Merge pull request #25 from strapi-community/v5/main
Browse files Browse the repository at this point in the history
  • Loading branch information
derrickmehaffy authored Sep 21, 2024
2 parents 320d922 + 0593789 commit b27895e
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
save-exact=true
package-lock=false
50 changes: 19 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
<a href="https://www.npmjs.org/package/strapi-plugin-redis">
<img src="https://img.shields.io/npm/dm/strapi-plugin-redis" alt="Monthly download on NPM" />
</a>
<a href="https://www.npmjs.org/package/strapi-plugin-redis">
<img src="https://img.shields.io/snyk/vulnerabilities/github/strapi-community/strapi-plugin-redis?label=snyk%20vulnerabilities" alt="Vulnerabilities on Snyk" />
</a>
</p>
</div>

Expand All @@ -40,28 +37,30 @@

## 🚦 Current Status

This package is currently under development and should be consider **ALPHA** in terms of state. I/We are currently accepting contributions and/or dedicated contributors to help develop and maintain this package.
This package is currently maintained and should be considered **Stable/GA** in terms of state. I/We are currently accepting contributions and/or dedicated contributors to help develop and maintain this package.

For more information on contributing please see [the contrib message below](#contributing).

## 🛑 Foreword

This package's lead maintainer is an employee of Strapi however this package is not officially maintained by Strapi Solutions SAS nor Strapi, Inc. and is currently maintained in the free time of the lead maintainer.

**Absolutely no part of this code should be considered covered under any agreement you have with Strapi proper** including but not limited to any Enterprise Agreement you have with Strapi.
**Absolutely no part of this code should be considered covered under any agreement you have with Strapi proper** including but not limited to any Enterprise and/or Cloud Agreement you have with Strapi.

## ✨ Features

This plugin utilizes 1 core package:
This plugin utilizes 2 core packages:

- [ioredis](https://github.com/luin/ioredis) - for all connection management
- [ioredis](https://github.com/luin/ioredis) - for all connection management to any Redis or Redis-compatible database
- [redlock](https://github.com/mike-marcacci/node-redlock) - for distributed locks related to Strapi's built in cron-tasks system

These are the primary features that are finished or currently being worked on:

- [x] Redis Single Node Support
- [x] Redis Replica + Sentinel Support
- [ ] Redis Sharding Support (assumed working, no config samples)
- [x] Multiple connections/databases
- [x] Redlock capabilities with Strapi's built-in cron tasks

## 🤔 Motivation

Expand All @@ -72,49 +71,38 @@ A few examples of where Redis could be used within a Strapi application:
- LRU-based response cache for REST
- Apollo server GraphQL cache
- IP Rate-limiting using something like [koa2-ratelimit](https://www.npmjs.com/package/koa2-ratelimit)
- Distributed Redis locks for Strapi clusters (useful for clustered usage of cron tasks)
- Server-side user session storage
- So much more

If you are currently using this package in your plugin and would like to be featured, please feel free to submit an issue to have your plugin added to the list below:

- [strapi-plugin-rest-cache](https://www.npmjs.com/package/strapi-plugin-rest-cache)
- via: [strapi-provider-rest-cache-redis](https://www.npmjs.com/package/strapi-provider-rest-cache-redis)
- [strapi-plugin-redcron](https://www.npmjs.com/package/strapi-plugin-redcron)
- More plugins coming soon!

Note the following packages used to use this package with Strapi v4 but have since been merged into this package:

- [strapi-plugin-redcron](https://www.npmjs.com/package/strapi-plugin-redcron)

## 🖐 Requirements

Supported Strapi Versions:

| Strapi Version | Supported | Tested On |
| -------------- | --------- | ------------- |
| v3 || N/A |
| v4.0.x || July 2022 |
| v4.1.x || July 2022 |
| v4.2.x || July 2022 |
| v4.3.x || December 2022 |
| v4.4.x || December 2022 |
| v4.5.x || December 2022 |
| v4.6.x || January 2024 |
| v4.7.x || January 2024 |
| v4.8.x || January 2024 |
| v4.9.x || January 2024 |
| v4.10.x || January 2024 |
| v4.11.x || January 2024 |
| v4.12.x || January 2024 |
| v4.13.x || January 2024 |
| v4.14.x || January 2024 |
| v4.15.x || January 2024 |
| v4.16.x || January 2024 |
| v4.17.x || January 2024 |
| v4.19.x || January 2024 |
| Strapi Version | Plugin Version | Supported | Tested On |
|----------------|----------------|-----------|-----------|
| v3.x.x | N/A || N/A |
| v4.x.x | 1.1.0 || Sept 2024 |
| v5.x.x | 2.0.0 || Sept 2024 |

**This plugin will not work with Strapi v3 projects as it utilizes APIs that don't exist in the v3!**

## ⏳ Installation

Install the plugin in your Strapi project or your Strapi plugin.

**Warning**
For Strapi 4 projects you should use the `1.x.x` version of this plugin, for Strapi 5 projects you should use the `2.x.x` version of this plugin.

```bash
# Using Yarn (Recommended)
yarn add strapi-plugin-redis
Expand Down
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "strapi-plugin-redis",
"version": "1.1.0",
"version": "2.0.0",
"description": "Plugin used to centralize management of Redis connections in Strapi",
"strapi": {
"displayName": "Redis",
Expand All @@ -11,8 +11,12 @@
},
"dependencies": {
"chalk": "4.1.2",
"debug": "4.3.4",
"ioredis": "5.2.4"
"debug": "4.3.5",
"ioredis": "5.4.1",
"redlock": "5.0.0-beta.2"
},
"peerDependencies": {
"@strapi/strapi": "^5.0.0"
},
"scripts": {},
"author": {
Expand All @@ -30,6 +34,9 @@
"email": "derrickmehaffy@gmail.com",
"url": "https://github.com/derrickmehaffy",
"lead": true
},
{
"name": "Excl Networks Inc."
}
],
"bugs": {
Expand Down
9 changes: 9 additions & 0 deletions server/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ module.exports = {
settings: {
debug: false,
debugIORedis: false,
redlockConfig: {
driftFactor: 0.01,
retryCount: 10,
retryDelay: 200,
retryJitter: 200,
},
enableRedlock: false,
lockDelay: null,
lockTTL: 5000,
},
connections: {
default: {
Expand Down
81 changes: 65 additions & 16 deletions server/register.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
'use strict'
'use strict';

const debug = require('debug')
const debug = require('debug');
const { default: Redlock } = require('redlock');

module.exports = async ({ strapi }) => {
// Load plugin Config
const coreConfig = strapi.config.get('plugin.redis');
const coreConfig = strapi.config.get('plugin::redis');

// Configure plugin debug
if (coreConfig.settings.debug === true) {
debug.enable('strapi:strapi-plugin-redis')
debug.enable('strapi:strapi-plugin-redis');
}

// Allow plugin + ioredis debug
if (coreConfig.settings.debug === true && coreConfig.settings.debugIORedis === true) {
debug.enable('strapi:strapi-plugin-redis,ioredis:*')
debug.enable('strapi:strapi-plugin-redis,ioredis:*');
}

// Construct Redis API
Expand All @@ -25,15 +26,63 @@ module.exports = async ({ strapi }) => {
// Build Redis database connections
await strapi.plugin('redis').service('connection').buildAll(coreConfig);

// Construct Admin Permissions
const actions = [
{
section: 'settings',
category: 'redis',
displayName: 'Access the Redis Overview page',
uid: 'settings.read',
pluginName: 'redis',
},
];
await strapi.admin.services.permission.actionProvider.registerMany(actions);
// Configure Redlock
if (coreConfig.settings.enableRedlock === true) {
const originalAdd = strapi.cron.add;
const redlockConfig = coreConfig.settings.redlockConfig;

strapi.cron.add = (tasks) => {
const generateRedlockFunction = (originalFunction, name) => {
return async (...args) => {
const connections = Object.keys(strapi.redis.connections).map((key) => {
return strapi.redis.connections[key].client;
});
const redlock = new Redlock(connections, redlockConfig);

let lock;
try {
lock = await redlock.acquire([name], coreConfig.settings.lockTTL);
debug(`Job ${name} acquired lock`);
await originalFunction(...args);
} catch (e) {
debug(`Job ${name} failed to acquire lock`);
} finally {
// wait some time so other processes will lose the lock
let lockDelay = coreConfig.settings.lockDelay
? coreConfig.settings.lockDelay
: coreConfig.settings.redlockConfig.retryCount *
(coreConfig.settings.redlockConfig.retryDelay +
coreConfig.settings.redlockConfig.retryJitter);
debug(`Job ${name} waiting ${lockDelay}ms before releasing lock`);
await new Promise((resolve) => setTimeout(resolve, lockDelay));
if (lock) {
debug(`Job ${name} releasing lock`);
try {
await lock.release();
} catch (e) {
debug(`Job ${name} failed to release lock ${e}`);
}
}
}
};
};
Object.keys(tasks).forEach((key) => {
const taskValue = tasks[key];
if (typeof taskValue === 'function') {
strapi.log.info('redlock requires tasks to use the object format');
return;
} else if (
typeof taskValue === 'object' &&
taskValue &&
typeof taskValue.task === 'function' &&
taskValue.bypassRedlock !== true
) {
// fallback to key if no name is provided
const taskName = taskValue.name || key;
taskValue.task = generateRedlockFunction(taskValue.task, 'redlock:' + taskName);
}
});
originalAdd(tasks);
};
}
};
4 changes: 2 additions & 2 deletions server/services/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = ({ strapi }) => ({
debug(`${chalk.red('Failed to build')} ${name} connection - ${chalk.blue('cluster')}`);
}

// Check for sentinel config
// Check for sentinel config
} else if (nameConfig.connection.sentinels) {
delete nameConfig.connection.host;
delete nameConfig.connection.port;
Expand All @@ -37,7 +37,7 @@ module.exports = ({ strapi }) => ({
debug(`${chalk.red('Failed to build')} ${name} connection - ${chalk.yellow('sentinel')}`);
}

// Check for regular single connection
// Check for regular single connection
} else {
try {
strapi.redis.connections[name] = {
Expand Down
33 changes: 26 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,21 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==

debug@4.3.4, debug@^4.3.4:
debug@4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
dependencies:
ms "2.1.2"

debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"

denque@^2.0.1:
denque@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
Expand All @@ -56,15 +63,15 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

ioredis@5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.2.4.tgz#9e262a668bc29bae98f2054c1e0d7efd86996b96"
integrity sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==
ioredis@5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40"
integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==
dependencies:
"@ioredis/commands" "^1.1.1"
cluster-key-slot "^1.1.0"
debug "^4.3.4"
denque "^2.0.1"
denque "^2.1.0"
lodash.defaults "^4.2.0"
lodash.isarguments "^3.1.0"
redis-errors "^1.2.0"
Expand All @@ -86,6 +93,11 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

node-abort-controller@^3.0.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==

redis-errors@^1.0.0, redis-errors@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
Expand All @@ -98,6 +110,13 @@ redis-parser@^3.0.0:
dependencies:
redis-errors "^1.0.0"

redlock@5.0.0-beta.2:
version "5.0.0-beta.2"
resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44"
integrity sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==
dependencies:
node-abort-controller "^3.0.1"

standard-as-callback@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
Expand Down

0 comments on commit b27895e

Please sign in to comment.