Skip to content

Commit e2eeac2

Browse files
authored
Merge pull request #84 from Resellio/feat/setup-redis
Setup Redis
2 parents 7e24c91 + 8e19596 commit e2eeac2

File tree

5 files changed

+131
-1
lines changed

5 files changed

+131
-1
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace TickAPI.Common.Redis.Abstractions;
2+
3+
public interface IRedisService
4+
{
5+
public Task<string?> GetStringAsync(string key);
6+
public Task<bool> SetStringAsync(string key, string value, TimeSpan? expiry = null);
7+
public Task<bool> DeleteKeyAsync(string key);
8+
public Task<T?> GetObjectAsync<T>(string key);
9+
public Task<bool> SetObjectAsync<T>(string key, T value, TimeSpan? expiry = null);
10+
public Task<bool> KeyExistsAsync(string key);
11+
public Task<bool> KeyExpireAsync(string key, TimeSpan expiry);
12+
public Task<long> IncrementValueAsync(string key, long value = 1);
13+
public Task<long> DecrementValueAsync(string key, long value = 1);
14+
public Task<long?> GetLongValueAsync(string key);
15+
public Task<bool> SetLongValueAsync(string key, long value, TimeSpan? expiry = null);
16+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Text.Json;
2+
using StackExchange.Redis;
3+
using TickAPI.Common.Redis.Abstractions;
4+
5+
namespace TickAPI.Common.Redis.Services;
6+
7+
public class RedisService : IRedisService
8+
{
9+
private readonly IDatabase _database;
10+
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
11+
12+
public RedisService(IConnectionMultiplexer connectionMultiplexer)
13+
{
14+
_database = connectionMultiplexer.GetDatabase();
15+
}
16+
17+
public async Task<string?> GetStringAsync(string key)
18+
{
19+
return await RetryAsync(async () =>
20+
{
21+
var value = await _database.StringGetAsync(key);
22+
return value.HasValue ? value.ToString() : null;
23+
});
24+
}
25+
26+
public async Task<bool> SetStringAsync(string key, string value, TimeSpan? expiry = null)
27+
{
28+
return await RetryAsync(async () => await _database.StringSetAsync(key, value, expiry));
29+
}
30+
31+
public async Task<bool> DeleteKeyAsync(string key)
32+
{
33+
return await RetryAsync(async () => await _database.KeyDeleteAsync(key));
34+
}
35+
36+
public async Task<T?> GetObjectAsync<T>(string key)
37+
{
38+
var json = await GetStringAsync(key);
39+
if (string.IsNullOrEmpty(json))
40+
{
41+
return default;
42+
}
43+
return JsonSerializer.Deserialize<T>(json, JsonOptions);
44+
}
45+
46+
public async Task<bool> SetObjectAsync<T>(string key, T value, TimeSpan? expiry = null)
47+
{
48+
var json = JsonSerializer.Serialize(value, JsonOptions);
49+
50+
return await SetStringAsync(key, json, expiry);
51+
}
52+
53+
public async Task<bool> KeyExistsAsync(string key)
54+
{
55+
return await RetryAsync(async () => await _database.KeyExistsAsync(key));
56+
}
57+
58+
public async Task<bool> KeyExpireAsync(string key, TimeSpan expiry)
59+
{
60+
return await RetryAsync(async () => await _database.KeyExpireAsync(key, expiry));
61+
}
62+
63+
public async Task<long> IncrementValueAsync(string key, long value = 1)
64+
{
65+
return await RetryAsync(async () => await _database.StringIncrementAsync(key, value));
66+
}
67+
68+
public async Task<long> DecrementValueAsync(string key, long value = 1)
69+
{
70+
return await RetryAsync(async () => await _database.StringDecrementAsync(key, value));
71+
}
72+
73+
public async Task<long?> GetLongValueAsync(string key)
74+
{
75+
var value = await RetryAsync(() => _database.StringGetAsync(key));
76+
if (value.HasValue && long.TryParse(value, out var result))
77+
{
78+
return result;
79+
}
80+
return null;
81+
}
82+
83+
public async Task<bool> SetLongValueAsync(string key, long value, TimeSpan? expiry = null)
84+
{
85+
return await RetryAsync(async () => await _database.StringSetAsync(key, value.ToString(), expiry));
86+
}
87+
88+
private static async Task<T> RetryAsync<T>(Func<Task<T>> action, int retryCount = 3, int millisecondsDelay = 100)
89+
{
90+
var attempt = 0;
91+
while (true)
92+
{
93+
try
94+
{
95+
return await action();
96+
}
97+
catch (Exception ex) when (ex is RedisConnectionException or RedisTimeoutException && attempt < retryCount)
98+
{
99+
attempt++;
100+
await Task.Delay(millisecondsDelay);
101+
}
102+
}
103+
}
104+
}

TickAPI/TickAPI/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.AspNetCore.Authentication.JwtBearer;
55
using Microsoft.EntityFrameworkCore;
66
using Microsoft.IdentityModel.Tokens;
7+
using StackExchange.Redis;
78
using TickAPI.Admins.Abstractions;
89
using TickAPI.Admins.Repositories;
910
using TickAPI.Admins.Services;
@@ -35,6 +36,8 @@
3536
using TickAPI.Categories.Services;
3637
using TickAPI.Common.Claims.Abstractions;
3738
using TickAPI.Common.Claims.Services;
39+
using TickAPI.Common.Redis.Abstractions;
40+
using TickAPI.Common.Redis.Services;
3841

3942
// Builder constants
4043
const string allowClientPolicyName = "AllowClient";
@@ -116,6 +119,7 @@
116119
builder.Services.AddScoped<IPaginationService, PaginationService>();
117120
builder.Services.AddScoped<IDateTimeService, DateTimeService>();
118121
builder.Services.AddScoped<IClaimsService, ClaimsService>();
122+
builder.Services.AddScoped<IRedisService, RedisService>();
119123

120124
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
121125
builder.Services.AddEndpointsApiExplorer();
@@ -154,6 +158,10 @@
154158
errorNumbersToAdd: null));
155159
});
156160

161+
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
162+
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("ResellioRedisCache"))
163+
);
164+
157165
// Create CORS policy
158166
builder.Services.AddCors(options =>
159167
{

TickAPI/TickAPI/TickAPI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
</PackageReference>
2020
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" />
21+
<PackageReference Include="StackExchange.Redis" Version="2.8.31" />
2122
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
2223
</ItemGroup>
2324

TickAPI/TickAPI/appsettings.example.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"https://another-site.com"
1212
],
1313
"ConnectionStrings": {
14-
"ResellioDatabase": "Server=tcp:localhost,1433;Initial Catalog=resellioDB;Persist Security Info=False;User ID=sa;Password=Rese11io;TrustServerCertificate=True;Encrypt=False"
14+
"ResellioDatabase": "Server=tcp:localhost,1433;Initial Catalog=resellioDB;Persist Security Info=False;User ID=sa;Password=Rese11io;TrustServerCertificate=True;Encrypt=False",
15+
"ResellioRedisCache": "localhost:6379"
1516
},
1617
"Authentication": {
1718
"Google": {

0 commit comments

Comments
 (0)