Windows Azure Table Storage InsertOrUpdate

Updated on 2011-12-14 21:17

E' ormai da diverse settimane che mi cimento nello sviluppo di un sistema destinato a girare nell'infrastruttura Cloud di Microsoft e, in particolare, le tecnologie che sto impiegando sono Windows Azure, SQL Azure, Windows Azure Table Storage, Windows Azure Blob Storage e AppFabric Cache. Una delle più grandi sfide quando si utilizza lo storage di Windows Azure è l'abbattimento del numero di accessi ad esso. Come probabilmente saprete, accedere agli storage di Windows Azure ha un costo calcolato sia sulla base della quantità di spazio occupata, sia sul numero di transazioni, cioè di operazioni di lettura/scrittura.

Supponiamo di dover eseguire la classica Insert Or Update di un dato all'interno del Table Storage. Avendo sempre ben in mente che stiamo parlando di un database NoSQL nel quale, quindi, il concetto di transazione non esiste, per implementare questo tipo di operazione è necessario scrivere qualcosa del genere:

CloudTableClient tableClient = GetTableClient();
TableServiceContext context = tableClient.GetDataServiceContext();
context.IgnoreResourceNotFoundException = true;
var item = context.CreateQuery(tableName)
           .Where(p => p.PartitionKey == partitionName && p.RowKey == storageKey)
           .FirstOrDefault();
if (item != null) 
{ 
   context.DeleteObject(item); 
} 
context.AddObject(tableName, newItem); 
context.SaveChangesWithRetries(); 

Il problema di questo approccio è che il numero di operazioni effettuate è, nel caso peggiore, pari a tre (in realtà si potrebbe fare la delete e la insert in batch, ma non è importante), mentre nel caso migliore è pari a due.

Grazie all'Insert-Or-Update Entity introdotto da qualche mese è possibile ridurre il numero di accessi nel modo seguente:

CloudTableClient tableClient = GetTableClient();
TableServiceContext context = tableClient.GetDataServiceContext();
context.AttachTo(tableName, newItem);
context.UpdateObject(tableName, newItem);
context.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate); 

Questo codice esegue la Insert o, eventualmente, l'update dell'elemento in tabella grazie all'uso dell'opzione ReplaceOnUpdate. Naturalmente non poteva essere tutto così semplice: se provate quest'ultimo codice in locale vi renderete conto che non funziona. Il problema è che la Insert-Or-Update Entity non è disponibile per il local storage, compreso quindi il vostro storage emulato. Per risolvere questo problema potete affidavi alla propria booleana statica RoleEnvironment.IsEmulated ed implementare, quindi, nel caso in cui sia true la soluzione meno efficiente che però verrà eseguita solo in ambiente di sviluppo, il che è assolutamente accettabile.