Skip to content
Closed
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
2 changes: 1 addition & 1 deletion src/Orleans.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{013DFD29-E1DB-4968-A67B-C2342E6F5B6E}"
ProjectSection(SolutionItems) = preProject
Expand Down
38 changes: 29 additions & 9 deletions src/OrleansAzureUtils/Providers/Storage/AzureTableStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@ public async Task ReadStateAsync(string grainType, GrainReference grainReference
{
if (tableDataManager == null) throw new ArgumentException("GrainState-Table property not initialized");

string pk = GetKeyString(grainReference);
string pk = GetPartitionKey(grainReference);
if (Log.IsVerbose3) Log.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_ReadingData, "Reading: GrainType={0} Pk={1} Grainid={2} from Table={3}", grainType, pk, grainReference, tableName);
string partitionKey = pk;
string rowKey = grainType;
GrainStateRecord record = await tableDataManager.Read(partitionKey, rowKey).ConfigureAwait(false);

string rk = GetRowKey(grainType);
GrainStateRecord record = await tableDataManager.Read(pk, rk).ConfigureAwait(false);
if (record != null)
{
var entity = record.Entity;
Expand All @@ -164,11 +164,13 @@ public async Task WriteStateAsync(string grainType, GrainReference grainReferenc
{
if (tableDataManager == null) throw new ArgumentException("GrainState-Table property not initialized");

string pk = GetKeyString(grainReference);
string pk = GetPartitionKey(grainReference);
if (Log.IsVerbose3)
Log.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_WritingData, "Writing: GrainType={0} Pk={1} Grainid={2} ETag={3} to Table={4}", grainType, pk, grainReference, grainState.ETag, tableName);

var entity = new DynamicTableEntity(pk, grainType);
string rk = GetRowKey(grainType);
var entity = new DynamicTableEntity(pk, rk);

ConvertToStorageFormat(grainState.State, entity);
var record = new GrainStateRecord { Entity = entity, ETag = grainState.ETag };
try
Expand All @@ -195,9 +197,12 @@ public async Task ClearStateAsync(string grainType, GrainReference grainReferenc
{
if (tableDataManager == null) throw new ArgumentException("GrainState-Table property not initialized");

string pk = GetKeyString(grainReference);
string pk = GetPartitionKey(grainReference);
if (Log.IsVerbose3) Log.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_WritingData, "Clearing: GrainType={0} Pk={1} Grainid={2} ETag={3} DeleteStateOnClear={4} from Table={5}", grainType, pk, grainReference, grainState.ETag, isDeleteStateOnClear, tableName);
var entity = new DynamicTableEntity(pk, grainType);

string rk = GetRowKey(grainType);
var entity = new DynamicTableEntity(pk, rk);

var record = new GrainStateRecord { Entity = entity, ETag = grainState.ETag };
string operation = "Clearing";
try
Expand Down Expand Up @@ -436,12 +441,27 @@ internal object ConvertFromStorageFormat(DynamicTableEntity entity)
return dataValue;
}

private string GetKeyString(GrainReference grainReference)
private string GetPartitionKey(GrainReference grainReference)
{
var key = String.Format("{0}_{1}", serviceId, grainReference.ToKeyString());
return AzureStorageUtils.SanitizeTableProperty(key);
}


private string GetRowKey(string grainType)
{
string rowKey;
int index = grainType.IndexOf("`");
if (index > 0)
// in case we have a generic grain, we use an empty rowkey as it seems Azure has a maximum length for the combined keys?
return String.Empty;
else
// for non-generic grains, we keep the existing behavior to not break existing storage, but we do sanitize
rowKey = AzureStorageUtils.SanitizeTableProperty(grainType);

return rowKey;
}

internal class GrainStateRecord
{
public string ETag { get; set; }
Expand Down
23 changes: 16 additions & 7 deletions src/OrleansAzureUtils/Storage/AzureStorageUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.WindowsAzure.Storage.RetryPolicies;
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
using Orleans.Runtime;
using System.Text;

namespace Orleans.AzureUtils
{
Expand Down Expand Up @@ -274,16 +275,24 @@ public static string SanitizeTableProperty(string key)
{
// Remove any characters that can't be used in Azure PartitionKey or RowKey values
// http://www.jamestharpe.com/web-development/azure-table-service-character-combinations-disallowed-in-partitionkey-rowkey/
key = key
.Replace('/', '_') // Forward slash
.Replace('\\', '_') // Backslash
.Replace('#', '_') // Pound sign
.Replace('?', '_'); // Question mark
var sb = new StringBuilder();
foreach (var c in key)
{
if (c == '/'
|| c == '\\'
|| c == '#'
|| c == '/'
|| c == '?'
|| char.IsControl(c))
sb.Append('_');
else
sb.Append(c);
}

if (key.Length >= 1024)
if (sb.Length >= 1024)
throw new ArgumentException(string.Format("Key length {0} is too long to be an Azure table key. Key={1}", key.Length, key));

return key;
return sb.ToString();
}


Expand Down
2 changes: 1 addition & 1 deletion test/Tester/GenericGrainsInAzureStorageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task Generic_OnAzureTableStorage_LongNamedGrain_EchoValue()
await grain.EchoAsync(42);

//ClearState() also exhibits the error, even with the shorter named grain
//await grain.ClearState();
await grain.ClearState();
}

[Fact, TestCategory("Azure"), TestCategory("Functional"), TestCategory("Generics")]
Expand Down