private async Task <OptimisticStoreWriteResult> SaveDataInternal(StoreLocation location, Metadata metadata, UpdateAuditInfo audit, Func <IWriteAsyncStream, Task <long?> > savingFunc, CancellationToken token, bool isOptimistic) { var blob = GetBlockBlob(location); // We always want to save the new audit information when saving! var currentMetadata = await GetBlobMetadata(blob).ConfigureAwait(false); var auditInfo = TransformAuditInformation(currentMetadata, audit); metadata = metadata ?? new Metadata(); metadata.Audit = auditInfo; var result = new OptimisticStoreWriteResult() { Result = true }; try { // If the ETag value is empty then the store value must not exist yet... var condition = isOptimistic ? (string.IsNullOrEmpty(metadata.ETag) ? AccessCondition.GenerateIfNoneMatchCondition("*") : AccessCondition.GenerateIfMatchCondition(metadata.ETag)) : null; // Copy the metadata across blob.Metadata.Clear(); foreach (var m in metadata) { blob.Metadata[m.Key] = m.Value; } // Always store the version - We use this to do more efficient things on read blob.Metadata[StoreVersionKey] = StoreVersionValue; long?length; using (var stream = new AzureWriteBlockBlobStream(blob, condition)) { length = await savingFunc(stream).ConfigureAwait(false); await stream.Complete(token).ConfigureAwait(false); } if (length.HasValue && (metadata == null || !metadata.ContentLength.HasValue)) { blob.Metadata[MetadataConstants.ContentLengthMetadataKey] = length.Value.ToString(CultureInfo.InvariantCulture); // Save the length straight away before the snapshot... await blob.SetMetadataAsync(null, null, null, token).ConfigureAwait(false); } // Create a snapshot straight away on azure // Note: this shouldnt matter for cost as any blocks that are the same do not cost extra if (_enableSnapshots) { var snapshotBlob = await blob.CreateSnapshotAsync(blob.Metadata, null, null, null, token).ConfigureAwait(false); var snapshot = snapshotBlob.SnapshotTime.Value.UtcTicks.ToString(CultureInfo.InvariantCulture); // Save the snapshot back to original blob... blob.Metadata[InternalSnapshotKey] = snapshot; await blob.SetMetadataAsync(null, null, null, token).ConfigureAwait(false); LeoTrace.WriteLine("Created Snapshot: " + blob.Name); } result.Metadata = await GetActualMetadata(blob).ConfigureAwait(false); } catch (StorageException exc) { if (isOptimistic) { // First condition occurrs when the eTags do not match // Second condition when we specified no eTag (ie must be new blob) if (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed || (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.Conflict && exc.RequestInformation.ExtendedErrorInformation.ErrorCode == "BlobAlreadyExists")) { result.Result = false; } else { // Might have been a different error? throw exc.Wrap(blob.Name); } } else { if (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.Conflict || exc.RequestInformation.ExtendedErrorInformation.ErrorCode == "LeaseIdMissing") { throw new LockException("The underlying storage is currently locked for save"); } // Might have been a different error? throw exc.Wrap(blob.Name); } } return(result); }
private async Task<OptimisticStoreWriteResult> SaveDataInternal(StoreLocation location, Metadata metadata, UpdateAuditInfo audit, Func<IWriteAsyncStream, Task<long?>> savingFunc, CancellationToken token, bool isOptimistic) { var blob = GetBlockBlob(location); // We always want to save the new audit information when saving! var currentMetadata = await GetBlobMetadata(blob).ConfigureAwait(false); var auditInfo = TransformAuditInformation(currentMetadata, audit); metadata = metadata ?? new Metadata(); metadata.Audit = auditInfo; var result = new OptimisticStoreWriteResult() { Result = true }; try { // If the ETag value is empty then the store value must not exist yet... var condition = isOptimistic ? (string.IsNullOrEmpty(metadata.ETag) ? AccessCondition.GenerateIfNoneMatchCondition("*") : AccessCondition.GenerateIfMatchCondition(metadata.ETag)) : null; // Copy the metadata across blob.Metadata.Clear(); foreach (var m in metadata) { blob.Metadata[m.Key] = m.Value; } // Always store the version - We use this to do more efficient things on read blob.Metadata[StoreVersionKey] = StoreVersionValue; long? length; using (var stream = new AzureWriteBlockBlobStream(blob, condition)) { length = await savingFunc(stream).ConfigureAwait(false); await stream.Complete(token).ConfigureAwait(false); } if (length.HasValue && (metadata == null || !metadata.ContentLength.HasValue)) { blob.Metadata[MetadataConstants.ContentLengthMetadataKey] = length.Value.ToString(CultureInfo.InvariantCulture); // Save the length straight away before the snapshot... await blob.SetMetadataAsync(token).ConfigureAwait(false); } // Create a snapshot straight away on azure // Note: this shouldnt matter for cost as any blocks that are the same do not cost extra if (_enableSnapshots) { var snapshotBlob = await blob.CreateSnapshotAsync(token).ConfigureAwait(false); var snapshot = snapshotBlob.SnapshotTime.Value.UtcTicks.ToString(CultureInfo.InvariantCulture); // Save the snapshot back to original blob... blob.Metadata[InternalSnapshotKey] = snapshot; await blob.SetMetadataAsync(token).ConfigureAwait(false); LeoTrace.WriteLine("Created Snapshot: " + blob.Name); } result.Metadata = await GetActualMetadata(blob).ConfigureAwait(false); } catch (StorageException exc) { if (isOptimistic) { // First condition occurrs when the eTags do not match // Second condition when we specified no eTag (ie must be new blob) if (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed || (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.Conflict && exc.RequestInformation.ExtendedErrorInformation.ErrorCode == "BlobAlreadyExists")) { result.Result = false; } else { // Might have been a different error? throw exc.Wrap(blob.Name); } } else { if (exc.RequestInformation.HttpStatusCode == (int)HttpStatusCode.Conflict || exc.RequestInformation.ExtendedErrorInformation.ErrorCode == "LeaseIdMissing") { throw new LockException("The underlying storage is currently locked for save"); } // Might have been a different error? throw exc.Wrap(blob.Name); } } return result; }
private async Task <OptimisticStoreWriteResult> SaveDataInternal(StoreLocation location, Metadata metadata, UpdateAuditInfo audit, Func <IWriteAsyncStream, Task <long?> > savingFunc, CancellationToken token, bool isOptimistic) { var blob = GetBlockBlob(location); // We always want to save the new audit information when saving! var props = await GetBlobProperties(blob); var currentMetadata = await GetActualMetadata(blob, props, null); var auditInfo = TransformAuditInformation(currentMetadata, audit); metadata = metadata ?? new Metadata(); metadata.Audit = auditInfo; var result = new OptimisticStoreWriteResult() { Result = true }; try { // If the ETag value is empty then the store value must not exist yet... var condition = isOptimistic ? (string.IsNullOrEmpty(metadata.ETag) ? new BlobRequestConditions { IfNoneMatch = ETag.All } : new BlobRequestConditions { IfMatch = new ETag(metadata.ETag) }) : null; // Copy the metadata across var newMeta = new Dictionary <string, string>(); foreach (var m in metadata) { newMeta[m.Key] = AzureStoreMetadataEncoder.EncodeMetadata(m.Value); } // Always store the version - We use this to do more efficient things on read newMeta[StoreVersionKey] = StoreVersionValue; long?length; using (var stream = new AzureWriteBlockBlobStream(blob, condition, newMeta)) { length = await savingFunc(stream); await stream.Complete(token); } if (length.HasValue && (metadata == null || !metadata.ContentLength.HasValue)) { newMeta[MetadataConstants.ContentLengthMetadataKey] = length.Value.ToString(CultureInfo.InvariantCulture); // Save the length straight away before the snapshot... await blob.SetMetadataAsync(newMeta, cancellationToken : token); } // Create a snapshot straight away on azure // Note: this shouldnt matter for cost as any blocks that are the same do not cost extra if (_enableSnapshots) { var snapshotBlob = await blob.CreateSnapshotAsync(cancellationToken : token); // Save the snapshot back to original blob... newMeta[InternalSnapshotKey] = snapshotBlob.Value.Snapshot; await blob.SetMetadataAsync(newMeta, cancellationToken : token); LeoTrace.WriteLine("Created Snapshot: " + blob.Name); } var newProps = await blob.GetPropertiesAsync(); result.Metadata = await GetActualMetadata(blob, newProps, null); } catch (RequestFailedException e) { if (isOptimistic) { // First condition occurrs when the eTags do not match // Second condition when we specified no eTag (ie must be new blob) if (e.Status == (int)HttpStatusCode.PreconditionFailed || (e.Status == (int)HttpStatusCode.Conflict && e.ErrorCode == BlobErrorCode.BlobAlreadyExists)) { result.Result = false; } else { // Might have been a different error? throw; } } else { if (e.Status == (int)HttpStatusCode.Conflict || e.ErrorCode == BlobErrorCode.LeaseIdMissing) { throw new LockException("The underlying storage is currently locked for save"); } // Might have been a different error? throw; } } return(result); }