Skip to content

Commit ac897cc

Browse files
shacharPashchayim
andauthored
Initial support for OSS Cluster API (#170)
* delete mock * coverage * fix test * indent * change to var - check * cluster test * add opthin to connect cluster with dotnet test * use key in topk tests * get env vars inside RedisFixture * skip if redis * add skip where needed * Execute broadcast * delete cluster tests * RedisFixture fix * add to contributing * run cluster on CI * wip * fix / * -d * delete restore * return restore * add -RC3 * add RC3 to docker-compose * try define both .net 6 and 7 * Skip if cluster where needed * add names * skip configOnTimeout if cluster * try to fix win tests * tests names +fix win version * fix versions * versions * win verer * wording * change to OSSCluster * isOSSCluster * update skip reason to OSS cluster * general dispose * general dispose for the rest of the disposes --------- Co-authored-by: Chayim <chayim@users.noreply.github.com>
1 parent 61a245d commit ac897cc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+692
-501
lines changed

.github/cluster.env

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
REDIS_CLUSTER=127.0.0.1:16379
2+
NUM_REDIS_CLUSTER_NODES=6

.github/docker-compose.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
version: "3.8"
3+
services:
4+
5+
redis-stack-7.2.0-RC3:
6+
image: redis/redis-stack-server:7.2.0-RC3
7+
ports: ["6379:6379"]
8+
9+
redis-stack-6.2.6:
10+
image: redis/redis-stack-server:6.2.6-v9
11+
ports: ["6379:6379"]
12+
13+
redis-stack-edge:
14+
image: redis/redis-stack-server:edge
15+
ports: ["6379:6379"]
16+
17+
redis-stack-cluster:
18+
container_name: redis-cluster
19+
build:
20+
context: .
21+
dockerfile: dockers/Dockerfile.cluster
22+
ports:
23+
- 16379:16379
24+
- 16380:16380
25+
- 16381:16381
26+
- 16382:16382
27+
- 16383:16383
28+
- 16384:16384
29+
volumes:
30+
- "./dockers/cluster.redis.conf:/redis.conf:ro"

.github/dockers/Dockerfile.cluster

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM redis/redis-stack-server:edge as rss
2+
3+
COPY dockers/create_cluster.sh /create_cluster.sh
4+
RUN ls -R /opt/redis-stack
5+
RUN chmod a+x /create_cluster.sh
6+
7+
ENTRYPOINT [ "/create_cluster.sh"]

.github/dockers/cluster.redis.conf

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
protected-mode no
2+
enable-debug-command yes
3+
loadmodule /opt/redis-stack/lib/redisearch.so
4+
loadmodule /opt/redis-stack/lib/redisgraph.so
5+
loadmodule /opt/redis-stack/lib/redistimeseries.so
6+
loadmodule /opt/redis-stack/lib/rejson.so
7+
loadmodule /opt/redis-stack/lib/redisbloom.so
8+
loadmodule /opt/redis-stack/lib/redisgears.so v8-plugin-path /opt/redis-stack/lib/libredisgears_v8_plugin.so

.github/dockers/create_cluster.sh

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#! /bin/bash
2+
3+
mkdir -p /nodes
4+
touch /nodes/nodemap
5+
if [ -z ${START_PORT} ]; then
6+
START_PORT=16379
7+
fi
8+
if [ -z ${END_PORT} ]; then
9+
END_PORT=16384
10+
fi
11+
if [ ! -z "$3" ]; then
12+
START_PORT=$2
13+
START_PORT=$3
14+
fi
15+
echo "STARTING: ${START_PORT}"
16+
echo "ENDING: ${END_PORT}"
17+
18+
for PORT in `seq ${START_PORT} ${END_PORT}`; do
19+
mkdir -p /nodes/$PORT
20+
if [[ -e /redis.conf ]]; then
21+
cp /redis.conf /nodes/$PORT/redis.conf
22+
else
23+
touch /nodes/$PORT/redis.conf
24+
fi
25+
cat << EOF >> /nodes/$PORT/redis.conf
26+
port ${PORT}
27+
cluster-enabled yes
28+
daemonize yes
29+
logfile /redis.log
30+
dir /nodes/$PORT
31+
EOF
32+
33+
set -x
34+
/opt/redis-stack/bin/redis-server /nodes/$PORT/redis.conf
35+
sleep 1
36+
if [ $? -ne 0 ]; then
37+
echo "Redis failed to start, exiting."
38+
continue
39+
fi
40+
echo 127.0.0.1:$PORT >> /nodes/nodemap
41+
done
42+
if [ -z "${REDIS_PASSWORD}" ]; then
43+
echo yes | /opt/redis-stack/bin/redis-cli --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1
44+
else
45+
echo yes | opt/redis-stack/bin/redis-cli -a ${REDIS_PASSWORD} --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1
46+
fi
47+
tail -f /redis.log

.github/standalone.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REDIS=localhost:6379

.github/workflows/integration.yml

+51-47
Original file line numberDiff line numberDiff line change
@@ -12,61 +12,65 @@ on:
1212
- cron: "0 1 * * *"
1313

1414
jobs:
15-
build_and_Test:
16-
name: Test [redis-stack ${{matrix.redis-stack-version}}]
17-
runs-on: ubuntu-latest
15+
dotnet_6_cluster:
16+
name: .NET 6 on [redis-stack cluster]
17+
uses: ./.github/workflows/reusable.yml
18+
with:
19+
redis_stack_type: cluster
20+
dotnet_version: 6.0.x
21+
clr_version: net6.0
22+
dotenv_file: .github/cluster.env
23+
secrets: inherit
24+
25+
dotnet_6:
26+
name: .NET 6 on [redis-stack ${{matrix.redis-stack-type}}]
27+
uses: ./.github/workflows/reusable.yml
1828
strategy:
1929
fail-fast: false
30+
max-parallel: 5
2031
matrix:
21-
redis-stack-version: ["6.2.6-v9", "7.2.0-RC3", "edge"]
22-
env:
23-
USER_NAME: ${{ secrets.USER_NAME }}
24-
PASSWORD: ${{ secrets.PASSWORD }}
25-
ENDPOINT: ${{ secrets.ENDPOINT }}
26-
REDIS_VERSION: ${{ matrix.redis-stack-version }}
27-
steps:
28-
- uses: actions/checkout@v3
29-
- name: .NET Core 6
30-
uses: actions/setup-dotnet@v2
31-
with:
32-
dotnet-version: '6.0.x'
33-
- name: .NET Core 7
34-
uses: actions/setup-dotnet@v2
35-
with:
36-
dotnet-version: '7.0.x'
37-
- name: run redis-stack-server docker
38-
run: docker run -p 6379:6379 -d redis/redis-stack-server:${{matrix.redis-stack-version}}
39-
- name: Restore dependencies
40-
run: dotnet restore
41-
- name: Build
42-
run: dotnet build --no-restore /p:ContinuousIntegrationBuild=true
43-
- name: Test
44-
run: |
45-
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_ca.pem
46-
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user.crt
47-
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user_private.key
48-
ls -R
49-
dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
50-
- name: Test
51-
run: |
52-
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_ca.pem
53-
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user.crt
54-
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user_private.key
55-
ls -R
56-
dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
57-
- name: Codecov
58-
uses: codecov/codecov-action@v3
59-
with:
60-
token: ${{secrets.CODECOV_TOKEN}}
61-
verbose: true
62-
- name: Build
63-
run: dotnet pack -c Release
32+
redis-stack-type: ['edge', '6.2.6', '7.2.0-RC3']
33+
with:
34+
redis_stack_type: ${{matrix.redis-stack-type}}
35+
dotnet_version: 6.0.x
36+
clr_version: net6.0
37+
dotenv_file: .github/standalone.env
38+
secrets: inherit
39+
40+
dotnet_7_cluster:
41+
name: .NET 7 on [redis-stack cluster]
42+
uses: ./.github/workflows/reusable.yml
43+
with:
44+
redis_stack_type: cluster
45+
dotnet_version: 7.0.x
46+
clr_version: net7.0
47+
dotenv_file: .github/cluster.env
48+
secrets: inherit
49+
50+
dotnet_7:
51+
name: .NET 7 on [redis-stack ${{matrix.redis-stack-type}}]
52+
uses: ./.github/workflows/reusable.yml
53+
strategy:
54+
fail-fast: false
55+
max-parallel: 5
56+
matrix:
57+
redis-stack-type: ['edge', '6.2.6', '7.2.0-RC3']
58+
with:
59+
redis_stack_type: ${{matrix.redis-stack-type}}
60+
dotnet_version: 7.0.x
61+
clr_version: net7.0
62+
dotenv_file: .github/standalone.env
63+
secrets: inherit
6464

6565
build_and_test_windows:
6666
name: Windows Test ${{matrix.redis-stack-version}}
6767
runs-on: windows-latest
68+
strategy:
69+
fail-fast: false
70+
matrix:
71+
redis-stack-version: ['6.2.6-v9', '7.2.0-RC3']
6872
env:
69-
redis_stack_version: 6.2.6-v7
73+
redis_stack_version: ${{matrix.redis-stack-version}}
7074
USER_NAME: ${{ secrets.USER_NAME }}
7175
PASSWORD: ${{ secrets.PASSWORD }}
7276
ENDPOINT: ${{ secrets.ENDPOINT }}

.github/workflows/reusable.yml

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Build and Test
2+
on:
3+
workflow_call:
4+
inputs:
5+
6+
redis_stack_type:
7+
required: true
8+
type: string
9+
10+
dotnet_version:
11+
required: true
12+
type: string
13+
14+
clr_version:
15+
required: true
16+
type: string
17+
18+
dotenv_file:
19+
required: true
20+
type: string
21+
jobs:
22+
23+
build_and_test:
24+
name: Test
25+
runs-on: ubuntu-latest
26+
27+
env:
28+
USER_NAME: ${{ secrets.USER_NAME }}
29+
PASSWORD: ${{ secrets.PASSWORD }}
30+
ENDPOINT: ${{ secrets.ENDPOINT }}
31+
steps:
32+
33+
- uses: actions/checkout@v3
34+
35+
- name: .NET Core 6
36+
uses: actions/setup-dotnet@v2
37+
with:
38+
dotnet-version: '6.0.x'
39+
40+
- name: .NET Core 7
41+
uses: actions/setup-dotnet@v2
42+
with:
43+
dotnet-version: '7.0.x'
44+
45+
- name: run redis-stack-server docker
46+
working-directory: .github
47+
run: docker-compose up -d redis-stack-${{inputs.redis_stack_type}}
48+
49+
- name: set variables in dotenv
50+
uses: c-py/action-dotenv-to-setenv@v2
51+
with:
52+
env-file: ${{inputs.dotenv_file}}
53+
54+
- name: Restore dependencies
55+
run: dotnet restore
56+
- name: Build
57+
run: dotnet build --no-restore /p:ContinuousIntegrationBuild=true
58+
- name: Test
59+
run: |
60+
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_ca.pem
61+
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user.crt
62+
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user_private.key
63+
ls -R
64+
dotnet test -f ${{inputs.clr_version}} --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
65+
- name: Codecov
66+
uses: codecov/codecov-action@v3
67+
with:
68+
token: ${{secrets.CODECOV_TOKEN}}
69+
verbose: true
70+
- name: Build
71+
run: dotnet pack -c Release
72+

CONTRIBUTING.md

+9
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ e.g:
112112
```bash
113113
dotnet test --environment="REDIS=172.17.0.1:6379"
114114
```
115+
116+
To run your tests against an oss cluster:
117+
```bash
118+
dotnet test --environment "REDIS_CLUSTER=<redisServer:port>" --environment "NUM_REDIS_CLUSTER_NODES=<number of nodes in the cluster>"
119+
```
120+
e.g. :
121+
```bash
122+
dotnet test --environment "REDIS_CLUSTER=127.0.0.1:16379" --environment "NUM_REDIS_CLUSTER_NODES=6"
123+
```
115124
## How to Report a Bug
116125

117126
### Security Vulnerabilities

src/NRedisStack/Auxiliary.cs

+48
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,53 @@ public async static Task<RedisResult> ExecuteAsync(this IDatabaseAsync db, Seria
3535
{
3636
return await db.ExecuteAsync(command.Command, command.Args);
3737
}
38+
39+
public static List<RedisResult> ExecuteBroadcast(this IDatabaseAsync db, string command)
40+
=> db.ExecuteBroadcast(new SerializedCommand(command));
41+
42+
public static List<RedisResult> ExecuteBroadcast(this IDatabaseAsync db, SerializedCommand command)
43+
{
44+
var redis = db.Multiplexer;
45+
var endpoints = redis.GetEndPoints();
46+
var results = new List<RedisResult>(endpoints.Length);
47+
48+
foreach (var endPoint in endpoints)
49+
{
50+
var server = redis.GetServer(endPoint);
51+
52+
if (server.IsReplica)
53+
{
54+
continue; // Skip replica nodes
55+
}
56+
// Send your command to the master node
57+
58+
results.Add(server.Multiplexer.GetDatabase().Execute(command));
59+
}
60+
return results;
61+
}
62+
63+
public async static Task<List<RedisResult>> ExecuteBroadcastAsync(this IDatabaseAsync db, string command)
64+
=> await db.ExecuteBroadcastAsync(new SerializedCommand(command));
65+
66+
public async static Task<List<RedisResult>> ExecuteBroadcastAsync(this IDatabaseAsync db, SerializedCommand command)
67+
{
68+
var redis = db.Multiplexer;
69+
var endpoints = redis.GetEndPoints();
70+
var results = new List<RedisResult>(endpoints.Length);
71+
72+
foreach (var endPoint in endpoints)
73+
{
74+
var server = redis.GetServer(endPoint);
75+
76+
if (server.IsReplica)
77+
{
78+
continue; // Skip replica nodes
79+
}
80+
// Send your command to the master node
81+
82+
results.Add(await server.Multiplexer.GetDatabase().ExecuteAsync(command));
83+
}
84+
return results;
85+
}
3886
}
3987
}

tests/Doc/Doc.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
</ItemGroup>
2121
<ItemGroup>
2222
<ProjectReference Include="..\..\src\NRedisStack\NRedisStack.csproj" />
23+
<ProjectReference Include="..\..\tests\NRedisStack.Tests\NRedisStack.Tests.csproj" />
2324
</ItemGroup>
2425
</Project>

tests/Doc/HashExample.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// EXAMPLE: hash_tutorial
22
// HIDE_START
3+
using NRedisStack.Tests;
34
using StackExchange.Redis;
45

56
//REMOVE_START
@@ -8,7 +9,7 @@ namespace NRedisStack.Doc;
89
//REMOVE_END
910
public class HashExample
1011
{
11-
[Fact]
12+
[SkipIfRedis(Is.OSSCluster)]
1213
public void run()
1314
{
1415
var muxer = ConnectionMultiplexer.Connect("localhost:6379");

0 commit comments

Comments
 (0)