Skip to content

Commit 231042e

Browse files
Sneed, Anthonytonysneed
Sneed, Anthony
authored andcommittedDec 10, 2023
Persist saga metadata to Redis
Implement RedisPersistableSagaRepository<TSaga, TMetaData>
1 parent 125b138 commit 231042e

File tree

4 files changed

+105
-30
lines changed

4 files changed

+105
-30
lines changed
 

‎src/EventDriven.Sagas.Persistence.Mongo/Repositories/PersistableSagaRepository.cs

+7-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ public PersistableSagaRepository(
3434
/// <inheritdoc />
3535
public async Task<TSaga?> GetAsync(Guid id, TSaga newEntity)
3636
{
37-
var dto = await FindOneAsync(e => e.SagaId == id);
38-
if (dto is null) return null;
39-
_mapper.Map(dto, newEntity);
40-
return newEntity;
37+
using (await _syncRoot.LockAsync())
38+
{
39+
var dto = await FindOneAsync(e => e.SagaId == id);
40+
if (dto is null) return null;
41+
_mapper.Map(dto, newEntity);
42+
return newEntity;
43+
}
4144
}
4245

4346
/// <inheritdoc />

‎src/EventDriven.Sagas.Persistence.Pool.Abstractions/Pools/PersistableSagaPool.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ protected async Task<TSaga> ConfigureSagaAsync()
111111
/// </summary>
112112
/// <typeparam name="TSaga">Persistable saga.</typeparam>
113113
/// <typeparam name="TMetadata">Saga metadata.</typeparam>
114-
public class PersistableSagaPool<TSaga,TMetadata>: PersistableSagaPool<TSaga>, IPersistableSagaPool<TSaga, TMetadata>
114+
public class PersistableSagaPool<TSaga,TMetadata> :
115+
PersistableSagaPool<TSaga>, IPersistableSagaPool<TSaga, TMetadata>
115116
where TSaga : PersistableSaga<TMetadata>
116117
where TMetadata : class
117118
{

‎src/EventDriven.Sagas.Persistence.Redis/EventDriven.Sagas.Persistence.Redis.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
88
<Authors>Tony Sneed</Authors>
99
<PackageReadmeFile>ReadMe.md</PackageReadmeFile>
10-
<PackageVersion>1.6.0</PackageVersion>
10+
<PackageVersion>1.6.1-beta1</PackageVersion>
1111
<Description>A set of abstractions for implementing the Saga pattern.</Description>
1212
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1313
<PackageProjectUrl>https://github.com/event-driven-dotnet/EventDriven.Sagas</PackageProjectUrl>
1414
<PackageIcon>eda-logo.jpeg</PackageIcon>
1515
<RepositoryUrl>https://github.com/event-driven-dotnet/EventDriven.Sagas.git</RepositoryUrl>
1616
<RepositoryType>git</RepositoryType>
1717
<PackageTags>sagas event-driven event-driven-architecture</PackageTags>
18-
<PackageReleaseNotes>https://github.com/event-driven-dotnet/EventDriven.Sagas/releases/tag/v1.6.0</PackageReleaseNotes>
18+
<PackageReleaseNotes>https://github.com/event-driven-dotnet/EventDriven.Sagas/releases/tag/v1.6.1-beta1</PackageReleaseNotes>
1919
<PackageId>EventDriven.Sagas.Persistence.Redis</PackageId>
2020
<GenerateDocumentationFile>True</GenerateDocumentationFile>
2121
</PropertyGroup>
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Text.Json;
22
using AutoMapper;
3-
using EventDriven.DDD.Abstractions.Repositories;
43
using EventDriven.Sagas.Persistence.Abstractions;
54
using EventDriven.Sagas.Persistence.Abstractions.DTO;
65
using EventDriven.Sagas.Persistence.Abstractions.Repositories;
@@ -15,11 +14,23 @@ namespace EventDriven.Sagas.Persistence.Redis.Repositories;
1514
public class RedisPersistableSagaRepository<TSaga> : IPersistableSagaRepository<TSaga>
1615
where TSaga : PersistableSaga
1716
{
18-
private readonly IDistributedCache _cache;
19-
private readonly DistributedCacheEntryOptions _cacheOptions;
20-
private readonly IMapper _mapper;
2117
private readonly AsyncLock _syncRoot = new();
2218

19+
/// <summary>
20+
/// Distributed cache.
21+
/// </summary>
22+
protected readonly IDistributedCache Cache;
23+
24+
/// <summary>
25+
/// Distributed cache entry options.
26+
/// </summary>
27+
protected readonly DistributedCacheEntryOptions CacheOptions;
28+
29+
/// <summary>
30+
/// Auto mapper.
31+
/// </summary>
32+
protected readonly IMapper Mapper;
33+
2334
/// <summary>
2435
/// PersistableSagaRepository constructor.
2536
/// </summary>
@@ -30,37 +41,40 @@ public RedisPersistableSagaRepository(IDistributedCache cache,
3041
DistributedCacheEntryOptions cacheOptions,
3142
IMapper mapper)
3243
{
33-
_cache = cache;
34-
_cacheOptions = cacheOptions;
35-
_mapper = mapper;
44+
Cache = cache;
45+
CacheOptions = cacheOptions;
46+
Mapper = mapper;
3647
}
3748

3849
/// <inheritdoc />
39-
public async Task<TSaga?> GetAsync(Guid id, TSaga newEntity)
50+
public virtual async Task<TSaga?> GetAsync(Guid id, TSaga newEntity)
4051
{
41-
var json = await _cache.GetStringAsync(id.ToString());
42-
if (json is null) return null;
43-
var dto = JsonSerializer.Deserialize<PersistableSagaDto>(json);
44-
_mapper.Map(dto, newEntity);
45-
return newEntity;
52+
using (await _syncRoot.LockAsync())
53+
{
54+
var json = await Cache.GetStringAsync(id.ToString());
55+
if (json is null) return null;
56+
var dto = JsonSerializer.Deserialize<PersistableSagaDto>(json);
57+
Mapper.Map(dto, newEntity);
58+
return newEntity;
59+
}
4660
}
4761

4862
/// <inheritdoc />
49-
public async Task<TSaga> CreateAsync(TSaga newEntity)
63+
public virtual async Task<TSaga> CreateAsync(TSaga newEntity)
5064
{
5165
using (await _syncRoot.LockAsync())
5266
{
5367
newEntity.ETag = Guid.NewGuid().ToString();
54-
var dto = _mapper.Map<PersistableSagaDto>(newEntity);
68+
var dto = Mapper.Map<PersistableSagaDto>(newEntity);
5569
dto.Id = Guid.NewGuid();
5670
await InsertAsync(dto);
57-
_mapper.Map(dto, newEntity);
71+
Mapper.Map(dto, newEntity);
5872
return newEntity;
5973
}
6074
}
6175

6276
/// <inheritdoc />
63-
public async Task<TSaga> SaveAsync(TSaga existingEntity, TSaga newEntity)
77+
public virtual async Task<TSaga> SaveAsync(TSaga existingEntity, TSaga newEntity)
6478
{
6579
using (await _syncRoot.LockAsync())
6680
{
@@ -69,23 +83,23 @@ public async Task<TSaga> SaveAsync(TSaga existingEntity, TSaga newEntity)
6983
return await CreateAsync(newEntity);
7084

7185
existingEntity.ETag = Guid.NewGuid().ToString();
72-
var dto = _mapper.Map<PersistableSagaDto>(existingEntity);
86+
var dto = Mapper.Map<PersistableSagaDto>(existingEntity);
7387
dto.Id = Guid.NewGuid();
7488
await RemoveAsync(existingEntity.Id);
7589
await InsertAsync(dto);
76-
_mapper.Map(dto, newEntity);
90+
Mapper.Map(dto, newEntity);
7791
return newEntity;
7892
}
7993
}
8094

8195
/// <inheritdoc />
82-
public async Task RemoveAsync(Guid id) =>
83-
await _cache.RemoveAsync(id.ToString());
96+
public virtual Task RemoveAsync(Guid id) =>
97+
Cache.RemoveAsync(id.ToString());
8498

85-
private async Task InsertAsync(PersistableSagaDto dto)
99+
private Task InsertAsync(PersistableSagaDto dto)
86100
{
87101
var json = JsonSerializer.Serialize(dto);
88-
await _cache.SetStringAsync(dto.SagaId.ToString(), json, _cacheOptions);
102+
return Cache.SetStringAsync(dto.SagaId.ToString(), json, CacheOptions);
89103
}
90104
}
91105

@@ -100,10 +114,67 @@ public class RedisPersistableSagaRepository<TSaga, TMetaData> :
100114
where TSaga : PersistableSaga<TMetaData>
101115
where TMetaData : class
102116
{
117+
private readonly AsyncLock _syncRoot = new();
118+
103119
/// <inheritdoc />
104120
public RedisPersistableSagaRepository(IDistributedCache cache,
105121
DistributedCacheEntryOptions cacheOptions, IMapper mapper) :
106122
base(cache, cacheOptions, mapper)
107123
{
108124
}
125+
126+
/// <inheritdoc />
127+
public override async Task<TSaga?> GetAsync(Guid id, TSaga newEntity)
128+
{
129+
using (await _syncRoot.LockAsync())
130+
{
131+
var json = await Cache.GetStringAsync(id.ToString());
132+
if (json is null) return null;
133+
var dto = JsonSerializer.Deserialize<PersistableSagaMetadataDto>(json);
134+
if (dto is null) return null;
135+
Mapper.Map(dto, newEntity);
136+
var metadata = JsonSerializer.Deserialize<TMetaData>(dto.Metadata);
137+
newEntity.Metadata = metadata;
138+
return newEntity;
139+
}
140+
}
141+
142+
/// <inheritdoc />
143+
public override async Task<TSaga> CreateAsync(TSaga newEntity)
144+
{
145+
using (await _syncRoot.LockAsync())
146+
{
147+
newEntity.ETag = Guid.NewGuid().ToString();
148+
var dto = Mapper.Map<PersistableSagaMetadataDto>(newEntity);
149+
dto.Id = Guid.NewGuid();
150+
await InsertAsync(dto);
151+
Mapper.Map(dto, newEntity);
152+
return newEntity;
153+
}
154+
}
155+
156+
/// <inheritdoc />
157+
public override async Task<TSaga> SaveAsync(TSaga existingEntity, TSaga newEntity)
158+
{
159+
using (await _syncRoot.LockAsync())
160+
{
161+
var existing = await GetAsync(existingEntity.Id, newEntity);
162+
if (existing is null)
163+
return await CreateAsync(newEntity);
164+
165+
existingEntity.ETag = Guid.NewGuid().ToString();
166+
var dto = Mapper.Map<PersistableSagaMetadataDto>(existingEntity);
167+
dto.Id = Guid.NewGuid();
168+
await RemoveAsync(existingEntity.Id);
169+
await InsertAsync(dto);
170+
Mapper.Map(dto, newEntity);
171+
return newEntity;
172+
}
173+
}
174+
175+
private Task InsertAsync(PersistableSagaMetadataDto dto)
176+
{
177+
var json = JsonSerializer.Serialize(dto);
178+
return Cache.SetStringAsync(dto.SagaId.ToString(), json, CacheOptions);
179+
}
109180
}

0 commit comments

Comments
 (0)
Please sign in to comment.