Skip to content

Commit 34d9f1c

Browse files
authored
Align behavior of grain storage providers when setting IGrainState<T> properties (#9417)
1 parent a4b98f0 commit 34d9f1c

File tree

49 files changed

+1142
-1891
lines changed

Some content is hidden

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

49 files changed

+1142
-1891
lines changed

src/AWS/Orleans.Persistence.DynamoDB/Provider/DynamoDBGrainStorage.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ public class DynamoDBGrainStorage : IGrainStorage, ILifecycleParticipant<ISiloLi
3232
private const string CURRENT_ETAG_ALIAS = ":currentETag";
3333

3434
private readonly DynamoDBStorageOptions options;
35+
private readonly IActivatorProvider _activatorProvider;
3536
private readonly ILogger logger;
36-
private readonly IServiceProvider serviceProvider;
37-
private readonly IActivatorProvider activatorProvider;
3837
private readonly string name;
3938

4039
private DynamoDBStorage storage;
@@ -45,14 +44,13 @@ public class DynamoDBGrainStorage : IGrainStorage, ILifecycleParticipant<ISiloLi
4544
public DynamoDBGrainStorage(
4645
string name,
4746
DynamoDBStorageOptions options,
48-
IServiceProvider serviceProvider,
47+
IActivatorProvider activatorProvider,
4948
ILogger<DynamoDBGrainStorage> logger)
5049
{
5150
this.name = name;
5251
this.logger = logger;
5352
this.options = options;
54-
this.serviceProvider = serviceProvider;
55-
this.activatorProvider = this.serviceProvider.GetRequiredService<IActivatorProvider>();
53+
_activatorProvider = activatorProvider;
5654
}
5755

5856
public void Participate(ISiloLifecycle lifecycle)
@@ -152,17 +150,13 @@ public async Task ReadStateAsync<T>(string grainType, GrainId grainId, IGrainSta
152150
{
153151
var loadedState = ConvertFromStorageFormat<T>(record);
154152
grainState.RecordExists = loadedState != null;
155-
grainState.State = loadedState ?? Activator.CreateInstance<T>();
153+
grainState.State = loadedState ?? CreateInstance<T>();
156154
grainState.ETag = record.ETag.ToString();
157155
}
158156
else
159157
{
160-
grainState.RecordExists = false;
161-
grainState.ETag = null;
162-
grainState.State = this.activatorProvider.GetActivator<T>().Create();
158+
ResetGrainState(grainState);
163159
}
164-
165-
// Else leave grainState in previous default condition
166160
}
167161

168162
/// <summary> Write state data function for this storage provider. </summary>
@@ -190,7 +184,7 @@ public async Task WriteStateAsync<T>(string grainType, GrainId grainId, IGrainSt
190184
this.logger.LogError(
191185
(int)ErrorCode.StorageProviderBase,
192186
exc,
193-
"Error Writing: GrainType={GrainType} Grainid={GrainId} ETag={ETag} to Table={TableName}",
187+
"Error Writing: GrainType={GrainType} GrainId={GrainId} ETag={ETag} to Table={TableName}",
194188
grainType,
195189
grainId,
196190
grainState.ETag,
@@ -298,12 +292,13 @@ public async Task ClearStateAsync<T>(string grainType, GrainId grainId, IGrainSt
298292
keys.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType));
299293

300294
await this.storage.DeleteEntryAsync(this.options.TableName, keys).ConfigureAwait(false);
301-
grainState.ETag = null;
295+
ResetGrainState(grainState);
302296
}
303297
else
304298
{
305299
await WriteStateInternal(grainState, record, true);
306-
grainState.State = this.activatorProvider.GetActivator<T>().Create();
300+
grainState.State = CreateInstance<T>();
301+
grainState.RecordExists = false;
307302
}
308303
}
309304
catch (Exception exc)
@@ -381,6 +376,15 @@ internal void ConvertToStorageFormat(object grainState, GrainStateRecord entity)
381376
throw new ArgumentOutOfRangeException("GrainState.Size", msg);
382377
}
383378
}
379+
380+
private void ResetGrainState<T>(IGrainState<T> grainState)
381+
{
382+
grainState.RecordExists = false;
383+
grainState.ETag = null;
384+
grainState.State = CreateInstance<T>();
385+
}
386+
387+
private T CreateInstance<T>() => _activatorProvider.GetActivator<T>().Create();
384388
}
385389

386390
public static class DynamoDBGrainStorageFactory

src/AdoNet/Orleans.Persistence.AdoNet/Storage/Provider/AdoNetGrainStorage.cs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Orleans.Configuration.Overrides;
1717
using Orleans.Configuration;
1818
using Orleans.Runtime.Configuration;
19+
using Orleans.Serialization.Serializers;
1920

2021
namespace Orleans.Storage
2122
{
@@ -69,7 +70,7 @@ public static AdoNetGrainStorage Create(IServiceProvider services, string name)
6970
/// </para>
7071
/// </remarks>
7172
[DebuggerDisplay("Name = {Name}, ConnectionString = {Storage.ConnectionString}")]
72-
public class AdoNetGrainStorage: IGrainStorage, ILifecycleParticipant<ISiloLifecycle>
73+
public class AdoNetGrainStorage : IGrainStorage, ILifecycleParticipant<ISiloLifecycle>
7374
{
7475
public IGrainStorageSerializer Serializer { get; set; }
7576

@@ -90,7 +91,7 @@ public class AdoNetGrainStorage: IGrainStorage, ILifecycleParticipant<ISiloLifec
9091
/// The Service ID for which this relational provider is used.
9192
/// </summary>
9293
private readonly string serviceId;
93-
94+
private readonly IActivatorProvider _activatorProvider;
9495
private readonly ILogger logger;
9596

9697
/// <summary>
@@ -121,29 +122,29 @@ public class AdoNetGrainStorage: IGrainStorage, ILifecycleParticipant<ISiloLifec
121122
public IStorageHasherPicker HashPicker { get; set; }
122123

123124
private readonly AdoNetGrainStorageOptions options;
124-
private readonly IProviderRuntime providerRuntime;
125125
private readonly string name;
126126

127127
public AdoNetGrainStorage(
128+
IActivatorProvider activatorProvider,
128129
ILogger<AdoNetGrainStorage> logger,
129-
IProviderRuntime providerRuntime,
130130
IOptions<AdoNetGrainStorageOptions> options,
131131
IOptions<ClusterOptions> clusterOptions,
132132
string name)
133133
{
134134
this.options = options.Value;
135-
this.providerRuntime = providerRuntime;
136135
this.name = name;
136+
_activatorProvider = activatorProvider;
137137
this.logger = logger;
138138
this.serviceId = clusterOptions.Value.ServiceId;
139139
this.Serializer = options.Value.GrainStorageSerializer;
140-
this.HashPicker = options.Value.HashPicker ?? new StorageHasherPicker(new[] { new OrleansDefaultHasher() });;
140+
this.HashPicker = options.Value.HashPicker ?? new StorageHasherPicker(new[] { new OrleansDefaultHasher() });
141141
}
142142

143143
public void Participate(ISiloLifecycle lifecycle)
144144
{
145145
lifecycle.Subscribe(OptionFormattingUtilities.Name<AdoNetGrainStorage>(this.name), this.options.InitStage, Init, Close);
146146
}
147+
147148
/// <summary>Clear state data function for this storage provider.</summary>
148149
/// <see cref="IGrainStorage.ClearStateAsync{T}"/>.
149150
public async Task ClearStateAsync<T>(string grainType, GrainId grainReference, IGrainState<T> grainState)
@@ -152,7 +153,7 @@ public async Task ClearStateAsync<T>(string grainType, GrainId grainReference, I
152153
//even if not as clear as when using explicitly checked parameters.
153154
var grainId = GrainIdAndExtensionAsString(grainReference);
154155
var baseGrainType = ExtractBaseClass(grainType);
155-
if(logger.IsEnabled(LogLevel.Trace))
156+
if (logger.IsEnabled(LogLevel.Trace))
156157
{
157158
logger.LogTrace(
158159
(int)RelationalStorageProviderCodes.RelationalProviderClearing,
@@ -164,6 +165,15 @@ public async Task ClearStateAsync<T>(string grainType, GrainId grainReference, I
164165
grainState.ETag);
165166
}
166167

168+
if (!grainState.RecordExists)
169+
{
170+
await ReadStateAsync(grainType, grainReference, grainState).ConfigureAwait(false);
171+
if (!grainState.RecordExists)
172+
{
173+
return;
174+
}
175+
}
176+
167177
string storageVersion = null;
168178
try
169179
{
@@ -182,7 +192,7 @@ public async Task ClearStateAsync<T>(string grainType, GrainId grainReference, I
182192
}, (selector, resultSetCount, token) => Task.FromResult(selector.GetValue(0).ToString()), cancellationToken: CancellationToken.None).ConfigureAwait(false));
183193
storageVersion = clearRecord.SingleOrDefault();
184194
}
185-
catch(Exception ex)
195+
catch (Exception ex)
186196
{
187197
logger.LogError(
188198
(int)RelationalStorageProviderCodes.RelationalProviderDeleteError,
@@ -198,15 +208,16 @@ public async Task ClearStateAsync<T>(string grainType, GrainId grainReference, I
198208

199209
const string OperationString = "ClearState";
200210
var inconsistentStateException = CheckVersionInconsistency(OperationString, serviceId, this.name, storageVersion, grainState.ETag, baseGrainType, grainId.ToString());
201-
if(inconsistentStateException != null)
211+
if (inconsistentStateException != null)
202212
{
203213
throw inconsistentStateException;
204214
}
205215

206216
//No errors found, the version of the state held by the grain can be updated and also the state.
207217
grainState.ETag = storageVersion;
208218
grainState.RecordExists = false;
209-
if(logger.IsEnabled(LogLevel.Trace))
219+
grainState.State = CreateInstance<T>();
220+
if (logger.IsEnabled(LogLevel.Trace))
210221
{
211222
logger.LogTrace(
212223
(int)RelationalStorageProviderCodes.RelationalProviderCleared,
@@ -273,20 +284,24 @@ public async Task ReadStateAsync<T>(string grainType, GrainId grainReference, IG
273284
},
274285
commandBehavior, CancellationToken.None).ConfigureAwait(false)).SingleOrDefault();
275286

276-
T state = readRecords != null ? (T) readRecords.Item1 : default;
287+
T state = readRecords != null ? (T)readRecords.Item1 : default;
277288
string etag = readRecords != null ? readRecords.Item2 : null;
278289
bool recordExists = readRecords != null;
279-
if(state == null)
290+
if (state == null)
280291
{
281-
logger.LogInformation(
282-
(int)RelationalStorageProviderCodes.RelationalProviderNoStateFound,
283-
"Null grain state read (default will be instantiated): ServiceId={ServiceId} ProviderName={Name} GrainType={BaseGrainType} GrainId={GrainId} ETag={ETag}.",
284-
serviceId,
285-
name,
286-
baseGrainType,
287-
grainId,
288-
grainState.ETag);
289-
state = Activator.CreateInstance<T>();
292+
if (logger.IsEnabled(LogLevel.Trace))
293+
{
294+
logger.LogTrace(
295+
(int)RelationalStorageProviderCodes.RelationalProviderNoStateFound,
296+
"Null grain state read (default will be instantiated): ServiceId={ServiceId} ProviderName={Name} GrainType={BaseGrainType} GrainId={GrainId} ETag={ETag}.",
297+
serviceId,
298+
name,
299+
baseGrainType,
300+
grainId,
301+
grainState.ETag);
302+
}
303+
304+
state = CreateInstance<T>();
290305
}
291306

292307
grainState.State = state;
@@ -304,7 +319,7 @@ public async Task ReadStateAsync<T>(string grainType, GrainId grainReference, IG
304319
grainState.ETag);
305320
}
306321
}
307-
catch(Exception ex)
322+
catch (Exception ex)
308323
{
309324
logger.LogError(
310325
(int)RelationalStorageProviderCodes.RelationalProviderReadError,
@@ -363,7 +378,7 @@ public async Task WriteStateAsync<T>(string grainType, GrainId grainReference, I
363378
{ return Task.FromResult(selector.GetNullableInt32("NewGrainStateVersion").ToString()); }, cancellationToken: CancellationToken.None).ConfigureAwait(false);
364379
storageVersion = writeRecord.SingleOrDefault();
365380
}
366-
catch(Exception ex)
381+
catch (Exception ex)
367382
{
368383
logger.LogError(
369384
(int)RelationalStorageProviderCodes.RelationalProviderWriteError,
@@ -379,7 +394,7 @@ public async Task WriteStateAsync<T>(string grainType, GrainId grainReference, I
379394

380395
const string OperationString = "WriteState";
381396
var inconsistentStateException = CheckVersionInconsistency(OperationString, serviceId, this.name, storageVersion, grainState.ETag, baseGrainType, grainId.ToString());
382-
if(inconsistentStateException != null)
397+
if (inconsistentStateException != null)
383398
{
384399
throw inconsistentStateException;
385400
}
@@ -424,7 +439,6 @@ private async Task Init(CancellationToken cancellationToken)
424439
ConfigUtilities.RedactConnectionStringInfo(Storage.ConnectionString));
425440
}
426441

427-
428442
/// <summary>
429443
/// Close this provider
430444
/// </summary>
@@ -433,7 +447,6 @@ private Task Close(CancellationToken token)
433447
return Task.CompletedTask;
434448
}
435449

436-
437450
/// <summary>
438451
/// Checks for version inconsistency as defined in the database scripts.
439452
/// </summary>
@@ -455,7 +468,7 @@ private static InconsistentStateException CheckVersionInconsistency(string opera
455468
//it means two grains were activated an the other one succeeded in writing its state.
456469
//
457470
//NOTE: the storage could return also the new and old ETag (Version), but currently it doesn't.
458-
if(storageVersion == grainVersion || storageVersion == string.Empty)
471+
if (storageVersion == grainVersion || storageVersion == string.Empty)
459472
{
460473
//TODO: Note that this error message should be canonical across back-ends.
461474
return new InconsistentStateException($"Version conflict ({operation}): ServiceId={serviceId} ProviderName={providerName} GrainType={normalizedGrainType} GrainId={grainId} ETag={grainVersion}.");
@@ -545,5 +558,7 @@ int GetGenericArity(string input)
545558
return 0;
546559
}
547560
}
561+
562+
private T CreateInstance<T>() => _activatorProvider.GetActivator<T>().Create();
548563
}
549564
}

0 commit comments

Comments
 (0)