/// <summary> Clear / Delete state data function for this storage provider. </summary> /// <remarks> /// If the <c>DeleteStateOnClear</c> is set to <c>true</c> then the table row /// for this grain will be deleted / removed, otherwise the table row will be /// cleared by overwriting with default / null values. /// </remarks> /// <see cref="IStorageProvider.ClearStateAsync"/> public async Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (tableDataManager == null) { throw new ArgumentException("GrainState-Table property not initialized"); } string pk = GetKeyString(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); var record = new GrainStateRecord { Entity = entity, ETag = grainState.ETag }; string operation = "Clearing"; try { if (isDeleteStateOnClear) { operation = "Deleting"; await tableDataManager.Delete(record).ConfigureAwait(false); } else { await tableDataManager.Write(record).ConfigureAwait(false); } grainState.ETag = record.ETag; // Update in-memory data to the new ETag } catch (Exception exc) { Log.Error((int)AzureProviderErrorCode.AzureTableProvider_DeleteError, string.Format("Error {0}: GrainType={1} Grainid={2} ETag={3} from Table={4} Exception={5}", operation, grainType, grainReference, grainState.ETag, tableName, exc.Message), exc); throw; } }
internal void ConvertToStorageFormat(object grainState, GrainStateRecord entity) { int dataSize; if (this.options.UseJson) { // http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_JsonConvert.htm entity.StringState = JsonConvert.SerializeObject(grainState, this.jsonSettings); dataSize = STRING_STATE_PROPERTY_NAME.Length + entity.StringState.Length; if (this.logger.IsEnabled(LogLevel.Trace)) { this.logger.Trace("Writing JSON data size = {0} for grain id = Partition={1} / Row={2}", dataSize, entity.GrainReference, entity.GrainType); } } else { // Convert to binary format entity.BinaryState = this.serializationManager.SerializeToByteArray(grainState); dataSize = BINARY_STATE_PROPERTY_NAME.Length + entity.BinaryState.Length; if (this.logger.IsEnabled(LogLevel.Trace)) { this.logger.Trace("Writing binary data size = {0} for grain id = Partition={1} / Row={2}", dataSize, entity.GrainReference, entity.GrainType); } } var pkSize = GRAIN_REFERENCE_PROPERTY_NAME.Length + entity.GrainReference.Length; var rkSize = GRAIN_TYPE_PROPERTY_NAME.Length + entity.GrainType.Length; var versionSize = ETAG_PROPERTY_NAME.Length + entity.ETag.ToString().Length; if ((pkSize + rkSize + versionSize + dataSize) > MAX_DATA_SIZE) { var msg = string.Format("Data too large to write to DynamoDB table. Size={0} MaxSize={1}", dataSize, MAX_DATA_SIZE); throw new ArgumentOutOfRangeException("GrainState.Size", msg); } }
/// <summary> Write state data function for this storage provider. </summary> /// <see cref="IGrainStorage.WriteStateAsync"/> public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (tableDataManager == null) { throw new ArgumentException("GrainState-Table property not initialized"); } string pk = GetKeyString(grainReference); if (logger.IsEnabled(LogLevel.Trace)) { logger.Trace((int)AzureProviderErrorCode.AzureTableProvider_WritingData, "Writing: GrainType={0} Pk={1} Grainid={2} ETag={3} to Table={4}", grainType, pk, grainReference, grainState.ETag, this.options.TableName); } var rowKey = AzureTableUtils.SanitizeTableProperty(grainType); var entity = new DynamicTableEntity(pk, rowKey); ConvertToStorageFormat(grainState.State, entity); var record = new GrainStateRecord { Entity = entity, ETag = grainState.ETag }; try { await DoOptimisticUpdate(() => tableDataManager.Write(record), grainType, grainReference.GrainId, this.options.TableName, grainState.ETag).ConfigureAwait(false); grainState.ETag = record.ETag; grainState.RecordExists = true; } catch (Exception exc) { logger.Error((int)AzureProviderErrorCode.AzureTableProvider_WriteError, $"Error Writing: GrainType={grainType} GrainId={grainReference.GrainId} ETag={grainState.ETag} to Table={this.options.TableName} Exception={exc.Message}", exc); throw; } }
/// <summary> Write state data function for this storage provider. </summary> /// <see cref="IStorageProvider.WriteStateAsync"/> public async Task WriteStateAsync(string grainType, GrainReference grainReference, GrainState grainState) { if (tableDataManager == null) { throw new ArgumentException("GrainState-Table property not initialized"); } string pk = GetKeyString(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 GrainStateEntity { PartitionKey = pk, RowKey = grainType }; ConvertToStorageFormat(grainState, entity); var record = new GrainStateRecord { Entity = entity, ETag = grainState.Etag }; try { await tableDataManager.Write(record); grainState.Etag = record.ETag; } catch (Exception exc) { Log.Error((int)AzureProviderErrorCode.AzureTableProvider_WriteError, string.Format("Error Writing: GrainType={0} Grainid={1} ETag={2} to Table={3} Exception={4}", grainType, grainReference, grainState.Etag, tableName, exc.Message), exc); throw; } }
/// <summary> Write state data function for this storage provider. </summary> /// <see cref="IGrainStorage.WriteStateAsync{T}"/> public async Task WriteStateAsync <T>(string grainType, GrainReference grainReference, IGrainState <T> grainState) { if (this.storage == null) { throw new ArgumentException("GrainState-Table property not initialized"); } string partitionKey = GetKeyString(grainReference); string rowKey = AWSUtils.ValidateDynamoDBRowKey(grainType); var record = new GrainStateRecord { GrainReference = partitionKey, GrainType = rowKey }; try { ConvertToStorageFormat(grainState.State, record); await WriteStateInternal(grainState, record); } catch (ConditionalCheckFailedException exc) { throw new InconsistentStateException($"Inconsistent grain state: {exc}"); } catch (Exception exc) { this.logger.LogError( (int)ErrorCode.StorageProviderBase, exc, "Error Writing: GrainType={GrainType} Grainid={GrainId} ETag={ETag} to Table={TableName}", grainType, grainReference, grainState.ETag, this.options.TableName); throw; } }
public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { var blobName = this.GetBlobNameString(grainType, grainReference); var newETag = string.IsNullOrEmpty(grainState.ETag) ? 0 : int.Parse(grainState.ETag) + 1; try { this._logger.LogTrace("Writing: GrainType={0} Grainid={1} ETag={2} to BlobName={3} in Container={4}", grainType, grainReference, grainState.ETag, blobName, this._container); var record = new GrainStateRecord { ETag = newETag, State = grainState.State }; using (var stream = new MemoryStream(this.ConvertToStorageFormat(record))) { await this._storage.UploadBlob(this._container, blobName, stream, contentType : "application/json"); } grainState.ETag = newETag.ToString(); this._logger.LogTrace("Wrote: GrainType={0} Grainid={1} ETag={2} to BlobName={3} in Container={4}", grainType, grainReference, grainState.ETag, blobName, this._container); } catch (Exception ex) { this._logger.LogError(ex, "Error writing: GrainType={0} Grainid={1} ETag={2} from BlobName={3} in Container={4} Exception={5}", grainType, grainReference, grainState.ETag, blobName, this._container, ex.Message); throw; } }
internal void ConvertToStorageFormat(object grainState, GrainStateRecord entity) { int dataSize; if (useJsonFormat) { // http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_JsonConvert.htm entity.StringState = JsonConvert.SerializeObject(grainState, jsonSettings); dataSize = STRING_STATE_PROPERTY_NAME.Length + entity.StringState.Length; if (Log.IsVerbose3) Log.Verbose3("Writing JSON data size = {0} for grain id = Partition={1} / Row={2}", dataSize, entity.GrainReference, entity.GrainType); } else { // Convert to binary format entity.BinaryState = SerializationManager.SerializeToByteArray(grainState); dataSize = BINARY_STATE_PROPERTY_NAME.Length + entity.BinaryState.Length; if (Log.IsVerbose3) Log.Verbose3("Writing binary data size = {0} for grain id = Partition={1} / Row={2}", dataSize, entity.GrainReference, entity.GrainType); } var pkSize = GRAIN_REFERENCE_PROPERTY_NAME.Length + entity.GrainReference.Length; var rkSize = GRAIN_TYPE_PROPERTY_NAME.Length + entity.GrainType.Length; var versionSize = ETAG_PROPERTY_NAME.Length + entity.ETag.ToString().Length; if ((pkSize + rkSize + versionSize + dataSize) > MAX_DATA_SIZE) { var msg = string.Format("Data too large to write to DynamoDB table. Size={0} MaxSize={1}", dataSize, MAX_DATA_SIZE); throw new ArgumentOutOfRangeException("GrainState.Size", msg); } }
internal object ConvertFromStorageFormat(GrainStateRecord entity) { var binaryData = entity.BinaryState; var stringData = entity.StringState; object dataValue = null; try { if (binaryData?.Length > 0) { // Rehydrate dataValue = SerializationManager.DeserializeFromByteArray<object>(binaryData); } else if (!string.IsNullOrEmpty(stringData)) { dataValue = JsonConvert.DeserializeObject<object>(stringData, jsonSettings); } // Else, no data found } catch (Exception exc) { var sb = new StringBuilder(); if (binaryData.Length > 0) { sb.AppendFormat("Unable to convert from storage format GrainStateEntity.Data={0}", binaryData); } else if (!string.IsNullOrEmpty(stringData)) { sb.AppendFormat("Unable to convert from storage format GrainStateEntity.StringData={0}", stringData); } if (dataValue != null) { sb.AppendFormat("Data Value={0} Type={1}", dataValue, dataValue.GetType()); } Log.Error(0, sb.ToString(), exc); throw new AggregateException(sb.ToString(), exc); } return dataValue; }
/// <summary> Clear / Delete state data function for this storage provider. </summary> /// <remarks> /// If the <c>DeleteStateOnClear</c> is set to <c>true</c> then the table row /// for this grain will be deleted / removed, otherwise the table row will be /// cleared by overwriting with default / null values. /// </remarks> /// <see cref="IStorageProvider.ClearStateAsync"/> public async Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (storage == null) throw new ArgumentException("GrainState-Table property not initialized"); string partitionKey = GetKeyString(grainReference); if (Log.IsVerbose3) Log.Verbose3(ErrorCode.StorageProviderBase, "Clearing: GrainType={0} Pk={1} Grainid={2} ETag={3} DeleteStateOnClear={4} from Table={5}", grainType, partitionKey, grainReference, grainState.ETag, isDeleteStateOnClear, tableName); string rowKey = AWSUtils.ValidateDynamoDBRowKey(grainType); var record = new GrainStateRecord { GrainReference = partitionKey, ETag = string.IsNullOrWhiteSpace(grainState.ETag) ? 0 : int.Parse(grainState.ETag), GrainType = rowKey }; var operation = "Clearing"; try { if (isDeleteStateOnClear) { operation = "Deleting"; var keys = new Dictionary<string, AttributeValue>(); keys.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); keys.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); await storage.DeleteEntryAsync(tableName, keys).ConfigureAwait(false); grainState.ETag = string.Empty; } else { await WriteStateInternal(grainState, record, true); } } catch (Exception exc) { Log.Error(ErrorCode.StorageProviderBase, string.Format("Error {0}: GrainType={1} Grainid={2} ETag={3} from Table={4} Exception={5}", operation, grainType, grainReference, grainState.ETag, tableName, exc.Message), exc); throw; } }
private async Task WriteStateInternal(IGrainState grainState, GrainStateRecord record, bool clear = false) { var fields = new Dictionary<string, AttributeValue>(); if (record.BinaryState != null && record.BinaryState.Length > 0) { fields.Add(BINARY_STATE_PROPERTY_NAME, new AttributeValue { B = new MemoryStream(record.BinaryState) }); } else if (!string.IsNullOrWhiteSpace(record.StringState)) { fields.Add(STRING_STATE_PROPERTY_NAME, new AttributeValue(record.StringState)); } int newEtag = 0; if (clear) { fields.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); fields.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); int currentEtag = 0; int.TryParse(grainState.ETag, out currentEtag); newEtag = currentEtag; fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = newEtag++.ToString() }); await storage.PutEntryAsync(tableName, fields).ConfigureAwait(false); } else if (string.IsNullOrWhiteSpace(grainState.ETag)) { fields.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); fields.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = "0" }); var expression = $"attribute_not_exists({GRAIN_REFERENCE_PROPERTY_NAME}) AND attribute_not_exists({GRAIN_TYPE_PROPERTY_NAME})"; await storage.PutEntryAsync(tableName, fields, expression).ConfigureAwait(false); } else { var keys = new Dictionary<string, AttributeValue>(); keys.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); keys.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); int currentEtag = 0; int.TryParse(grainState.ETag, out currentEtag); newEtag = currentEtag; newEtag++; fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = newEtag.ToString() }); var conditionalValues = new Dictionary<string, AttributeValue> { { CURRENT_ETAG_ALIAS, new AttributeValue { N = currentEtag.ToString() } } }; var expression = $"{ETAG_PROPERTY_NAME} = {CURRENT_ETAG_ALIAS}"; await storage.UpsertEntryAsync(tableName, keys, fields, expression, conditionalValues).ConfigureAwait(false); } grainState.ETag = newEtag.ToString(); }
/// <summary> Write state data function for this storage provider. </summary> /// <see cref="IStorageProvider.WriteStateAsync"/> public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (storage == null) throw new ArgumentException("GrainState-Table property not initialized"); string partitionKey = GetKeyString(grainReference); string rowKey = AWSUtils.ValidateDynamoDBRowKey(grainType); var record = new GrainStateRecord { GrainReference = partitionKey, GrainType = rowKey }; try { ConvertToStorageFormat(grainState.State, record); await WriteStateInternal(grainState, record); } catch (ConditionalCheckFailedException exc) { throw new InconsistentStateException("Invalid grain state", exc); } catch (Exception exc) { Log.Error(ErrorCode.StorageProviderBase, string.Format("Error Writing: GrainType={0} Grainid={1} ETag={2} to Table={3} Exception={4}", grainType, grainReference, grainState.ETag, tableName, exc.Message), exc); throw; } }
/// <summary> Write state data function for this storage provider. </summary> /// <see cref="IStorageProvider.WriteStateAsync"/> public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (tableDataManager == null) throw new ArgumentException("GrainState-Table property not initialized"); string pk = GetKeyString(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 GrainStateEntity { PartitionKey = pk, RowKey = grainType }; ConvertToStorageFormat(grainState, entity); var record = new GrainStateRecord { Entity = entity, ETag = grainState.Etag }; try { await tableDataManager.Write(record); grainState.Etag = record.ETag; } catch (Exception exc) { Log.Error((int)AzureProviderErrorCode.AzureTableProvider_WriteError, string.Format("Error Writing: GrainType={0} Grainid={1} ETag={2} to Table={3} Exception={4}", grainType, grainReference, grainState.Etag, tableName, exc.Message), exc); throw; } }
private async Task WriteStateInternal(IGrainState grainState, GrainStateRecord record, bool clear = false) { var fields = new Dictionary <string, AttributeValue>(); if (this.options.TimeToLive.HasValue) { fields.Add(GRAIN_TTL_PROPERTY_NAME, new AttributeValue { N = ((DateTimeOffset)DateTime.UtcNow.Add(this.options.TimeToLive.Value)).ToUnixTimeSeconds().ToString() }); } if (record.BinaryState != null && record.BinaryState.Length > 0) { fields.Add(BINARY_STATE_PROPERTY_NAME, new AttributeValue { B = new MemoryStream(record.BinaryState) }); } else if (!string.IsNullOrWhiteSpace(record.StringState)) { fields.Add(STRING_STATE_PROPERTY_NAME, new AttributeValue(record.StringState)); } int newEtag = 0; if (clear) { fields.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); fields.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); int currentEtag = 0; int.TryParse(grainState.ETag, out currentEtag); newEtag = currentEtag; fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = newEtag++.ToString() }); await this.storage.PutEntryAsync(this.options.TableName, fields).ConfigureAwait(false); } else if (string.IsNullOrWhiteSpace(grainState.ETag)) { fields.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); fields.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = "0" }); var expression = $"attribute_not_exists({GRAIN_REFERENCE_PROPERTY_NAME}) AND attribute_not_exists({GRAIN_TYPE_PROPERTY_NAME})"; await this.storage.PutEntryAsync(this.options.TableName, fields, expression).ConfigureAwait(false); } else { var keys = new Dictionary <string, AttributeValue>(); keys.Add(GRAIN_REFERENCE_PROPERTY_NAME, new AttributeValue(record.GrainReference)); keys.Add(GRAIN_TYPE_PROPERTY_NAME, new AttributeValue(record.GrainType)); int currentEtag = 0; int.TryParse(grainState.ETag, out currentEtag); newEtag = currentEtag; newEtag++; fields.Add(ETAG_PROPERTY_NAME, new AttributeValue { N = newEtag.ToString() }); var conditionalValues = new Dictionary <string, AttributeValue> { { CURRENT_ETAG_ALIAS, new AttributeValue { N = currentEtag.ToString() } } }; var expression = $"{ETAG_PROPERTY_NAME} = {CURRENT_ETAG_ALIAS}"; await this.storage.UpsertEntryAsync(this.options.TableName, keys, fields, expression, conditionalValues).ConfigureAwait(false); } grainState.ETag = newEtag.ToString(); }
/// <summary> Clear / Delete state data function for this storage provider. </summary> /// <remarks> /// If the <c>DeleteStateOnClear</c> is set to <c>true</c> then the table row /// for this grain will be deleted / removed, otherwise the table row will be /// cleared by overwriting with default / null values. /// </remarks> /// <see cref="IStorageProvider.ClearStateAsync"/> public async Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { if (tableDataManager == null) throw new ArgumentException("GrainState-Table property not initialized"); string pk = GetKeyString(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 GrainStateEntity { PartitionKey = pk, RowKey = grainType }; var record = new GrainStateRecord { Entity = entity, ETag = grainState.Etag }; string operation = "Clearing"; try { if (isDeleteStateOnClear) { operation = "Deleting"; await tableDataManager.Delete(record); } else { await tableDataManager.Write(record); } grainState.Etag = record.ETag; // Update in-memory data to the new ETag } catch (Exception exc) { Log.Error((int)AzureProviderErrorCode.AzureTableProvider_DeleteError, string.Format("Error {0}: GrainType={1} Grainid={2} ETag={3} from Table={4} Exception={5}", operation, grainType, grainReference, grainState.Etag, tableName, exc.Message), exc); throw; } }
public async Task Delete(GrainStateRecord record) { GrainStateEntity entity = record.Entity; if (logger.IsVerbose3) logger.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_Storage_Writing, "Deleting: PartitionKey={0} RowKey={1} from Table={2} with ETag={3}", entity.PartitionKey, entity.RowKey, TableName, record.ETag); await tableManager.DeleteTableEntryAsync(entity, record.ETag); record.ETag = null; }
public async Task Write(GrainStateRecord record) { GrainStateEntity entity = record.Entity; if (logger.IsVerbose3) logger.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_Storage_Writing, "Writing: PartitionKey={0} RowKey={1} to Table={2} with ETag={3}", entity.PartitionKey, entity.RowKey, TableName, record.ETag); string eTag = String.IsNullOrEmpty(record.ETag) ? await tableManager.CreateTableEntryAsync(record.Entity) : await tableManager.UpdateTableEntryAsync(entity, record.ETag); record.ETag = eTag; }
public async Task<GrainStateRecord> Read(string partitionKey, string rowKey) { if (logger.IsVerbose3) logger.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_Storage_Reading, "Reading: PartitionKey={0} RowKey={1} from Table={2}", partitionKey, rowKey, TableName); try { Tuple<GrainStateEntity, string> data = await tableManager.ReadSingleTableEntryAsync(partitionKey, rowKey); if (data == null || data.Item1 == null) { if (logger.IsVerbose2) logger.Verbose2((int)AzureProviderErrorCode.AzureTableProvider_DataNotFound, "DataNotFound reading: PartitionKey={0} RowKey={1} from Table={2}", partitionKey, rowKey, TableName); return null; } GrainStateEntity stateEntity = data.Item1; var record = new GrainStateRecord { Entity = stateEntity, ETag = data.Item2 }; if (logger.IsVerbose3) logger.Verbose3((int)AzureProviderErrorCode.AzureTableProvider_Storage_DataRead, "Read: PartitionKey={0} RowKey={1} from Table={2} with ETag={3}", stateEntity.PartitionKey, stateEntity.RowKey, TableName, record.ETag); return record; } catch (Exception exc) { if (AzureStorageUtils.TableStorageDataNotFound(exc)) { if (logger.IsVerbose2) logger.Verbose2((int)AzureProviderErrorCode.AzureTableProvider_DataNotFound, "DataNotFound reading (exception): PartitionKey={0} RowKey={1} from Table={2} Exception={3}", partitionKey, rowKey, TableName, TraceLogger.PrintException(exc)); return null; // No data } throw; } }