Skip to content
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

nv-2054-implement-redis-cluster-service #3177

Merged
merged 17 commits into from
Apr 12, 2023

Conversation

p-fernandez
Copy link
Contributor

What change does this PR introduce?

Adds two in memory cluster providers (Redis based configuration and Elasticache provider; both using Redis Cluster downstream) and connects it to the API, enabling Cache Service and Distributed Lock service to take advantage of a Redis Cluster instance for improving the performance.
It also adds the tests and the CI/CD changes needed for those to run.

Why was this change needed?

One of the performance improvements we are executing.

Other information (Screenshots)

Several environment variables need to be added and set depending on the different environments configuration.
For DEV environment we would like to set:
IN_MEMORY_CLUSTER_MODE_ENABLED=true
ELASTICACHE_CLUSTER_SERVICE_HOST=<ElastiCache_URL>
ELASTICACHE_CLUSTER_SERVICE_PORT=<ElastiCache_PORT>

For CI/CD and tests environments we would like to set:
IN_MEMORY_CLUSTER_MODE_ENABLED=true
REDIS_CLUSTER_SERVICE_HOST=localhost
REDIS_CLUSTER_SERVICE_PORTS=[7000,7001,7002,7003,7004,7005]

@linear
Copy link

linear bot commented Apr 11, 2023

NV-2054 Implement Redis cluster service

Definition of done:

  • Implements a Redis Cluster service and its connection with the current system
  • Gets integrated with Cache service and Distributed Lock service.
  • Adds a Redis Cluster provider
  • Adds a Elasticache Cluster provider as it will be the one enabled in DEV environment for operations.
  • Allows if Elasticache env variables set to use it as provider and if not fallback to the Redis Cluster provider.
  • Adds tests and passes them.
  • Add action in CI/CD to run a Redis Cluster environment for testing purposes.

@@ -0,0 +1,15 @@
name: Setup Novu Redis Cluster
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action to create a Redis Cluster to be used by the CI/CD in the tests.

@@ -32,6 +32,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-project
- uses: ./.github/actions/setup-redis-cluster
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precondition for the tests.

@p-fernandez p-fernandez force-pushed the nv-2054-implement-redis-cluster-service branch from 858b51a to cc53e0b Compare April 11, 2023 13:19
Comment on lines 14 to 15
REDIS_CACHE_SERVICE_HOST=localhost
REDIS_CACHE_SERVICE_PORT=6379
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer if by default cache will be disabled locally, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have that mentioned in the documentation I do agree.

It is:

Caching is turned off by default, but can easily be activated by setting the following environment variables:

@@ -10,6 +10,8 @@ import { DistributedLockService } from './distributed-lock.service';

const originalRedisCacheServiceHost = process.env.REDIS_CACHE_SERVICE_HOST;
const originalRedisCacheServicePort = process.env.REDIS_CACHE_SERVICE_PORT;
const originalRedisClusterServiceHost = process.env.REDIS_CLUSTER_SERVICE_HOST;
const originalRedisClusterServicePorts = process.env.REDIS_CLUSTER_SERVICE_PORTS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be?

Suggested change
const originalRedisClusterServicePorts = process.env.REDIS_CLUSTER_SERVICE_PORTS;
const originalRedisClusterServicePorts = process.env.REDIS_CLUSTER_SERVICE_PORT;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the name is right as this is for the case where we provide the Redis Cluster with the ports nodes into an array. Nothing to do with the Elasticache cluster set up. This is the fallback.

});

inMemoryProviderClient.on('ready', () => {
Logger.warn('In-memory cluster ready', LOG_CONTEXT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Logger.warn('In-memory cluster ready', LOG_CONTEXT);
Logger.log('In-memory cluster ready', LOG_CONTEXT);

I think better suited as it's a success one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set it up higher as the readiness was being a problem because my infra problems. I can revert it back. 👍🏻

const { host, ttl } = getConfig();

if (!host) {
Logger.log('Missing host for in-memory cluster provider', LOG_CONTEXT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Logger.log('Missing host for in-memory cluster provider', LOG_CONTEXT);
Logger.warn('Missing host for in-memory cluster provider', LOG_CONTEXT);

@p-fernandez p-fernandez force-pushed the nv-2054-implement-redis-cluster-service branch from cc53e0b to 6cbb287 Compare April 11, 2023 13:28
@@ -22,6 +22,19 @@ REDIS_CACHE_KEEP_ALIVE=
REDIS_CACHE_FAMILY=
REDIS_CACHE_KEY_PREFIX=

IN_MEMORY_CLUSTER_MODE_ENABLED=true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing we will use the Redis Cluster instance we are adding to the CI/CD. For running locally it would be needed to run one for tests to pass unless IN_MEMORY_CLUSTER_MODE_ENABLED is set to false and the tests for the InMemoryService are skipped.

const stream = client.scanStream({
match: pattern,
});
const stream = this.inMemoryProviderService.inMemoryScan(pattern);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djabarovgeorge here is where I had to do this proxy to solve the lack of availability of this method in the Redis Cluster instances.

Comment on lines +7 to +12
const DEFAULT_TTL_SECONDS = 60 * 60 * 2;
const DEFAULT_CONNECT_TIMEOUT = 50000;
const DEFAULT_KEEP_ALIVE = 30000;
const DEFAULT_FAMILY = 4;
const DEFAULT_KEY_PREFIX = '';
const TTL_VARIANT_PERCENTAGE = 0.1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carrying away the existing Redis instance configurations. Worth to be reviewed.

Comment on lines 82 to 90
dnsLookup: (address, callback) => callback(null, address),
redisOptions: {
tls: {},
},
/*
* Disabled in Prod as affects performance
*/
showFriendlyErrorStack: process.env.NODE_ENV !== 'prod',
slotsRefreshTimeout: 2000,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specific Elasticache configuration but showFriendlyErrorStack.

}
}

public getOptions(): RedisOptions | undefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downstream the Redis Cluster can have Redis Options set up and configured just like a single instance, but they live in a different place.

},
};

return this.isElasticacheEnabled() ? clusterProviders.elasticache : clusterProviders.redis;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Elasticache configuration is set in the environment we use that provider and if not we fall back to the Redis Cluster provider that needs automated setting in the CI/CD and in your local environment when running the tests. If the Cluster mode is enabled in the configuration through IN_MEMORY_CLUSTER_MODE_ENABLED.

Comment on lines +58 to +63
REDIS_CLUSTER_SERVICE_HOST: str({
default: '',
}),
REDIS_CLUSTER_SERVICE_PORTS: str({
default: '',
}),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should validate the Elasticache variables as they are optional and depending on the environment deployed.

@@ -36,16 +36,18 @@ import { UserService } from './user.service';
/**
* TODO: move this to a reusable area
*/
const connection = {
db: Number(process.env.REDIS_DB_INDEX || '1'),
port: Number(process.env.REDIS_PORT || 6379),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and other hardcodes regarding the port need to be addressed in another PR.

@p-fernandez p-fernandez force-pushed the nv-2054-implement-redis-cluster-service branch from 3cf76e3 to 43c642e Compare April 11, 2023 13:38
Comment on lines +28 to +29
REDIS_CLUSTER_SERVICE_HOST=localhost
REDIS_CLUSTER_SERVICE_PORTS=[7000,7001,7002,7003,7004,7005]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@p-fernandez I think we should remove this from the .env.test, so that tests are running against single node on our machines, but in ci we can override those env variables in runtime

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do that just by setting: IN_MEMORY_CLUSTER_MODE_ENABLED to false. Just want to highlight that in-memory-provider.service.spec.ts won't pass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can do that

@scopsy scopsy temporarily deployed to Development April 11, 2023 13:49 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 11, 2023 14:18 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 11, 2023 14:33 — with GitHub Actions Inactive
@p-fernandez p-fernandez temporarily deployed to Development April 11, 2023 14:36 — with GitHub Actions Inactive
@p-fernandez p-fernandez temporarily deployed to development April 11, 2023 14:50 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 12, 2023 06:45 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 12, 2023 06:59 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 12, 2023 07:07 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 12, 2023 07:19 — with GitHub Actions Inactive
@scopsy scopsy temporarily deployed to Development April 12, 2023 07:58 — with GitHub Actions Inactive
};

Logger.log(`Initializing Elasticache Cluster with ${instances?.length} instances`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not going to count the real instances as the set up for Elasticache is different. Not sure if through the URL or any API call we would know the exact number.

@@ -86,6 +87,8 @@ export const getRedisCluster = (): Cluster | undefined => {
slotsRefreshTimeout: 2000,
};

Logger.log(`Initializing Redis Cluster Provider with ${instances?.length} instances`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ok. 👍🏻

@scopsy scopsy temporarily deployed to Development April 12, 2023 08:28 — with GitHub Actions Inactive
- name: Run a test
- name: Run E2E tests
env:
IN_MEMORY_CLUSTER_MODE_ENABLED: true
run: |
cd apps/api && pnpm test:e2e
pnpm test
Copy link
Contributor

@LetItRock LetItRock Apr 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line will run unit tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops forgot to remove it

@scopsy scopsy temporarily deployed to development April 12, 2023 08:43 — with GitHub Actions Inactive
@scopsy scopsy merged commit c951d4b into next Apr 12, 2023
11 checks passed
@scopsy scopsy deleted the nv-2054-implement-redis-cluster-service branch April 12, 2023 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants