Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add retry policies into DPS service #2927

Merged
merged 6 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion SDK v2 migration guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ What was a loose affiliation of separate clients is now a consolidated client wi
- Added support for setting a client web socket instance in the client options so that users can have better control over AMQP web socket connections.
- Added support for setting the web socket level keep alive interval for AMQP web socket connections.
- Added support for setting the remote certificate validation callback for AMQP TCP connections.
- The library now includes `IProvisioningClientRetryPolicy` implementations: `ProvisioningClientExponentialBackoffRetryPolicy`, `ProvisioningClientFixedDelayRetryPolicy`, `IotHubServiceIncrementalDelayRetryPolicy` and `ProvisioningClientNoRetry`,
- The library now includes `IProvisioningClientRetryPolicy` implementations: `ProvisioningClientExponentialBackoffRetryPolicy`, `ProvisioningClientFixedDelayRetryPolicy`, `ProvisioningClientIncrementalDelayRetryPolicy` and `ProvisioningClientNoRetry`,
which can be set via `ProvisioningClientOptions.RetryPolicy`.

#### API mapping
Expand All @@ -350,6 +350,11 @@ What was a loose affiliation of separate clients is now a consolidated client wi
- ETag fields on the classes `IndividualEnrollment`, `EnrollmentGroup`, and `DeviceRegistrationState` are now taken as the `Azure.ETag` type instead of strings.
- Twin.Tags is now of type `IDictionary<string, object>`.

#### Notable additions

- The library now includes `IProvisioningServiceRetryPolicy` implementations: `ProvisioningServiceExponentialBackoffRetryPolicy`, `ProvisioningServiceFixedDelayRetryPolicy`, `ProvisioningServiceIncrementalDelayRetryPolicy` and `ProvisioningServiceNoRetry`,
which can be set via `ProvisioningServiceOptions.RetryPolicy`.

#### API mapping

| v1 API | Equivalent v2 API | Notes |
Expand Down
59 changes: 0 additions & 59 deletions e2e/test/helpers/ProvisioningServiceRetryPolicy.cs

This file was deleted.

10 changes: 5 additions & 5 deletions e2e/test/helpers/RetryOperationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Provisioning.Service;

namespace Microsoft.Azure.Devices.E2ETests.Helpers
{
Expand All @@ -17,14 +17,14 @@ public class RetryOperationHelper
/// Retry an async operation based on the retry strategy supplied.
/// </summary>
/// <remarks>
/// This is for E2E tests of provisioning service clients.
/// This is for E2E tests of the provisioning service clients.
/// </remarks>
/// <param name="asyncOperation">The async operation to be retried.</param>
/// <param name="retryPolicy">The retry policy of hub device/module to be applied.</param>
/// <param name="cancellationToken">The cancellation token to cancel the operation.</param>
public static async Task RunWithHubClientRetryAsync(
public static async Task RunWithProvisioningServiceRetryAsync(
Func<Task> asyncOperation,
IIotHubClientRetryPolicy retryPolicy,
IProvisioningServiceRetryPolicy retryPolicy,
CancellationToken cancellationToken = default)
{
uint counter = 0;
Expand Down Expand Up @@ -57,7 +57,7 @@ public class RetryOperationHelper
/// Retry an async operation based on the retry strategy supplied.
/// </summary>
/// <remarks>
/// This is for E2E tests of provisioning service clients.
/// This is for E2E tests of the hub service clients.
/// </remarks>
/// <param name="asyncOperation">The async operation to be retried.</param>
/// <param name="retryPolicy">The retry policy of hub service to be applied.</param>
Expand Down
7 changes: 4 additions & 3 deletions e2e/test/provisioning/ProvisioningE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class ProvisioningE2ETests : E2EMsTestBase
private static readonly string s_proxyServerAddress = TestConfiguration.IotHub.ProxyServerAddress;
private static readonly string s_certificatePassword = TestConfiguration.Provisioning.CertificatePassword;

private static readonly IIotHubClientRetryPolicy s_provisioningServiceRetryPolicy = new ProvisioningServiceRetryPolicy();
private static readonly IProvisioningServiceRetryPolicy s_provisioningServiceRetryPolicy =
new ProvisioningServiceExponentialBackoffRetryPolicy(20, TimeSpan.FromSeconds(3), true);

private readonly string _idPrefix = $"e2e-{nameof(ProvisioningE2ETests).ToLower()}-";

Expand Down Expand Up @@ -1124,7 +1125,7 @@ private static void ValidateDeviceRegistrationResult(bool validatePayload, Devic
if (enrollmentType == EnrollmentType.Individual)
{
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
await dpsClient.IndividualEnrollments.DeleteAsync(authProvider.GetRegistrationId()).ConfigureAwait(false);
Expand All @@ -1136,7 +1137,7 @@ await RetryOperationHelper
else if (enrollmentType == EnrollmentType.Group)
{
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
await dpsClient.EnrollmentGroups.DeleteAsync(groupId).ConfigureAwait(false);
Expand Down
26 changes: 13 additions & 13 deletions e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.E2ETests.Helpers;
using Microsoft.Azure.Devices.Provisioning.Service;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand All @@ -23,7 +22,8 @@ public class ProvisioningServiceClientE2ETests : E2EMsTestBase
private static readonly string s_proxyServerAddress = TestConfiguration.IotHub.ProxyServerAddress;
private static readonly string s_devicePrefix = $"{nameof(ProvisioningServiceClientE2ETests)}_";

private static readonly IIotHubClientRetryPolicy s_provisioningServiceRetryPolicy = new ProvisioningServiceRetryPolicy();
private static readonly IProvisioningServiceRetryPolicy s_provisioningServiceRetryPolicy =
new ProvisioningServiceExponentialBackoffRetryPolicy(20, TimeSpan.FromSeconds(3), true);

public enum EnrollmentType
{
Expand Down Expand Up @@ -172,7 +172,7 @@ public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(A

AttestationMechanism attestationMechanism = null;
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
attestationMechanism = await provisioningServiceClient.IndividualEnrollments.GetAttestationAsync(individualEnrollment.RegistrationId);
Expand Down Expand Up @@ -224,7 +224,7 @@ public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation(Attest

AttestationMechanism attestationMechanism = null;
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
attestationMechanism = await provisioningServiceClient.EnrollmentGroups.GetAttestationAsync(enrollmentGroup.EnrollmentGroupId);
Expand Down Expand Up @@ -325,7 +325,7 @@ public async Task ProvisioningServiceClient_IndividualEnrollments_Create_Ok(stri

IndividualEnrollment individualEnrollmentResult = null;
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
individualEnrollmentResult = await provisioningServiceClient.IndividualEnrollments.GetAsync(individualEnrollment.RegistrationId).ConfigureAwait(false);
Expand Down Expand Up @@ -398,7 +398,7 @@ public async Task ProvisioningServiceClient_GroupEnrollments_Create_Ok(string pr

EnrollmentGroup enrollmentGroupResult = null;
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
enrollmentGroupResult = await provisioningServiceClient.EnrollmentGroups.GetAsync(enrollmentGroup.EnrollmentGroupId).ConfigureAwait(false);
Expand Down Expand Up @@ -479,7 +479,7 @@ await RetryOperationHelper
};

await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
createdEnrollment = await provisioningServiceClient.IndividualEnrollments
Expand Down Expand Up @@ -534,7 +534,7 @@ await RetryOperationHelper

EnrollmentGroup createdEnrollmentGroup = null;
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
createdEnrollmentGroup = await provisioningServiceClient.EnrollmentGroups.CreateOrUpdateAsync(enrollmentGroup).ConfigureAwait(false);
Expand All @@ -556,28 +556,28 @@ await RetryOperationHelper
string registrationId,
string groupId)
{
using ProvisioningServiceClient dpsClient = CreateProvisioningService();
using ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService();

try
{
if (enrollmentType == EnrollmentType.Individual)
{
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
await dpsClient.IndividualEnrollments.DeleteAsync(registrationId).ConfigureAwait(false);
await provisioningServiceClient.IndividualEnrollments.DeleteAsync(registrationId).ConfigureAwait(false);
},
s_provisioningServiceRetryPolicy)
.ConfigureAwait(false);
}
else if (enrollmentType == EnrollmentType.Group)
{
await RetryOperationHelper
.RunWithHubClientRetryAsync(
.RunWithProvisioningServiceRetryAsync(
async () =>
{
await dpsClient.EnrollmentGroups.DeleteAsync(groupId).ConfigureAwait(false);
await provisioningServiceClient.EnrollmentGroups.DeleteAsync(groupId).ConfigureAwait(false);
},
s_provisioningServiceRetryPolicy)
.ConfigureAwait(false);
Expand Down
2 changes: 1 addition & 1 deletion provisioning/device/src/RetryPolicies/RetryHandler.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;
using System;

namespace Microsoft.Azure.Devices.Provisioning.Client
{
Expand Down
50 changes: 34 additions & 16 deletions provisioning/service/src/DeviceRegistrationsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class DeviceRegistrationStatesClient

private readonly IContractApiHttp _contractApiHttp;
private readonly ServiceConnectionString _serviceConnectionString;
private readonly RetryHandler _internalRetryHandler;

/// <summary>
/// Creates an instance of this class. Provided for unit testing purposes only.
Expand All @@ -31,10 +32,11 @@ protected DeviceRegistrationStatesClient()
{
}

internal DeviceRegistrationStatesClient(ServiceConnectionString serviceConnectionString, IContractApiHttp contractApiHttp)
internal DeviceRegistrationStatesClient(ServiceConnectionString serviceConnectionString, IContractApiHttp contractApiHttp, RetryHandler retryHandler)
{
_serviceConnectionString = serviceConnectionString;
_contractApiHttp = contractApiHttp;
_internalRetryHandler = retryHandler;
}

/// <summary>
Expand All @@ -55,13 +57,22 @@ public async Task<DeviceRegistrationState> GetAsync(string registrationId, Cance

cancellationToken.ThrowIfCancellationRequested();

ContractApiResponse contractApiResponse = await _contractApiHttp
.RequestAsync(
HttpMethod.Get,
GetDeviceRegistrationStatusUri(registrationId),
null,
null,
new ETag(),
ContractApiResponse contractApiResponse = null;

await _internalRetryHandler
.RunWithRetryAsync(
async () =>
{
contractApiResponse = await _contractApiHttp
.RequestAsync(
HttpMethod.Get,
GetDeviceRegistrationStatusUri(registrationId),
null,
null,
new ETag(),
cancellationToken)
.ConfigureAwait(false);
},
cancellationToken)
.ConfigureAwait(false);

Expand Down Expand Up @@ -102,13 +113,20 @@ public async Task DeleteAsync(DeviceRegistrationState deviceRegistrationState, C

cancellationToken.ThrowIfCancellationRequested();

await _contractApiHttp
.RequestAsync(
HttpMethod.Delete,
GetDeviceRegistrationStatusUri(deviceRegistrationState.RegistrationId),
null,
null,
deviceRegistrationState.ETag,
await _internalRetryHandler
.RunWithRetryAsync(
async () =>
{
await _contractApiHttp
.RequestAsync(
HttpMethod.Delete,
GetDeviceRegistrationStatusUri(deviceRegistrationState.RegistrationId),
null,
null,
deviceRegistrationState.ETag,
cancellationToken)
.ConfigureAwait(false);
},
cancellationToken)
.ConfigureAwait(false);
}
Expand All @@ -128,7 +146,7 @@ await _contractApiHttp
public Query CreateEnrollmentGroupQuery(string query, string enrollmentGroupId, int pageSize = 0, CancellationToken cancellationToken = default)
{
Argument.AssertNotNullOrWhiteSpace(query, nameof(query));
return new Query(_serviceConnectionString, GetDeviceRegistrationStatusUri(enrollmentGroupId).ToString(), query, _contractApiHttp, pageSize, cancellationToken);
return new Query(_serviceConnectionString, GetDeviceRegistrationStatusUri(enrollmentGroupId).ToString(), query, _contractApiHttp, pageSize, _internalRetryHandler, cancellationToken);
}

private static Uri GetDeviceRegistrationStatusUri(string id)
Expand Down