Skip to content

Commit

Permalink
Brings back support for StringData in ATS provider (#8965)
Browse files Browse the repository at this point in the history
* brings back support for StringData in AzureTS

* changed property from 'UseJson' to 'UseStringFormat' + added test to check if 'UseStringFormat' works correctly.

* added 'InvalidOperationException' to check for [useStringFormat && !useJson]

---------

Co-authored-by: Ledjon Behluli <Ledjon@notiphy.io>
  • Loading branch information
ledjon-behluli and Ledjon Behluli committed May 23, 2024
1 parent fd89211 commit 1d3f7d0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class AzureTableGrainStorage : IGrainStorage, IRestExceptionDecoder, ILif

private const string BINARY_DATA_PROPERTY_NAME = "Data";
private const string STRING_DATA_PROPERTY_NAME = "StringData";

private readonly string name;

/// <summary> Default constructor </summary>
Expand Down Expand Up @@ -202,23 +203,29 @@ private static async Task DoOptimisticUpdate(Func<Task> updateOperation, string
/// </remarks>
internal void ConvertToStorageFormat<T>(T grainState, TableEntity entity)
{
int dataSize;
IEnumerable<ReadOnlyMemory<byte>> properties;
string basePropertyName;
var binaryData = storageSerializer.Serialize<T>(grainState);

// Convert to binary format
var data = this.storageSerializer.Serialize<T>(grainState);
basePropertyName = BINARY_DATA_PROPERTY_NAME;
CheckMaxDataSize(binaryData.ToMemory().Length, MAX_DATA_CHUNK_SIZE * MAX_DATA_CHUNKS_COUNT);

dataSize = data.ToMemory().Length;
properties = SplitBinaryData(data);
if (options.UseStringFormat)
{
var properties = SplitStringData(binaryData.ToString().AsMemory());

CheckMaxDataSize(dataSize, MAX_DATA_CHUNK_SIZE * MAX_DATA_CHUNKS_COUNT);
foreach (var keyValuePair in properties.Zip(GetPropertyNames(STRING_DATA_PROPERTY_NAME),
(property, name) => new KeyValuePair<string, object>(name, property.ToString())))
{
entity[keyValuePair.Key] = keyValuePair.Value;
}
}
else
{
var properties = SplitBinaryData(binaryData);

foreach (var keyValuePair in properties.Zip(GetPropertyNames(basePropertyName),
foreach (var keyValuePair in properties.Zip(GetPropertyNames(BINARY_DATA_PROPERTY_NAME),
(property, name) => new KeyValuePair<string, object>(name, property.ToArray())))
{
entity[keyValuePair.Key] = keyValuePair.Value;
{
entity[keyValuePair.Key] = keyValuePair.Value;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public class AzureTableStorageOptions : AzureStorageOperationOptions, IStoragePr
/// </summary>
public bool DeleteStateOnClear { get; set; } = false;

/// <summary>
/// Indicates if grain data should be stored in string or in binary format.
/// </summary>
public bool UseStringFormat { get; set; }

/// <summary>
/// Stage of silo lifecycle where storage should be initialized. Storage must be initialized prior to use.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,40 @@ public async Task PersistenceProvider_Azure_ChangeWriteFormat(int? stringLength,
await Test_PersistenceProvider_WriteRead(testName, store, grainState, grainId);
}

[SkippableTheory, TestCategory("Functional"), TestCategory("AzureStorage")]
[InlineData(null, true, false)]
[InlineData(null, false, true)]
[InlineData(15 * 32 * 1024 - 256, true, false)]
[InlineData(15 * 32 * 1024 - 256, false, true)]
public async Task PersistenceProvider_Azure_ChangeStorageDataFormat_WhenJsonSerializerIsUsed(int? stringLength, bool useStringFormatForFirstWrite, bool useStringFormatForSecondWrite)
{
// always use JsonSerializer over OrleansSerializer since specifying 'useStringFormat = true'
// writes to 'StringData', the OrleansSerializer can not read from the 'StringData' column as its not a format which it expects.
const bool useJson = true;

var testName = string.Format("{0}({1}={2},{3}={4},{5}={6})",
nameof(PersistenceProvider_Azure_ChangeStorageDataFormat_WhenJsonSerializerIsUsed),
nameof(stringLength), stringLength == null ? "default" : stringLength.ToString(),
"strFormat1stW", useStringFormatForFirstWrite,
"strFormat2ndW", useStringFormatForSecondWrite);

var grainState = TestStoreGrainState.NewRandomState(stringLength);
EnsureEnvironmentSupportsState(grainState);

var grainId = LegacyGrainId.NewId();

var store = await InitAzureTableGrainStorage(useJson: useJson, useStringFormat: useStringFormatForFirstWrite);

await Test_PersistenceProvider_WriteRead(testName, store, grainState, grainId);

grainState = TestStoreGrainState.NewRandomState(stringLength);
grainState.ETag = "*";

store = await InitAzureTableGrainStorage(useJson: useJson, useStringFormat: useStringFormatForSecondWrite);

await Test_PersistenceProvider_WriteRead(testName, store, grainState, grainId);
}

[SkippableTheory, TestCategory("Functional"), TestCategory("AzureStorage")]
[InlineData(null, false)]
[InlineData(null, true)]
Expand Down Expand Up @@ -266,8 +300,13 @@ private async Task<AzureTableGrainStorage> InitAzureTableGrainStorage(AzureTable
return store;
}

private async Task<AzureTableGrainStorage> InitAzureTableGrainStorage(bool useJson = false, TypeNameHandling? typeNameHandling = null)
private async Task<AzureTableGrainStorage> InitAzureTableGrainStorage(bool useJson = false, bool useStringFormat = false, TypeNameHandling? typeNameHandling = null)
{
if (useStringFormat && !useJson)
{
throw new InvalidOperationException($"Using {nameof(OrleansGrainStorageSerializer)} in conjuction with string data format makes no sense, there for stopping attempt.");
}

var options = new AzureTableStorageOptions();
var jsonOptions = this.providerRuntime.ServiceProvider.GetService<IOptions<OrleansJsonSerializerOptions>>();
if (typeNameHandling != null)
Expand All @@ -276,6 +315,7 @@ private async Task<AzureTableGrainStorage> InitAzureTableGrainStorage(bool useJs
}

options.ConfigureTestDefaults();
options.UseStringFormat = useStringFormat;

// TODO change test to include more serializer?
var binarySerializer = new OrleansGrainStorageSerializer(this.providerRuntime.ServiceProvider.GetRequiredService<Serializer>());
Expand Down

0 comments on commit 1d3f7d0

Please sign in to comment.