I have an program that updates entries in an Azure Table Storage table like this:
- recovers the entry from the table filtering by partitionKey and item Id
- makes some changes in some properties of the recovered object.
- saves changes back to BD.
Since I have read that Azure Storage tables use optimistic concurrency and the Etag property (for example according to this, or this stackoverflow question on what is stored in Etag) to manage it I came up with this code:
var query = table.CreateQuery<DynamicTableEntity>()
.Where(d => d.PartitionKey == customItem.PartitionKey
&& d.Properties[nameof(CustomItem.CustomItemId)].GuidValue == Guid.Parse(StringValueOfId)
)
.AsTableQuery();
var tr = table.ExecuteQuery<DynamicTableEntity>(query);
if (tr != null)
{
var entity = new DynamicTableEntity(customItem.PartitionKey, StringValueOfId);
entity.Properties.Add("MyDate", dateValue);
entity.Properties.Add("CustomProperty", new EntityProperty(JsonConvert.SerializeObject(customItem)));
entity.ETag = tr.FirstOrDefault().ETag;
TableOperation mergeOperation = TableOperation.Replace(entity);
try
{
TableResult result = await table.ExecuteAsync(mergeOperation);
}
catch (Exception ex)
{
throw new StorageException("Error saving data:" + ex.Message);
}
}
Now I am still having issues with concurrency. Basically my "CustomProperty" has a serialized property with some fields to null. It looks like, both A and B try to set the the value of "CustomProperty" at the same time. And one of them is overwriting the changes of the other.
Yet I expected that that would not be possible due to the settings of "entity.ETag". I was expecting that if A, and B read at the same time of the BD, and then A updates, the update of B would fail due to a mismatch of the value of entity.ETag.
Can someone tell me what I am doing wrong? Did I not understand how to correctly avoid these concurrencies issues?
I found what the problem was. This line here had the wrong etag:
That is the Etag of when I read the row from the DB to recover the item to update, but actually I needed the Etag value of when I read the customItem property.
Otherwise, this line may overwrite any change that happen after I recovered the customItem, but before I executed the query var tr = table.ExecuteQuery(query);
In other words, this solved my problem: