Hantera samtidighet i Blob Storage
Moderna program har ofta flera användare som visar och uppdaterar data samtidigt. Programutvecklare måste tänka noga på hur de ska ge slutanvändarna en förutsägbar upplevelse, särskilt för scenarier där flera användare kan uppdatera samma data. Det finns tre huvudsakliga strategier för samtidighet av data som utvecklare vanligtvis överväger:
Optimistisk samtidighet: Ett program som utför en uppdatering avgör, som en del av uppdateringen, om data har ändrats sedan programmet senast läste dessa data. Om till exempel två användare som visar en wiki-sida gör en uppdatering av den sidan måste wiki-plattformen se till att den andra uppdateringen inte skriver över den första uppdateringen. Den måste också se till att båda användarna förstår om uppdateringen lyckades. Den här strategin används oftast i webbprogram.
Pessimistisk samtidighet: Ett program som vill utföra en uppdatering låser ett objekt som hindrar andra användare från att uppdatera data tills låset släpps. I till exempel ett primärt/sekundärt datareplikeringsscenario där endast den primära utför uppdateringar, har den primära vanligtvis ett exklusivt lås på data under en längre tid för att säkerställa att ingen annan kan uppdatera dem.
Senaste skrivaren vinner: En metod som gör att uppdateringsåtgärder kan fortsätta utan att först avgöra om ett annat program har uppdaterat data sedan det lästes. Den här metoden används vanligtvis när data partitioneras på ett sådant sätt att flera användare inte kommer åt samma data samtidigt. Det kan också vara användbart när kortlivade dataströmmar bearbetas.
Azure Storage har stöd för alla tre strategierna, även om det är distinkt i sin förmåga att ge fullt stöd för optimistisk och pessimistisk samtidighet. Azure Storage har utformats för att omfatta en stark konsekvensmodell som garanterar att efterföljande läs- eller liståtgärder returnerar den senaste uppdateringen efter att tjänsten har utför en infognings- eller uppdateringsåtgärd.
Förutom att välja en lämplig samtidighetsstrategi bör utvecklare också vara medvetna om hur en lagringsplattform isolerar ändringar, särskilt ändringar i samma objekt mellan transaktioner. Azure Storage använder ögonblicksbildisolering för att tillåta läsåtgärder samtidigt med skrivåtgärder inom en enda partition. Isolering av ögonblicksbilder garanterar att alla läsåtgärder returnerar en konsekvent ögonblicksbild av data även när uppdateringar sker.
Du kan välja att använda antingen optimistiska eller pessimistiska samtidighetsmodeller för att hantera åtkomst till blobar och containrar. Om du inte uttryckligen anger en strategi vinner den senaste skrivaren som standard.
Optimistisk samtidighet
Azure Storage tilldelar en identifierare till varje objekt som lagras. Den här identifieraren uppdateras varje gång en skrivåtgärd utförs på ett objekt. Identifieraren returneras till klienten som en del av ett HTTP GET-svar i ETag-huvudet som definieras av HTTP-protokollet.
En klient som utför en uppdatering kan skicka den ursprungliga ETag tillsammans med en villkorsstyrd rubrik för att säkerställa att en uppdatering endast inträffar om ett visst villkor har uppfyllts. Om till exempel If-Match-huvudet har angetts kontrollerar Azure Storage att värdet för ETag som anges i uppdateringsbegäran är detsamma som ETag för objektet som uppdateras. Mer information om villkorsstyrda rubriker finns i Ange villkorsstyrda huvuden för Blob Service-åtgärder.
Konturen av den här processen är följande:
- Hämta en blob från Azure Storage. Svaret innehåller ett HTTP ETag-huvudvärde som identifierar den aktuella versionen av objektet.
- När du uppdaterar bloben tar du med det ETag-värde som du fick i steg 1 i villkorsrubriken If-Match i skrivbegäran. Azure Storage jämför ETag-värdet i begäran med blobens aktuella ETag-värde.
- Om blobens aktuella ETag-värde skiljer sig från det ETag-värde som anges i villkorsrubriken If-Match som anges i begäran returnerar Azure Storage HTTP-statuskod 412 (förhandsvillkoret misslyckades). Det här felet anger för klienten att en annan process har uppdaterat bloben sedan klienten först hämtade den. Klienten bör hämta bloben igen för att hämta uppdaterat innehåll och egenskaper.
- Om det aktuella ETag-värdet för bloben är samma version som ETag i villkorsrubriken If-Match i begäran, utför Azure Storage den begärda åtgärden och uppdaterar blobens aktuella ETag-värde.
Följande kodexempel visar hur du konstruerar ett If-Match-villkor för skrivbegäran som kontrollerar ETag-värdet för en blob. Azure Storage utvärderar om blobens aktuella ETag är samma som ETag som anges i begäran och utför skrivåtgärden endast om de två ETag-värdena matchar. Om en annan process har uppdaterat bloben under tiden returnerar Azure Storage ett HTTP 412-statusmeddelande (villkorsfel).
private static async Task DemonstrateOptimisticConcurrencyBlob(BlobClient blobClient)
{
Console.WriteLine("Demonstrate optimistic concurrency");
try
{
// Download a blob
Response<BlobDownloadResult> response = await blobClient.DownloadContentAsync();
BlobDownloadResult downloadResult = response.Value;
string blobContents = downloadResult.Content.ToString();
ETag originalETag = downloadResult.Details.ETag;
Console.WriteLine("Blob ETag = {0}", originalETag);
// This function simulates an external change to the blob after we've fetched it
// The external change updates the contents of the blob and the ETag value
await SimulateExternalBlobChangesAsync(blobClient);
// Now try to update the blob using the original ETag value
string blobContentsUpdate2 = $"{blobContents} Update 2. If-Match condition set to original ETag.";
// Set the If-Match condition to the original ETag
BlobUploadOptions blobUploadOptions = new()
{
Conditions = new BlobRequestConditions()
{
IfMatch = originalETag
}
};
// This call should fail with error code 412 (Precondition Failed)
BlobContentInfo blobContentInfo =
await blobClient.UploadAsync(BinaryData.FromString(blobContentsUpdate2), blobUploadOptions);
}
catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.PreconditionFailed)
{
Console.WriteLine(
@"Blob's ETag does not match ETag provided. Fetch the blob to get updated contents and properties.");
}
}
private static async Task SimulateExternalBlobChangesAsync(BlobClient blobClient)
{
// Simulates an external change to the blob for this example
// Download a blob
Response<BlobDownloadResult> response = await blobClient.DownloadContentAsync();
BlobDownloadResult downloadResult = response.Value;
string blobContents = downloadResult.Content.ToString();
// Update the existing block blob contents
// No ETag condition is provided, so original blob is overwritten and ETag is updated
string blobContentsUpdate1 = $"{blobContents} Update 1";
BlobContentInfo blobContentInfo =
await blobClient.UploadAsync(BinaryData.FromString(blobContentsUpdate1), overwrite: true);
Console.WriteLine("Blob update. Updated ETag = {0}", blobContentInfo.ETag);
}
Azure Storage har också stöd för andra villkorsstyrda rubriker, till exempel If-Modified-Since, If-Unmodified-Since och If-None-Match. Mer information finns i Ange villkorsstyrda rubriker för Blob Service-åtgärder.
Pessimistisk samtidighet för blobar
Om du vill låsa en blob för exklusiv användning kan du skaffa ett lån på den. När du skaffar lånet anger du lånets varaktighet. Ett ändlig lån kan vara giltigt mellan 15 och 60 sekunder. Ett lån kan också vara oändligt, vilket är ett exklusivt lås. Du kan förnya ett ändlig lån för att utöka det och du kan frigöra lånet när du är klar med det. Azure Storage släpper automatiskt ändliga lån när de upphör att gälla.
Lån gör det möjligt att stödja olika synkroniseringsstrategier, inklusive exklusiva skriv-/delade läsåtgärder, exklusiva skriv-/exklusiva läsåtgärder och delade skrivåtgärder/exklusiva läsåtgärder. När det finns ett lån framtvingar Azure Storage exklusiv åtkomst till skrivåtgärder för låneinnehavaren. För att säkerställa exklusivitet för läsåtgärder måste utvecklaren dock se till att alla klientprogram använder ett låne-ID och att endast en klient i taget har ett giltigt låne-ID. Läsåtgärder som inte innehåller ett låne-ID resulterar i delade läsningar.
Följande kodexempel visar hur du hämtar ett exklusivt lån på en blob, uppdaterar innehållet i bloben genom att ange låne-ID:t och sedan släpper lånet. Om lånet är aktivt och låne-ID:t inte tillhandahålls på en skrivbegäran misslyckas skrivåtgärden med felkoden 412 (villkoret misslyckades).
public static async Task DemonstratePessimisticConcurrencyBlob(BlobClient blobClient)
{
Console.WriteLine("Demonstrate pessimistic concurrency");
BlobContainerClient containerClient = blobClient.GetParentBlobContainerClient();
BlobLeaseClient blobLeaseClient = blobClient.GetBlobLeaseClient();
try
{
// Create the container if it does not exist.
await containerClient.CreateIfNotExistsAsync();
// Upload text to a blob.
string blobContents1 = "First update. Overwrite blob if it exists.";
byte[] byteArray = Encoding.ASCII.GetBytes(blobContents1);
using (MemoryStream stream = new MemoryStream(byteArray))
{
BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream, overwrite: true);
}
// Acquire a lease on the blob.
BlobLease blobLease = await blobLeaseClient.AcquireAsync(TimeSpan.FromSeconds(15));
Console.WriteLine("Blob lease acquired. LeaseId = {0}", blobLease.LeaseId);
// Set the request condition to include the lease ID.
BlobUploadOptions blobUploadOptions = new BlobUploadOptions()
{
Conditions = new BlobRequestConditions()
{
LeaseId = blobLease.LeaseId
}
};
// Write to the blob again, providing the lease ID on the request.
// The lease ID was provided, so this call should succeed.
string blobContents2 = "Second update. Lease ID provided on request.";
byteArray = Encoding.ASCII.GetBytes(blobContents2);
using (MemoryStream stream = new MemoryStream(byteArray))
{
BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream, blobUploadOptions);
}
// This code simulates an update by another client.
// The lease ID is not provided, so this call fails.
string blobContents3 = "Third update. No lease ID provided.";
byteArray = Encoding.ASCII.GetBytes(blobContents3);
using (MemoryStream stream = new MemoryStream(byteArray))
{
// This call should fail with error code 412 (Precondition Failed).
BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream);
}
}
catch (RequestFailedException e)
{
if (e.Status == (int)HttpStatusCode.PreconditionFailed)
{
Console.WriteLine(
@"Precondition failure as expected. The lease ID was not provided.");
}
else
{
Console.WriteLine(e.Message);
throw;
}
}
finally
{
await blobLeaseClient.ReleaseAsync();
}
}
Pessimistisk samtidighet för containrar
Lån på containrar möjliggör samma synkroniseringsstrategier som stöds för blobar, inklusive exklusiv skriv-/delad läsning, exklusiv skriv-/exklusiv läsning och delad skriv-/exklusiv läsning. För containrar framtvingas dock det exklusiva låset endast vid borttagningsåtgärder. Om du vill ta bort en container med ett aktivt lån måste en klient inkludera det aktiva låne-ID:t med borttagningsbegäran. Alla andra containeråtgärder lyckas på en hyrd container utan låne-ID.
Nästa steg
Resurser
Relaterade kodexempel med inaktuella .NET version 11.x SDK:er finns i Kodexempel med .NET version 11.x.