protected override async Task <SelectedSnapshot> LoadAsync(string persistenceId, SnapshotSelectionCriteria criteria) { var requestOptions = GenerateOptions(); BlobResultSegment results = null; using (var cts = new CancellationTokenSource(_settings.RequestTimeout)) { results = await Container.ListBlobsSegmentedAsync(SeqNoHelper.ToSnapshotSearchQuery(persistenceId), true, BlobListingDetails.Metadata, null, null, requestOptions, new OperationContext(), cts.Token); } // if we made it down here, the initial request succeeded. async Task <SelectedSnapshot> FilterAndFetch(BlobResultSegment segment) { // apply filter criteria var filtered = segment.Results .Where(x => x is CloudBlockBlob) .Cast <CloudBlockBlob>() .Where(x => FilterBlobSeqNo(criteria, x)) .Where(x => FilterBlobTimestamp(criteria, x)) .OrderByDescending(x => FetchBlobSeqNo(x)) // ordering matters - get highest seqNo item .ThenByDescending(x => FetchBlobTimestamp( x)) // if there are multiple snapshots taken at same SeqNo, need latest timestamp .FirstOrDefault(); // couldn't find what we were looking for. Onto the next part of the query // or return null to sender possibly. if (filtered == null) { return(null); } using (var cts = new CancellationTokenSource(_settings.RequestTimeout)) using (var memoryStream = new MemoryStream()) { await filtered.DownloadToStreamAsync(memoryStream, AccessCondition.GenerateIfExistsCondition(), GenerateOptions(), new OperationContext(), cts.Token); var snapshot = _serialization.SnapshotFromBytes(memoryStream.ToArray()); return(new SelectedSnapshot(new SnapshotMetadata(persistenceId, FetchBlobSeqNo(filtered)), snapshot.Data)); } } // TODO: see if there's ever a scenario where the most recent snapshots aren't in the beginning of the pagination list. var result = await FilterAndFetch(results); return(result); }
protected override async Task <SelectedSnapshot> LoadAsync(string persistenceId, SnapshotSelectionCriteria criteria) { using var cts = new CancellationTokenSource(_settings.RequestTimeout); { var results = Container.GetBlobsAsync( prefix: SeqNoHelper.ToSnapshotSearchQuery(persistenceId), traits: BlobTraits.Metadata, cancellationToken: cts.Token); var pageEnumerator = results.AsPages().GetAsyncEnumerator(cts.Token); if (!await pageEnumerator.MoveNextAsync()) { return(null); } // TODO: see if there's ever a scenario where the most recent snapshots aren't in the first page of the pagination list. // apply filter criteria var filtered = pageEnumerator.Current.Values .Where(x => FilterBlobSeqNo(criteria, x)) .Where(x => FilterBlobTimestamp(criteria, x)) .OrderByDescending(FetchBlobSeqNo) // ordering matters - get highest seqNo item .ThenByDescending(FetchBlobTimestamp) // if there are multiple snapshots taken at same SeqNo, need latest timestamp .FirstOrDefault(); // couldn't find what we were looking for. Onto the next part of the query // or return null to sender possibly. if (filtered == null) { return(null); } using var memoryStream = new MemoryStream(); var blobClient = Container.GetBlockBlobClient(filtered.Name); var downloadInfo = await blobClient.DownloadAsync(cts.Token); await downloadInfo.Value.Content.CopyToAsync(memoryStream); var snapshot = _serialization.SnapshotFromBytes(memoryStream.ToArray()); var result = new SelectedSnapshot( new SnapshotMetadata( persistenceId, FetchBlobSeqNo(filtered), new DateTime(FetchBlobTimestamp(filtered))), snapshot.Data); return(result); } }
protected override async Task DeleteAsync(string persistenceId, SnapshotSelectionCriteria criteria) { using var cts = new CancellationTokenSource(_settings.RequestTimeout); var items = Container.GetBlobsAsync( prefix: SeqNoHelper.ToSnapshotSearchQuery(persistenceId), traits: BlobTraits.Metadata, cancellationToken: cts.Token); var filtered = items .Where(x => FilterBlobSeqNo(criteria, x)) .Where(x => FilterBlobTimestamp(criteria, x)); var deleteTasks = new List <Task>(); await foreach (var blob in filtered.WithCancellation(cts.Token)) { var blobClient = Container.GetBlobClient(blob.Name); deleteTasks.Add(blobClient.DeleteIfExistsAsync(cancellationToken: cts.Token)); } await Task.WhenAll(deleteTasks); }
protected override async Task DeleteAsync(string persistenceId, SnapshotSelectionCriteria criteria) { var requestOptions = GenerateOptions(); BlobResultSegment results = null; using (var cts = new CancellationTokenSource(_settings.RequestTimeout)) { /* * Query only the metadata - don't need to stream the entire blob back to us * in order to delete it from storage in the next request. */ results = await Container.ListBlobsSegmentedAsync(SeqNoHelper.ToSnapshotSearchQuery(persistenceId), true, BlobListingDetails.Metadata, null, null, requestOptions, new OperationContext(), cts.Token); } // if we made it down here, the initial request succeeded. async Task FilterAndDelete(BlobResultSegment segment) { // apply filter criteria var filtered = segment.Results.Where(x => x is CloudBlockBlob) .Cast <CloudBlockBlob>() .Where(x => FilterBlobSeqNo(criteria, x)) .Where(x => FilterBlobTimestamp(criteria, x)); var deleteTasks = new List <Task>(); using (var cts = new CancellationTokenSource(_settings.RequestTimeout)) { foreach (var blob in filtered) { deleteTasks.Add(blob.DeleteIfExistsAsync(DeleteSnapshotsOption.None, AccessCondition.GenerateIfExistsCondition(), GenerateOptions(), new OperationContext(), cts.Token)); } await Task.WhenAll(deleteTasks); } } var continuationToken = results.ContinuationToken; var deleteTask = FilterAndDelete(results); while (continuationToken != null) { // get the next round of results in parallel with the deletion of the previous var nextResults = await Container.ListBlobsSegmentedAsync(continuationToken); // finish our previous delete tasks await deleteTask; // start next round of deletes deleteTask = FilterAndDelete(nextResults); // move the loop forward if there are more results to be processed still continuationToken = nextResults.ContinuationToken; } // wait for the final delete operation to complete await deleteTask; }