Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brings back support for StringData in ATS provider #8965

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to add a test to check the behavior of the new option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

{
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
Loading