Skip to content

Commit 65b5522

Browse files
IDistributedCache implementation and refactoring for cartservice (#838)
* better implementation for cartservice * Microsoft.Extensions.Caching.StackExchangeRedis * cleanup * Grpc.Net.Client 2.46.0 * Grpc.AspNetCore 2.46.0 * Grpc.HealthCheck 2.46.3 * cleanup * value.IsNull * value == null Co-authored-by: Christine Kim <[email protected]>
1 parent 8e4855e commit 65b5522

File tree

7 files changed

+28
-216
lines changed

7 files changed

+28
-216
lines changed

src/cartservice/src/Startup.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.Extensions.Hosting;
1010
using cartservice.cartstore;
1111
using cartservice.services;
12+
using Microsoft.Extensions.Caching.StackExchangeRedis;
1213

1314
namespace cartservice
1415
{
@@ -26,23 +27,20 @@ public Startup(IConfiguration configuration)
2627
public void ConfigureServices(IServiceCollection services)
2728
{
2829
string redisAddress = Configuration["REDIS_ADDR"];
29-
ICartStore cartStore = null;
3030
if (!string.IsNullOrEmpty(redisAddress))
3131
{
32-
cartStore = new RedisCartStore(redisAddress);
32+
services.AddStackExchangeRedisCache(options =>
33+
{
34+
options.Configuration = redisAddress;
35+
});
3336
}
3437
else
3538
{
36-
Console.WriteLine("Redis cache host(hostname+port) was not specified. Starting a cart service using local store");
37-
Console.WriteLine("If you wanted to use Redis Cache as a backup store, you should provide its address via command line or REDIS_ADDR environment variable.");
38-
cartStore = new LocalCartStore();
39+
Console.WriteLine("Redis cache host(hostname+port) was not specified. Starting a cart service using in memory store");
40+
services.AddDistributedMemoryCache();
3941
}
4042

41-
// Initialize the redis store
42-
cartStore.InitializeAsync().GetAwaiter().GetResult();
43-
Console.WriteLine("Initialization completed");
44-
45-
services.AddSingleton<ICartStore>(cartStore);
43+
services.AddSingleton<ICartStore, RedisCartStore>();
4644

4745
services.AddGrpc();
4846
}

src/cartservice/src/cartservice.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="Grpc.AspNetCore" Version="2.45.0" />
9-
<PackageReference Include="Grpc.HealthCheck" Version="2.45.0" />
10-
<PackageReference Include="StackExchange.Redis" Version="2.5.61" />
8+
<PackageReference Include="Grpc.AspNetCore" Version="2.46.0" />
9+
<PackageReference Include="Grpc.HealthCheck" Version="2.46.3" />
10+
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.5" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

src/cartservice/src/cartstore/ICartStore.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ namespace cartservice.cartstore
1818
{
1919
public interface ICartStore
2020
{
21-
Task InitializeAsync();
22-
2321
Task AddItemAsync(string userId, string productId, int quantity);
2422
Task EmptyCartAsync(string userId);
25-
2623
Task<Hipstershop.Cart> GetCartAsync(string userId);
27-
2824
bool Ping();
2925
}
3026
}

src/cartservice/src/cartstore/LocalCartStore.cs

Lines changed: 0 additions & 89 deletions
This file was deleted.

src/cartservice/src/cartstore/RedisCartStore.cs

Lines changed: 12 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -16,95 +16,18 @@
1616
using System.Linq;
1717
using System.Threading.Tasks;
1818
using Grpc.Core;
19-
using StackExchange.Redis;
19+
using Microsoft.Extensions.Caching.Distributed;
2020
using Google.Protobuf;
2121

2222
namespace cartservice.cartstore
2323
{
2424
public class RedisCartStore : ICartStore
2525
{
26-
private const string CART_FIELD_NAME = "cart";
27-
private const int REDIS_RETRY_NUM = 30;
26+
private readonly IDistributedCache _cache;
2827

29-
private volatile ConnectionMultiplexer redis;
30-
private volatile bool isRedisConnectionOpened = false;
31-
32-
private readonly object locker = new object();
33-
private readonly byte[] emptyCartBytes;
34-
private readonly string connectionString;
35-
36-
private readonly ConfigurationOptions redisConnectionOptions;
37-
38-
public RedisCartStore(string redisAddress)
39-
{
40-
// Serialize empty cart into byte array.
41-
var cart = new Hipstershop.Cart();
42-
emptyCartBytes = cart.ToByteArray();
43-
connectionString = $"{redisAddress},ssl=false,allowAdmin=true,abortConnect=false";
44-
45-
redisConnectionOptions = ConfigurationOptions.Parse(connectionString);
46-
47-
// Try to reconnect multiple times if the first retry fails.
48-
redisConnectionOptions.ConnectRetry = REDIS_RETRY_NUM;
49-
redisConnectionOptions.ReconnectRetryPolicy = new ExponentialRetry(1000);
50-
51-
redisConnectionOptions.KeepAlive = 180;
52-
}
53-
54-
public Task InitializeAsync()
28+
public RedisCartStore(IDistributedCache cache)
5529
{
56-
EnsureRedisConnected();
57-
return Task.CompletedTask;
58-
}
59-
60-
private void EnsureRedisConnected()
61-
{
62-
if (isRedisConnectionOpened)
63-
{
64-
return;
65-
}
66-
67-
// Connection is closed or failed - open a new one but only at the first thread
68-
lock (locker)
69-
{
70-
if (isRedisConnectionOpened)
71-
{
72-
return;
73-
}
74-
75-
Console.WriteLine("Connecting to Redis: " + connectionString);
76-
redis = ConnectionMultiplexer.Connect(redisConnectionOptions);
77-
78-
if (redis == null || !redis.IsConnected)
79-
{
80-
Console.WriteLine("Wasn't able to connect to redis");
81-
82-
// We weren't able to connect to Redis despite some retries with exponential backoff.
83-
throw new ApplicationException("Wasn't able to connect to redis");
84-
}
85-
86-
Console.WriteLine("Successfully connected to Redis");
87-
var cache = redis.GetDatabase();
88-
89-
Console.WriteLine("Performing small test");
90-
cache.StringSet("cart", "OK" );
91-
object res = cache.StringGet("cart");
92-
Console.WriteLine($"Small test result: {res}");
93-
94-
redis.InternalError += (o, e) => { Console.WriteLine(e.Exception); };
95-
redis.ConnectionRestored += (o, e) =>
96-
{
97-
isRedisConnectionOpened = true;
98-
Console.WriteLine("Connection to redis was retored successfully");
99-
};
100-
redis.ConnectionFailed += (o, e) =>
101-
{
102-
Console.WriteLine("Connection failed. Disposing the object");
103-
isRedisConnectionOpened = false;
104-
};
105-
106-
isRedisConnectionOpened = true;
107-
}
30+
_cache = cache;
10831
}
10932

11033
public async Task AddItemAsync(string userId, string productId, int quantity)
@@ -113,15 +36,9 @@ public async Task AddItemAsync(string userId, string productId, int quantity)
11336

11437
try
11538
{
116-
EnsureRedisConnected();
117-
118-
var db = redis.GetDatabase();
119-
120-
// Access the cart from the cache
121-
var value = await db.HashGetAsync(userId, CART_FIELD_NAME);
122-
12339
Hipstershop.Cart cart;
124-
if (value.IsNull)
40+
var value = await _cache.GetAsync(userId);
41+
if (value == null)
12542
{
12643
cart = new Hipstershop.Cart();
12744
cart.UserId = userId;
@@ -140,8 +57,7 @@ public async Task AddItemAsync(string userId, string productId, int quantity)
14057
existingItem.Quantity += quantity;
14158
}
14259
}
143-
144-
await db.HashSetAsync(userId, new[]{ new HashEntry(CART_FIELD_NAME, cart.ToByteArray()) });
60+
await _cache.SetAsync(userId, cart.ToByteArray());
14561
}
14662
catch (Exception ex)
14763
{
@@ -155,11 +71,8 @@ public async Task EmptyCartAsync(string userId)
15571

15672
try
15773
{
158-
EnsureRedisConnected();
159-
var db = redis.GetDatabase();
160-
161-
// Update the cache with empty cart for given user
162-
await db.HashSetAsync(userId, new[] { new HashEntry(CART_FIELD_NAME, emptyCartBytes) });
74+
var cart = new Hipstershop.Cart();
75+
await _cache.SetAsync(userId, cart.ToByteArray());
16376
}
16477
catch (Exception ex)
16578
{
@@ -173,14 +86,10 @@ public async Task EmptyCartAsync(string userId)
17386

17487
try
17588
{
176-
EnsureRedisConnected();
177-
178-
var db = redis.GetDatabase();
179-
18089
// Access the cart from the cache
181-
var value = await db.HashGetAsync(userId, CART_FIELD_NAME);
90+
var value = await _cache.GetAsync(userId);
18291

183-
if (!value.IsNull)
92+
if (value != null)
18493
{
18594
return Hipstershop.Cart.Parser.ParseFrom(value);
18695
}
@@ -198,9 +107,7 @@ public bool Ping()
198107
{
199108
try
200109
{
201-
var cache = redis.GetDatabase();
202-
var res = cache.Ping();
203-
return res != TimeSpan.Zero;
110+
return true;
204111
}
205112
catch (Exception)
206113
{

src/cartservice/src/services/HealthCheckService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ namespace cartservice.services
2323
{
2424
internal class HealthCheckService : HealthBase
2525
{
26-
private ICartStore _dependency { get; }
26+
private ICartStore _cartStore { get; }
2727

28-
public HealthCheckService (ICartStore dependency)
28+
public HealthCheckService (ICartStore cartStore)
2929
{
30-
_dependency = dependency;
30+
_cartStore = cartStore;
3131
}
3232

3333
public override Task<HealthCheckResponse> Check(HealthCheckRequest request, ServerCallContext context)
3434
{
3535
Console.WriteLine ("Checking CartService Health");
3636
return Task.FromResult(new HealthCheckResponse {
37-
Status = _dependency.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing
37+
Status = _cartStore.Ping() ? HealthCheckResponse.Types.ServingStatus.Serving : HealthCheckResponse.Types.ServingStatus.NotServing
3838
});
3939
}
4040
}

src/cartservice/tests/cartservice.tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Grpc.Net.Client" Version="2.45.0" />
10+
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />
1111
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.0" />
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
1313
<PackageReference Include="xunit" Version="2.4.1" />

0 commit comments

Comments
 (0)