-
Notifications
You must be signed in to change notification settings - Fork 1
/
RedisStore.cs
148 lines (119 loc) · 6.99 KB
/
RedisStore.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using System.Collections.Concurrent;
using StackExchange.Redis;
namespace PlanningPoker.Server;
public class RedisStore(IDatabase database) : IStore
{
public async Task AddPointAsync(string sessionId, string point) =>
await database.ListRightPushAsync($"{sessionId}:points", point, flags: CommandFlags.FireAndForget);
public async Task CreateParticipantAsync(string sessionId, string participantId, string name) {
await database.HashSetAsync(
key: $"{sessionId}:participants:{participantId}",
hashFields: [
new HashEntry(nameof(Participant.Name), name),
new HashEntry(nameof(Participant.Points), ""),
new HashEntry(nameof(Participant.Stars), 0)
],
flags: CommandFlags.FireAndForget
);
await database.ListRightPushAsync($"{sessionId}:participants", participantId, flags: CommandFlags.FireAndForget);
await database.KeyExpireAsync($"{sessionId}:participants:{participantId}", DateTime.UtcNow.AddDays(1), flags: CommandFlags.FireAndForget);
await database.KeyExpireAsync($"{sessionId}:participants:", DateTime.UtcNow.AddDays(1), when: ExpireWhen.HasNoExpiry, flags: CommandFlags.FireAndForget);
}
public async Task<string> CreateSessionAsync(string title, IEnumerable<string> points) {
string newSessionId;
bool insertResult;
do {
newSessionId = Guid.NewGuid().ToString().Split('-').First();
var transaction = database.CreateTransaction();
transaction.AddCondition(Condition.KeyNotExists(newSessionId));
var _ = transaction.HashSetAsync(
key: newSessionId,
hashFields: [
new HashEntry(nameof(Session.Title), title),
new HashEntry(nameof(Session.State), Enum.GetName(State.Hidden))
]
);
insertResult = await transaction.ExecuteAsync();
}
while (!insertResult);
#pragma warning disable CS4014 // Fire and forget
Task.Run(async () => {
foreach (var point in points) {
await database.ListRightPushAsync($"{newSessionId}:points", point);
}
});
#pragma warning restore CS4014
await database.KeyExpireAsync($"{newSessionId}", DateTime.UtcNow.AddDays(1), flags: CommandFlags.FireAndForget);
await database.KeyExpireAsync($"{newSessionId}:points", DateTime.UtcNow.AddDays(1), flags: CommandFlags.FireAndForget);
return newSessionId;
}
public async Task DeleteParticipantAsync(string sessionId, string participantId) {
await database.ListRemoveAsync($"{sessionId}:participants", participantId, flags: CommandFlags.FireAndForget);
await database.KeyDeleteAsync($"{sessionId}:participants:{participantId}", flags: CommandFlags.FireAndForget);
if (await database.ListLengthAsync($"{sessionId}:participants") == 0) {
await database.KeyDeleteAsync(sessionId, flags: CommandFlags.FireAndForget);
await database.KeyDeleteAsync($"{sessionId}:participants", flags: CommandFlags.FireAndForget);
await database.KeyDeleteAsync($"{sessionId}:points", flags: CommandFlags.FireAndForget);
}
}
public async Task<bool> ExistsSessionAsync(string sessionId) =>
await database.KeyExistsAsync(sessionId);
public async Task<Session?> GetSessionAsync(string sessionId) {
var participantIds = await database.ListRangeAsync($"{sessionId}:participants");
Session? session = null;
var participants = new ConcurrentBag<Participant>();
var points = Enumerable.Empty<string>();
Task[] readTasks = [
database
.HashGetAllAsync(sessionId)
.ContinueWith(s => {
if (s.Result.Length > 0) {
session = new Session(
Title: s.Result.Single(h => h.Name == nameof(Session.Title)).Value!,
Participants: [],
State: Enum.Parse<State>((string)s.Result.Single(h => h.Name == nameof(Session.State)).Value!),
Points: []
);
}
}),
database.ListRangeAsync($"{sessionId}:points")
.ContinueWith(p => points = p?.Result?.Select(v => (string)v!)?.ToArray() ?? []),
..participantIds.Select(i =>
database
.HashGetAllAsync($"{sessionId}:participants:{i}")
.ContinueWith(p =>
participants.Add(new(
i!,
p.Result.Single(h => h.Name == nameof(Participant.Name)).Value!,
p.Result.Single(h => h.Name == nameof(Participant.Points)).Value!,
(int)p.Result.Single(h => h.Name == nameof(Participant.Stars)).Value
))
)
)
];
await Task.WhenAll(readTasks);
return session switch {
Session s => s with {
Participants = [.. participants],
Points = points
},
var n => n
};
}
public async Task IncrementParticipantStarsAsync(string sessionId, string participantId, int count = 1) =>
await database.HashIncrementAsync($"{sessionId}:participants:{participantId}", nameof(Participant.Stars), count, flags: CommandFlags.FireAndForget);
public async Task RemovePointAsync(string sessionId, string point) =>
await database.ListRemoveAsync($"{sessionId}:points", point, flags: CommandFlags.FireAndForget);
public async Task UpdateAllParticipantPointsAsync(string sessionId, string points = "") {
var participantIds = await database.ListRangeAsync($"{sessionId}:participants");
Parallel.ForEach(participantIds, i => database.HashSet($"{sessionId}:participants:{i}", [ new HashEntry(nameof(Participant.Points), points) ], flags: CommandFlags.FireAndForget));
}
public async Task UpdateParticipantNameAsync(string sessionId, string participantId, string name) =>
await database.HashSetAsync($"{sessionId}:participants:{participantId}", [new HashEntry(nameof(Participant.Name), name)], flags: CommandFlags.FireAndForget);
public async Task UpdateParticipantPointsAsync(string sessionId, string participantId, string points) =>
await database.HashSetAsync($"{sessionId}:participants:{participantId}", [new HashEntry(nameof(Participant.Points), points)], flags: CommandFlags.FireAndForget);
public async Task UpdateSessionStateAsync(string sessionId, State state) =>
await database.HashSetAsync(sessionId, [new HashEntry(nameof(Session.State), Enum.GetName(state))], flags: CommandFlags.FireAndForget);
public async Task UpdateSessionTitleAsync(string sessionId, string title) =>
await database.HashSetAsync(sessionId, [new HashEntry(nameof(Session.Title), title)], flags: CommandFlags.FireAndForget);
}