diff --git a/code.sln b/code.sln index 3f412a7e..5b5816de 100644 --- a/code.sln +++ b/code.sln @@ -44,12 +44,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ServiceFabric.Ser EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ServiceFabric.Services.Tests", "test\unittests\Microsoft.ServiceFabric.Services.Tests\Microsoft.ServiceFabric.Services.Tests.csproj", "{ECB3825D-B309-4C76-A20D-76544459F611}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ServiceFabric.Actors.KVSToRCMigration", "src\Microsoft.ServiceFabric.Actors.KVSToRCMigration\Microsoft.ServiceFabric.Actors.KVSToRCMigration.csproj", "{B5E3B9D7-E91E-4B76-959D-D418E973FCAF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ServiceFabric.Actors.KVSToRCMigration_netstandard", "src\netstandard\Microsoft.ServiceFabric.Actors.KVSToRCMigration\Microsoft.ServiceFabric.Actors.KVSToRCMigration_netstandard.csproj", "{E5B52C61-66BE-4D71-88D2-E50F78011C72}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ServiceFabric.Actors.StateMigration.Tests", "test\unittests\Microsoft.ServiceFabric.Actors.StateMigration.Tests\Microsoft.ServiceFabric.Actors.StateMigration.Tests.csproj", "{1BDC4681-FDBA-4E55-A247-5F779627A4D7}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -162,30 +156,6 @@ Global {ECB3825D-B309-4C76-A20D-76544459F611}.Release|Any CPU.Build.0 = Release|Any CPU {ECB3825D-B309-4C76-A20D-76544459F611}.Release|x64.ActiveCfg = Release|x64 {ECB3825D-B309-4C76-A20D-76544459F611}.Release|x64.Build.0 = Release|x64 - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Debug|x64.ActiveCfg = Debug|x64 - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Debug|x64.Build.0 = Debug|x64 - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Release|Any CPU.Build.0 = Release|Any CPU - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Release|x64.ActiveCfg = Release|x64 - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF}.Release|x64.Build.0 = Release|x64 - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Debug|x64.ActiveCfg = Debug|x64 - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Debug|x64.Build.0 = Debug|x64 - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Release|Any CPU.Build.0 = Release|Any CPU - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Release|x64.ActiveCfg = Release|x64 - {E5B52C61-66BE-4D71-88D2-E50F78011C72}.Release|x64.Build.0 = Release|x64 - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Debug|x64.ActiveCfg = Debug|x64 - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Debug|x64.Build.0 = Debug|x64 - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Release|Any CPU.Build.0 = Release|Any CPU - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Release|x64.ActiveCfg = Release|x64 - {1BDC4681-FDBA-4E55-A247-5F779627A4D7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -207,9 +177,6 @@ Global {63B58D31-66BB-4879-B4F1-0969FA3F4464} = {BB93122C-21C5-4BB8-B385-74FC423D6027} {AE4034AC-27BC-43B8-9176-068B4794E25E} = {BB93122C-21C5-4BB8-B385-74FC423D6027} {ECB3825D-B309-4C76-A20D-76544459F611} = {BB93122C-21C5-4BB8-B385-74FC423D6027} - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF} = {861D6F40-D3CE-46CF-B8AD-0215B9F38BE1} - {E5B52C61-66BE-4D71-88D2-E50F78011C72} = {8B80B63B-5107-4845-9F19-647CFA7911A3} - {1BDC4681-FDBA-4E55-A247-5F779627A4D7} = {BB93122C-21C5-4BB8-B385-74FC423D6027} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8CC7D7F7-CBD9-428B-A190-7A4D56C920FE} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/AmbiguousActorIdHandlerBase.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/AmbiguousActorIdHandlerBase.cs deleted file mode 100644 index 894cb3f9..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/AmbiguousActorIdHandlerBase.cs +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Runtime; - - internal abstract class AmbiguousActorIdHandlerBase : IAmbiguousActorIdHandler - { - private ActorStateProviderHelper stateProviderHelper; - - public AmbiguousActorIdHandlerBase(ActorStateProviderHelper stateProviderHelper) - { - this.stateProviderHelper = stateProviderHelper; - } - - public abstract Task TryResolveActorIdAsync(string stateStorageKey, CancellationToken cancellationToken); - - protected List GetActorIdsToResolve(string actorIdWithUnderscores) - { - var result = new List(); - var tokens = actorIdWithUnderscores.Split('_'); - var currentSearch = string.Empty; - foreach (var token in tokens) - { - currentSearch = currentSearch == string.Empty ? token : $"{currentSearch}_{token}"; - result.Add(currentSearch); - } - - return result; - } - - protected async Task TryResolveActorIdAsync(List search, Func> resolveFunc, CancellationToken cancellationToken) - { - var match = string.Empty; - foreach (var s in search) - { - if (await resolveFunc.Invoke(s, cancellationToken)) - { - if (match == string.Empty) - { - match = s; - } - else - { - return new IAmbiguousActorIdHandler.ConditionalValue - { - HasValue = false, - }; - } - } - } - - return new IAmbiguousActorIdHandler.ConditionalValue - { - HasValue = match != string.Empty, - Value = match, - }; - } - - protected virtual string StripPrefixAndSuffixTokens(string storageKey) - { - if (storageKey.StartsWith($"{ActorIdKind.String.ToString()}_")) - { - var remaining = storageKey.Substring(storageKey.IndexOf('_') + 1); - return remaining.Substring(0, remaining.LastIndexOf('_')); - } - else - { - var remaining = storageKey.Substring(storageKey.IndexOf('_') + 1); - return remaining.Substring(0, remaining.IndexOf('_')); - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CatchupPhaseWorkload.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CatchupPhaseWorkload.cs deleted file mode 100644 index dd62a5fb..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CatchupPhaseWorkload.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Fabric; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class CatchupPhaseWorkload : MigrationPhaseWorkloadBase - { - public CatchupPhaseWorkload( - int curentIteration, - KVStoRCMigrationActorStateProvider stateProvider, - ServicePartitionClient servicePartitionClient, - StatefulServiceContext statefulServiceContext, - MigrationSettings migrationSettings, - ActorTypeInformation actorTypeInfo, - string traceId) - : base(MigrationPhase.Catchup, curentIteration, 1, stateProvider, servicePartitionClient, statefulServiceContext, migrationSettings, actorTypeInfo, traceId) - { - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/AspNetCoreCommunicationListener.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/AspNetCoreCommunicationListener.cs deleted file mode 100644 index 643b83d5..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/AspNetCoreCommunicationListener.cs +++ /dev/null @@ -1,185 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Fabric.Description; - using System.Globalization; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; - using Microsoft.ServiceFabric.Services.Communication.Runtime; - - /// - /// Base class for creating AspNetCore based communication listener for Service Fabric stateless or stateful service. - /// - internal abstract class AspNetCoreCommunicationListener : ICommunicationListener - { - private readonly ServiceContext serviceContext; - private readonly ICommunicationListener internalListener; - private string urlSuffix = null; - private bool configuredToUseUniqueServiceUrl = false; - - /// - /// Initializes a new instance of the class. - /// - /// The context of the service for which this communication listener is being constructed. - /// Delegate to build Microsoft.AspNetCore.Hosting.IWebHost, endpoint url generated by the listener is given as input to this delegate. - /// This gives the flexibility to change the url before creating Microsoft.AspNetCore.Hosting.IWebHost if needed. - public AspNetCoreCommunicationListener(ServiceContext serviceContext, Func build) - { - if (serviceContext == null) - { - throw new ArgumentNullException("serviceContext"); - } - - if (build == null) - { - throw new ArgumentNullException("build"); - } - - this.serviceContext = serviceContext; - this.internalListener = new WebHostCommunicationListener(build, this); - this.urlSuffix = string.Empty; - } - - /// - /// Initializes a new instance of the class. - /// - /// The context of the service for which this communication listener is being constructed. - /// Delegate to build Microsoft.Extensions.Hosting.IHost, endpoint url generated by the listener is given as input to this delegate. - /// This gives the flexibility to change the url before creating Microsoft.Extensions.Hosting.IHost if needed. - public AspNetCoreCommunicationListener(ServiceContext serviceContext, Func build) - { - if (serviceContext == null) - { - throw new ArgumentNullException("serviceContext"); - } - - if (build == null) - { - throw new ArgumentNullException("build"); - } - - this.serviceContext = serviceContext; - this.internalListener = new GenericHostCommunicationListener(build, this); - this.urlSuffix = string.Empty; - } - - /// - /// Gets the context of the service for which this communication listener is being constructed. - /// - public ServiceContext ServiceContext - { - get { return this.serviceContext; } - } - - /// - /// Gets the url suffix to be used based on specified in - /// . - /// - public string UrlSuffix - { - get - { - return this.urlSuffix; - } - } - - /// - /// This method causes the communication listener to close. Close is a terminal state and - /// this method causes the transition to close ungracefully. Any outstanding operations - /// (including close) should be canceled when this method is called. - /// - public virtual void Abort() - { - this.internalListener.Abort(); - } - - /// - /// This method causes the communication listener to close. Close is a terminal state and - /// this method allows the communication listener to transition to this state in a graceful manner. - /// - /// Cancellation token. - /// - /// A Task that represents outstanding operation. - /// - public virtual Task CloseAsync(CancellationToken cancellationToken) - { - return this.internalListener.CloseAsync(cancellationToken); - } - - /// - /// This method causes the communication listener to be opened. Once the Open - /// completes, the communication listener becomes usable - accepts and sends messages. - /// - /// Cancellation token. - /// - /// A Task that represents outstanding operation. The result of the Task is - /// is endpoint string on which IWebHost/IHost is listening. - /// - public virtual Task OpenAsync(CancellationToken cancellationToken) - { - return this.internalListener.OpenAsync(cancellationToken); - } - - /// - /// Configures the listener to use UniqueServiceUrl by appending a urlSuffix PartitionId and ReplicaId. - /// It helps in scenarios when ServiceA listening on a node on port X moves and another Service takes its place on the same node and starts using the same port X, - /// The UniqueServiceUrl in conjunction with middleware rejects requests meant for serviceA arriving at ServiceB. - /// Example: - /// Service A is dynamically assigned port 30000 on node with IP 10.0.0.1, it listens on http://+:30000/ and reports to Naming service http://10.0.0.1:30000/serviceName-A/partitionId-A/replicaId-A - /// Client resolves URL from NS: http://10.0.0.1:30000/serviceName-A/partitionId-A/replicaId-A and sends a request, Service A compares URL path segments to its own service name, partition ID, replica ID, finds they are equal, serves request. - /// Now Service A moves to a different node and Service B comes up at the node with IP 10.0.0.1 and is dynamically assigned port 30000. - /// Service B listens on: http://+:30000/ and reports to NS http://10.0.0.1:30000/serviceName-B/partitionId-B/replicaId-B, Client for Service a sends request to http://10.0.0.1:30000/serviceName-A/partitionId-A/replicaId-A - /// Service B compares URL path segments to its own service name, partition ID, replica ID, finds they do not match, ends the request and responds with HTTP 410. Client receives 410 and re-resolves for service A. - /// - internal void ConfigureToUseUniqueServiceUrl() - { - if (!this.configuredToUseUniqueServiceUrl) - { - this.urlSuffix = string.Format(CultureInfo.InvariantCulture, "/{0}/{1}", this.serviceContext.PartitionId, this.serviceContext.ReplicaOrInstanceId); - - if (this.ServiceContext is StatefulServiceContext) - { - // For stateful service, also append a Guid, Guid makes the url unique in scenarios for stateful services when Listener is - // created to support read on secondary and change role happens from Primary->Secondary for the replica. - this.urlSuffix += "/" + Guid.NewGuid(); - } - - this.configuredToUseUniqueServiceUrl = true; - } - } - - /// - /// Gets url for this listener to be used with Web Server. - /// - /// url for this listener to be used with Web Server. - protected internal abstract string GetListenerUrl(); - - /// - /// Retrieves the endpoint resource with a given name from the service manifest. - /// - /// The name of the endpoint. - /// The endpoint resource with the specified name. - protected EndpointResourceDescription GetEndpointResourceDescription(string endpointName) - { - if (endpointName == null) - { - throw new ArgumentNullException("endpointName"); - } - - if (!this.serviceContext.CodePackageActivationContext.GetEndpoints().Contains(endpointName)) - { - throw new InvalidOperationException(string.Format("{0} not found in Service Manifest.", endpointName)); - } - - return this.serviceContext.CodePackageActivationContext.GetEndpoint(endpointName); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/GenericHostCommunicationListener.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/GenericHostCommunicationListener.cs deleted file mode 100644 index 4c6a4df0..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/GenericHostCommunicationListener.cs +++ /dev/null @@ -1,87 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting.Server; - using Microsoft.AspNetCore.Hosting.Server.Features; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - using Microsoft.ServiceFabric.Services.Communication.Runtime; - - internal class GenericHostCommunicationListener : ICommunicationListener - { - private readonly Func build; - private readonly ServiceContext serviceContext; - private readonly AspNetCoreCommunicationListener listener; - private IHost host; - - public GenericHostCommunicationListener(Func build, AspNetCoreCommunicationListener listener) - { - this.serviceContext = listener.ServiceContext; - this.build = build; - this.listener = listener; - } - - public void Abort() - { - if (this.host != null) - { - this.host.Dispose(); - } - } - - public async Task CloseAsync(CancellationToken cancellationToken) - { - if (this.host != null) - { - await this.host.StopAsync(cancellationToken); - this.host.Dispose(); - } - } - - public async Task OpenAsync(CancellationToken cancellationToken) - { - this.host = this.build(this.listener.GetListenerUrl(), this.listener); - if (this.host == null) - { - throw new InvalidOperationException("IHost returned from build delegate is null."); - } - - await this.host.StartAsync(cancellationToken); - - var server = this.host.Services.GetService(); - if (server == null) - { - throw new InvalidOperationException("No web server found over IHost"); - } - - var url = server.Features.Get().Addresses.FirstOrDefault(); - if (url == null) - { - throw new InvalidOperationException("No Url returned from AspNetCore IServerAddressesFeature."); - } - - var publishAddress = this.serviceContext.PublishAddress; - - if (url.Contains("://+:")) - { - url = url.Replace("://+:", $"://{publishAddress}:"); - } - else if (url.Contains("://[::]:")) - { - url = url.Replace("://[::]:", $"://{publishAddress}:"); - } - - url = url.TrimEnd(new[] { '/' }); - - return url; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/HttpSysCommunicationListener.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/HttpSysCommunicationListener.cs deleted file mode 100644 index e285c0f3..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/HttpSysCommunicationListener.cs +++ /dev/null @@ -1,82 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Globalization; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; - - /// - /// An AspNetCore HttpSys server based communication listener for Service Fabric stateless or stateful service. - /// - internal class HttpSysCommunicationListener : AspNetCoreCommunicationListener - { - private readonly string endpointName; - - /// - /// Initializes a new instance of the class. - /// - /// The context of the service for which this communication listener is being constructed. - /// Name of endpoint resource defined in service manifest that should be used to create the address for listener. - /// Delegate to build Microsoft.AspNetCore.Hosting.IWebHost, endpoint url generated by the listener is given as input to this delegate. - /// This gives the flexibility to change the url before creating Microsoft.AspNetCore.Hosting.IWebHost if needed. - public HttpSysCommunicationListener(ServiceContext serviceContext, string endpointName, Func build) - : base(serviceContext, build) - { - if (string.IsNullOrEmpty(endpointName)) - { - throw new ArgumentException("endpointName cannot be null or empty string."); - } - - this.endpointName = endpointName; - } - - /// - /// Initializes a new instance of the class. - /// - /// The context of the service for which this communication listener is being constructed. - /// Name of endpoint resource defined in service manifest that should be used to create the address for listener. - /// Delegate to build Microsoft.Extensions.Hosting.IHost, endpoint url generated by the listener is given as input to this delegate. - /// This gives the flexibility to change the url before creating Microsoft.Extensions.Hosting.IHost if needed. - public HttpSysCommunicationListener(ServiceContext serviceContext, string endpointName, Func build) - : base(serviceContext, build) - { - if (string.IsNullOrEmpty(endpointName)) - { - throw new ArgumentException("endpointName cannot be null or empty string."); - } - - this.endpointName = endpointName; - } - - /// - /// Gets url for the listener. Listener url is created using the endpointName passed in the constructor. - /// - /// url for the listener. - protected internal override string GetListenerUrl() - { - var serviceEndpoint = this.GetEndpointResourceDescription(this.endpointName); - var listenUrl = string.Format( - CultureInfo.InvariantCulture, - "{0}://+:{1}", - serviceEndpoint.Protocol.ToString().ToLowerInvariant(), - serviceEndpoint.Port); - - // When returning url to naming service, add UrlSuffix to it. - // This UrlSuffix will be used by middleware to: - // - drop calls not intended for the service and return 410. - // - modify Path and PathBase in Microsoft.AspNetCore.Http.HttpRequest to be sent correctly to the service code. - var uniqueUrl = string.Format( - CultureInfo.InvariantCulture, - "{0}{1}", - listenUrl, - this.UrlSuffix.ToLowerInvariant()); - - return uniqueUrl; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/WebHostCommunicationListener.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/WebHostCommunicationListener.cs deleted file mode 100644 index 1e8d601b..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CommunicationListener/WebHostCommunicationListener.cs +++ /dev/null @@ -1,80 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Server.Features; - using Microsoft.ServiceFabric.Services.Communication.Runtime; - - internal class WebHostCommunicationListener : ICommunicationListener - { - private readonly Func build; - private readonly ServiceContext serviceContext; - private readonly AspNetCoreCommunicationListener listener; - private IWebHost host; - - public WebHostCommunicationListener(Func build, AspNetCoreCommunicationListener listener) - { - this.serviceContext = listener.ServiceContext; - this.build = build; - this.listener = listener; - } - - public void Abort() - { - if (this.host != null) - { - this.host.Dispose(); - } - } - - public async Task CloseAsync(CancellationToken cancellationToken) - { - if (this.host != null) - { - await this.host.StopAsync(cancellationToken); - this.host.Dispose(); - } - } - - public async Task OpenAsync(CancellationToken cancellationToken) - { - this.host = this.build(this.listener.GetListenerUrl(), this.listener); - if (this.host == null) - { - throw new InvalidOperationException("IWebHost returned from build delegate is null."); - } - - await this.host.StartAsync(cancellationToken); - - var url = this.host.ServerFeatures.Get().Addresses.FirstOrDefault(); - - if (url == null) - { - throw new InvalidOperationException("No Url returned from AspNetCore IServerAddressesFeature."); - } - - var publishAddress = this.serviceContext.PublishAddress; - - if (url.Contains("://+:")) - { - url = url.Replace("://+:", $"://{publishAddress}:"); - } - else if (url.Contains("://[::]:")) - { - url = url.Replace("://[::]:", $"://{publishAddress}:"); - } - - url = url.TrimEnd(new[] { '/' }); - - return url; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/KvsMigrationController.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/KvsMigrationController.cs deleted file mode 100644 index 3f694512..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/KvsMigrationController.cs +++ /dev/null @@ -1,133 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Controllers -{ - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - - /// - /// Represents the controller class for KVS migration REST API. - /// - [Route("[controller]")] - internal class KvsMigrationController : ControllerBase - { - private KvsActorStateProvider kvsActorStateProvider; - private IMigrationOrchestrator migrationOrchestrator; - private KVSAmbiguousActorIdHandler ambiguousActorIdHandler; - private string traceId; - - /// - /// Initializes a new instance of the class. - /// - /// Source migraiton orchestrator - public KvsMigrationController(IMigrationOrchestrator migrationOrchestrator) - { - this.migrationOrchestrator = migrationOrchestrator; - this.kvsActorStateProvider = (KvsActorStateProvider)this.migrationOrchestrator.GetMigrationActorStateProvider(); - this.traceId = ((MigrationOrchestratorBase)this.migrationOrchestrator).TraceId; - this.ambiguousActorIdHandler = new KVSAmbiguousActorIdHandler(this.kvsActorStateProvider); - } - - /// - /// Gets the First Sequence number of KVS - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpGet("GetFirstSequenceNumber")] - public async Task GetFirstSequenceNumberAsync(CancellationToken cancellationToken) - { - return await MigrationUtility.ExecuteWithRetriesAsync( - () => this.kvsActorStateProvider.GetFirstSequenceNumberAsync(this.traceId, cancellationToken), - this.traceId, - $"{this.GetType().Name}.GetFirstSequenceNumberAsync"); - } - - /// - /// Gets the Last Sequence number of KVS - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpGet("GetLastSequenceNumber")] - public Task GetLastSequenceNumberAsync(CancellationToken cancellationToken) - { - return MigrationUtility.ExecuteWithRetriesAsync( - () => this.kvsActorStateProvider.GetLastSequenceNumberAsync(this.traceId, cancellationToken), - this.traceId, - $"{this.GetType().Name}.GetLastSequenceNumber"); - } - - /// - /// Enumerates Key value store data by Sequence Number - /// - /// EnumerationRequest - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpGet("EnumerateBySequenceNumber")] - public async Task EnumerateBySequenceNumberAsync([FromBody] EnumerationRequest request, CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.kvsActorStateProvider.EnumerateAsync(request, this.Response, this.traceId, cancellationToken), - this.traceId, - $"{this.GetType().Name}.EnumerateAsync"); - } - - /// - /// Gets DisableTombstoneCleanup Setting value if KVSReplica - /// - /// A representing the result of the asynchronous operation. - [HttpGet("IsTombstoneCleanupDisabled")] - public bool IsTombstoneCleanupDisabled() - { - return this.kvsActorStateProvider.IsTombstoneCleanupDisabled(); - } - - /// - /// Starts the Downtime phase on the current partition. In the downtime phase all the actor calls are actively rejected with MigrationException. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the asynchronous operation. - [HttpPut("StartDowntime")] - public async Task StartDowntimeAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.StartDowntimeAsync(false, cancellationToken), - this.traceId, - $"{this.GetType().Name}.StartDowntimeAsync"); - } - - /// - /// Aborts the Actor state migration on the current partition. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpPut("AbortMigration")] - public async Task AbortMigrationAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.AbortMigrationAsync(false, cancellationToken), - this.traceId, - $"{this.GetType().Name}.AbortMigrationAsync"); - } - - /// - /// Starts the Actor state migration on the current partition. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpPut("StartMigration")] - public async Task StartMigrationAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.StartMigrationAsync(false, cancellationToken), - this.traceId, - $"{this.GetType().Name}.StartMigrationAsync"); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/MigrationControllerFeatureProvider.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/MigrationControllerFeatureProvider.cs deleted file mode 100644 index 0987b445..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/MigrationControllerFeatureProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Controllers -{ - using System.Reflection; - using Microsoft.AspNetCore.Mvc.Controllers; - - /// - /// MigrationControllerFeatureProvider class to override IsController to be able - /// to load internal migration controllers - /// - internal class MigrationControllerFeatureProvider : ControllerFeatureProvider - { - protected override bool IsController(TypeInfo typeInfo) - { - var isMigrationController = !typeInfo.IsAbstract - && (typeof(RcMigrationController).IsAssignableFrom(typeInfo) - || typeof(KvsMigrationController).IsAssignableFrom(typeInfo)); - return isMigrationController || base.IsController(typeInfo); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/RcMigrationController.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/RcMigrationController.cs deleted file mode 100644 index ce4e2270..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Controllers/RcMigrationController.cs +++ /dev/null @@ -1,90 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Controllers -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - - /// - /// Represents the controller class for KVS migration REST API. - /// - [Route("[controller]")] - internal class RcMigrationController : ControllerBase - { - private IMigrationOrchestrator migrationOrchestrator; - private string traceId; - - /// - /// Initializes a new instance of the class. - /// - /// Target Migration orchestrator - public RcMigrationController(IMigrationOrchestrator migrationOrchestrator) - { - this.migrationOrchestrator = migrationOrchestrator; - this.traceId = ((MigrationOrchestratorBase)this.migrationOrchestrator).TraceId; - } - - /// - /// Gets migration status - /// - /// Cancellation Token - /// A representing the result of the asynchronous operation. - [HttpGet("GetMigrationStatus")] - public async Task GetMigrationStatusAsync(CancellationToken cancellationToken) - { - return await MigrationUtility.ExecuteWithRetriesAsync( - () => ((TargetMigrationOrchestrator)this.migrationOrchestrator).GetResultAsync(cancellationToken), - this.traceId, - "RcMigrationController.GetMigrationStatusAsync"); - } - - /// - /// Starts the Downtime phase on the current partition. In the downtime phase all the actor calls are actively rejected with MigrationException. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the asynchronous operation. - [HttpPut("StartDowntime")] - public async Task StartDowntimeAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.StartDowntimeAsync(true, cancellationToken), - this.traceId, - $"{this.GetType().Name}.StartDowntimeAsync"); - } - - /// - /// Aborts the Actor state migration on the current partition. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpPut("AbortMigration")] - public async Task AbortMigrationAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.AbortMigrationAsync(true, cancellationToken), - this.traceId, - $"{this.GetType().Name}.AbortMigrationAsync"); - } - - /// - /// Starts the Actor state migration on the current partition. - /// - /// Token to signal cancellation on the asynchronous operation - /// A representing the result of the asynchronous operation. - [HttpPut("StartMigration")] - public async Task StartMigrationAsync(CancellationToken cancellationToken) - { - await MigrationUtility.ExecuteWithRetriesAsync( - () => this.migrationOrchestrator.StartMigrationAsync(true, cancellationToken), - this.traceId, - $"{this.GetType().Name}.StartMigrationAsync"); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CopyPhaseWorkload.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CopyPhaseWorkload.cs deleted file mode 100644 index d706edaf..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/CopyPhaseWorkload.cs +++ /dev/null @@ -1,65 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Fabric; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Extensions; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services.Communication.Client; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationUtility; - - internal class CopyPhaseWorkload : MigrationPhaseWorkloadBase - { - private static readonly string TraceType = typeof(CopyPhaseWorkload).Name; - - public CopyPhaseWorkload( - KVStoRCMigrationActorStateProvider stateProvider, - ServicePartitionClient servicePartitionClient, - StatefulServiceContext statefulServiceContext, - MigrationSettings migrationSettings, - ActorTypeInformation actorTypeInfo, - string traceId) - : base(MigrationPhase.Copy, 1, migrationSettings.CopyPhaseParallelism, stateProvider, servicePartitionClient, statefulServiceContext, migrationSettings, actorTypeInfo, traceId) - { - } - - protected override async Task GetStartSequenceNumberAsync(CancellationToken cancellationToken) - { - long startSequenceNumber; - using (var tx = this.Transaction) - { - var cond = await this.MetaDataDictionary.TryGetValueAsync( - tx, - MigrationLastAppliedSeqNum, - DefaultRCTimeout, - cancellationToken); - await tx.CommitAsync(); - - if (cond.HasValue) - { - startSequenceNumber = ParseLong(cond.Value, this.TraceId); - } - else - { - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.GetAsync($"{KVSMigrationControllerName}/{GetStartSNEndpoint}"); - }, - "GetStartSequenceNumberAsync", - cancellationToken); - return (await ParseLongAsync(async () => await response.Content.ReadAsStringAsync(), this.TraceId)).Value; - } - } - - return startSequenceNumber; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/DowntimeWorkload.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/DowntimeWorkload.cs deleted file mode 100644 index 12a55243..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/DowntimeWorkload.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Fabric; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class DowntimeWorkload : MigrationPhaseWorkloadBase - { - public DowntimeWorkload( - KVStoRCMigrationActorStateProvider stateProvider, - ServicePartitionClient servicePartitionClient, - StatefulServiceContext statefulServiceContext, - MigrationSettings migrationSettings, - ActorTypeInformation actorTypeInfo, - string traceId) - : base(MigrationPhase.Downtime, 1, 1, stateProvider, servicePartitionClient, statefulServiceContext, migrationSettings, actorTypeInfo, traceId) - { - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ApplicationBuilderExtensions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ApplicationBuilderExtensions.cs deleted file mode 100644 index e15f5f36..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ApplicationBuilderExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using Microsoft.AspNetCore.Builder; - - /// - /// Class containing extension methods for IApplicationBuilder. - /// - internal static class ApplicationBuilderExtensions - { - /// - /// Extension method to use ServiceFabricMiddleware for Service Fabric stateful or stateless service - /// using Kestrel or HttpSys as WebServer. - /// - /// Microsoft.AspNetCore.Builder.IApplicationBuilder. - /// Url suffix to determine if the request is meant for current partition and replica. - /// IApplicationBuilder instance. - public static IApplicationBuilder UseServiceFabricMiddleware(this IApplicationBuilder builder, string urlSuffix) - { - if (builder == null) - { - throw new ArgumentNullException("builder"); - } - - if (urlSuffix == null) - { - throw new ArgumentNullException("urlSuffix"); - } - - return builder.UseMiddleware(urlSuffix); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/KvsActorStateProviderExtensions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/KvsActorStateProviderExtensions.cs deleted file mode 100644 index f7ee9733..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/KvsActorStateProviderExtensions.cs +++ /dev/null @@ -1,337 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Globalization; - using System.Runtime.Serialization.Json; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - - internal static class KvsActorStateProviderExtensions - { - public static readonly string TombstoneCleanupMessage = "KeyValueStoreReplicaSettings.DisableTombstoneCleanup is either not enabled or set to false"; - public static readonly DataContractJsonSerializer ResponseSerializer = new DataContractJsonSerializer(typeof(EnumerationResponse), new[] { typeof(List) }); - private static readonly string TraceType = typeof(KvsActorStateProviderExtensions).Name; - - internal static async Task GetFirstSequenceNumberAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - var storeReplica = stateProvider.GetStoreReplica(); - var lsn = storeReplica.GetLastCommittedSequenceNumber(); - return await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - () => - { - using (var txn = storeReplica.CreateTransaction()) - { - var enumerator = storeReplica.EnumerateBySequenceNumber(txn, 0); - var hasData = enumerator.MoveNext(); - - while (hasData) - { - cancellationToken.ThrowIfCancellationRequested(); - if (enumerator.Current.Metadata.ValueSizeInBytes > 0) - { - return Task.FromResult(enumerator.Current.Metadata.SequenceNumber); - } - - hasData = enumerator.MoveNext(); - } - } - - return Task.FromResult(lsn); - }, - "GetFirstSequeceNumberAsync", - cancellationToken); - } - - internal static async Task GetLastSequenceNumberAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - cancellationToken.ThrowIfCancellationRequested(); - return await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - return await Task.Run(() => - { - return stateProvider.GetStoreReplica().GetLastCommittedSequenceNumber(); - }); - }, - "GetLastSequenceNumber", - cancellationToken); - } - - internal static Task EnumerateAsync(this KvsActorStateProvider stateProvider, EnumerationRequest request, HttpResponse response, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - var storeReplica = stateProvider.GetStoreReplica(); - var lsn = storeReplica.GetLastCommittedSequenceNumber(); - return stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - try - { - bool hasData; - bool endSequenceNumberReached = false; - - using (var txn = storeReplica.CreateTransaction()) - { - IEnumerator enumerator; - - if (request.IncludeDeletes) - { - enumerator = storeReplica.EnumerateKeysAndTombstonesBySequenceNumber(txn, request.StartSequenceNumber); - } - else - { - enumerator = storeReplica.EnumerateBySequenceNumber(txn, request.StartSequenceNumber); - } - - hasData = enumerator.MoveNext(); - int chunk = 1; - while (hasData - && chunk <= request.NumberOfChunksPerEnumeration - && !endSequenceNumberReached) - { - cancellationToken.ThrowIfCancellationRequested(); - var pairs = new List(); - var valuePairs = new List(); - var sequenceNumberFullyDrained = true; - long? firstSNInChunk = null; - long? endSNInChunk = null; - - while (hasData - && !endSequenceNumberReached - && (pairs.Count < request.ChunkSize || !sequenceNumberFullyDrained)) - { - cancellationToken.ThrowIfCancellationRequested(); - if (firstSNInChunk == null) - { - firstSNInChunk = enumerator.Current.Metadata.SequenceNumber; - } - - var keyValuePair = await MakeKeyValuePairAsync(stateProvider, enumerator.Current, request, cancellationToken); - if (request.ComputeHash - && !keyValuePair.IsDeleted - && !MigrationUtility.IgnoreKey(keyValuePair.Key)) - { - valuePairs.Add(keyValuePair.Value); - } - - var currentSequenceNumber = keyValuePair.Version; - pairs.Add(keyValuePair); - hasData = enumerator.MoveNext(); - - if (hasData) - { - var nextKeyValuePair = enumerator.Current; - var nextKeySequenceNumber = nextKeyValuePair.Metadata.SequenceNumber; - endSequenceNumberReached = nextKeySequenceNumber > request.EndSequenceNumber; - sequenceNumberFullyDrained = !(nextKeySequenceNumber == currentSequenceNumber); - //// TODO : sequenceNumberFullyDrained?? - } - - endSNInChunk = currentSequenceNumber; - } - - var computedHash = string.Empty; - if (request.ComputeHash) - { - computedHash = CRC64.ToCRC64(valuePairs.ToArray()).ToString("X", CultureInfo.InvariantCulture); - } - - await WriteKeyValuePairsToResponseAsync( - new EnumerationResponse - { - KeyValuePairs = pairs, - EndSequenceNumberReached = endSequenceNumberReached, - ResolveActorIdsForStateKVPairs = request.ResolveActorIdsForStateKVPairs, - ValueHash = computedHash, - }, - response); - ++chunk; - } - } - } - catch (Exception e) - { - ActorTrace.Source.WriteErrorWithId(TraceType, traceId, $"{e.Message} {e.StackTrace}"); - if (e.InnerException != null) - { - ActorTrace.Source.WriteErrorWithId(TraceType, traceId, $"{e.InnerException.Message} {e.InnerException.StackTrace}/n"); - } - } - }, - "EnumerateAsync", - cancellationToken); - } - - internal static async Task TryAbortExistingTransactionsAndRejectWritesAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - cancellationToken.ThrowIfCancellationRequested(); - return await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - return await Task.Run(() => - { - return stateProvider.GetStoreReplica().TryAbortExistingTransactionsAndRejectWrites(); - }); - }, - "TryAbortExistingTransactionsAndRejectWrites", - cancellationToken); - } - - internal static async Task RejectWritesAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - cancellationToken.ThrowIfCancellationRequested(); - await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - using (var tx = stateProvider.GetStoreReplica().CreateTransaction()) - { - if (stateProvider.GetStoreReplica().TryGet(tx, RejectWritesKey) != null) - { - stateProvider.GetStoreReplica().TryUpdate(tx, RejectWritesKey, BitConverter.GetBytes(true)); - } - else - { - stateProvider.GetStoreReplica().TryAdd(tx, RejectWritesKey, BitConverter.GetBytes(true)); - } - - await tx.CommitAsync(); - } - }, - "RejectWritesAsync", - cancellationToken); - - await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - () => - { - if (!stateProvider.GetStoreReplica().TryAbortExistingTransactionsAndRejectWrites()) - { - throw new FabricTransientException("Unable to abort exiting transactions."); - } - - return Task.CompletedTask; - }, - "TryAbortExistingTransactionsAndRejectWrites", - cancellationToken); - } - - internal static async Task ResumeWritesAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - stateProvider.ThrowIfTombCleanupIsNotEnabled(); - cancellationToken.ThrowIfCancellationRequested(); - await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - using (var tx = stateProvider.GetStoreReplica().CreateTransaction()) - { - if (stateProvider.GetStoreReplica().TryGet(tx, RejectWritesKey) != null) - { - stateProvider.GetStoreReplica().TryUpdate(tx, RejectWritesKey, BitConverter.GetBytes(false)); - } - else - { - stateProvider.GetStoreReplica().TryAdd(tx, RejectWritesKey, BitConverter.GetBytes(false)); - } - - await tx.CommitAsync(); - } - }, - "ResumeWritesAsync", - cancellationToken); - } - - internal static async Task GetRejectWriteStateAsync(this KvsActorStateProvider stateProvider, string traceId, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - return await stateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - return await Task.Run(() => - { - using (var tx = stateProvider.GetStoreReplica().CreateTransaction()) - { - var result = stateProvider.GetStoreReplica().TryGet(tx, RejectWritesKey); - - if (result != null) - { - return BitConverter.ToBoolean(result.Value, 0); - } - } - - return false; - }); - }, - "GetRejectWriteStateAsync", - cancellationToken); - } - - internal static bool IsTombstoneCleanupDisabled(this KvsActorStateProvider stateProvider) - { - return stateProvider.GetStoreReplica().KeyValueStoreReplicaSettings.DisableTombstoneCleanup; - } - - private static async Task WriteKeyValuePairsToResponseAsync(EnumerationResponse enumerationResponse, HttpResponse httpResponse) - { - var byteArray = SerializationUtility.Serialize(ResponseSerializer, enumerationResponse); - var newLine = Encoding.UTF8.GetBytes("\n"); - - ActorTrace.Source.WriteNoise(TraceType, $"ByteArray: {byteArray} ArrayLength: {byteArray.Length}"); - - if (string.IsNullOrEmpty(httpResponse.ContentType)) - { - // Set the content type - httpResponse.ContentType = "application/json; charset=utf-8"; - } - - await httpResponse.Body.WriteAsync(byteArray, 0, byteArray.Length); - await httpResponse.Body.WriteAsync(newLine, 0, newLine.Length); - await httpResponse.Body.FlushAsync(); - } - - private static async Task MakeKeyValuePairAsync(this KvsActorStateProvider stateProvider, KeyValueStoreItem item, EnumerationRequest request, CancellationToken cancellationToken) - { - bool isDeleted = item.Metadata.ValueSizeInBytes < 0; - var result = new KeyValuePair - { - IsDeleted = isDeleted, - Version = item.Metadata.SequenceNumber, - Key = item.Metadata.Key, - Value = isDeleted ? new byte[0] : item.Value, - }; - - var ambiguousActoIdHandler = new KVSAmbiguousActorIdHandler(stateProvider); - if (request.ResolveActorIdsForStateKVPairs && item.Metadata.Key.StartsWith("Actor_")) - { - var cv = await ambiguousActoIdHandler.TryResolveActorIdAsync(item.Metadata.Key, cancellationToken); - result.ActorId = cv.HasValue ? cv.Value : null; - } - - return result; - } - - private static void ThrowIfTombCleanupIsNotEnabled(this KvsActorStateProvider stateProvider) - { - if (!stateProvider.IsTombstoneCleanupDisabled()) - { - throw new InvalidMigrationConfigException(TombstoneCleanupMessage); - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ServiceParitionClientExtensions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ServiceParitionClientExtensions.cs deleted file mode 100644 index ef7d78b5..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/ServiceParitionClientExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Extensions -{ - using System; - using System.Fabric; - using System.Net.Http; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Json; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal static class ServiceParitionClientExtensions - { - private static readonly DataContractJsonSerializer ErrorSerializer = new DataContractJsonSerializer(typeof(ErrorResponse)); - - public static async Task InvokeWebRequestWithRetryAsync( - this ServicePartitionClient partitionClient, - Func> asyncFunc, - string funcTag, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - return await partitionClient.InvokeWithRetryAsync( - async client => - { - var response = await asyncFunc.Invoke(client); - await ThrowIfErrorResponseAsync(response); - - return response; - }, - cancellationToken); - } - - private static async Task ThrowIfErrorResponseAsync(HttpResponseMessage response) - { - if (!response.IsSuccessStatusCode) - { - var buffer = await response.Content.ReadAsByteArrayAsync(); - var error = SerializationUtility.Deserialize(ErrorSerializer, buffer); - Exception ex = null; - if (error.IsFabricError) - { - ex = new FabricException(error.Message, error.ErrorCode); - } - else - { - ex = new Exception(error.Message); - } - - ex.Data.Add("ActualExceptionType", error.ExceptionType); - ex.Data.Add("IsErrorAtSource", true); - throw ex; - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/WebHostBuilderServiceFabricExtensions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/WebHostBuilderServiceFabricExtensions.cs deleted file mode 100644 index f6298eb1..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Extensions/WebHostBuilderServiceFabricExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.DependencyInjection; - - /// - /// Class containing Service Fabric related extension methods for Microsoft.AspNetCore.Hosting.IWebHostBuilder. - /// - internal static class WebHostBuilderServiceFabricExtensions - { - private static readonly string SettingName = "UseServiceFabricIntegration"; - - /// - /// Configures the Service to use ServiceFabricMiddleware and tells the listener that middleware is configured for the service so that it can - /// suffix PartitionId and ReplicaOrInstanceId to url before providing it to Service Fabric Runtime. - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. - /// The to configure. - /// Options to configure ServiceFabricMiddleware and AspNetCoreCommunicationListener. - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. - public static IWebHostBuilder UseServiceFabricIntegration(this IWebHostBuilder hostBuilder, AspNetCoreCommunicationListener listener, ServiceFabricIntegrationOptions options) - { - if (hostBuilder == null) - { - throw new ArgumentNullException("hostBuilder"); - } - - // Check if 'UseServiceFabricIntegration' has already been called. - if (hostBuilder.GetSetting(SettingName) == true.ToString()) - { - return hostBuilder; - } - - // Set flag to prevent double service configuration - hostBuilder.UseSetting(SettingName, true.ToString()); - - // Configure listener to use PartitionId and ReplicaId as urlSuffix only when specified in options. - if (options.HasFlag(ServiceFabricIntegrationOptions.UseUniqueServiceUrl)) - { - // notify listener to use urlSuffix when giving url to Service Fabric Runtime from OpenAsync() - listener.ConfigureToUseUniqueServiceUrl(); - } - - hostBuilder.ConfigureServices(services => - { - // Configure MiddleWare - services.AddSingleton(new ServiceFabricSetupFilter(listener.UrlSuffix, options)); - }); - - return hostBuilder; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Friend.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Friend.cs deleted file mode 100644 index 011cc64b..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Friend.cs +++ /dev/null @@ -1,18 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("FabActUtil,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("Microsoft.ServiceFabric.Actors,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("ActorMigrationTestKvsActorService,PublicKey=00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0")] -[assembly: InternalsVisibleTo("ActorMigrationTestRcActorService,PublicKey=00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0")] -[assembly: InternalsVisibleTo("ActorMigrationTestKvsActorService,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("ActorMigrationTestRcActorService,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("FabActTest.ClientWorkload, PublicKey=00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0")] -[assembly: InternalsVisibleTo("FabActTest.ClientWorkload, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("Microsoft.ServiceFabric.Actors.StateMigration.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("Microsoft.ServiceFabric.Actors.StateMigration.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003f8c902c8fe7ac83af7401b14c1bd103973b26dfafb2b77eda478a2539b979b56ce47f36336741b4ec52bbc51fecd51ba23810cec47070f3e29a2261a2d1d08e4b2b4b457beaa91460055f78cc89f21cd028377af0cc5e6c04699b6856a1e49d5fad3ef16d3c3d6010f40df0a7d6cc2ee11744b5cfb42e0f19a52b8a29dc31b0")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClient.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClient.cs deleted file mode 100644 index 3d470d05..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClient.cs +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Net.Http; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class HttpCommunicationClient : ICommunicationClient - { - private Uri endpointUri; - private HttpClient httpClient; - - public HttpCommunicationClient(string address) - { - this.endpointUri = new Uri(address.EndsWith("/") ? address : $"{address}/"); -#if !DotNetCoreClr - var handler = new WinHttpHandler(); - this.httpClient = new HttpClient(handler) - { - BaseAddress = this.endpointUri, - }; -#endif -#if DotNetCoreClr - this.httpClient = new HttpClient() - { - BaseAddress = this.endpointUri, - }; -#endif - } - - public HttpCommunicationClient(HttpClient httpClient) - { - this.endpointUri = httpClient.BaseAddress; - this.httpClient = httpClient; - } - - public Uri EndpointUri { get => this.endpointUri; } - - public HttpClient HttpClient { get => this.httpClient; } - - public ResolvedServicePartition ResolvedServicePartition { get; set; } - - public string ListenerName { get; set; } - - public ResolvedServiceEndpoint Endpoint { get; set; } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClientFactory.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClientFactory.cs deleted file mode 100644 index 24275cc0..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpCommunicationClientFactory.cs +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Services.Client; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class HttpCommunicationClientFactory : CommunicationClientFactoryBase - { - public HttpCommunicationClientFactory( - IServicePartitionResolver servicePartitionResolver = null, - IEnumerable exceptionHandlers = null, - string traceId = null) - : base(servicePartitionResolver, exceptionHandlers, traceId) - { - } - - protected override void AbortClient(HttpCommunicationClient client) - { - // Do nothing - } - - protected override Task CreateClientAsync(string endpoint, CancellationToken cancellationToken) - { - return Task.FromResult(new HttpCommunicationClient(endpoint)); - } - - protected override bool ValidateClient(HttpCommunicationClient client) - { - // Do nothing - return true; - } - - protected override bool ValidateClient(string endpoint, HttpCommunicationClient client) - { - // Do nothing - return true; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpExceptionHandler.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpExceptionHandler.cs deleted file mode 100644 index da4afd20..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/HttpExceptionHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Fabric; - using System.Net.Http; - using System.Net.Sockets; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class HttpExceptionHandler : Services.Communication.Client.IExceptionHandler - { - public bool TryHandleException(ExceptionInformation exceptionInformation, OperationRetrySettings retrySettings, out ExceptionHandlingResult result) - { - ExceptionHandlingResult tempResult = new ExceptionHandlingThrowResult - { - ExceptionToThrow = exceptionInformation.Exception, - }; - - bool handled = false; - if (exceptionInformation.Exception is FabricException) - { - var fabricEx = exceptionInformation.Exception as FabricException; - switch (fabricEx.ErrorCode) - { - case FabricErrorCode.NotPrimary: - case FabricErrorCode.NotReadable: - case FabricErrorCode.FabricEndpointNotFound: - tempResult = new ExceptionHandlingRetryResult(fabricEx, false, retrySettings, retrySettings.DefaultMaxRetryCountForTransientErrors); - handled = true; - break; - default: - handled = false; - break; - } - - if (fabricEx is FabricTransientException - || (fabricEx.Data.Contains("ActualExceptionType") && (string)fabricEx.Data["ActualExceptionType"] == typeof(FabricTransientException).FullName)) - { - switch (fabricEx.ErrorCode) - { - case FabricErrorCode.DatabaseMigrationInProgress: - handled = true; - new ExceptionHandlingThrowResult { ExceptionToThrow = fabricEx }; - break; - default: - new ExceptionHandlingRetryResult(fabricEx, true, retrySettings, retrySettings.DefaultMaxRetryCountForNonTransientErrors); - handled = true; - break; - } - } - } - else if (exceptionInformation.Exception is HttpRequestException) - { - if (exceptionInformation.Exception.InnerException != null && exceptionInformation.Exception.InnerException is SocketException) - { - if (((SocketException)exceptionInformation.Exception.InnerException).SocketErrorCode == SocketError.ConnectionRefused) - { - tempResult = new ExceptionHandlingRetryResult(exceptionInformation.Exception, false, retrySettings, retrySettings.DefaultMaxRetryCountForNonTransientErrors); - handled = true; - } - } - } - - result = tempResult; - return handled; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IAmbiguousActorIdHandler.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IAmbiguousActorIdHandler.cs deleted file mode 100644 index fedbfa72..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IAmbiguousActorIdHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Threading; - using System.Threading.Tasks; - - /// - /// ActorId Handler. - /// - public interface IAmbiguousActorIdHandler - { - /// - /// Resolves actor id for a give storage key. - /// - /// State storage key. - /// Token to cancel asynchronous operation. - /// Conditional value with resolved actor id. - public Task TryResolveActorIdAsync(string stateStorageKey, CancellationToken cancellationToken); - - /// - /// Condtional Value. - /// - public class ConditionalValue - { - /// - /// Gets or sets the Value. - /// - public string Value { get; set; } - - /// - /// Gets or sets a value indicating whether the ConditionalValue.Value exists. - /// - public bool HasValue { get; set; } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IMigrationPhaseWorkload.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IMigrationPhaseWorkload.cs deleted file mode 100644 index 5b9dbd9d..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IMigrationPhaseWorkload.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Migration; - - /// - /// Interface definition for migration workload by phase. - /// - internal interface IMigrationPhaseWorkload - { - /// - /// Gets the current migration phase. - /// - MigrationPhase Phase { get; } - - /// - /// Starts a new phase or resumes the current migration phase after failover. - /// - /// Token to cancel the task. - /// Returns the migration result. - Task StartOrResumeMigrationAsync(CancellationToken cancellationToken); - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IWorker.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IWorker.cs deleted file mode 100644 index 0f0dbd3e..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/IWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Threading; - using System.Threading.Tasks; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.PhaseInput; - using static Microsoft.ServiceFabric.Actors.Migration.PhaseResult; - - /// - /// Interface definition for workers. - /// - internal interface IWorker - { - /// - /// Gets the worker input information. - /// - public WorkerInput Input { get; } - - /// - /// Starts a new worker. - /// - /// Token to cancel the task. - /// Returns the worker result. - Task StartWorkAsync(CancellationToken cancellationToken); - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVSAmbiguousActorIdHandler.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVSAmbiguousActorIdHandler.cs deleted file mode 100644 index 862f378d..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVSAmbiguousActorIdHandler.cs +++ /dev/null @@ -1,64 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Runtime; - - internal class KVSAmbiguousActorIdHandler : AmbiguousActorIdHandlerBase - { - private const string ActorStorageKeyPrefix = "Actor"; - private KvsActorStateProvider stateProvider; - - public KVSAmbiguousActorIdHandler(KvsActorStateProvider stateProvider) - : base(stateProvider.GetActorStateProviderHelper()) - { - this.stateProvider = stateProvider; - } - - public override async Task TryResolveActorIdAsync(string stateStorageKey, CancellationToken cancellationToken) - { - if (!stateStorageKey.StartsWith($"{ActorStorageKeyPrefix}_")) - { - return new IAmbiguousActorIdHandler.ConditionalValue - { - HasValue = false, - }; - } - - var actorIdMatch = this.StripPrefixAndSuffixTokens(stateStorageKey); - var matchList = this.GetActorIdsToResolve(actorIdMatch); - if (matchList.Count == 1) - { - return new IAmbiguousActorIdHandler.ConditionalValue - { - HasValue = true, - Value = matchList[0], - }; - } - - return await this.TryResolveActorIdAsync( - matchList, - (match, _) => - { - using (var txn = this.stateProvider.GetStoreReplica().CreateTransaction()) - { - var contains = this.stateProvider.GetStoreReplica().Contains(txn, $"@@_String_{match}"); - - return Task.FromResult(contains); - } - }, - cancellationToken); - } - - protected override string StripPrefixAndSuffixTokens(string stateStorageKey) - { - var remaining = stateStorageKey.Substring(stateStorageKey.IndexOf('_') + 1); - return base.StripPrefixAndSuffixTokens(remaining); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVStoRCMigrationActorStateProvider.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVStoRCMigrationActorStateProvider.cs deleted file mode 100644 index 25d3ded6..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/KVStoRCMigrationActorStateProvider.cs +++ /dev/null @@ -1,698 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Globalization; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Query; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Data; - using Microsoft.ServiceFabric.Data.Collections; - using Microsoft.ServiceFabric.Services; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - - /// - /// Provides an implementation of which - /// uses to store and persist the actor state. - /// - internal class KVStoRCMigrationActorStateProvider : - IActorStateProvider, VolatileLogicalTimeManager.ISnapshotHandler, IActorStateProviderInternal - { - private string traceId; - private IReliableCollectionsActorStateProviderInternal rcStateProvider; - private IStatefulServicePartition servicePartition; - private IReliableDictionary2 metadataDictionary; - private bool isMetadataDictInitialized = false; - private Task stateProviderInitTask; - private StatefulServiceInitializationParameters initParams; - private CancellationTokenSource stateProviderInitCts; - private RCAmbiguousActorIdHandler ambiguousActorIdHandler; - - /// - /// Initializes a new instance of the class. - /// - public KVStoRCMigrationActorStateProvider() - : this(new ReliableCollectionsActorStateProvider()) - { - } - - /// - /// Initializes a new instance of the class with specified reliableCollectionsActorStateProvider - /// - /// - /// The that carries out regular operations of state provider. - /// - public KVStoRCMigrationActorStateProvider(IReliableCollectionsActorStateProviderInternal reliableCollectionsActorStateProvider) - { - this.rcStateProvider = reliableCollectionsActorStateProvider; - this.ambiguousActorIdHandler = new RCAmbiguousActorIdHandler(this.rcStateProvider); - } - - /// - public Func OnRestoreCompletedAsync { set => ((IStateProviderReplica2)this.rcStateProvider).OnRestoreCompletedAsync = value; } - - /// - public Func> OnDataLossAsync { set => ((IStateProviderReplica)this.rcStateProvider).OnDataLossAsync = value; } - - /// - public string TraceType - { - get { return "KVStoRCMigrationActorStateProvider"; } - } - - /// - public string TraceId { get => ((IActorStateProviderInternal)this.rcStateProvider).TraceId; } - - /// - public ReplicaRole CurrentReplicaRole { get => ((IActorStateProviderInternal)this.rcStateProvider).CurrentReplicaRole; } - - /// - public TimeSpan TransientErrorRetryDelay { get => ((IActorStateProviderInternal)this.rcStateProvider).TransientErrorRetryDelay; } - - /// - public TimeSpan OperationTimeout { get => ((IActorStateProviderInternal)this.rcStateProvider).OperationTimeout; } - - /// - public TimeSpan CurrentLogicalTime { get => ((IActorStateProviderInternal)this.rcStateProvider).CurrentLogicalTime; } - - /// - public long RoleChangeTracker { get => ((IActorStateProviderInternal)this.rcStateProvider).RoleChangeTracker; } - - internal IStatefulServicePartition StatefulServicePartition { get => this.servicePartition; } - - /// - public void Initialize(ActorTypeInformation actorTypeInformation) - { - ((IActorStateProvider)this.rcStateProvider).Initialize(actorTypeInformation); - } - - /// - public Task ActorActivatedAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).ActorActivatedAsync(actorId, cancellationToken); - } - - /// - public Task ReminderCallbackCompletedAsync(ActorId actorId, IActorReminder reminder, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).ReminderCallbackCompletedAsync(actorId, reminder, cancellationToken); - } - - /// - public Task LoadStateAsync(ActorId actorId, string stateName, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).LoadStateAsync(actorId, stateName, cancellationToken); - } - - /// - public Task SaveStateAsync(ActorId actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).SaveStateAsync(actorId, stateChanges, cancellationToken); - } - - /// - public Task ContainsStateAsync(ActorId actorId, string stateName, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).ContainsStateAsync(actorId, stateName, cancellationToken); - } - - /// - public Task RemoveActorAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).RemoveActorAsync(actorId, cancellationToken); - } - - /// - public Task> EnumerateStateNamesAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).EnumerateStateNamesAsync(actorId, cancellationToken); - } - - /// - public Task> GetActorsAsync(int numItemsToReturn, ContinuationToken continuationToken, CancellationToken cancellationToken) - { - return ((IActorStateProvider)this.rcStateProvider).GetActorsAsync(numItemsToReturn, continuationToken, cancellationToken); - } - - /// - public Task>>> GetRemindersAsync(int numItemsToReturn, ActorId actorId, ContinuationToken continuationToken, CancellationToken cancellationToken) - { - return ((IActorStateProvider)this.rcStateProvider).GetRemindersAsync(numItemsToReturn, actorId, continuationToken, cancellationToken); - } - - /// - public Task SaveReminderAsync(ActorId actorId, IActorReminder reminder, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).SaveReminderAsync(actorId, reminder, cancellationToken); - } - - /// - public Task DeleteReminderAsync(ActorId actorId, string reminderName, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).DeleteReminderAsync(actorId, reminderName, cancellationToken); - } - - /// - public Task DeleteRemindersAsync(IReadOnlyDictionary> reminderNames, CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).DeleteRemindersAsync(reminderNames, cancellationToken); - } - - /// - public Task LoadRemindersAsync(CancellationToken cancellationToken = default) - { - return ((IActorStateProvider)this.rcStateProvider).LoadRemindersAsync(cancellationToken); - } - - /// - public void Initialize(StatefulServiceInitializationParameters initializationParameters) - { - this.traceId = ActorTrace.GetTraceIdForReplica(initializationParameters.PartitionId, initializationParameters.ReplicaId); - this.initParams = initializationParameters; - ((IStateProviderReplica)this.rcStateProvider).Initialize(initializationParameters); - } - - /// - public Task OpenAsync(ReplicaOpenMode openMode, IStatefulServicePartition partition, CancellationToken cancellationToken) - { - this.servicePartition = partition; - return ((IStateProviderReplica)this.rcStateProvider).OpenAsync(openMode, partition, cancellationToken); - } - - /// - async Task IStateProviderReplica.ChangeRoleAsync(ReplicaRole newRole, CancellationToken cancellationToken) - { - await ((IStateProviderReplica)this.rcStateProvider).ChangeRoleAsync(newRole, cancellationToken); - if (newRole == ReplicaRole.Primary) - { - this.stateProviderInitCts = new CancellationTokenSource(); - this.stateProviderInitTask = this.StartStateProviderInitialization(this.stateProviderInitCts.Token); - } - else - { - await this.CancelStateProviderInitializationAsync(); - } - } - - /// - public Task CloseAsync(CancellationToken cancellationToken) - { - return ((IStateProviderReplica)this.rcStateProvider).CloseAsync(cancellationToken); - } - - /// - public void Abort() - { - ((IStateProviderReplica)this.rcStateProvider).Abort(); - } - - /// - public Task BackupAsync(Func> backupCallback) - { - return ((IStateProviderReplica)this.rcStateProvider).BackupAsync(backupCallback); - } - - /// - public Task BackupAsync(BackupOption option, TimeSpan timeout, CancellationToken cancellationToken, Func> backupCallback) - { - return ((IStateProviderReplica)this.rcStateProvider).BackupAsync(option, timeout, cancellationToken, backupCallback); - } - - /// - public Task RestoreAsync(string backupFolderPath) - { - return ((IStateProviderReplica)this.rcStateProvider).RestoreAsync(backupFolderPath); - } - - /// - public Task RestoreAsync(string backupFolderPath, RestorePolicy restorePolicy, CancellationToken cancellationToken) - { - return ((IStateProviderReplica)this.rcStateProvider).RestoreAsync(backupFolderPath, restorePolicy, cancellationToken); - } - - /// - public Task OnSnapshotAsync(TimeSpan currentLogicalTime) - { - return ((VolatileLogicalTimeManager.ISnapshotHandler)this.rcStateProvider).OnSnapshotAsync(currentLogicalTime); - } - - /// - /// Modifies data from KVS store into a suitable format for RC. Saves the modified data in RC store. - /// - /// - /// Data from KVS store that needs to be modified and saved in RC - /// - /// - /// Cancellation token - /// - /// If true, attempts to resolve the actorid(ambiguous) from user resolver implementation. - /// If false, then local presence dictionary is used before user resolvers. - /// A representing the result of the asynchronous operation. - public virtual async Task SaveStateAsync(List kvsData, CancellationToken cancellationToken, bool skipPresenceDictResolve = false) - { - List keysMigrated = new List(); - int presenceKeyCount = 0, reminderCompletedKeyCount = 0, logicalTimeCount = 0, actorStateCount = 0, reminderCount = 0; - long lastAppliedSN = -1; - return await this.rcStateProvider.GetActorStateProviderHelper().ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.GetStateManager().CreateTransaction()) - { - try - { - foreach (var data in kvsData) - { - byte[] rcValue = { }; - var dictionary = await this.GetDictionaryFromKVSKeyAsync(data, tx, skipPresenceDictResolve, cancellationToken); - if (dictionary == null) - { - continue; - } - - var rcKey = this.TransformKVSKeyToRCFormat(data.Key); - if (!data.IsDeleted) - { - rcValue = data.Value; - if (data.Key.StartsWith(ActorPresenceKeyPrefix)) - { - presenceKeyCount++; - } - else if (data.Key.StartsWith(ActorStorageKeyPrefix)) - { - actorStateCount++; - } - else if (data.Key.StartsWith(ReminderCompletedeStorageKeyPrefix)) - { - ReminderCompletedData reminderCompletedData = MigrationUtility.KVS.DeserializeReminderCompletedData(data.Key, data.Value, this.TraceId); - rcValue = MigrationUtility.RC.SerializeReminderCompletedData(data.Key, reminderCompletedData, this.TraceId); - reminderCompletedKeyCount++; - } - else if (data.Key.StartsWith(ReminderStorageKeyPrefix)) - { - ActorReminderData reminderCompletedData = MigrationUtility.KVS.DeserializeReminder(data.Key, data.Value, this.TraceId); - rcValue = MigrationUtility.RC.SerializeReminder(data.Key, reminderCompletedData, this.TraceId); - reminderCount++; - } - - cancellationToken.ThrowIfCancellationRequested(); - await dictionary.AddOrUpdateAsync(tx, rcKey, rcValue, (k, v) => rcValue); - } - else - { - if (!(await dictionary.TryRemoveAsync(tx, rcKey)).HasValue) - { - // It should be fine, if we are unable to delete the entry, since the data validation would catch any abnormallity. - ActorTrace.Source.WriteInfoWithId( - this.TraceType, - this.traceId, - $"Failed to apply tombstone. Common cause for this behavior could be a failover."); - } - } - - keysMigrated.Add(data.Key); - lastAppliedSN = data.Version; - } - - await tx.CommitAsync(); - } - catch (Exception ex) - { - await this.metadataDictionary.TryAddAsync( - tx, - MigrationConstants.MigrationEndDateTimeUTC, - DateTime.UtcNow.ToString(), - MigrationConstants.DefaultRCTimeout, - cancellationToken); - - await this.metadataDictionary.AddOrUpdateAsync( - tx, - MigrationConstants.MigrationCurrentStatus, - MigrationState.Aborted.ToString(), - (_, __) => MigrationState.Aborted.ToString(), - MigrationConstants.DefaultRCTimeout, - cancellationToken); - - // Commit with the same transaction to avoid race condition during failover. - await tx.CommitAsync(); - - throw ex; - } - - ActorTrace.Source.WriteNoiseWithId(this.TraceType, this.traceId, string.Join(MigrationConstants.DefaultDelimiter.ToString(), keysMigrated)); - - string infoLevelMessage = "Migrated " + presenceKeyCount + " presence keys, " - + reminderCompletedKeyCount + " reminder completed keys, " - + logicalTimeCount + " logical timestamps, " - + actorStateCount + " actor states and " - + reminderCount + " reminders."; - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, infoLevelMessage); - return keysMigrated.Count; - } - }, - "KVSToRCMigrationActorStateProvide.SaveStateAsync", - cancellationToken); - } - - internal virtual async Task> GetMetadataDictionaryAsync() - { - await this.stateProviderInitTask; - return this.metadataDictionary; - } - - internal StatefulServiceInitializationParameters GetInitParams() - { - return this.initParams; - } - - internal IReliableStateManagerReplica2 GetStateManager() - { - return this.rcStateProvider.GetStateManager(); - } - - internal virtual async Task ValidateDataPostMigrationAsync(List kvsData, string hashToCompare, bool skipPresenceDictResolve, CancellationToken cancellationToken) - { - var values = new List(); - foreach (var data in kvsData) - { - if (MigrationUtility.IgnoreKey(data.Key)) - { - continue; - } - - using (var tx = this.GetStateManager().CreateTransaction()) - { - var rcKey = this.TransformKVSKeyToRCFormat(data.Key); - var dictionary = await this.GetDictionaryFromKVSKeyAsync(data, tx, skipPresenceDictResolve, cancellationToken); - if (dictionary == null) - { - continue; - } - - var condValue = await dictionary.TryGetValueAsync(tx, rcKey); - if (condValue.HasValue) - { - if (data.IsDeleted) - { - ActorTrace.Source.WriteErrorWithId( - this.TraceType, - this.traceId, - $"{data.Key} exists while it is expected to be deleted"); - - throw new MigrationDataValidationException("Post migration validation checks failed."); - } - else - { - var rcValue = condValue.Value; - if (data.Key.StartsWith(ReminderStorageKeyPrefix)) - { - var reminderdata = MigrationUtility.RC.DeserializeReminder(rcKey, rcValue, this.TraceId); - rcValue = MigrationUtility.KVS.SerializeReminder(data.Key, reminderdata, this.TraceId); - } - else if (data.Key.StartsWith(ReminderCompletedeStorageKeyPrefix)) - { - var remCompletedData = MigrationUtility.RC.DeserializeReminderCompletedData(rcKey, rcValue, this.TraceId); - rcValue = MigrationUtility.KVS.SerializeReminderCompletedData(data.Key, remCompletedData, this.TraceId); - var temp = MigrationUtility.KVS.DeserializeReminderCompletedData(data.Key, rcValue, this.TraceId); - var rcValue1 = MigrationUtility.KVS.SerializeReminderCompletedData(data.Key, remCompletedData, this.TraceId); - } - - values.Add(rcValue); - } - } - else - { - if (!data.IsDeleted) - { - ActorTrace.Source.WriteErrorWithId( - this.TraceType, - this.traceId, - $"{data.Key} is not found."); - - throw new MigrationDataValidationException("Post migration validation checks failed."); - } - } - } - } - - var computedHash = CRC64.ToCRC64(values.ToArray()).ToString("X", CultureInfo.InvariantCulture); - if (!computedHash.Equals(hashToCompare, StringComparison.InvariantCulture)) - { - ActorTrace.Source.WriteErrorWithId( - this.TraceType, - this.traceId, - "Migration data hashes do not compare to equal"); - - throw new MigrationDataValidationException("Post migration validation checks failed."); - } - } - - internal IReliableCollectionsActorStateProviderInternal GetInternalStateProvider() - { - return this.rcStateProvider; - } - - internal string TransformKVSKeyToRCFormat(string key) - { - int firstUnderscorePosition = key.IndexOf("_"); - if (key.StartsWith("@@")) - { - return key.Substring(firstUnderscorePosition + 1) + "_"; - } - - return key.Substring(firstUnderscorePosition + 1); - } - - private async Task> GetDictionaryFromKVSKeyAsync(KeyValuePair data, Data.ITransaction tx, bool skipPresenceDictResolve, CancellationToken cancellationToken) - { - var key = data.Key; - IReliableDictionary2 temp = null; - if (key.StartsWith(ActorPresenceKeyPrefix)) - { - temp = this.rcStateProvider.GetActorPresenceDictionary(); - } - else if (key.StartsWith(ActorStorageKeyPrefix)) - { - ActorId actorId; - if (string.IsNullOrEmpty(data.ActorId)) - { - var rcKey = this.TransformKVSKeyToRCFormat(data.Key); - actorId = await this.ambiguousActorIdHandler.ResolveActorIdAsync(rcKey, tx, cancellationToken, skipPresenceDictResolve); - } - else - { - actorId = new ActorId(data.ActorId); - } - - temp = this.rcStateProvider.GetActorStateDictionary(actorId); - } - else if (key.StartsWith(ReminderCompletedeStorageKeyPrefix)) - { - temp = this.rcStateProvider.GetReminderCompletedDictionary(); - } - else if (key.StartsWith(ReminderStorageKeyPrefix)) - { - temp = this.rcStateProvider.GetReminderDictionary(this.GetActorIdFromStorageKey(key)); - } - else if (key.Equals(LogicalTimestampKey) - || key.StartsWith(MigrationConstants.RejectWritesKey)) - { - ActorTrace.Source.WriteInfoWithId( - this.TraceType, - this.traceId, - $"Ignoring KVS key - {key}", - key); - } - else - { - var message = "Migration Error: Failed to parse the KVS key - " + key; - - ActorTrace.Source.WriteWarningWithId( - this.TraceType, - this.traceId, - message); - } - - return temp; - } - - private ActorId GetActorIdFromStorageKey(string key) - { - // It is not right to assume the ActorId wouldn't have underscores. - // TODO: Handle this in ambiguous ActorId PR - var startIndex = this.GetNthIndex(key, '_', 2); - var endIndex = this.GetNthIndex(key, '_', 3); - var actorId = new ActorId(key.Substring(startIndex + 1, endIndex - startIndex - 1)); - - ActorTrace.Source.WriteNoiseWithId( - this.TraceType, - this.traceId, - $"GetActorIdFromStorageKey - ActorId: {actorId}, Key : {key}"); - - return actorId; - } - - private int GetNthIndex(string s, char t, int n) - { - int count = 0; - for (int i = 0; i < s.Length; i++) - { - if (s[i] == t) - { - count++; - if (count == n) - { - return i; - } - } - } - - return -1; - } - - private async Task InitializeMetadataDictAsync(CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, "Initializing metadata dictionary"); - - if (this.isMetadataDictInitialized) - { - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, "Metadata dictionary already registered."); - return; - } - - IReliableDictionary2 metadataDict = null; - - using (var tx = this.rcStateProvider.GetStateManager().CreateTransaction()) - { - try - { - metadataDict = await this.GetOrAddDictionaryAsync(tx, MigrationConstants.MetadataDictionaryName); - cancellationToken.ThrowIfCancellationRequested(); - await tx.CommitAsync(); - } - catch (Exception e) - { - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, e.Message); - } - } - - this.metadataDictionary = metadataDict; - - Volatile.Write(ref this.isMetadataDictInitialized, true); - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, "Registering Metadata dictionary SUCCEEDED."); - } - - private Task> GetOrAddDictionaryAsync(ITransaction tx, string dictionaryName) - { - return this.rcStateProvider.GetStateManager().GetOrAddAsync>(tx, dictionaryName); - } - - private async Task WaitForWriteStatusAsync(CancellationToken cancellationToken) - { - var retryCount = 0; - - while (!cancellationToken.IsCancellationRequested && - this.servicePartition.WriteStatus != PartitionAccessStatus.Granted) - { - retryCount++; - await Task.Delay(retryCount * 500, cancellationToken); - } - } - - private async Task StartStateProviderInitialization(CancellationToken cancellationToken) - { - Exception unexpectedException = null; - try - { - var stateProviderHelper = new ActorStateProviderHelper(this); - cancellationToken.ThrowIfCancellationRequested(); - await stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - await this.WaitForWriteStatusAsync(cancellationToken); - await this.InitializeMetadataDictAsync(cancellationToken); - }, - "StartStateProviderInitialization", - cancellationToken); - } - catch (OperationCanceledException opEx) - { - if (!cancellationToken.IsCancellationRequested) - { - unexpectedException = opEx; - } - } - catch (FabricObjectClosedException) - { - // This can happen when replica is closing. CancellationToken should get signaled. - // Fall through and let the task check for CancellationToken. - } - catch (FabricNotPrimaryException) - { - // This replica is no more primary. CancellationToken should get signaled. - // Fall through and let the task check for CancellationToken. - } - catch (Exception ex) - { - unexpectedException = ex; - } - - if (unexpectedException != null) - { - var msgFormat = "StartStateProviderInitialization() failed due to " + - "an unexpected Exception causing replica to fault: {0}"; - - ActorTrace.Source.WriteErrorWithId( - this.TraceType, - this.traceId, - string.Format(msgFormat, unexpectedException.ToString())); - - this.servicePartition.ReportFault(FaultType.Transient); - } - } - - private async Task CancelStateProviderInitializationAsync() - { - if (this.stateProviderInitCts != null - && this.stateProviderInitTask != null - && this.stateProviderInitCts.IsCancellationRequested == false) - { - try - { - ActorTrace.Source.WriteInfoWithId(this.TraceType, this.traceId, "Canceling state provider initialization"); - - this.stateProviderInitCts.Cancel(); - - await this.stateProviderInitTask; - } - catch (Exception ex) - { - var msgFormat = "StartStateProviderInitialization() failed due to " + - "an unexpected Exception causing replica to fault: {0}"; - - ActorTrace.Source.WriteErrorWithId( - this.TraceType, - this.traceId, - string.Format(msgFormat, ex.ToString())); - - this.servicePartition.ReportFault(FaultType.Transient); - } - finally - { - this.stateProviderInitCts = null; - this.stateProviderInitTask = null; - } - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration.csproj b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration.csproj deleted file mode 100644 index 7d6180d0..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - {B5E3B9D7-E91E-4B76-959D-D418E973FCAF} - Microsoft.ServiceFabric.Actors.KVSToRCMigration - $(AssemblyName) - $(AssemblyName) - $(OutputPath)\$(AssemblyName).xml - net462 - true - 1591 - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/DefaultMigrationExceptionMiddleware.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/DefaultMigrationExceptionMiddleware.cs deleted file mode 100644 index b22b5f56..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/DefaultMigrationExceptionMiddleware.cs +++ /dev/null @@ -1,63 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Middleware -{ - using System; - using System.Fabric; - using System.Net; - using System.Runtime.Serialization.Json; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - using Microsoft.ServiceFabric.Actors.Migration; - - internal class DefaultMigrationExceptionMiddleware - { - private static readonly DataContractJsonSerializer Serializer = new DataContractJsonSerializer(typeof(ErrorResponse)); - private readonly RequestDelegate next; - - public DefaultMigrationExceptionMiddleware(RequestDelegate next) - { - this.next = next; - } - - public async Task Invoke(HttpContext context) - { - try - { - await this.next.Invoke(context); - } - catch (Exception ex) - { - await this.HandleExceptionAsync(context, ex); - } - } - - private async Task HandleExceptionAsync(HttpContext context, Exception exception) - { - var response = context.Response; - var error = new ErrorResponse - { - Message = exception.Message, - ErrorCode = 0, - ExceptionType = exception.GetType().FullName, - IsFabricError = false, - }; - - if (exception is FabricException) - { - var fabEx = exception as FabricException; - error.IsFabricError = true; - error.ErrorCode = fabEx.ErrorCode; - } - - response.ContentType = "application/json; charset=utf-8"; - response.StatusCode = (int)HttpStatusCode.InternalServerError; - var buffer = SerializationUtility.Serialize(Serializer, error); - await response.Body.WriteAsync(buffer, 0, buffer.Length); - await response.Body.FlushAsync(); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricIntegrationOptions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricIntegrationOptions.cs deleted file mode 100644 index c5e5bfba..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricIntegrationOptions.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - - /// - /// Integration options for method when used with Microsoft.AspNetCore.Hosting.IWebHostBuilder. - /// - [Flags] - internal enum ServiceFabricIntegrationOptions - { - /// - /// This option will not configure the to add any suffix to url when providing the url to Service Fabric Runtime from its method. - /// - None = 0x00, - - /// - /// This option will configure the to add a url suffix containing and - /// to url when providing the url to Service Fabric Runtime from its method. - /// - UseUniqueServiceUrl = 0x01, - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricMiddleware.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricMiddleware.cs deleted file mode 100644 index 19e92ef7..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricMiddleware.cs +++ /dev/null @@ -1,64 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - - /// - /// A middleware to be used with Service Fabric stateful and stateless services hosted in Kestrel or HttpSys. - /// This middleware examines the Microsoft.AspNetCore.Http.HttpRequest.Path in request to determine if the request is intended for this replica. - /// - /// - /// This middleware when used with Kestrel and HttpSys based Service Fabric Communication Listeners allows handling of scenarios when - /// the Replica1 listening on Node1 and por11 has moved and another Replica2 is opened on Node1 got Port1. - /// A client which has resolved Replica1 before it moved, will send the request to Node1:Port1. Using this middleware - /// Replica2 can reject calls which were meant for Replica1 by examining the Path in incoming request. - /// - internal class ServiceFabricMiddleware - { - private readonly RequestDelegate next; - private readonly string urlSuffix; - - /// - /// Initializes a new instance of the class. - /// - /// Next request handler in pipeline. - /// Url suffix to determine if the request is meant for current partition and replica. - public ServiceFabricMiddleware(RequestDelegate next, string urlSuffix) - { - if (next == null) - { - throw new ArgumentNullException("next"); - } - - if (urlSuffix == null) - { - throw new ArgumentNullException("urlSuffix"); - } - - this.urlSuffix = urlSuffix; - this.next = next; - } - - /// - /// Invoke. - /// - /// Context. - /// Task for the execution by next middleware in pipeline. - public async Task Invoke(HttpContext context) - { - // TODO: According to public documentation - // 1. SF HttpSysCommunicationListener does not support port sharing - // "Multiple HTTP.sys instances can share a port by using the underlying HTTP.sys port sharing feature. - // But it's not supported by HttpSysCommunicationListener due to the complications it introduces for client requests." - // 2. Since the unique url with partitionid + replicaid + random guid is given as prefix to http.sys host, the request lands on this replica only if the unique base path match. - // This middleware is probably no longer needed. - await this.next(context); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricSetupFilter.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricSetupFilter.cs deleted file mode 100644 index afd0c41d..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Middleware/ServiceFabricSetupFilter.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - - internal class ServiceFabricSetupFilter : IStartupFilter - { - private readonly string urlSuffix; - private readonly ServiceFabricIntegrationOptions options; - - internal ServiceFabricSetupFilter(string urlSuffix, ServiceFabricIntegrationOptions options) - { - this.urlSuffix = urlSuffix; - this.options = options; - } - - public Action Configure(Action next) - { - return app => - { - if (!string.IsNullOrEmpty(this.urlSuffix)) - { - app.UseServiceFabricMiddleware(this.urlSuffix); - } - - next(app); - }; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationConstants.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationConstants.cs deleted file mode 100644 index ab15d917..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationConstants.cs +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - - internal static class MigrationConstants - { - internal static readonly TimeSpan MaxBackoffForTransientErrors = TimeSpan.FromSeconds(1); - internal static readonly int MaxRetryCountForTransientErrors = 4; - internal static readonly TimeSpan DefaultOperationTimeout = TimeSpan.FromMinutes(5); - - internal static readonly TimeSpan DefaultRCTimeout = TimeSpan.FromMinutes(5); - internal static readonly int DefaultRetryCount = 4; - internal static readonly TimeSpan ConstantBackoffInterval = TimeSpan.FromSeconds(1); - internal static readonly char DefaultDelimiter = ','; - internal static readonly string KVSMigrationControllerName = "KvsMigration"; - internal static readonly string MetadataDictionaryName = "store://kvsrcmigration//metadata"; - internal static readonly string AbortMigrationEndpoint = "AbortMigration"; - internal static readonly string StartDowntimeEndpoint = "StartDowntime"; - internal static readonly string GetStartSNEndpoint = "GetFirstSequenceNumber"; - internal static readonly string GetEndSNEndpoint = "GetLastSequenceNumber"; - internal static readonly string EnumeratebySNEndpoint = "EnumerateBySequenceNumber"; - internal static readonly string GetDisableTCSEndpoint = "IsTombstoneCleanupDisabled"; - - internal static readonly string RejectWritesKey = "_RejectWrites_"; - internal static readonly string IsDowntimeInvoked = "_IsDowntimeInvoked_"; - - #region Actor Key constants - internal static readonly string ActorPresenceKeyPrefix = "@@"; - internal static readonly string ActorStorageKeyPrefix = "Actor"; - internal static readonly string ReminderStorageKeyPrefix = "Reminder"; - internal static readonly string ReminderCompletedeStorageKeyPrefix = "RC@@"; - internal static readonly string LogicalTimestampKey = "Timestamp_VLTM"; - #endregion - - #region Global Migration constants - internal static readonly string MigrationStartDateTimeUTC = "_MigrationStartDateTimeUTC_"; - internal static readonly string MigrationEndDateTimeUTC = "_MigrationEndDateTimeUTC_"; - internal static readonly string MigrationCurrentStatus = "_MigrationCurrentStatus_"; - internal static readonly string MigrationNoOfKeysMigrated = "_MigrationNoOfKeysMigrated_"; - internal static readonly string MigrationKeysMigrated = "_MigrationKeysMigrated_{0}_"; - internal static readonly string MigrationKeysMigratedChunksCount = "_MigrationKeysMigratedChunksCount_"; - internal static readonly string MigrationCurrentPhase = "_MigrationCurrentPhase_"; - internal static readonly string MigrationStartSeqNum = "_MigrationStartSeqNum_"; - internal static readonly string MigrationEndSeqNum = "_MigrationEndSeqNum_"; - internal static readonly string MigrationLastAppliedSeqNum = "_MigrationLastAppliedSeqNum_"; - internal static readonly string AbortMigrationInvokedOnSource = "_AbortMigrationInvokedOnSource_"; - #endregion Global Migration constants - - #region Phase constants - internal static readonly string PhaseStartDateTimeUTC = "_{0}Phase_Iteration-{1}_StartDateTimeUTC_"; - internal static readonly string PhaseEndDateTimeUTC = "_{0}Phase_Iteration-{1}_EndDateTimeUTC_"; - internal static readonly string PhaseCurrentStatus = "_{0}Phase_Iteration-{1}_CurrentStatus_"; - internal static readonly string PhaseStartSeqNum = "_{0}Phase_Iteration-{1}_StartSeqNum_"; - internal static readonly string PhaseEndSeqNum = "_{0}Phase_Iteration-{1}_EndSeqNum_"; - internal static readonly string PhaseLastAppliedSeqNum = "_{0}Phase_Iteration-{1}_LastAppliedSeqNum_"; - internal static readonly string PhaseNoOfKeysMigrated = "_{0}Phase_Iteration-{1}_NoOfKeysMigrated_"; - internal static readonly string PhaseWorkerCount = "_{0}Phase_Iteration-{1}_WorkerCount_"; - internal static readonly string PhaseIterationCount = "_{0}Phase_IterationCount_"; - #endregion Phase constants - - #region Worker constants - internal static readonly string PhaseWorkerStartDateTimeUTC = "_{0}Phase_Iteration-{1}_Worker{2}_StartDateTimeUTC_"; - internal static readonly string PhaseWorkerEndDateTimeUTC = "_{0}Phase_Iteration-{1}_Worker{2}_EndDateTimeUTC_"; - internal static readonly string PhaseWorkerCurrentStatus = "_{0}Phase_Iteration-{1}_Worker{2}_CurrentStatus_"; - internal static readonly string PhaseWorkerStartSeqNum = "_{0}Phase_Iteration-{1}_Worker{2}_StartSeqNum_"; - internal static readonly string PhaseWorkerEndSeqNum = "_{0}Phase_Iteration-{1}_Worker{2}_EndSeqNum_"; - internal static readonly string PhaseWorkerLastAppliedSeqNum = "_{0}Phase_Iteration-{1}_Worker{2}_LastAppliedSeqNum_"; - internal static readonly string PhaseWorkerNoOfKeysMigrated = "_{0}Phase_Iteration-{1}_Worker{2}_NoOfKeysMigrated_"; - #endregion Worker constants - - public static string Key(string format, params object[] args) - { - return string.Format(format, args); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationDataValidationException.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationDataValidationException.cs deleted file mode 100644 index 1d58f2ef..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationDataValidationException.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Runtime.Serialization; - - [Serializable] - internal sealed class MigrationDataValidationException : FabricException - { - public MigrationDataValidationException() - : base() - { - } - - public MigrationDataValidationException(string message) - : base(message) - { - } - - public MigrationDataValidationException(string message, Exception inner) - : base(message, inner) - { - } - - private MigrationDataValidationException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationOrchestratorBase.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationOrchestratorBase.cs deleted file mode 100644 index 07bc41ad..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationOrchestratorBase.cs +++ /dev/null @@ -1,197 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.ServiceFabric.Actors.Generator; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - using Microsoft.ServiceFabric.Services.Client; - using Microsoft.ServiceFabric.Services.Communication.Client; - using Microsoft.ServiceFabric.Services.Communication.Runtime; - using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; - - /// - /// Base class for migration orchestration. - /// - internal abstract class MigrationOrchestratorBase : IMigrationOrchestrator - { - private static readonly string TraceType = typeof(MigrationOrchestratorBase).Name; - - private StatefulServiceContext serviceContext; - private ActorTypeInformation actorTypeInformation; - private string traceId; - private MigrationSettings migrationSettings; - private Func completionCallback; - - /// - /// Initializes a new instance of the class. - /// - /// The type information of the Actor. - /// Service context the actor service is operating under. - /// Migration settings. - /// Trace Id. - public MigrationOrchestratorBase(StatefulServiceContext serviceContext, ActorTypeInformation actorTypeInformation, Actors.Runtime.Migration.MigrationSettings migrationSettings, string traceId = null) - { - this.actorTypeInformation = actorTypeInformation; - this.serviceContext = serviceContext; - this.traceId = string.IsNullOrEmpty(traceId) ? this.serviceContext.TraceId : traceId; - if (migrationSettings != null && migrationSettings is MigrationSettings) - { - this.migrationSettings = (MigrationSettings)migrationSettings; - } - else - { - this.migrationSettings = new MigrationSettings(); - this.migrationSettings.LoadFrom( - this.StatefulServiceContext.CodePackageActivationContext, - ActorNameFormat.GetMigrationConfigSectionName(this.actorTypeInformation.ImplementationType)); - } - } - - internal ActorTypeInformation ActorTypeInformation { get => this.actorTypeInformation; } - - internal StatefulServiceContext StatefulServiceContext { get => this.serviceContext; } - - internal string TraceId { get => this.traceId; } - - internal MigrationSettings MigrationSettings { get => this.migrationSettings; } - - internal Func CompletionCallback { get => this.completionCallback; } - - /// - public abstract bool AreActorCallsAllowed(); - - /// - public abstract IActorStateProvider GetMigrationActorStateProvider(); - - /// - public ICommunicationListener GetMigrationCommunicationListener() - { - var endpointName = this.GetMigrationEndpointName(); - - return new HttpSysCommunicationListener(this.serviceContext, endpointName, (url, listener) => - { - try - { - var endpoint = this.serviceContext.CodePackageActivationContext.GetEndpoint(endpointName); - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Starting Kestrel on url: {url} host: {FabricRuntime.GetNodeContext().IPAddressOrFQDN} endpointPort: {endpoint.Port}"); - - var webHostBuilder = - new WebHostBuilder() - .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl) - .UseHttpSys(options => - { - options.UrlPrefixes.Add(listener.GetListenerUrl()); - }) - .ConfigureServices( - services => services - .AddSingleton(this)) - .UseStartup() - .Build(); - - return webHostBuilder; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - "Error encountered while creating WebHostBuilder: " + ex); - - throw; - } - }); - } - - /// - public IServiceRemotingMessageHandler GetMessageHandler(ActorService actorService, IServiceRemotingMessageHandler messageHandler, Func requestForwarderFactory) - { - var forwardServiceUri = this.GetForwardServiceUri(); - if (forwardServiceUri == null) - { - // Service is not configured to forward requests. - return messageHandler; - } - - var partitionInformation = this.GetInt64RangePartitionInformation(); - var lowKey = partitionInformation.LowKey; - - return new RequestForwardableRemotingDispatcher( - actorService, - messageHandler, - requestForwarderFactory.Invoke(new RequestForwarderContext - { - ServiceUri = forwardServiceUri, - ServicePartitionKey = new ServicePartitionKey(lowKey), - ReplicaSelector = TargetReplicaSelector.PrimaryReplica, - TraceId = this.StatefulServiceContext.TraceId, - })); - } - - /// - /// Gets the migration start mode. - /// - /// Return true if the MigrationMode is Auto, false otherwise. - public virtual bool IsAutoStartMigration() - { - return this.MigrationSettings.MigrationMode == MigrationMode.Auto; - } - - public void RegisterCompletionCallback(Func completionCallback) - { - this.completionCallback = completionCallback; - } - - /// - public abstract Task StartDowntimeAsync(bool userTriggered, CancellationToken cancellationToken); - - /// - public abstract Task StartMigrationAsync(bool userTriggered, CancellationToken cancellationToken); - - /// - public abstract Task AbortMigrationAsync(bool userTriggered, CancellationToken cancellationToken); - - public abstract bool IsActorCallToBeForwarded(); - - public abstract void ThrowIfActorCallsDisallowed(); - - protected Task InvokeCompletionCallback(bool actorCallsAllowed, CancellationToken cancellationToken) - { - if (this.completionCallback != null) - { - return this.completionCallback.Invoke(actorCallsAllowed, cancellationToken); - } - - ActorTrace.Source.WriteWarningWithId( - TraceType, - this.TraceId, - "Completion callback not registered. Ignoriing callback invocation."); - - return Task.CompletedTask; - } - - /// - /// Gets the migration endpoint name. - /// - /// Migration endpoint name. - protected abstract string GetMigrationEndpointName(); - - protected abstract Int64RangePartitionInformation GetInt64RangePartitionInformation(); - - protected abstract Uri GetForwardServiceUri(); - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationPhaseWorkloadBase.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationPhaseWorkloadBase.cs deleted file mode 100644 index ab054c9c..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationPhaseWorkloadBase.cs +++ /dev/null @@ -1,543 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Extensions; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - using Microsoft.ServiceFabric.Data.Collections; - using Microsoft.ServiceFabric.Services.Communication.Client; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationUtility; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.PhaseInput; - using static Microsoft.ServiceFabric.Actors.Migration.PhaseResult; - - internal abstract class MigrationPhaseWorkloadBase : IMigrationPhaseWorkload - { - private static readonly string TraceType = typeof(MigrationPhaseWorkloadBase).Name; - private MigrationPhase migrationPhase; - private IReliableDictionary2 metadataDict; - private ServicePartitionClient servicePartitionClient; - private StatefulServiceContext statefulServiceContext; - private MigrationSettings migrationSettings; - private KVStoRCMigrationActorStateProvider stateProvider; - private string traceId; - private ActorTypeInformation actorTypeInformation; - private int currentIteration; - private int workerCount; - private ActorStateProviderHelper stateProviderHelper; - - public MigrationPhaseWorkloadBase( - MigrationPhase migrationPhase, - int currentIteration, - int workerCount, - KVStoRCMigrationActorStateProvider stateProvider, - ServicePartitionClient servicePartitionClient, - StatefulServiceContext statefulServiceContext, - MigrationSettings migrationSettings, - ActorTypeInformation actorTypeInfo, - string traceId) - { - this.migrationPhase = migrationPhase; - this.servicePartitionClient = servicePartitionClient; - this.migrationSettings = migrationSettings; - this.statefulServiceContext = statefulServiceContext; - this.stateProvider = stateProvider; - this.actorTypeInformation = actorTypeInfo; - this.currentIteration = currentIteration; - this.workerCount = workerCount; - this.traceId = traceId; - this.metadataDict = this.stateProvider.GetMetadataDictionaryAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - this.stateProviderHelper = this.stateProvider.GetInternalStateProvider().GetActorStateProviderHelper(); - } - - public IReliableDictionary2 MetaDataDictionary { get => this.metadataDict; } - - public ServicePartitionClient ServicePartitionClient { get => this.servicePartitionClient; } - - public StatefulServiceContext StatefulServiceContext { get => this.statefulServiceContext; } - - public MigrationSettings MigrationSettings { get => this.migrationSettings; } - - public KVStoRCMigrationActorStateProvider StateProvider { get => this.stateProvider; } - - public ActorTypeInformation ActorTypeInformation { get => this.actorTypeInformation; } - - public string TraceId { get => this.traceId; } - - public Data.ITransaction Transaction { get => this.stateProvider.GetStateManager().CreateTransaction(); } - - public MigrationPhase Phase { get => this.migrationPhase; } - - public static async Task GetResultAsync( - ActorStateProviderHelper stateProviderHelper, - Func txFactory, - IReliableDictionary2 metadataDict, - MigrationPhase migrationPhase, - int currentIteration, - string traceId, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var status = await ParseMigrationStateAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseCurrentStatus, migrationPhase, currentIteration), cancellationToken), - traceId); - - if (status == MigrationState.None) - { - return new PhaseResult - { - Phase = migrationPhase, - Iteration = currentIteration, - Status = MigrationState.None, - }; - } - - var result = new PhaseResult(); - result.Phase = migrationPhase; - result.Iteration = currentIteration; - result.Status = status; - - result.StartDateTimeUTC = (await ParseDateTimeAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseStartDateTimeUTC, migrationPhase, currentIteration), cancellationToken), - traceId)).Value; - - result.EndDateTimeUTC = await ParseDateTimeAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseEndDateTimeUTC, migrationPhase, currentIteration), cancellationToken), - traceId); - - result.StartSeqNum = (await ParseLongAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseStartSeqNum, migrationPhase, currentIteration), cancellationToken), - traceId)).Value; - - result.EndSeqNum = (await ParseLongAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseEndSeqNum, migrationPhase, currentIteration), cancellationToken), - traceId)).Value; - - result.LastAppliedSeqNum = await ParseLongAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseLastAppliedSeqNum, migrationPhase, currentIteration), cancellationToken), - traceId); - - result.NoOfKeysMigrated = await ParseLongAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseNoOfKeysMigrated, migrationPhase, currentIteration), cancellationToken), - traceId); - - result.WorkerCount = await ParseIntAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerCount, migrationPhase, currentIteration), cancellationToken), - traceId); - - var workerResults = new List(); - - for (int i = 1; i <= result.WorkerCount; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - var workerResult = await MigrationWorker.GetResultAsync(stateProviderHelper, txFactory, metadataDict, migrationPhase, currentIteration, i, traceId, cancellationToken); - if (workerResult.Status != MigrationState.None) - { - workerResults.Add(workerResult); - } - } - - result.WorkerResults = workerResults.ToArray(); - - return result; - } - - public async Task StartOrResumeMigrationAsync(CancellationToken cancellationToken) - { - PhaseInput input = null; - - try - { - input = await this.GetOrAddInputAsync(cancellationToken); - if (input.Status == MigrationState.Completed) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.traceId, - $"Phase already completed \n Input: {input.ToString()}"); - - return await GetResultAsync( - this.stateProviderHelper, - () => this.Transaction, - this.MetaDataDictionary, - this.migrationPhase, - this.currentIteration, - this.TraceId, - cancellationToken); - } - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.traceId, - $"Starting or resuming {this.migrationPhase} - {this.currentIteration} Phase\n Input: {input.ToString()}"); - MigrationTelemetry.MigrationPhaseStartEvent(this.StatefulServiceContext, input.ToString()); - - var workers = this.CreateMigrationWorkers(input, cancellationToken); - var tasks = new List>(); - foreach (var worker in workers) - { - cancellationToken.ThrowIfCancellationRequested(); - if (worker.Input.Status != MigrationState.Completed) - { - tasks.Add(worker.StartWorkAsync(cancellationToken)); - } - } - - var results = await Task.WhenAll(tasks); - await this.AddOrUpdateResultAsync(input, results, cancellationToken); - PhaseResult phaseResult = await GetResultAsync( - this.stateProviderHelper, - () => this.Transaction, - this.MetaDataDictionary, - this.migrationPhase, - this.currentIteration, - this.TraceId, - cancellationToken); - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.traceId, - $"Completed Migration phase\n Result: {phaseResult.ToString()}"); - MigrationTelemetry.MigrationPhaseEndEvent(this.StatefulServiceContext, phaseResult.ToString()); - - return phaseResult; - } - catch (Exception ex) - { - var inputString = input == null ? null : input.ToString(); - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.traceId, - $"Migration phase failed with error: {ex} \n Input: {inputString}"); - - throw ex; - } - } - - protected virtual async Task GetEndSequenceNumberAsync(CancellationToken cancellationToken) - { - var response = await this.servicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.GetAsync($"{KVSMigrationControllerName}/{GetEndSNEndpoint}"); - }, - "GetEndSequenceNumberAsync", - cancellationToken); - return (await ParseLongAsync(async () => await response.Content.ReadAsStringAsync(), this.TraceId)).Value; - } - - protected virtual async Task GetStartSequenceNumberAsync(CancellationToken cancellationToken) - { - return (await ParseLongAsync( - () => GetValueAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationLastAppliedSeqNum, cancellationToken), - this.TraceId)).Value; - } - - protected virtual async Task GetOrAddInputAsync(CancellationToken token) - { - token.ThrowIfCancellationRequested(); - - var input = new PhaseInput(); - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - input.Phase = this.migrationPhase; - - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - MigrationCurrentPhase, - this.migrationPhase.ToString(), - (_, __) => this.migrationPhase.ToString(), - DefaultRCTimeout, - token); - - input.StartDateTimeUTC = (await ParseDateTimeAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseStartDateTimeUTC, this.migrationPhase, this.currentIteration), - DateTime.UtcNow.ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - input.EndDateTimeUTC = await ParseDateTimeAsync( - () => this.MetaDataDictionary.GetValueOrDefaultAsync( - tx, - Key(PhaseStartDateTimeUTC, this.migrationPhase, this.currentIteration), - DefaultRCTimeout, - token), - this.TraceId); - - input.Status = await ParseMigrationStateAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseCurrentStatus, this.migrationPhase, this.currentIteration), - MigrationState.InProgress.ToString(), - DefaultRCTimeout, - token), - this.TraceId); - - input.StartSeqNum = (await ParseLongAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseStartSeqNum, this.migrationPhase, this.currentIteration), - this.GetStartSequenceNumberAsync(token).ConfigureAwait(false).GetAwaiter().GetResult().ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - // Update Migration seq num if it not present(first time) - await this.MetaDataDictionary.GetOrAddAsync( - tx, - MigrationStartSeqNum, - input.StartSeqNum.ToString(), - DefaultRCTimeout, - token); - - input.EndSeqNum = (await ParseLongAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseEndSeqNum, this.migrationPhase, this.currentIteration), - this.GetEndSequenceNumberAsync(token).ConfigureAwait(false).GetAwaiter().GetResult().ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - input.LastAppliedSeqNum = await ParseLongAsync( - () => this.MetaDataDictionary.GetValueOrDefaultAsync( - tx, - Key(PhaseLastAppliedSeqNum, this.migrationPhase, this.currentIteration), - DefaultRCTimeout, - token), - this.TraceId); - - input.IterationCount = await ParseIntAsync( - () => this.MetaDataDictionary.AddOrUpdateAsync( - tx, - Key(PhaseIterationCount, this.migrationPhase), - this.currentIteration.ToString(), - (_, __) => this.currentIteration.ToString(), - DefaultRCTimeout, - token), - this.TraceId); - - input.WorkerCount = await ParseIntAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseWorkerCount, this.migrationPhase, this.currentIteration), - this.workerCount.ToString(), - DefaultRCTimeout, - token), - this.TraceId); - - long perWorker = (input.EndSeqNum - input.StartSeqNum) / this.workerCount; - var perWorkerStartSN = input.StartSeqNum; - var perWorkerEndSN = input.StartSeqNum + perWorker; - var workerInputs = new List(); - for (int i = 1; i <= this.workerCount; i++) - { - token.ThrowIfCancellationRequested(); - - var workerInput = new WorkerInput - { - WorkerId = i, - Phase = this.migrationPhase, - Iteration = this.currentIteration, - }; - - workerInput.StartDateTimeUTC = (await ParseDateTimeAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseWorkerStartDateTimeUTC, this.migrationPhase, this.currentIteration, i), - DateTime.UtcNow.ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - workerInput.EndDateTimeUTC = await ParseDateTimeAsync( - () => this.MetaDataDictionary.GetValueOrDefaultAsync( - tx, - Key(PhaseWorkerEndDateTimeUTC, this.migrationPhase, this.currentIteration, i), - DefaultRCTimeout, - token), - this.TraceId); - - workerInput.Status = await ParseMigrationStateAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseWorkerCurrentStatus, this.migrationPhase, this.currentIteration, i), - MigrationState.InProgress.ToString(), - DefaultRCTimeout, - token), - this.TraceId); - - workerInput.StartSeqNum = (await ParseLongAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseWorkerStartSeqNum, this.migrationPhase, this.currentIteration, i), - perWorkerStartSN.ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - workerInput.EndSeqNum = (await ParseLongAsync( - () => this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseWorkerEndSeqNum, this.migrationPhase, this.currentIteration, i), - perWorkerEndSN.ToString(), - DefaultRCTimeout, - token), - this.TraceId)).Value; - - workerInput.LastAppliedSeqNum = await ParseLongAsync( - () => this.MetaDataDictionary.GetValueOrDefaultAsync( - tx, - Key(PhaseWorkerLastAppliedSeqNum, this.migrationPhase, this.currentIteration, i), - DefaultRCTimeout, - token), - this.TraceId); - - workerInputs.Add(workerInput); - - if (perWorkerEndSN == input.EndSeqNum) - { - break; - } - - perWorkerStartSN = perWorkerEndSN + 1; - perWorkerEndSN = (perWorkerStartSN + perWorker) < input.EndSeqNum ? (perWorkerStartSN + perWorker) : input.EndSeqNum; - } - - input.WorkerInputs = workerInputs.ToArray(); - - await tx.CommitAsync(); - } - }, - "MigrationPhaseWorkloadBase.GetOrAddInputAsync", - token); - - return input; - } - - protected virtual List CreateMigrationWorkers(PhaseInput input, CancellationToken cancellationToken) - { - var workers = new List(); - - foreach (var workerInput in input.WorkerInputs) - { - cancellationToken.ThrowIfCancellationRequested(); - - workers.Add(new MigrationWorker( - this.StateProvider, - this.ActorTypeInformation, - this.ServicePartitionClient, - this.MigrationSettings, - workerInput, - this.TraceId)); - } - - return workers; - } - - protected virtual async Task AddOrUpdateResultAsync(PhaseInput phaseInput, WorkerResult[] workerResults, CancellationToken token) - { - token.ThrowIfCancellationRequested(); - - long? keysMigratedByPhase = null; - DateTime? endTime = DateTime.UtcNow; - long keysMigratedByWorkers = 0L; - foreach (var workerResult in workerResults) - { - keysMigratedByWorkers += workerResult.NoOfKeysMigrated.HasValue ? workerResult.NoOfKeysMigrated.Value : 0L; - } - - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - endTime = await ParseDateTimeAsync( - () => this.MetaDataDictionary.AddOrUpdateAsync( - tx, - Key(PhaseEndDateTimeUTC, this.migrationPhase, this.currentIteration), - endTime.ToString(), - (_, __) => endTime.ToString(), - DefaultRCTimeout, - token), - this.TraceId); - - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - Key(PhaseLastAppliedSeqNum, this.migrationPhase, this.currentIteration), - phaseInput.EndSeqNum.ToString(), - (_, __) => phaseInput.EndSeqNum.ToString(), - DefaultRCTimeout, - token); - - keysMigratedByPhase = await ParseLongAsync( - () => this.MetaDataDictionary.AddOrUpdateAsync( - tx, - Key(PhaseNoOfKeysMigrated, this.migrationPhase, this.currentIteration), - keysMigratedByWorkers.ToString(), - (k, v) => - { - long currVal = ParseLong(v, this.TraceId); - long newVal = currVal + keysMigratedByWorkers; - - return newVal.ToString(); - }, - DefaultRCTimeout, - token), - this.TraceId); - - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - Key(PhaseCurrentStatus, this.migrationPhase, this.currentIteration), - MigrationState.Completed.ToString(), - (_, __) => MigrationState.Completed.ToString(), - DefaultRCTimeout, - token); - - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - MigrationLastAppliedSeqNum, - phaseInput.EndSeqNum.ToString(), - (_, __) => phaseInput.EndSeqNum.ToString(), - DefaultRCTimeout, - token); - - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - MigrationNoOfKeysMigrated, - keysMigratedByWorkers.ToString(), - (k, v) => - { - var currentVal = ParseLong(v, this.TraceId); - var newVal = currentVal + keysMigratedByPhase; - - return newVal.ToString(); - }, - DefaultRCTimeout, - token); - - await tx.CommitAsync(); - } - }, - "MigrationPhaseWorkloadBase.AddOrUpdateResultAsync", - token); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationSettings.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationSettings.cs deleted file mode 100644 index 8befa908..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationSettings.cs +++ /dev/null @@ -1,110 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Json; - using System.Text; - using Microsoft.ServiceFabric.Actors.Generator; - - [DataContract] - [KnownType(typeof(Actors.Runtime.Migration.MigrationSettings))] - internal class MigrationSettings : Actors.Runtime.Migration.MigrationSettings - { - private static readonly string TraceType = typeof(MigrationSettings).ToString(); - - private static DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(MigrationSettings), new DataContractJsonSerializerSettings - { - UseSimpleDictionaryFormat = true, - }); - - [DataMember] - public int CopyPhaseParallelism { get; set; } - - [DataMember] - public long DowntimeThreshold { get; set; } - - [DataMember] - public int ChunksPerEnumeration { get; set; } = 1; - - [DataMember] - public long KeyValuePairsPerChunk { get; set; } - - [DataMember] - public bool EnableDataIntegrityChecks { get; set; } - - // A comma separated list of exception full name(including namespace) for which abort should not be called. - // A partition error will still be reported - [DataMember] - public string ExceptionExclusionListForAbort { get; set; } - - public override string ToString() - { - using (var stream = new MemoryStream()) - { - serializer.WriteObject(stream, this); - - var returnVal = Encoding.UTF8.GetString(stream.GetBuffer()); - - return returnVal; - } - } - - internal override void LoadFrom(ICodePackageActivationContext codePackageActivationContext, string configSectionName = "MigrationConfig") - { - base.LoadFrom(codePackageActivationContext, configSectionName); - - this.CopyPhaseParallelism = Environment.ProcessorCount; - this.DowntimeThreshold = 1024; - this.EnableDataIntegrityChecks = true; - var configPackageName = ActorNameFormat.GetConfigPackageName(); - try - { - var configPackageObj = codePackageActivationContext.GetConfigurationPackageObject(configPackageName); - if (configPackageObj.Settings.Sections.Contains(configSectionName)) - { - var migrationSettings = configPackageObj.Settings.Sections[configSectionName]; - if (migrationSettings.Parameters.Contains("CopyPhaseParallelism")) - { - this.CopyPhaseParallelism = int.Parse(migrationSettings.Parameters["CopyPhaseParallelism"].Value); - } - - if (migrationSettings.Parameters.Contains("DowntimeThreshold")) - { - this.DowntimeThreshold = int.Parse(migrationSettings.Parameters["DowntimeThreshold"].Value); - } - - if (migrationSettings.Parameters.Contains("ChunksPerEnumeration")) - { - this.ChunksPerEnumeration = int.Parse(migrationSettings.Parameters["ChunksPerEnumeration"].Value); - } - - if (migrationSettings.Parameters.Contains("KeyValuePairsPerChunk")) - { - this.KeyValuePairsPerChunk = long.Parse(migrationSettings.Parameters["KeyValuePairsPerChunk"].Value); - } - - if (migrationSettings.Parameters.Contains("EnableDataIntegrityChecks")) - { - this.EnableDataIntegrityChecks = bool.Parse(migrationSettings.Parameters["EnableDataIntegrityChecks"].Value); - } - } - } - catch (Exception e) - { - ActorTrace.Source.WriteError(TraceType, $"Failed to load Migration settings from config package : {e.Message}"); - throw e; // TODO: consider throwing SF Exception. - } - } - - internal override void Validate(bool isSource) - { - base.Validate(isSource); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationUtility.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationUtility.cs deleted file mode 100644 index 2e078cd0..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationUtility.cs +++ /dev/null @@ -1,564 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization; - using System.Threading; - using System.Threading.Tasks; - using System.Xml; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Data.Collections; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - - internal static class MigrationUtility - { - private static readonly string TraceType = typeof(MigrationUtility).ToString(); - - public static bool ShouldRetryOperation( - string currentExceptionId, - int maxRetryCount, - ref string lastSeenExceptionId, - ref int currentRetryCount) - { - if (maxRetryCount == 0) - { - return false; - } - - if (currentExceptionId == lastSeenExceptionId) - { - if (currentRetryCount >= maxRetryCount) - { - // We have retried max number of times. - return false; - } - - ++currentRetryCount; - return true; - } - - // The current retriable exception is different from the exception that was last seen, - // reset the retry tracking variables - lastSeenExceptionId = currentExceptionId; - currentRetryCount = 1; - return true; - } - - public static async Task GetValueOrDefaultAsync(ActorStateProviderHelper stateProviderHelper, Func txFactory, IReliableDictionary2 metadataDict, string key, CancellationToken cancellationToken) - { - return await stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = txFactory.Invoke()) - { - var res = await metadataDict.TryGetValueAsync( - tx, - key, - DefaultRCTimeout, - cancellationToken); - return res.HasValue ? res.Value : null; - } - }, - $"MigrationPhaseWorkloadBase.TryGetValueAsync.{key}", - cancellationToken); - } - - public static async Task GetValueAsync(ActorStateProviderHelper stateProviderHelper, Func txFactory, IReliableDictionary2 metadataDict, string key, CancellationToken cancellationToken) - { - return await stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = txFactory.Invoke()) - { - var res = await metadataDict.TryGetValueAsync( - tx, - key, - DefaultRCTimeout, - cancellationToken); - if (res.HasValue) - { - return res.Value; - } - - throw new KeyNotFoundException(key); - } - }, - $"MigrationPhaseWorkloadBase.TryGetValueAsync.{key}", - cancellationToken); - } - - public static async Task ParseDateTimeAsync(Func> func, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return null; - } - - if (!DateTime.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseLongAsync(Func> func, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return null; - } - - if (!long.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static long ParseLong(string valueString, string traceId) - { - if (!long.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseIntAsync(Func> func, string traceId) - { - string valueString = await func(); - if (!int.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseIntAsync(Func> func, int defaultValue, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return defaultValue; - } - - if (!int.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseMigrationPhaseAsync(Func> func, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return MigrationPhase.None; - } - - if (!Enum.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseMigrationStateAsync(Func> func, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return MigrationState.None; - } - - if (!Enum.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ParseBoolAsync(Func> func, string traceId) - { - string valueString = await func(); - if (string.IsNullOrEmpty(valueString)) - { - return false; - } - - if (!bool.TryParse(valueString, out var value)) - { - TraceAndThrowException(valueString, traceId); - } - - return value; - } - - public static async Task ExecuteWithRetriesAsync(Func> asyncFunc, string traceId, string funcTag, int retryCount = 0, IEnumerable retryableExceptions = null) - { - try - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - traceId, - $"Invoking migration func - {funcTag}"); - return await ExecuteWithRetriesInternalAsync(asyncFunc, traceId, funcTag, retryCount); - } - finally - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - traceId, - $"Migration func - {funcTag} completed"); - } - } - - public static T ExecuteWithRetries(Func func, string traceId, string funcTag, int retryCount = 0, IEnumerable retryableExceptions = null) - { - try - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - traceId, - $"Invoking migration func - {funcTag}"); - return ExecuteWithRetriesInternal(func, traceId, funcTag, retryCount); - } - finally - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - traceId, - $"Migration func - {funcTag} completed"); - } - } - - public static async Task ExecuteWithRetriesAsync(Func asyncFunc, string traceId, string funcTag, int retryCount = 0, IEnumerable retryableExceptions = null) - { - await ExecuteWithRetriesAsync( - async () => - { - await asyncFunc.Invoke(); - return (object)null; - }, - traceId, - funcTag, - retryCount, - retryableExceptions); - } - - public static bool IgnoreKey(string key) - { - return key == MigrationConstants.RejectWritesKey - || key == MigrationConstants.LogicalTimestampKey; - } - - private static async Task ExecuteWithRetriesInternalAsync(Func> func, string traceId, string funcTag, int retriesLeft = 0, IEnumerable retryableExceptions = null) - { - Exception exToThrow = null; - try - { - return await func.Invoke(); - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Migration func - {funcTag} failed with exception - {ex}, retries left - {retriesLeft}"); - - exToThrow = ex; - } - - if (exToThrow != null) - { - var exMatch = retryableExceptions != null ? retryableExceptions.FirstOrDefault(type => type.IsAssignableFrom(exToThrow.GetType())) : default(Type); - if (exMatch == default(Type) || retriesLeft <= 0) - { - throw exToThrow; - } - } - - await Task.Delay(MigrationConstants.ConstantBackoffInterval); - - return await ExecuteWithRetriesInternalAsync(func, traceId, funcTag, retriesLeft - 1); - } - - private static T ExecuteWithRetriesInternal(Func func, string traceId, string funcTag, int retriesLeft = 0, IEnumerable retryableExceptions = null) - { - Exception exToThrow = null; - try - { - return func.Invoke(); - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Migration func - {funcTag} failed with exception - {ex}, retries left - {retriesLeft}"); - - exToThrow = ex; - } - - if (exToThrow != null) - { - var exMatch = retryableExceptions != null ? retryableExceptions.FirstOrDefault(type => type.IsAssignableFrom(exToThrow.GetType())) : default(Type); - if (exMatch == default(Type) || retriesLeft <= 0) - { - throw exToThrow; - } - } - - return ExecuteWithRetriesInternal(func, traceId, funcTag, retriesLeft - 1); - } - - private static void TraceAndThrowException(TData data, string traceId) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to parse {data}"); - - throw new Exception($"Failed to parse {data}"); // TODO: SFException. - } - - internal static class RC - { - private static readonly string TraceType = "MigrationUtility.RC"; - - internal static byte[] SerializeReminderCompletedData(string key, ReminderCompletedData data, string traceId) - { - try - { - var res = ReminderCompletedDataSerializer.Serialize(data); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully serialized Reminder Completed Data - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to serialize Reminder Completed Data - Key : {key}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static ReminderCompletedData DeserializeReminderCompletedData(string key, byte[] data, string traceId) - { - try - { - var res = ReminderCompletedDataSerializer.Deserialize(data); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully deserialized Reminder Completed Data - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder Completed Data - Key : {key}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static byte[] SerializeReminder(string key, ActorReminderData data, string traceId) - { - try - { - var res = ActorReminderDataSerializer.Serialize(data); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully serialized Reminder - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to serialize Reminder - Key : {key}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static ActorReminderData DeserializeReminder(string key, byte[] data, string traceId) - { - try - { - var res = ActorReminderDataSerializer.Deserialize(data); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully deserialized Reminder - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder - Key : {key}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - } - - internal static class KVS - { - private static readonly string TraceType = "MigrationUtility.KVS"; - - private static DataContractSerializer reminderSerializer = new DataContractSerializer(typeof(ActorReminderData)); - private static DataContractSerializer reminderCompletedDataSerializer = new DataContractSerializer(typeof(ReminderCompletedData)); - - internal static byte[] SerializeReminder(string key, ActorReminderData reminder, string traceId) - { - try - { - var res = Serialize(reminderSerializer, reminder); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully serialized Reminder - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder - Key : {key}, ActorId : {reminder.ActorId}, DueTime : {reminder.DueTime}, IsReadOnly : {reminder.IsReadOnly}, LogicalCreationTime : {reminder.LogicalCreationTime}, Name : {reminder.Name}, Period : {reminder.Period}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static byte[] SerializeReminderCompletedData(string key, ReminderCompletedData reminderCompletedData, string traceId) - { - try - { - var res = Serialize(reminderCompletedDataSerializer, reminderCompletedData); - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully serialized Reminder Completed data - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder Completed data - Key : {key}, {reminderCompletedData}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static ActorReminderData DeserializeReminder(string key, byte[] reminder, string traceId) - { - try - { - var res = Deserialize(reminderSerializer, reminder) as ActorReminderData; - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully deserialized Reminder - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - internal static ReminderCompletedData DeserializeReminderCompletedData(string key, byte[] reminder, string traceId) - { - try - { - var res = Deserialize(reminderCompletedDataSerializer, reminder) as ReminderCompletedData; - ActorTrace.Source.WriteNoiseWithId( - TraceType, - traceId, - $"Successfully deserialized Reminder Completed data - Key : {key}"); - - return res; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - traceId, - $"Failed to deserialize Reminder Completed Data - {key}, ErrorMessage : {ex.Message}"); - - throw ex; - } - } - - private static byte[] Serialize(DataContractSerializer serializer, T data) - { - using (var memoryStream = new MemoryStream()) - { - var binaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memoryStream); - serializer.WriteObject(binaryWriter, data); - binaryWriter.Flush(); - - return memoryStream.ToArray(); - } - } - - private static object Deserialize(DataContractSerializer serializer, byte[] data) - { - using (var memoryStream = new MemoryStream(data)) - { - var binaryReader = XmlDictionaryReader.CreateBinaryReader( - memoryStream, - XmlDictionaryReaderQuotas.Max); - - return serializer.ReadObject(binaryReader); - } - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationWorker.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationWorker.cs deleted file mode 100644 index 92b1ff91..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/MigrationWorker.cs +++ /dev/null @@ -1,310 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Runtime.Serialization.Json; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Extensions; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services.Communication.Client; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationUtility; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.PhaseInput; - using static Microsoft.ServiceFabric.Actors.Migration.PhaseResult; - - internal class MigrationWorker : WorkerBase - { - private static readonly string TraceType = typeof(MigrationWorker).Name; - private static readonly DataContractJsonSerializer ResponseSerializer = new DataContractJsonSerializer(typeof(EnumerationResponse), new[] { typeof(List) }); - private static readonly DataContractJsonSerializer Requestserializer = new DataContractJsonSerializer(typeof(EnumerationRequest)); - private ServicePartitionClient servicePartitionClient; - private MigrationSettings migrationSettings; - private ActorStateProviderHelper stateProviderHelper; - - public MigrationWorker( - KVStoRCMigrationActorStateProvider stateProvider, - ActorTypeInformation actorTypeInfo, - ServicePartitionClient servicePartitionClient, - MigrationSettings migrationSettings, - WorkerInput workerInput, - string traceId) - : base(stateProvider, workerInput, traceId) - { - this.servicePartitionClient = servicePartitionClient; - this.migrationSettings = migrationSettings; - this.stateProviderHelper = this.StateProvider.GetInternalStateProvider().GetActorStateProviderHelper(); - } - - public override async Task StartWorkAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Starting or resuming migration worker\n Input: {this.Input.ToString()}"); - - try - { - var startSN = this.Input.StartSeqNum; - if (this.Input.LastAppliedSeqNum.HasValue) - { - startSN = this.Input.LastAppliedSeqNum.Value + 1; - if (startSN > this.Input.EndSeqNum) - { - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.StateProvider.GetStateManager().CreateTransaction()) - { - await this.CompleteWorkerAsync(tx, cancellationToken); - await tx.CommitAsync(); - } - }, - "MigrationWorker.StartWorkAsync", - cancellationToken); - - return await GetResultAsync( - this.stateProviderHelper, - () => this.StateProvider.GetStateManager().CreateTransaction(), - this.MetadataDict, - this.Input.Phase, - this.Input.Iteration, - this.Input.WorkerId, - this.TraceId, - cancellationToken); - } - } - - long keysMigrated = 0L; - while (startSN <= this.Input.EndSeqNum) - { - cancellationToken.ThrowIfCancellationRequested(); - var fetchAndSaveResponse = await this.FetchAndSaveAsync(startSN, cancellationToken); - startSN = fetchAndSaveResponse.LastAppliedSequenceNumber + 1; - keysMigrated += fetchAndSaveResponse.NumberOfKeysApplied; - } - - var result = await GetResultAsync( - this.stateProviderHelper, - () => this.StateProvider.GetStateManager().CreateTransaction(), - this.MetadataDict, - this.Input.Phase, - this.Input.Iteration, - this.Input.WorkerId, - this.TraceId, - cancellationToken); - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Completed migration worker\n Result: {result.ToString()} "); - - return result; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - $"Migration worker failed with error: {ex} \n Input: /*Dump input*/"); - - throw ex; - } - } - - private async Task FetchAndSaveAsync(long startSN, CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Enumerating from KVS - StartSN: {startSN}"); - - long keysMigrated = 0L; - long laSN = -1; - EnumerationResponse enumerationResponse = null; - - try - { - cancellationToken.ThrowIfCancellationRequested(); - var response = await this.servicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - var response = await client.HttpClient.SendAsync( - this.CreateKvsApiRequestMessage(client.EndpointUri, startSN), - HttpCompletionOption.ResponseHeadersRead, - cancellationToken); - - return response; - }, - "FetchAndSaveAsync", - cancellationToken); - - using (var stream = await response.Content.ReadAsStreamAsync()) - { - using (var streamReader = new StreamReader(stream)) - { - string responseLine = await streamReader.ReadLineAsync(); - cancellationToken.ThrowIfCancellationRequested(); - while (responseLine != null) - { - cancellationToken.ThrowIfCancellationRequested(); - enumerationResponse = SerializationUtility.Deserialize(ResponseSerializer, Encoding.UTF8.GetBytes(responseLine)); - var kvsData = enumerationResponse.KeyValuePairs; - if (kvsData.Count > 0) - { - laSN = kvsData[kvsData.Count - 1].Version; - var keysMigratedInChunk = await this.StateProvider.SaveStateAsync(kvsData, cancellationToken, this.Input.Phase == MigrationPhase.Copy); - keysMigrated += keysMigratedInChunk; - await this.PostHydrationValidationAsync(enumerationResponse, cancellationToken); - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.StateProvider.GetStateManager().CreateTransaction()) - { - await this.MetadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerNoOfKeysMigrated, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - keysMigrated.ToString(), - (k, v) => - { - long currVal = ParseLong(v, this.TraceId); - return (currVal + keysMigratedInChunk).ToString(); - }, - DefaultRCTimeout, - cancellationToken); - - await this.MetadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerLastAppliedSeqNum, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - laSN.ToString(), - (_, __) => laSN.ToString(), - DefaultRCTimeout, - cancellationToken); - - if (laSN == this.Input.EndSeqNum) - { - await this.CompleteWorkerAsync(tx, cancellationToken); - } - - await tx.CommitAsync(); - } - }, - "MigrationWorker.FetchAndSaveAsync", - cancellationToken); - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"KeysFetched in chunk : {kvsData.Count}, KeysMigrated in chunk: {keysMigrated}"); - } - - responseLine = streamReader.ReadLine(); - } - } - } - - if (enumerationResponse != null && enumerationResponse.EndSequenceNumberReached) - { - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.StateProvider.GetStateManager().CreateTransaction()) - { - await this.MetadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerLastAppliedSeqNum, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - this.Input.EndSeqNum.ToString(), - (_, __) => this.Input.EndSeqNum.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.CompleteWorkerAsync(tx, cancellationToken); - await tx.CommitAsync(); - } - }, - "MigrationWorker.FetchAndSaveAsync", - cancellationToken); - } - - return new FetchAndSaveResponse - { - LastAppliedSequenceNumber = enumerationResponse == null || enumerationResponse.EndSequenceNumberReached ? this.Input.EndSeqNum : laSN, - NumberOfKeysApplied = keysMigrated, - }; - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - "Error occured while enumerating and saving data - StartSN: {0}, Exception: {1}", - startSN, - ex); - throw ex; - } - } - - private EnumerationRequest CreateEnumerationRequestObject(long startSN) - { - var req = new EnumerationRequest - { - StartSequenceNumber = startSN, - EndSequenceNumber = this.Input.EndSeqNum, - ChunkSize = this.migrationSettings.KeyValuePairsPerChunk, - NumberOfChunksPerEnumeration = this.migrationSettings.ChunksPerEnumeration, - IncludeDeletes = this.Input.Phase != MigrationPhase.Copy, - ResolveActorIdsForStateKVPairs = this.Input.Phase == MigrationPhase.Copy, - ComputeHash = this.migrationSettings.EnableDataIntegrityChecks, - }; - - return req; - } - - private HttpRequestMessage CreateKvsApiRequestMessage(Uri baseUri, long startSN) - { - var enumerationRequestContent = this.CreateEnumerationRequestObject(startSN); - - var requestBuffer = new ByteArrayContent(SerializationUtility.Serialize(Requestserializer, enumerationRequestContent)); - requestBuffer.Headers.ContentType = new MediaTypeHeaderValue("application/json") - { - CharSet = Encoding.UTF8.WebName, - }; - - return new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri(baseUri, $"{MigrationConstants.KVSMigrationControllerName}/{MigrationConstants.EnumeratebySNEndpoint}"), - Content = requestBuffer, - }; - } - - private async Task PostHydrationValidationAsync(EnumerationResponse enumerationResponse, CancellationToken cancellationToken) - { - if (this.migrationSettings.EnableDataIntegrityChecks) - { - await this.StateProvider.ValidateDataPostMigrationAsync( - enumerationResponse.KeyValuePairs, - enumerationResponse.ValueHash, - this.Input.Phase == MigrationPhase.Copy, - cancellationToken); - } - } - - internal class FetchAndSaveResponse - { - public long LastAppliedSequenceNumber { get; set; } - - public long NumberOfKeysApplied { get; set; } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationRequest.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationRequest.cs deleted file mode 100644 index 11f69a44..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationRequest.cs +++ /dev/null @@ -1,66 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models -{ - using System.ComponentModel.DataAnnotations; - using System.Runtime.Serialization; - - /// - /// EnumerationRequest - /// - [DataContract] - public class EnumerationRequest - { - /// - /// Gets or Sets start sequence number per enumeration. - /// - [DataMember] - [Required] - public long StartSequenceNumber { get; set; } - - /// - /// Gets or Sets end sequence number per enumeration. - /// - [DataMember] - [Required] - public long EndSequenceNumber { get; set; } - - /// - /// Gets or Sets ChunkSize - /// - [DataMember] - [Required] - public long ChunkSize { get; set; } - - /// - /// Gets or Sets number of chunks per enumeration. - /// - [DataMember] - [Required] - public int NumberOfChunksPerEnumeration { get; set; } - - /// - /// Gets or sets a value indicating whether to include tombstones in the response. - /// - [DataMember] - [Required] - public bool IncludeDeletes { get; set; } - - /// - /// Gets or sets a value indicating whether data intergrity checks are enabled. - /// - [DataMember] - [Required] - public bool ComputeHash { get; set; } - - /// - /// Gets or sets a value indicating whether to include actorids for actor state KV pairs. - /// - [DataMember] - [Required] - public bool ResolveActorIdsForStateKVPairs { get; set; } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationResponse.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationResponse.cs deleted file mode 100644 index d4677255..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/EnumerationResponse.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models -{ - using System.Collections.Generic; - using System.Runtime.Serialization; - - /// - /// Migration state response. - /// - [DataContract] - public class EnumerationResponse - { - /// - /// Gets the key value pairs. - /// - [DataMember] - public List KeyValuePairs { get; internal set; } - - /// - /// Gets or sets a value indicating whether the end of response is reached or not. - /// - [DataMember] - public bool EndSequenceNumberReached { get; set; } = false; - - /// - /// Gets or sets a value indicating whether the actorids are resolved from state storage key. - /// - [DataMember] - public bool ResolveActorIdsForStateKVPairs { get; set; } - - /// - /// Gets the hash for the values in the response. - /// - [DataMember] - public string ValueHash { get; internal set; } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/KeyValuePair.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/KeyValuePair.cs deleted file mode 100644 index 72bed3a7..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Models/KeyValuePair.cs +++ /dev/null @@ -1,49 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models -{ - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Json; - using System.Text; - - /// - /// KeyValuePair - /// - [DataContract] - public class KeyValuePair - { - /// - /// Gets or Sets Version - /// - [DataMember] - public long Version { get; set; } - - /// - /// Gets or Sets Key - /// - [DataMember] - public string Key { get; set; } - - /// - /// Gets or Sets Value - /// - [DataMember] - public byte[] Value { get; set; } - - /// - /// Gets or sets a value indicating whether IsDeleted - /// - [DataMember] - public bool IsDeleted { get; set; } - - /// - /// Gets or sets the actor id for the key value pair. - /// - [DataMember] - public string ActorId { get; set; } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PartitionHealthExceptionFilter.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PartitionHealthExceptionFilter.cs deleted file mode 100644 index d98fded2..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PartitionHealthExceptionFilter.cs +++ /dev/null @@ -1,170 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Fabric.Health; - - internal class PartitionHealthExceptionFilter - { - private const int MaxHealthDescriptionLength = (4 * 1024) - 1; - private Dictionary exceptions; - private MigrationSettings migrationSettings; - - public PartitionHealthExceptionFilter(MigrationSettings migrationSettings) - { - this.migrationSettings = migrationSettings; - - // All the exceptions with out an entry will be reported with health state warning and migration is aborted. - this.exceptions = new Dictionary() - { - // Ignore - { typeof(FabricNotPrimaryException).FullName, new ExceptionInfo(typeof(FabricNotPrimaryException).FullName, HealthState.Ok, false, false) }, - { typeof(OperationCanceledException).FullName, new ExceptionInfo(typeof(OperationCanceledException).FullName, HealthState.Ok, false, false) }, - - // Warning - //// Add warning list - - // Error - //// Add error list - }; - - this.PopulateExceptionListFromMigrationSettings(); - } - - public virtual void ReportPartitionHealth(Exception exception, IStatefulServicePartition partition, string healthMessage) - { - var actual = exception; - if (exception is AggregateException) - { - actual = ((AggregateException)exception).InnerException; - } - - var healthInfo = new HealthInformation("ActorStateMigration", "MigrationUnhandledException", HealthState.Warning) - { - TimeToLive = TimeSpan.MaxValue, - RemoveWhenExpired = true, - Description = this.GetPartitionHealthMesssage(actual, false, healthMessage), - }; - - partition.ReportPartitionHealth(healthInfo); - } - - public virtual void ReportPartitionHealthIfNeeded(Exception exception, IStatefulServicePartition partition, out bool abortMigration, out bool rethrow) - { - var actual = exception; - if (exception is AggregateException) - { - actual = ((AggregateException)exception).InnerException; - } - - var exceptionInfo = this.GetExceptionInfo(actual, out var rethrowT); - var healthInfo = new HealthInformation("ActorStateMigration", "MigrationUnhandledException", exceptionInfo.HealthState) - { - TimeToLive = exceptionInfo.IsPermanentError ? TimeSpan.MaxValue : TimeSpan.FromMinutes(2), - RemoveWhenExpired = true, - Description = this.GetPartitionHealthMesssage(actual, exceptionInfo.AbortMigration, null), - }; - - partition.ReportPartitionHealth(healthInfo); - abortMigration = exceptionInfo.AbortMigration; - rethrow = rethrowT; - } - - private void PopulateExceptionListFromMigrationSettings() - { - if (!string.IsNullOrWhiteSpace(this.migrationSettings.ExceptionExclusionListForAbort)) - { - var tokens = this.migrationSettings.ExceptionExclusionListForAbort.Trim().Split(','); - foreach (var token in tokens) - { - var type = token.Trim(); - this.exceptions.Add(type, new ExceptionInfo(type, HealthState.Error, false, false)); - } - } - } - - private string GetPartitionHealthMesssage(Exception exception, bool abortMigration, string healthMessage) - { - string healthDesc = string.Empty; - if (abortMigration) - { - healthDesc = "Aborting migration. "; - } - - if (!string.IsNullOrEmpty(healthMessage)) - { - healthDesc += $" {healthMessage}."; - } - - healthDesc += $"Exception message : {exception.Message}"; - - return healthDesc.Length <= MaxHealthDescriptionLength ? healthDesc : healthDesc.Substring(0, MaxHealthDescriptionLength); - } - - private ExceptionInfo GetExceptionInfo(Exception exception, out bool rethrow) - { - ExceptionInfo exceptionInfo = null; - rethrow = true; - if (!this.exceptions.TryGetValue(exception.GetType().FullName, out exceptionInfo)) - { - if (exception.Data.Contains("ActualExceptionType")) - { - if (!this.exceptions.TryGetValue((string)exception.Data["ActualExceptionType"], out exceptionInfo)) - { - exceptionInfo = new ExceptionInfo - { - ExceptionType = (string)exception.Data["ActualExceptionType"], - AbortMigration = true, - HealthState = HealthState.Warning, - IsPermanentError = true, - }; - } - - // Exception at source. Need not rethrow - rethrow = false; - } - else - { - exceptionInfo = new ExceptionInfo - { - ExceptionType = exception.GetType().FullName, - AbortMigration = true, - HealthState = HealthState.Warning, - IsPermanentError = true, - }; - } - } - - return exceptionInfo; - } - - public class ExceptionInfo - { - public ExceptionInfo() - { - } - - public ExceptionInfo(string exceptionType, HealthState healthState, bool abortMigration, bool isPermanentError) - { - this.ExceptionType = exceptionType; - this.HealthState = healthState; - this.AbortMigration = abortMigration; - this.IsPermanentError = isPermanentError; - } - - public string ExceptionType { get; set; } - - public HealthState HealthState { get; set; } - - public bool AbortMigration { get; set; } - - public bool IsPermanentError { get; set; } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PhaseInput.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PhaseInput.cs deleted file mode 100644 index 9993ef88..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/PhaseInput.cs +++ /dev/null @@ -1,113 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Json; - using System.Text; - using Microsoft.ServiceFabric.Actors.Migration; - - [DataContract] - internal class PhaseInput - { - private static DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(PhaseInput), new DataContractJsonSerializerSettings - { - UseSimpleDictionaryFormat = true, - }); - - [DataMember] - public DateTime StartDateTimeUTC { get; set; } - - [DataMember] - public DateTime? EndDateTimeUTC { get; set; } - - [DataMember] - public long StartSeqNum { get; set; } - - [DataMember] - public long EndSeqNum { get; set; } - - [DataMember] - public long? LastAppliedSeqNum { get; set; } - - [DataMember] - public MigrationState Status { get; set; } - - [DataMember] - public int WorkerCount { get; set; } - - [DataMember] - public int IterationCount { get; set; } - - [DataMember] - public MigrationPhase Phase { get; set; } - - [DataMember] - public WorkerInput[] WorkerInputs { get; set; } - - public override string ToString() - { - using (var stream = new MemoryStream()) - { - serializer.WriteObject(stream, this); - - var returnVal = Encoding.ASCII.GetString(stream.GetBuffer()); - - return returnVal; - } - } - - [DataContract] - public class WorkerInput - { - private static DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(WorkerInput), new DataContractJsonSerializerSettings - { - UseSimpleDictionaryFormat = true, - }); - - [DataMember] - public int WorkerId { get; set; } - - [DataMember] - public int Iteration { get; set; } - - [DataMember] - public DateTime StartDateTimeUTC { get; set; } - - [DataMember] - public DateTime? EndDateTimeUTC { get; set; } - - [DataMember] - public long StartSeqNum { get; set; } - - [DataMember] - public long EndSeqNum { get; set; } - - [DataMember] - public long? LastAppliedSeqNum { get; set; } - - [DataMember] - public MigrationPhase Phase { get; set; } - - [DataMember] - public MigrationState Status { get; set; } - - public override string ToString() - { - using (var stream = new MemoryStream()) - { - serializer.WriteObject(stream, this); - - var returnVal = Encoding.ASCII.GetString(stream.GetBuffer()); - - return returnVal; - } - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Properties/AssemblyInfo.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Properties/AssemblyInfo.cs deleted file mode 100644 index 0c8aa27b..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -[assembly: System.CLSCompliant(true)] diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/RCAmbiguousActorIdHandler.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/RCAmbiguousActorIdHandler.cs deleted file mode 100644 index 7d2c756c..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/RCAmbiguousActorIdHandler.cs +++ /dev/null @@ -1,109 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - - internal class RCAmbiguousActorIdHandler : AmbiguousActorIdHandlerBase - { - private IReliableCollectionsActorStateProviderInternal stateProvider; - private List resolvers; - - public RCAmbiguousActorIdHandler(IReliableCollectionsActorStateProviderInternal stateProvider) - : base(stateProvider.GetActorStateProviderHelper()) - { - this.stateProvider = stateProvider; - this.resolvers = new List(); - foreach (var type in AmbiguousActorIdResolverAttribute.GetTypesWithAttribute()) - { - this.resolvers.Add((IAmbiguousActorIdResolver)Activator.CreateInstance(type)); - } - } - - public override Task TryResolveActorIdAsync(string stateStorageKey, CancellationToken cancellationToken) - { - throw new System.NotImplementedException(); - } - - public async Task ResolveActorIdAsync(string stateStorageKey, Data.ITransaction tx, CancellationToken cancellationToken, bool skipPresenceDictResolve = false) - { - var actorIdMatch = this.StripPrefixAndSuffixTokens(stateStorageKey); - var matchList = this.GetActorIdsToResolve(actorIdMatch); - if (matchList.Count == 1) - { - var kind = GetActorIdKind(stateStorageKey); - ActorId result; - switch (kind) - { - case ActorIdKind.Long: - result = new ActorId(long.Parse(matchList[0])); - break; - case ActorIdKind.Guid: - result = new ActorId(Guid.Parse(matchList[0])); - break; - default: - result = new ActorId(matchList[0]); - break; - } - - return result; - } - - if (!skipPresenceDictResolve) - { - var presenceDict = this.stateProvider.GetActorPresenceDictionary(); - var cv = await this.TryResolveActorIdAsync( - matchList, - async (match, token) => - { - return await presenceDict.ContainsKeyAsync(tx, $"String_{match}_", MigrationConstants.DefaultRCTimeout, token); - }, - cancellationToken); - - if (cv.HasValue) - { - return new ActorId(cv.Value); - } - } - - var toResolve = stateStorageKey.Substring(stateStorageKey.IndexOf("_") + 1); - foreach (var resolver in this.resolvers) - { - cancellationToken.ThrowIfCancellationRequested(); - if (resolver.TryResolveActorIdAndStateName(toResolve, out var resolvedId)) - { - return new ActorId(resolvedId); - } - } - - throw new AmbiguousActorIdDetectedException($"Ambiguous actor id detected - _ : {toResolve}. Implement Microsoft.ServiceFabric.Actors.Runtime.Migration.IAmbiguousActorIdResolver to resolve ambiguity."); - } - - private static ActorIdKind GetActorIdKind(string key) - { - var prefix = key.Substring(0, key.IndexOf('_')); - - if (prefix == ActorIdKind.Guid.ToString()) - { - return ActorIdKind.Guid; - } - else if (prefix == ActorIdKind.Long.ToString()) - { - return ActorIdKind.Long; - } - else - { - return ActorIdKind.String; - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/ReliableDictionaryExtensions.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/ReliableDictionaryExtensions.cs deleted file mode 100644 index a7ebeea8..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/ReliableDictionaryExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Data.Collections; - - internal static class ReliableDictionaryExtensions - { - public static async Task GetAsync( - this IReliableDictionary2 dict, - Data.ITransaction tx, - string key, - TimeSpan timeout, - CancellationToken cancellationToken) - { - var result = await dict.TryGetValueAsync(tx, key, timeout, cancellationToken); - if (result.HasValue) - { - return result.Value; - } - - throw new KeyNotFoundException(key.ToString()); // TODO consider throwing SF exception. - } - - public static async Task GetValueOrDefaultAsync( - this IReliableDictionary2 dict, - Data.ITransaction tx, - string key, - TimeSpan timeout, - CancellationToken cancellationToken) - { - var result = await dict.TryGetValueAsync(tx, key, timeout, cancellationToken); - if (result.HasValue) - { - return result.Value; - } - - return null; - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SerializationUtility.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SerializationUtility.cs deleted file mode 100644 index 8aa2b3ae..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SerializationUtility.cs +++ /dev/null @@ -1,61 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Json; - using System.Xml; - - internal static class SerializationUtility - { - public static byte[] Serialize(DataContractSerializer serializer, T obj) - { - using (var memoryStream = new MemoryStream()) - { - using (var binaryWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream)) - { - serializer.WriteObject(binaryWriter, obj); - binaryWriter.Flush(); - - return memoryStream.ToArray(); - } - } - } - - public static T Deserialize(DataContractSerializer serializer, byte[] buffer) - { - using (Stream memoryStream = new MemoryStream()) - { - memoryStream.Write(buffer, 0, buffer.Length); - memoryStream.Position = 0; - - using (var reader = XmlDictionaryReader.CreateTextReader(memoryStream, XmlDictionaryReaderQuotas.Max)) - { - return (T)serializer.ReadObject(reader); - } - } - } - - public static byte[] Serialize(DataContractJsonSerializer serializer, T obj) - { - using (var memoryStream = new MemoryStream()) - { - serializer.WriteObject(memoryStream, obj); - - return memoryStream.ToArray(); - } - } - - public static T Deserialize(DataContractJsonSerializer serializer, byte[] buffer) - { - using (var memoryStream = new MemoryStream(buffer)) - { - return (T)serializer.ReadObject(memoryStream); - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SourceMigrationOrchestrator.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SourceMigrationOrchestrator.cs deleted file mode 100644 index cfae07fe..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/SourceMigrationOrchestrator.cs +++ /dev/null @@ -1,188 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Fabric; - using System.Fabric.Health; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Generator; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime; - - /// - /// Migration orchestrator for source(KVS based) service. - /// - internal class SourceMigrationOrchestrator : MigrationOrchestratorBase - { - private static readonly string TraceType = typeof(SourceMigrationOrchestrator).Name; - private KvsActorStateProvider migrationActorStateProvider; - private bool actorCallsAllowed; - private bool forwardRequest; - private ActorStateProviderHelper stateProviderHelper; - - /// - /// Initializes a new instance of the class. - /// - /// KVS actor state provider. - /// The type information of the Actor. - /// Service context the actor service is operating under. - /// Migration settings. - public SourceMigrationOrchestrator(IActorStateProvider stateProvider, ActorTypeInformation actorTypeInfo, StatefulServiceContext serviceContext, Actors.Runtime.Migration.MigrationSettings migrationSettings) - : base(serviceContext, actorTypeInfo, migrationSettings) - { - if (stateProvider.GetType() != typeof(KvsActorStateProvider)) - { - var errorMsg = $"{stateProvider.GetType()} not a valid state provider type for source of migration. {typeof(KvsActorStateProvider)} is the valid type."; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationStateProviderException(errorMsg); - } - - this.actorCallsAllowed = false; - this.forwardRequest = false; - this.migrationActorStateProvider = stateProvider as KvsActorStateProvider; - this.stateProviderHelper = new ActorStateProviderHelper(this.migrationActorStateProvider); - } - - /// - public override async Task AbortMigrationAsync(bool userTriggered, CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "Aborting Migration"); - - await this.migrationActorStateProvider.ResumeWritesAsync(this.TraceId, cancellationToken); - this.actorCallsAllowed = true; - this.forwardRequest = false; - await this.InvokeCompletionCallback(this.actorCallsAllowed, cancellationToken); - } - - /// - public override bool AreActorCallsAllowed() - { - return this.actorCallsAllowed; - } - - /// - public override IActorStateProvider GetMigrationActorStateProvider() - { - return this.migrationActorStateProvider; - } - - /// - public override async Task StartDowntimeAsync(bool userTriggered, CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "Starting Downtime"); - - await this.migrationActorStateProvider.RejectWritesAsync(this.TraceId, cancellationToken); - this.actorCallsAllowed = false; - this.forwardRequest = true; - - await this.InvokeCompletionCallback(this.actorCallsAllowed, cancellationToken); - } - - /// - public override async Task StartMigrationAsync(bool userTriggered, CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "Starting Migration"); - - await this.StartOrResumeMigrationAsync(cancellationToken); - } - - public override bool IsActorCallToBeForwarded() - { - return this.forwardRequest && this.MigrationSettings.TargetServiceUri != null; - } - - public override void ThrowIfActorCallsDisallowed() - { - if (this.actorCallsAllowed) - { - return; - } - - var errorMsg = $"Actor calls are not allowed on the service."; - if (this.MigrationSettings.TargetServiceUri == null) - { - errorMsg += $" Configure TargetServiceUri in {this.MigrationSettings.MigrationConfigSectionName} section of settings file to forward the request."; - } - - throw new ActorCallsDisallowedException(errorMsg); - } - - public override bool IsAutoStartMigration() - { - return true; - } - - protected override Uri GetForwardServiceUri() - { - return this.MigrationSettings.TargetServiceUri; - } - - protected override Int64RangePartitionInformation GetInt64RangePartitionInformation() - { - var servicePartition = this.migrationActorStateProvider.StatefulServicePartition; - return servicePartition.PartitionInfo as Int64RangePartitionInformation; - } - - /// - protected override string GetMigrationEndpointName() - { - // TODO: Validate migration EP in service manifest - return ActorNameFormat.GetMigrationSourceEndpointName(this.ActorTypeInformation.ImplementationType); - } - - private async Task AreActorCallsAllowedInternalAsync(CancellationToken cancellationToken) - { - return await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => !(await this.migrationActorStateProvider.GetRejectWriteStateAsync(this.TraceId, cancellationToken)), - "KVSActorStateProvider.GetRejectWriteState", - CancellationToken.None); - } - - private async Task StartOrResumeMigrationAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "Starting or resuming Migration"); - - this.actorCallsAllowed = await this.AreActorCallsAllowedInternalAsync(cancellationToken); - this.forwardRequest = !this.actorCallsAllowed; - if (!this.migrationActorStateProvider.IsTombstoneCleanupDisabled()) - { - ActorTrace.Source.WriteWarningWithId( - TraceType, - this.TraceId, - "Tombstone cleanup is not enabled."); - - var healthInfo = new HealthInformation("ActorStateMigration", "ActorStateMigrationChecks", HealthState.Warning) - { - TimeToLive = TimeSpan.MaxValue, - RemoveWhenExpired = false, - Description = KvsActorStateProviderExtensions.TombstoneCleanupMessage, - }; - - this.migrationActorStateProvider.ReportPartitionHealth(healthInfo); - } - - await this.InvokeCompletionCallback(this.actorCallsAllowed, cancellationToken); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Startup.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Startup.cs deleted file mode 100644 index 9240df2b..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Startup.cs +++ /dev/null @@ -1,49 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.Infrastructure; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Controllers; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Middleware; - - internal class Startup - { - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddMvc() - .SetCompatibilityVersion(CompatibilityVersion.Version_2_0) - .ConfigureApplicationPartManager(manager => - { - // load internal migration controllers - manager.FeatureProviders.Add(new MigrationControllerFeatureProvider()); - }); - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseStatusCodePages(); - app.UseMiddleware(); - app.UseMvc(); - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/TargetMigrationOrchestrator.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/TargetMigrationOrchestrator.cs deleted file mode 100644 index 1ad1eba4..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/TargetMigrationOrchestrator.cs +++ /dev/null @@ -1,999 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Fabric.Description; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Generator; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Extensions; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - using Microsoft.ServiceFabric.Data.Collections; - using Microsoft.ServiceFabric.Services.Client; - using Microsoft.ServiceFabric.Services.Communication.Client; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationUtility; - - /// - /// Orchestrator for Target(RC based) service. - /// - internal class TargetMigrationOrchestrator : MigrationOrchestratorBase - { - private static readonly string TraceType = typeof(TargetMigrationOrchestrator).Name; - private volatile int isReadyForMigrationOperations = 0; - private volatile int isMigrationWorkflowRunning = 0; - private MigrationPhase currentPhase; - private KVStoRCMigrationActorStateProvider migrationActorStateProvider; - private IReliableDictionary2 metadataDict; - private ServicePartitionClient partitionClient; - private volatile MigrationState currentMigrationState; - private CancellationTokenSource childCancellationTokenSource; - private PartitionHealthExceptionFilter exceptionFilter; - private ActorStateProviderHelper stateProviderHelper; - private volatile int validationComplete = 0; - - /// - /// Initializes a new instance of the class. - /// - /// KVS actor state provider. - /// The type information of the Actor. - /// Service context the actor service is operating under. - /// Migration settings. - public TargetMigrationOrchestrator(IActorStateProvider stateProvider, ActorTypeInformation actorTypeInfo, StatefulServiceContext serviceContext, Actors.Runtime.Migration.MigrationSettings migrationSettings) - : base(serviceContext, actorTypeInfo, migrationSettings) - { - if (stateProvider.GetType() != typeof(ReliableCollectionsActorStateProvider)) - { - var errorMsg = $"{stateProvider.GetType()} not a valid state provider type for source of migration. {typeof(ReliableCollectionsActorStateProvider)} is the valid type."; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationStateProviderException(errorMsg); - } - - this.currentPhase = MigrationPhase.None; - this.currentMigrationState = MigrationState.None; - this.migrationActorStateProvider = new KVStoRCMigrationActorStateProvider(stateProvider as ReliableCollectionsActorStateProvider); - this.stateProviderHelper = (stateProvider as IReliableCollectionsActorStateProviderInternal).GetActorStateProviderHelper(); - this.exceptionFilter = new PartitionHealthExceptionFilter(this.MigrationSettings); - } - - // Constructor for UTs. - public TargetMigrationOrchestrator( - KVStoRCMigrationActorStateProvider migrationActorStateProviderProvider, - ActorTypeInformation actorTypeInfo, - StatefulServiceContext serviceContext, - Actors.Runtime.Migration.MigrationSettings migrationSettings, - PartitionHealthExceptionFilter exceptionFilter, - ServicePartitionClient partitionClient, - string traceId) - : base(serviceContext, actorTypeInfo, migrationSettings, traceId) - { - this.currentPhase = MigrationPhase.None; - this.currentMigrationState = MigrationState.None; - this.migrationActorStateProvider = migrationActorStateProviderProvider; - this.stateProviderHelper = migrationActorStateProviderProvider.GetInternalStateProvider().GetActorStateProviderHelper(); - this.exceptionFilter = exceptionFilter; - this.partitionClient = partitionClient; - } - - internal Data.ITransaction Transaction { get => this.migrationActorStateProvider.GetStateManager().CreateTransaction(); } - - internal IReliableDictionary2 MetaDataDictionary { get => this.metadataDict; } - - internal ServicePartitionClient ServicePartitionClient - { - get - { - if (this.partitionClient == null) - { //// TODO: Operation retry settings - var partitionInformation = this.GetInt64RangePartitionInformation(); - this.partitionClient = new ServicePartitionClient( - new HttpCommunicationClientFactory(null, new List() { new HttpExceptionHandler() }), - this.MigrationSettings.SourceServiceUri, - new ServicePartitionKey(partitionInformation.LowKey), - TargetReplicaSelector.PrimaryReplica, - Constants.MigrationListenerName); - } - - return this.partitionClient; - } - } - - internal KVStoRCMigrationActorStateProvider StateProvider { get => this.migrationActorStateProvider; } - - public async override Task StartMigrationAsync(bool isUserTriggered, CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"StartMigrationAsync isUserTriggered : {isUserTriggered}, MigrationMode : {this.MigrationSettings.MigrationMode}"); - - if (Interlocked.CompareExchange(ref this.validationComplete, 1, 0) == 0) - { - await this.ValidateConfigForMigrationAsync(cancellationToken); - } - - try - { - if (!isUserTriggered) - { - this.metadataDict = await this.migrationActorStateProvider.GetMetadataDictionaryAsync(); - this.childCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - this.currentMigrationState = await this.GetCurrentMigrationStateAsync(this.childCancellationTokenSource.Token); - Interlocked.CompareExchange(ref this.isReadyForMigrationOperations, 1, 0); - this.childCancellationTokenSource.Token.Register(() => - { - // Interlocked.CompareExchange(ref this.isReadyForMigrationOperations, 0, 1); - Interlocked.CompareExchange(ref this.isMigrationWorkflowRunning, 0, 1); - }); - } - else - { - if (this.MigrationSettings.MigrationMode == MigrationMode.Auto) - { - throw new InvalidMigrationOperationException("MigrationMode is set to Auto. Manual migration starts are not allowed."); - } - else - { - this.ThrowIfNotReady(); - } - } - - var childToken = this.childCancellationTokenSource.Token; - if (this.currentMigrationState == MigrationState.Aborted) - { - var abortInvokedOnSource = await ParseBoolAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, AbortMigrationInvokedOnSource, cancellationToken), - this.TraceId); - - if (!abortInvokedOnSource) - { - await this.AbortMigrationAsync(false, childToken); - } - - return; - } - - if (this.MigrationSettings.MigrationMode == MigrationMode.Auto) - { - if (this.currentMigrationState == MigrationState.None) - { - await this.InitializeAsync(childToken); - } - - await this.StartOrResumeMigrationAsync(childToken); - } - else - { - if (isUserTriggered) - { - if (this.currentMigrationState == MigrationState.None) - { - await this.InitializeAsync(childToken); - - // TODO: Bug fix: 15538471 - StartOrResumeMigrationAsync should not be awaited for Manual Mode Migration - await this.StartOrResumeMigrationAsync(childToken); - } - else - { - throw new InvalidMigrationOperationException("Migration is either in progress or already completed"); - } - } - else - { - if (this.currentMigrationState == MigrationState.InProgress) - { - await this.StartOrResumeMigrationAsync(childToken); - } - else - { - return; - } - } - } - } - catch (Exception ex) - { - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - $"Migration {this.currentPhase} Phase failed with error: {ex}"); - MigrationTelemetry.MigrationFailureEvent(this.StatefulServiceContext, this.currentPhase.ToString(), ex.Message); - //// TODO: Emit exception type in telemetry - - this.exceptionFilter.ReportPartitionHealthIfNeeded(ex, this.StateProvider.StatefulServicePartition, out var abortMigration, out var rethrow); - if (abortMigration) - { - await this.AbortMigrationAsync(userTriggered: false, this.GetToken()); - } - - if (rethrow) - { - throw ex; - } - } - finally - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"StartMigrationAsync Completed - isUserTriggered : {isUserTriggered}, MigrationMode : {this.MigrationSettings.MigrationMode}"); - - await this.InvokeCompletionCallback(this.AreActorCallsAllowed(), this.GetToken()); - } - } - - public async override Task AbortMigrationAsync(bool userTriggered, CancellationToken cancellationToken) - { - this.ThrowIfNotReady(); - if (userTriggered) - { - this.ThrowIfInvalidOperation(); - if (this.childCancellationTokenSource != null) - { - this.childCancellationTokenSource.Cancel(); - } - } - - try - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Aborting Migration. UserTriggered : {userTriggered}"); - MigrationTelemetry.MigrationAbortEvent(this.StatefulServiceContext, userTriggered); - - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - await this.metadataDict.TryAddAsync( - tx, - MigrationEndDateTimeUTC, - DateTime.UtcNow.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.AddOrUpdateAsync( - tx, - MigrationCurrentStatus, - MigrationState.Aborted.ToString(), - (_, __) => MigrationState.Aborted.ToString(), - DefaultRCTimeout, - cancellationToken); - - await tx.CommitAsync(); - } - }, - "TargetMigrationOrchestrator.AbortMigrationAsync", - cancellationToken); - this.currentMigrationState = MigrationState.Aborted; - - await this.InvokeResumeWritesAsync(cancellationToken); - } - catch (Exception ex) - { - this.exceptionFilter.ReportPartitionHealth(ex, this.StateProvider.StatefulServicePartition, "Aborting Migration workflow failed."); - - throw ex; - } - } - - /// - public override bool AreActorCallsAllowed() - { - return this.currentMigrationState == MigrationState.Completed; - } - - /// - public override IActorStateProvider GetMigrationActorStateProvider() - { - return this.migrationActorStateProvider; - } - - /// - public override async Task StartDowntimeAsync(bool userTriggered, CancellationToken cancellationToken) - { - this.ThrowIfNotReady(); - this.ThrowIfInvalidOperation(); - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - await this.metadataDict.TryAddAsync( - tx, - IsDowntimeInvoked, - true.ToString(), - DefaultRCTimeout, - cancellationToken); - - await tx.CommitAsync(); - } - }, - "TargetMigrationOrchestrator.StartDowntimeAsync", - cancellationToken); - } - - public override bool IsActorCallToBeForwarded() - { - this.ThrowIfNotReady(); - - // No reason to forward the call in downtime phase as the KVS service also cannot service the request. - return (this.currentPhase < MigrationPhase.Downtime || this.currentMigrationState == MigrationState.Aborted) && this.MigrationSettings.SourceServiceUri != null; - } - - public override void ThrowIfActorCallsDisallowed() - { - if (this.AreActorCallsAllowed()) - { - return; - } - - var errorMsg = $"Actor calls are not allowed on the service."; - if (this.MigrationSettings.SourceServiceUri == null) - { - errorMsg += $" Configure SourceServiceUri in {this.MigrationSettings.MigrationConfigSectionName} section of settings file to forward the request."; - } - - throw new ActorCallsDisallowedException(errorMsg); - } - - internal async Task GetResultAsync(CancellationToken cancellationToken) - { - this.ThrowIfNotReady(); - - ActorTrace.Source.WriteNoiseWithId( - TraceType, - this.TraceId, - $"Getting migration result."); - - var startSN = await this.GetStartSequenceNumberAsync(cancellationToken); - var endSN = await this.GetEndSequenceNumberAsync(cancellationToken); - var status = await this.GetCurrentMigrationStateAsync(cancellationToken); - if (status == MigrationState.None) - { - return new MigrationResult - { - MigrationMode = this.MigrationSettings.MigrationMode, - CurrentPhase = MigrationPhase.None, - Status = MigrationState.None, - StartSeqNum = startSN, - EndSeqNum = endSN, - }; - } - - var result = new MigrationResult - { - MigrationMode = this.MigrationSettings.MigrationMode, - Status = status, - EndSeqNum = endSN, - }; - - result.CurrentPhase = await this.GetCurrentMigrationPhaseAsync(cancellationToken); - - result.StartDateTimeUTC = await ParseDateTimeAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationStartDateTimeUTC, cancellationToken), - this.TraceId); - - result.EndDateTimeUTC = await ParseDateTimeAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationEndDateTimeUTC, cancellationToken), - this.TraceId); - - result.StartSeqNum = await ParseLongAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationStartSeqNum, cancellationToken), - this.TraceId); - - result.LastAppliedSeqNum = await ParseLongAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationLastAppliedSeqNum, cancellationToken), - this.TraceId); - - result.NoOfKeysMigrated = await ParseLongAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationNoOfKeysMigrated, cancellationToken), - this.TraceId); - - var currentPhase = MigrationPhase.Copy; - var phaseResults = new List(); - while (currentPhase <= result.CurrentPhase) - { - var currentIteration = await ParseIntAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, Key(PhaseIterationCount, currentPhase), cancellationToken), - 0, - this.TraceId); - for (int i = 1; i <= currentIteration; i++) - { - phaseResults.Add(await MigrationPhaseWorkloadBase.GetResultAsync( - this.stateProviderHelper, - () => this.Transaction, - this.MetaDataDictionary, - currentPhase, - i, - this.TraceId, - cancellationToken)); - } - - currentPhase++; - } - - result.PhaseResults = phaseResults.ToArray(); - - return result; - } - - internal virtual IMigrationPhaseWorkload GetMigrationPhaseWorkload(MigrationPhase currentPhase, int currentIteration) - { - IMigrationPhaseWorkload migrationWorkload = null; - switch (currentPhase) - { - case MigrationPhase.None: - case MigrationPhase.Copy: - migrationWorkload = new CopyPhaseWorkload( - this.StateProvider, - this.ServicePartitionClient, - this.StatefulServiceContext, - this.MigrationSettings, - this.ActorTypeInformation, - this.TraceId); - break; - case MigrationPhase.Catchup: - migrationWorkload = new CatchupPhaseWorkload( - currentIteration, - this.StateProvider, - this.ServicePartitionClient, - this.StatefulServiceContext, - this.MigrationSettings, - this.ActorTypeInformation, - this.TraceId); - break; - case MigrationPhase.Downtime: - migrationWorkload = new DowntimeWorkload( - this.StateProvider, - this.ServicePartitionClient, - this.StatefulServiceContext, - this.MigrationSettings, - this.ActorTypeInformation, - this.TraceId); - break; - default: - migrationWorkload = null; - break; - } - - return migrationWorkload; - } - - internal virtual async Task ValidateConfigForMigrationAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Validating Migration config."); - - var migrationConfigSectionName = this.MigrationSettings.MigrationConfigSectionName; - - this.MigrationSettings.Validate(isSource: false); - var fabricClient = new FabricClient(); - var kvsServiceDescription = await fabricClient.ServiceManager.GetServiceDescriptionAsync(this.MigrationSettings.SourceServiceUri); - if (kvsServiceDescription == null) - { - var errorMsg = $"Unable to load service description for migration service name - {this.MigrationSettings.SourceServiceUri}."; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationConfigException(errorMsg); - } - - var kvsServicePartitionCount = this.GetServicePartitionCount(kvsServiceDescription); - var rcServiceDescription = await fabricClient.ServiceManager.GetServiceDescriptionAsync(this.StatefulServiceContext.ServiceName); - var rcServicePartitionCount = this.GetServicePartitionCount(rcServiceDescription); - var isDisableTombstoneCleanup = await this.GetKVSDisableTombstoneCleanupSettingAsync(cancellationToken); - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "kvsServiceDescription.PartitionSchemeDescription.Scheme = {0}; kvsServicePartitionCount = {1}; rcServiceDescription.PartitionSchemeDescription.Scheme = {2}; rcServicePartitionCount = {3}; isDisableTombstoneCleanup = {4}", - kvsServiceDescription.PartitionSchemeDescription.Scheme, - kvsServicePartitionCount, - rcServiceDescription.PartitionSchemeDescription.Scheme, - rcServicePartitionCount, - isDisableTombstoneCleanup); - - if (kvsServiceDescription.PartitionSchemeDescription.Scheme != rcServiceDescription.PartitionSchemeDescription.Scheme) - { - var errorMsg = $"Source migration service({this.MigrationSettings.SourceServiceUri}) partition scheme({kvsServiceDescription.PartitionSchemeDescription.Scheme}) does not match with target migration service({this.MigrationSettings.TargetServiceUri}) partition scheme({rcServiceDescription.PartitionSchemeDescription.Scheme})"; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationConfigException(errorMsg); - } - - if (kvsServicePartitionCount != rcServicePartitionCount) - { - var errorMsg = $"Source migration service({this.MigrationSettings.SourceServiceUri}) partition count({kvsServicePartitionCount}) does not match with target migration service({this.MigrationSettings.TargetServiceUri}) partition count({rcServicePartitionCount})"; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationConfigException(errorMsg); - } - - if (!isDisableTombstoneCleanup) - { - var errorMsg = $"DisableTombstoneCleanup is not disabled in source migration service({this.MigrationSettings.SourceServiceUri})"; - ActorTrace.Source.WriteErrorWithId( - TraceType, - this.TraceId, - errorMsg); - - throw new InvalidMigrationConfigException(errorMsg); - } - //// TODO: Emit telemetry - } - - protected override Uri GetForwardServiceUri() - { - return this.MigrationSettings.SourceServiceUri; - } - - protected override Int64RangePartitionInformation GetInt64RangePartitionInformation() - { - var servicePartition = this.migrationActorStateProvider.StatefulServicePartition; - if (servicePartition == null) - { - // TODO throw - } - - return servicePartition.PartitionInfo as Int64RangePartitionInformation; - } - - /// - protected override string GetMigrationEndpointName() - { - // TODO: Validate migration EP in service manifest - return ActorNameFormat.GetMigrationTargetEndpointName(this.ActorTypeInformation.ImplementationType); - } - - private CancellationToken GetToken() - { - var token = CancellationToken.None; - if (this.childCancellationTokenSource != null && !this.childCancellationTokenSource.IsCancellationRequested) - { - var token1 = this.childCancellationTokenSource.Token; - if (!token1.IsCancellationRequested) - { - return token1; - } - } - - return token; - } - - private async Task StartOrResumeMigrationAsync(CancellationToken cancellationToken) - { - if (Interlocked.CompareExchange(ref this.isMigrationWorkflowRunning, 1, 0) != 0) - { - ActorTrace.Source.WriteWarningWithId( - TraceType, - this.TraceId, - "Migration workflow already running. Ignoring the request."); - - return; - } - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - "Starting or resuming migration Migration."); - MigrationTelemetry.MigrationStartEvent(this.StatefulServiceContext, this.MigrationSettings.ToString()); - - var migrationCurrentPhase = await this.GetCurrentMigrationPhaseAsync(cancellationToken); - var workloadRunner = await this.NextWorkloadRunnerAsync(migrationCurrentPhase, cancellationToken); - - PhaseResult currentResult = null; - while (workloadRunner != null) - { - this.currentPhase = workloadRunner.Phase; - currentResult = await workloadRunner.StartOrResumeMigrationAsync(cancellationToken); - workloadRunner = await this.NextWorkloadRunnerAsync(currentResult, cancellationToken); - } - - if (currentResult != null) - { - await this.CompleteMigrationAsync(currentResult, cancellationToken); - this.currentPhase = MigrationPhase.Completed; - - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Migration successfully completed - {currentResult.ToString()}"); - MigrationTelemetry.MigrationEndEvent(this.StatefulServiceContext, currentResult.ToString()); - } - } - - private async Task GetCurrentMigrationPhaseAsync(CancellationToken cancellationToken) - { - return await ParseMigrationPhaseAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationCurrentPhase, cancellationToken), - this.TraceId); - } - - private async Task GetCurrentMigrationStateAsync(CancellationToken cancellationToken) - { - return await ParseMigrationStateAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, MigrationCurrentStatus, cancellationToken), - this.TraceId); - } - - private async Task CompleteMigrationAsync(PhaseResult result, CancellationToken cancellationToken) - { - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - await this.metadataDict.TryAddAsync( - tx, - MigrationEndDateTimeUTC, - DateTime.UtcNow.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.TryAddAsync( - tx, - MigrationEndSeqNum, - result.EndSeqNum.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.AddOrUpdateAsync( - tx, - MigrationCurrentStatus, - MigrationState.Completed.ToString(), - (_, __) => MigrationState.Completed.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.AddOrUpdateAsync( - tx, - MigrationCurrentPhase, - MigrationPhase.Completed.ToString(), - (_, __) => MigrationPhase.Completed.ToString(), - DefaultRCTimeout, - cancellationToken); - - await tx.CommitAsync(); - } - }, - "TargetMigrationOrchestrator.CompleteMigrationAsync", - cancellationToken); - - this.currentMigrationState = MigrationState.Completed; - Interlocked.CompareExchange(ref this.isMigrationWorkflowRunning, 0, 1); - } - - private async Task GetEndSequenceNumberAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteNoiseWithId( - TraceType, - this.TraceId, - $"Getting End Seq num."); - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.GetAsync($"{KVSMigrationControllerName}/{GetEndSNEndpoint}"); - }, - "GetEndSequenceNumberAsync", - cancellationToken); - return (await ParseLongAsync(async () => await response.Content.ReadAsStringAsync(), this.TraceId)).Value; - } - - private async Task GetStartSequenceNumberAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Gettting start seq num."); - - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.GetAsync($"{KVSMigrationControllerName}/{GetStartSNEndpoint}"); - }, - "GetStartSequenceNumberAsync", - cancellationToken); - return (await ParseLongAsync(async () => await response.Content.ReadAsStringAsync(), this.TraceId)).Value; - } - - private async Task InvokeRejectWritesAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Invoking reject writes in source migration service."); - - try - { - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.PutAsync($"{KVSMigrationControllerName}/{StartDowntimeEndpoint}", null); - }, - "InvokeRejectWritesAsync", - cancellationToken); - } - catch (FabricException ex) - { - if (ex.ErrorCode == FabricErrorCode.DatabaseMigrationInProgress) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Downtime is already invoked on source migration service."); - } - else - { - throw ex; - } - } - } - - private async Task InvokeResumeWritesAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Invoking resume writes in source migration service."); - - try - { - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.PutAsync($"{KVSMigrationControllerName}/{AbortMigrationEndpoint}", null); - }, - "InvokeResumeWritesAsync", - cancellationToken); - } - catch (FabricException ex) - { - if (ex.ErrorCode == FabricErrorCode.DatabaseMigrationInProgress) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Source migration service is rejecting writes."); - - await this.RestartPrimaryAndInvokeResumeWritesAsync(cancellationToken); - } - else - { - throw ex; - } - } - - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - await this.MetaDataDictionary.AddOrUpdateAsync( - tx, - AbortMigrationInvokedOnSource, - bool.TrueString, - (_, __) => bool.TrueString, - DefaultRCTimeout, - cancellationToken); - - await tx.CommitAsync(); - } - }, - $"TargetMigrationOrchestrator.NextWorkloadRunnerAsync.{Key(PhaseIterationCount, MigrationPhase.Catchup)}", - cancellationToken); - } - - private async Task RestartPrimaryAndInvokeResumeWritesAsync(CancellationToken cancellationToken) - { - await this.RestartPrimaryReplicaAsync(cancellationToken); - - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.PutAsync($"{KVSMigrationControllerName}/{AbortMigrationEndpoint}", null); - }, - "InvokeResumeWritesAsync", - cancellationToken); - } - - private async Task NextWorkloadRunnerAsync(PhaseResult currentResult, CancellationToken cancellationToken) - { - var endSN = await this.GetEndSequenceNumberAsync(cancellationToken); - var delta = endSN - currentResult.EndSeqNum; - var isDowntimeInvoked = false; - - if (currentResult.Phase == MigrationPhase.Catchup) - { - if (!this.IsAutoStartMigration()) - { - isDowntimeInvoked = await ParseBoolAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, IsDowntimeInvoked, cancellationToken), - this.TraceId); - } - else if (delta < this.MigrationSettings.DowntimeThreshold) - { - isDowntimeInvoked = true; - } - - if (isDowntimeInvoked) - { - await this.InvokeRejectWritesAsync(cancellationToken); - return await this.NextWorkloadRunnerAsync(MigrationPhase.Downtime, cancellationToken); - } - else - { - return await this.NextWorkloadRunnerAsync(MigrationPhase.Catchup, cancellationToken); - } - } - - return await this.NextWorkloadRunnerAsync(currentResult.Phase + 1, cancellationToken); - } - - private async Task NextWorkloadRunnerAsync(MigrationPhase currentPhase, CancellationToken cancellationToken) - { - var currentIteration = 0; - if (currentPhase == MigrationPhase.Catchup) - { - currentIteration = await ParseIntAsync( - () => this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - var res = await this.MetaDataDictionary.GetOrAddAsync( - tx, - Key(PhaseIterationCount, MigrationPhase.Catchup), - "1", - DefaultRCTimeout, - cancellationToken); - await tx.CommitAsync(); - - return res; - } - }, - $"TargetMigrationOrchestrator.NextWorkloadRunnerAsync.{Key(PhaseIterationCount, MigrationPhase.Catchup)}", - cancellationToken), - this.TraceId); - - var status = await ParseMigrationStateAsync( - () => GetValueOrDefaultAsync(this.stateProviderHelper, () => this.Transaction, this.MetaDataDictionary, Key(PhaseCurrentStatus, MigrationPhase.Catchup, currentIteration), cancellationToken), - this.TraceId); - if (status == MigrationState.Completed) - { - currentIteration++; - } - } - - return this.GetMigrationPhaseWorkload(currentPhase, currentIteration); - } - - private async Task InitializeAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Initializing Migration."); - - await this.stateProviderHelper.ExecuteWithRetriesAsync( - async () => - { - using (var tx = this.Transaction) - { - await this.MetaDataDictionary.TryAddAsync( - tx, - MigrationStartDateTimeUTC, - DateTime.UtcNow.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.MetaDataDictionary.TryAddAsync( - tx, - MigrationCurrentStatus, - MigrationState.InProgress.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.MetaDataDictionary.TryAddAsync( - tx, - MigrationCurrentPhase, - MigrationPhase.None.ToString(), - DefaultRCTimeout, - cancellationToken); - - await tx.CommitAsync(); - } - }, - "TargetMigrationOrchestrator.InitializeAsync", - cancellationToken); - - this.currentMigrationState = MigrationState.InProgress; - } - - private async Task RestartPrimaryReplicaAsync(CancellationToken cancellationToken) - { - var partitionInformation = this.GetInt64RangePartitionInformation(); - var lowkey = partitionInformation.LowKey; - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Attempting to restart primary replica of partition - {partitionInformation}"); - var faultManagementClient = new FabricClient().FaultManager; - await faultManagementClient.RestartReplicaAsync(ReplicaSelector.PrimaryOf(PartitionSelector.PartitionKeyOf(this.MigrationSettings.SourceServiceUri, lowkey)), CompletionMode.Verify, cancellationToken); - } - - private int GetServicePartitionCount(ServiceDescription serviceDescription) - { - switch (serviceDescription.PartitionSchemeDescription.Scheme) - { - case PartitionScheme.Singleton: - return 1; - case PartitionScheme.UniformInt64Range: - return (serviceDescription.PartitionSchemeDescription as UniformInt64RangePartitionSchemeDescription).PartitionCount; - case PartitionScheme.Named: - return (serviceDescription.PartitionSchemeDescription as NamedPartitionSchemeDescription).PartitionNames.Count; - case PartitionScheme.Invalid: - default: - return 0; - } - } - - private async Task GetKVSDisableTombstoneCleanupSettingAsync(CancellationToken cancellationToken) - { - ActorTrace.Source.WriteInfoWithId( - TraceType, - this.TraceId, - $"Getting tombstone cleanup setting"); - - var response = await this.ServicePartitionClient.InvokeWebRequestWithRetryAsync( - async client => - { - return await client.HttpClient.GetAsync($"{KVSMigrationControllerName}/{GetDisableTCSEndpoint}"); - }, - "GetDisableTCSEndpoint", - cancellationToken); - return (await ParseBoolAsync(async () => await response.Content.ReadAsStringAsync(), this.TraceId)); - } - - private void ThrowIfNotReady() - { - if (this.isReadyForMigrationOperations != 1) - { - throw new MigrationFrameworkNotInitializedException("MigrationFramework not initialized. Retry the request"); - } - } - - private void ThrowIfInvalidOperation() - { - if (this.currentMigrationState == MigrationState.Completed || this.currentMigrationState == MigrationState.Aborted) - { - throw new InvalidMigrationOperationException($"Operation not allowed when current nigration state is {this.currentMigrationState}"); - } - } - } -} diff --git a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/WorkerBase.cs b/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/WorkerBase.cs deleted file mode 100644 index 9c288e75..00000000 --- a/src/Microsoft.ServiceFabric.Actors.KVSToRCMigration/WorkerBase.cs +++ /dev/null @@ -1,141 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.KVSToRCMigration -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Data; - using Microsoft.ServiceFabric.Data.Collections; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationConstants; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.MigrationUtility; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.PhaseInput; - using static Microsoft.ServiceFabric.Actors.Migration.PhaseResult; - - internal class WorkerBase : IWorker - { - private static readonly string TraceType = typeof(WorkerBase).Name; - private KVStoRCMigrationActorStateProvider stateProvider; - private IReliableDictionary2 metadataDict; - private WorkerInput workerInput; - private string traceId; - - public WorkerBase( - KVStoRCMigrationActorStateProvider stateProvider, - WorkerInput workerInput, - string traceId) - { - this.stateProvider = stateProvider; - this.workerInput = workerInput; - this.metadataDict = this.stateProvider.GetMetadataDictionaryAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - this.traceId = traceId; - } - - public IReliableDictionary2 MetadataDict { get => this.metadataDict; } - - public KVStoRCMigrationActorStateProvider StateProvider { get => this.stateProvider; } - - public WorkerInput Input { get => this.workerInput; } - - public string TraceId { get => this.traceId; } - - public static async Task GetResultAsync( - ActorStateProviderHelper stateProviderHelper, - Func txFactory, - IReliableDictionary2 metadataDict, - MigrationPhase migrationPhase, - int currentIteration, - int workerId, - string traceId, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var status = await ParseMigrationStateAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerCurrentStatus, migrationPhase, currentIteration, workerId), cancellationToken), - traceId); - - if (status == MigrationState.None) - { - return new WorkerResult - { - Phase = migrationPhase, - Iteration = currentIteration, - WorkerId = workerId, - Status = MigrationState.None, - }; - } - - var workerResult = new WorkerResult - { - Status = status, - Phase = migrationPhase, - Iteration = currentIteration, - WorkerId = workerId, - }; - - workerResult.StartDateTimeUTC = (await ParseDateTimeAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerStartDateTimeUTC, migrationPhase, currentIteration, workerId), cancellationToken), - traceId)).Value; - - workerResult.EndDateTimeUTC = await ParseDateTimeAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerEndDateTimeUTC, migrationPhase, currentIteration, workerId), cancellationToken), - traceId); - - workerResult.StartSeqNum = (await ParseLongAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerStartSeqNum, migrationPhase, currentIteration, workerId), cancellationToken), - traceId)).Value; - - workerResult.EndSeqNum = (await ParseLongAsync( - () => GetValueAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerEndSeqNum, migrationPhase, currentIteration, workerId), cancellationToken), - traceId)).Value; - - workerResult.LastAppliedSeqNum = await ParseLongAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerLastAppliedSeqNum, migrationPhase, currentIteration, workerId), cancellationToken), - traceId); - - workerResult.NoOfKeysMigrated = await ParseLongAsync( - () => GetValueOrDefaultAsync(stateProviderHelper, txFactory, metadataDict, Key(PhaseWorkerNoOfKeysMigrated, migrationPhase, currentIteration, workerId), cancellationToken), - traceId); - - return workerResult; - } - - public virtual Task StartWorkAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - protected async Task CompleteWorkerAsync(ITransaction tx, CancellationToken cancellationToken) - { - await this.metadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerLastAppliedSeqNum, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - this.workerInput.EndSeqNum.ToString(), - (_, __) => this.workerInput.EndSeqNum.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerCurrentStatus, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - MigrationState.Completed.ToString(), - (_, __) => MigrationState.Completed.ToString(), - DefaultRCTimeout, - cancellationToken); - - await this.metadataDict.AddOrUpdateAsync( - tx, - Key(PhaseWorkerEndDateTimeUTC, this.Input.Phase, this.Input.Iteration, this.Input.WorkerId), - DateTime.UtcNow.ToString(), - (_, v) => v, - DefaultRCTimeout, - cancellationToken); - } - } -} diff --git a/src/netstandard/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration_netstandard.csproj b/src/netstandard/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration_netstandard.csproj deleted file mode 100644 index 2932e57f..00000000 --- a/src/netstandard/Microsoft.ServiceFabric.Actors.KVSToRCMigration/Microsoft.ServiceFabric.Actors.KVSToRCMigration_netstandard.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - Microsoft.ServiceFabric.Actors.KVSToRCMigration Library - Microsoft.ServiceFabric.Actors.KVSToRCMigration - $(AssemblyName) - $(RepoRoot)src\Microsoft.ServiceFabric.Actors.KVSToRCMigration\ - netstandard2.0 - $(OutputPath)\$(AssemblyName).xml - false - true - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/AmbiguousActorIdTest.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/AmbiguousActorIdTest.cs deleted file mode 100644 index 6bcf4f4b..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/AmbiguousActorIdTest.cs +++ /dev/null @@ -1,100 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests -{ - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - using Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes; - using Xunit; - using static Microsoft.ServiceFabric.Actors.KVSToRCMigration.IAmbiguousActorIdHandler; - - /// - /// Ambiguous actor id tests. - /// - public class AmbiguousActorIdTest - { - /// - /// Test non ambiguous actors scenario. - /// - /// Task for async operation. - [Fact] - public async Task NonAmbiguousActorIds() - { - var sp = new MockReliableCollectionsStateProvider(); - var presenceDict = sp.GetActorPresenceDictionary(); - await presenceDict.AddAsync(null, "String_MyActor1_", new byte[0]); - await presenceDict.AddAsync(null, "String_MyActor2_", new byte[0]); - await presenceDict.AddAsync(null, "String_MyActor3_MyEx1_", new byte[0]); - await presenceDict.AddAsync(null, "String_MyActor4_MyEx1_MyEx2_", new byte[0]); - - var handler = new RCAmbiguousActorIdHandler(sp); - Assert.Equal(await handler.ResolveActorIdAsync("String_MyActor1_MyState1", null, CancellationToken.None), new ActorId("MyActor1")); - Assert.Equal(await handler.ResolveActorIdAsync("String_MyActor3_MyEx1_MyState1", null, CancellationToken.None), new ActorId("MyActor3_MyEx1")); - Assert.Equal(await handler.ResolveActorIdAsync("Long_1234_MyState", null, CancellationToken.None), new ActorId(1234)); - } - - /// - /// Test ambiguous actors scenario. - /// - /// Task for async operation. - [Fact] - public async Task AmbiguousActorIds() - { - var sp = new MockReliableCollectionsStateProvider(); - var presenceDict = sp.GetActorPresenceDictionary(); - await presenceDict.AddAsync(null, "String_MyActor1_", new byte[0]); - await presenceDict.AddAsync(null, "String_MyActor1_MyEx1_", new byte[0]); - - var handler = new RCAmbiguousActorIdHandler(sp); - await Assert.ThrowsAsync(() => handler.ResolveActorIdAsync("String_MyActor1_MyEx1_MyState1", null, CancellationToken.None)); - } - - /// - /// Test ambiguous actors scenario with resolvers - /// - /// Task for async operation. - // [Fact] - internal async Task AmbiguousActorIdsWithResolvers() - { - var sp = new MockReliableCollectionsStateProvider(); - var presenceDict = sp.GetActorPresenceDictionary(); - await presenceDict.AddAsync(null, "String_MyActor1_", new byte[0]); - await presenceDict.AddAsync(null, "String_MyActor1_MyEx1_", new byte[0]); - - var handler = new RCAmbiguousActorIdHandler(sp); - Assert.Equal(await handler.ResolveActorIdAsync("String_MyActor1_MyEx1_MyState1", null, CancellationToken.None), new ActorId("MyActor1_MyEx1")); - } - - /// - /// Actor Id resolver - /// - [AmbiguousActorIdResolverAttribute] - public class ActorIdResolver : IAmbiguousActorIdHandler - { - /// - public Task TryResolveActorIdAsync(string stateStorageKey, CancellationToken cancellationToken) - { - if (stateStorageKey == "MyActor1_MyEx1_MyState1") - { - return Task.FromResult(new ConditionalValue - { - HasValue = true, - Value = "MyActor1_MyEx1", - }); - } - - return Task.FromResult(new ConditionalValue - { - HasValue = false, - Value = null, - }); - } - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/Microsoft.ServiceFabric.Actors.StateMigration.Tests.csproj b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/Microsoft.ServiceFabric.Actors.StateMigration.Tests.csproj deleted file mode 100644 index 9a5b74e5..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/Microsoft.ServiceFabric.Actors.StateMigration.Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - - {1BDC4681-FDBA-4E55-A247-5F779627A4D7} - Microsoft.ServiceFabric.Actors.StateMigration.Tests - $(AssemblyName) - net6.0 - $(OutputPath)\$(AssemblyName).xml - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationActorStateProviderTests.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationActorStateProviderTests.cs deleted file mode 100644 index cbfb078d..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationActorStateProviderTests.cs +++ /dev/null @@ -1,279 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Runtime.Serialization; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using System.Xml; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Services; - using Xunit; - - /// - /// Migration state provider tests. - /// - public class MigrationActorStateProviderTests - { - private DataContractSerializer reminderSerializer = new DataContractSerializer(typeof(ActorReminderData)); - private DataContractSerializer reminderCompletedDataSerializer = new DataContractSerializer(typeof(ReminderCompletedData)); - - /// - /// Save state test. - /// - /// Task to represent asynchronous operation. - [Fact] - public async Task SaveStateTypesTest() - { - var migrationSP = new KVStoRCMigrationActorStateProvider(new MockTypes.MockReliableCollectionsStateProvider()); - - var stateSerializer = new ActorStateProviderSerializer(); - var kvsData = new List(); - - int j = 0; - for (int i = 0; i < 10; i++) - { - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"@@_String_Actor{i}", - Value = new byte[0], - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_State{0}", - Value = stateSerializer.Serialize(typeof(string), $"Value{0}"), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_State{1}", - Value = stateSerializer.Serialize(typeof(long), 1), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_State{2}", - Value = stateSerializer.Serialize(typeof(Guid), Guid.NewGuid()), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_State{3}", - Value = stateSerializer.Serialize(typeof(byte[]), Encoding.UTF8.GetBytes("ValueString")), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_State{4}", - Value = stateSerializer.Serialize(typeof(double), 12354.1234), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Reminder_String_Actor{i}_Reminder{i}", - Value = Serialize( - this.reminderSerializer, - new ActorReminderData( - new ActorId($"Actor{i}"), - $"Reminder{i}", - TimeSpan.FromSeconds(10), - TimeSpan.FromSeconds(10), - Encoding.UTF8.GetBytes("ReminderState"), - TimeSpan.Zero)), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"RC@@_String_Actor{i}_Reminder{i}", - Value = Serialize(this.reminderCompletedDataSerializer, new ReminderCompletedData(TimeSpan.Zero, DateTime.UtcNow)), - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"Actor_String_Actor{i}_StateComplexObject", - Value = stateSerializer.Serialize(typeof(MigrationSettings), new MigrationSettings - { - ChunksPerEnumeration = 10, - CopyPhaseParallelism = 2, - DowntimeThreshold = 100, - EnableDataIntegrityChecks = true, - MigrationMode = Runtime.Migration.MigrationMode.Auto, - TargetServiceUri = new Uri("fabric:/blah/blahblah"), - }), - Version = i + j++, - }); - } - - var valueList = new List(); - foreach (var data in kvsData) - { - valueList.Add(data.Value); - } - - var sourceCRC = CRC64.ToCRC64(valueList.ToArray()); - - Assert.Equal(await migrationSP.SaveStateAsync(kvsData, CancellationToken.None), kvsData.Count); - await migrationSP.ValidateDataPostMigrationAsync(kvsData, sourceCRC.ToString("X", CultureInfo.InvariantCulture), true, CancellationToken.None); - } - - /// - /// Ignore keys test. - /// - /// Task to represent asynchronous operation. - [Fact] - public async Task IgnoreKeysTest() - { - var migrationSP = new KVStoRCMigrationActorStateProvider(new MockTypes.MockReliableCollectionsStateProvider()); - - var stateSerializer = new ActorStateProviderSerializer(); - var kvsData = new List(); - - int j = 0; - for (int i = 0; i < 1; i++) - { - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = MigrationConstants.RejectWritesKey, - Value = new byte[0], - Version = i + j++, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = MigrationConstants.LogicalTimestampKey, - Value = stateSerializer.Serialize(typeof(TimeSpan), TimeSpan.FromSeconds(10)), - Version = i + j++, - }); - } - - var valueList = new List(); - var sourceCRC = CRC64.ToCRC64(valueList.ToArray()); - - Assert.Equal(0L, await migrationSP.SaveStateAsync(kvsData, CancellationToken.None)); - await migrationSP.ValidateDataPostMigrationAsync(kvsData, sourceCRC.ToString("X", CultureInfo.InvariantCulture), true, CancellationToken.None); - } - - /// - /// Deleted test. - /// - /// Task to represent asynchronous operation. - [Fact] - public async Task DeletedKeysTest() - { - var migrationSP = new KVStoRCMigrationActorStateProvider(new MockTypes.MockReliableCollectionsStateProvider()); - - var stateSerializer = new ActorStateProviderSerializer(); - var kvsData = new List(); - - int j = 0; - for (int i = 0; i < 10; i++) - { - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"@@_String_Actor{i}", - Value = new byte[0], - Version = i + j++, - }); - } - - Assert.Equal(await migrationSP.SaveStateAsync(kvsData, CancellationToken.None), kvsData.Count); - - kvsData = new List(); - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = true, - Key = $"@@_String_Actor2", - Value = new byte[0], - Version = 1000, - }); - - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = true, - Key = $"@@_String_Actor5", - Value = new byte[0], - Version = 1001, - }); - - var valueList = new List(); - var sourceCRC = CRC64.ToCRC64(valueList.ToArray()); - Assert.Equal(2, await migrationSP.SaveStateAsync(kvsData, CancellationToken.None)); - - await migrationSP.ValidateDataPostMigrationAsync(kvsData, sourceCRC.ToString("X", CultureInfo.InvariantCulture), true, CancellationToken.None); - } - - /// - /// Data validation failure test. - /// - /// Task to represent asynchronous operation. - [Fact] - public async Task DataValidationFailureTest() - { - var migrationSP = new KVStoRCMigrationActorStateProvider(new MockTypes.MockReliableCollectionsStateProvider()); - - var stateSerializer = new ActorStateProviderSerializer(); - var kvsData = new List(); - - int j = 0; - for (int i = 0; i < 10; i++) - { - kvsData.Add(new KVSToRCMigration.Models.KeyValuePair() - { - IsDeleted = false, - Key = $"@@_String_Actor{i}", - Value = new byte[0], - Version = i + j++, - }); - } - - await Assert.ThrowsAsync(() => migrationSP.ValidateDataPostMigrationAsync(kvsData, string.Empty, true, CancellationToken.None)); - Assert.Equal(await migrationSP.SaveStateAsync(kvsData, CancellationToken.None), kvsData.Count); - kvsData[0].IsDeleted = true; - await Assert.ThrowsAsync(() => migrationSP.ValidateDataPostMigrationAsync(kvsData, string.Empty, true, CancellationToken.None)); - kvsData[0].IsDeleted = false; - await Assert.ThrowsAsync(() => migrationSP.ValidateDataPostMigrationAsync(kvsData, "BLAH", true, CancellationToken.None)); - } - - private static byte[] Serialize(DataContractSerializer serializer, T data) - { - using (var memoryStream = new MemoryStream()) - { - var binaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memoryStream); - serializer.WriteObject(binaryWriter, data); - binaryWriter.Flush(); - - return memoryStream.ToArray(); - } - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationSettingsTest.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationSettingsTest.cs deleted file mode 100644 index 85c5f3b2..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationSettingsTest.cs +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests -{ - using System; - using System.Globalization; - using Microsoft.ServiceFabric.Actors; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Xunit; - - /// - /// Migration Settings tests. - /// - public class MigrationSettingsTest - { - /// - /// Tests two actor ids for null equality. - /// - [Fact] - public void TestMigrationSettings() - { - var tgtSettings = new MigrationSettings - { - ChunksPerEnumeration = 10, - CopyPhaseParallelism = 2, - DowntimeThreshold = 1000, - KeyValuePairsPerChunk = 100, - MigrationMode = Runtime.Migration.MigrationMode.Auto, - TargetServiceUri = new Uri("fabric:/MyApp/MySourceSvc"), - SourceServiceUri = new Uri("fabric:/MyApp/MyTargetSvc"), - }; - - tgtSettings.Validate(isSource: false); - - tgtSettings.SourceServiceUri = null; - Assert.Throws(() => tgtSettings.Validate(isSource: false)); - - var sourceSettings = new MigrationSettings - { - TargetServiceUri = new Uri("fabric:/MyApp/MyTargetSvc"), - }; - - sourceSettings.Validate(isSource: true); - sourceSettings.TargetServiceUri = new Uri("//blah"); - Assert.Throws(() => sourceSettings.Validate(isSource: true)); - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationWorkerTests.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationWorkerTests.cs deleted file mode 100644 index 89aae998..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MigrationWorkerTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes; - using Xunit; - - /// - /// Migration Worker Tests - /// - public class MigrationWorkerTests - { - /// - /// Worker test. - /// - /// A representing the asynchronous unit test. - [Fact] - public async Task StartWorkTest() - { - var worker = new MigrationWorker( - new MockKVStoRCMigrationActorStateProvider(new MockReliableCollectionsStateProvider()), - new Runtime.ActorTypeInformation(), - new MockServicePartitionClient(null, new Uri("fabric:/Blah/BlahBlah")), - new MigrationSettings - { - ChunksPerEnumeration = 10, - KeyValuePairsPerChunk = 100, - SourceServiceUri = new Uri("fabric:/blah/blahblah"), - }, - new PhaseInput.WorkerInput - { - StartSeqNum = 0, - EndSeqNum = 100, - Iteration = 1, - WorkerId = 1, - Phase = Migration.MigrationPhase.Copy, - Status = Migration.MigrationState.InProgress, - StartDateTimeUTC = DateTime.UtcNow, - }, - "TestTrace"); - await this.PersistInputAsync(worker); - var result = await worker.StartWorkAsync(CancellationToken.None); - - Assert.NotEqual(default(DateTime), result.StartDateTimeUTC); - Assert.NotEqual(default(DateTime), result.EndDateTimeUTC); - Assert.Equal(result.StartSeqNum, worker.Input.StartSeqNum); - Assert.Equal(result.EndSeqNum, worker.Input.EndSeqNum); - Assert.Equal(MockServicePartitionClient.EnumerationSize, result.LastAppliedSeqNum); - Assert.Equal(result.Phase, worker.Input.Phase); - Assert.Equal(Migration.MigrationState.Completed, result.Status); - } - - private async Task PersistInputAsync(MigrationWorker worker) - { - var metaDict = await worker.StateProvider.GetMetadataDictionaryAsync(); - await metaDict.AddAsync( - worker.StateProvider.GetStateManager().CreateTransaction(), - MigrationConstants.Key(MigrationConstants.PhaseWorkerStartDateTimeUTC, worker.Input.Phase, worker.Input.Iteration, worker.Input.WorkerId), - worker.Input.StartDateTimeUTC.ToString()); - await metaDict.AddAsync( - worker.StateProvider.GetStateManager().CreateTransaction(), - MigrationConstants.Key(MigrationConstants.PhaseWorkerStartSeqNum, worker.Input.Phase, worker.Input.Iteration, worker.Input.WorkerId), - worker.Input.StartSeqNum.ToString()); - await metaDict.AddAsync( - worker.StateProvider.GetStateManager().CreateTransaction(), - MigrationConstants.Key(MigrationConstants.PhaseWorkerEndSeqNum, worker.Input.Phase, worker.Input.Iteration, worker.Input.WorkerId), - worker.Input.EndSeqNum.ToString()); - await metaDict.AddAsync( - worker.StateProvider.GetStateManager().CreateTransaction(), - MigrationConstants.Key(MigrationConstants.PhaseWorkerCurrentStatus, worker.Input.Phase, worker.Input.Iteration, worker.Input.WorkerId), - worker.Input.Status.ToString()); - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockKVStoRCMigrationActorStateProvider.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockKVStoRCMigrationActorStateProvider.cs deleted file mode 100644 index 1e87680b..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockKVStoRCMigrationActorStateProvider.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes -{ - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Data.Collections; - - internal class MockKVStoRCMigrationActorStateProvider : KVStoRCMigrationActorStateProvider - { - private IReliableDictionary2 metaDict; - - public MockKVStoRCMigrationActorStateProvider(IReliableCollectionsActorStateProviderInternal reliableCollectionsActorStateProvider) - : base(reliableCollectionsActorStateProvider) - { - this.metaDict = new MockReliableDictionary(); - } - - public override Task SaveStateAsync(List kvsData, CancellationToken cancellationToken, bool skipPresenceDictResolve = false) - { - return Task.FromResult((long)kvsData.Count); - } - - internal override Task ValidateDataPostMigrationAsync(List kvsData, string hashToCompare, bool skipPresenceDictResolve, CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - internal override Task> GetMetadataDictionaryAsync() - { - return Task.FromResult(this.metaDict); - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableCollectionsStateProvider.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableCollectionsStateProvider.cs deleted file mode 100644 index 2b1a4466..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableCollectionsStateProvider.cs +++ /dev/null @@ -1,205 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors; - using Microsoft.ServiceFabric.Actors.Query; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Data; - using Microsoft.ServiceFabric.Data.Collections; - using Moq; - - internal class MockReliableCollectionsStateProvider : - IActorStateProvider, IActorStateProviderInternal, IReliableCollectionsActorStateProviderInternal - { - private IReliableDictionary2 presenceDict = new MockReliableDictionary(); - private IReliableDictionary2 stateDict = new MockReliableDictionary(); - private IReliableDictionary2 reminderDict = new MockReliableDictionary(); - private IReliableDictionary2 reminderCompletedDict = new MockReliableDictionary(); - private IReliableDictionary2 logicalTimeDict = new MockReliableDictionary(); - - public Func OnRestoreCompletedAsync { set => throw new NotImplementedException(); } - - public Func> OnDataLossAsync { set => throw new NotImplementedException(); } - - public string TraceType { get => "MockReliableCollectionsStateProvider"; } - - public string TraceId { get => "MockReliableCollectionsStateProvider"; } - - public ReplicaRole CurrentReplicaRole { get => ReplicaRole.Primary; } - - public TimeSpan TransientErrorRetryDelay { get => TimeSpan.FromSeconds(1); } - - public TimeSpan OperationTimeout { get => TimeSpan.FromSeconds(10); } - - public TimeSpan CurrentLogicalTime { get => TimeSpan.FromSeconds(1); } - - public long RoleChangeTracker { get => 0L; } - - public void Abort() - { - throw new NotImplementedException(); - } - - public Task ActorActivatedAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task BackupAsync(Func> backupCallback) - { - throw new NotImplementedException(); - } - - public Task BackupAsync(BackupOption option, TimeSpan timeout, CancellationToken cancellationToken, Func> backupCallback) - { - throw new NotImplementedException(); - } - - public Task ChangeRoleAsync(ReplicaRole newRole, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task CloseAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task ContainsStateAsync(ActorId actorId, string stateName, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task DeleteReminderAsync(ActorId actorId, string reminderName, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task DeleteRemindersAsync(IReadOnlyDictionary> reminderNames, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> EnumerateStateNamesAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public IReliableDictionary2 GetActorPresenceDictionary() - { - return this.presenceDict; - } - - public Task> GetActorsAsync(int numItemsToReturn, ContinuationToken continuationToken, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public IReliableDictionary2 GetActorStateDictionary(ActorId actorId) - { - return this.stateDict; - } - - public ActorStateProviderHelper GetActorStateProviderHelper() - { - return new ActorStateProviderHelper(this); - } - - public IReliableDictionary2 GetLogicalTimeDictionary() - { - return this.logicalTimeDict; - } - - public IReliableDictionary2 GetReminderCompletedDictionary() - { - return this.reminderCompletedDict; - } - - public IReliableDictionary2 GetReminderDictionary(ActorId actorId) - { - return this.reminderDict; - } - - public Task>>> GetRemindersAsync(int numItemsToReturn, ActorId actorId, ContinuationToken continuationToken, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public IReliableStateManagerReplica2 GetStateManager() - { - var mockSM = new Mock(); - mockSM.Setup(sm => sm.CreateTransaction()).Returns(() => - { - var mockTx = new Mock(); - mockTx.Setup(tx => tx.CommitAsync()).Returns(Task.CompletedTask); - return mockTx.Object; - }); - - return mockSM.Object; - } - - public void Initialize(ActorTypeInformation actorTypeInformation) - { - throw new NotImplementedException(); - } - - public void Initialize(StatefulServiceInitializationParameters initializationParameters) - { - throw new NotImplementedException(); - } - - public Task LoadRemindersAsync(CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task LoadStateAsync(ActorId actorId, string stateName, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task OpenAsync(ReplicaOpenMode openMode, IStatefulServicePartition partition, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task ReminderCallbackCompletedAsync(ActorId actorId, IActorReminder reminder, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task RemoveActorAsync(ActorId actorId, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task RestoreAsync(string backupFolderPath) - { - throw new NotImplementedException(); - } - - public Task RestoreAsync(string backupFolderPath, RestorePolicy restorePolicy, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task SaveReminderAsync(ActorId actorId, IActorReminder reminder, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task SaveStateAsync(ActorId actorId, IReadOnlyCollection stateChanges, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableDictionary.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableDictionary.cs deleted file mode 100644 index ce3ea381..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockReliableDictionary.cs +++ /dev/null @@ -1,342 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Data; - using Microsoft.ServiceFabric.Data.Collections; - using Microsoft.ServiceFabric.Data.Notifications; - - internal class MockReliableDictionary : IReliableDictionary2 - where TKey : IComparable, IEquatable - { - private Dictionary kvPairs; - - public MockReliableDictionary() - { - this.kvPairs = new Dictionary(); - } - - event EventHandler> IReliableDictionary.DictionaryChanged - { - add - { - throw new NotImplementedException(); - } - - remove - { - throw new NotImplementedException(); - } - } - - long IReliableDictionary2.Count { get => this.kvPairs.Count; } - - Func, NotifyDictionaryRebuildEventArgs, Task> IReliableDictionary.RebuildNotificationAsyncCallback { set => throw new NotImplementedException(); } - - Uri IReliableState.Name { get => throw new NotImplementedException(); } - - Task IReliableDictionary.AddAsync(ITransaction tx, TKey key, TValue value) - { - return Task.Run(() => this.kvPairs.Add(key, value)); - } - - Task IReliableDictionary.AddAsync(ITransaction tx, TKey key, TValue value, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.Run(() => this.kvPairs.Add(key, value)); - } - - Task IReliableDictionary.AddOrUpdateAsync(ITransaction tx, TKey key, Func addValueFactory, Func updateValueFactory) - { - return Task.Run(() => - { - if (this.kvPairs.ContainsKey(key)) - { - this.kvPairs[key] = updateValueFactory.Invoke(key, this.kvPairs[key]); - } - else - { - this.kvPairs.Add(key, addValueFactory.Invoke(key)); - } - - return this.kvPairs[key]; - }); - } - - Task IReliableDictionary.AddOrUpdateAsync(ITransaction tx, TKey key, TValue addValue, Func updateValueFactory) - { - return Task.Run(() => - { - if (this.kvPairs.ContainsKey(key)) - { - this.kvPairs[key] = updateValueFactory.Invoke(key, this.kvPairs[key]); - } - else - { - this.kvPairs.Add(key, addValue); - } - - return this.kvPairs[key]; - }); - } - - Task IReliableDictionary.AddOrUpdateAsync(ITransaction tx, TKey key, Func addValueFactory, Func updateValueFactory, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.Run(() => - { - if (this.kvPairs.ContainsKey(key)) - { - this.kvPairs[key] = updateValueFactory.Invoke(key, this.kvPairs[key]); - } - else - { - this.kvPairs.Add(key, addValueFactory.Invoke(key)); - } - - return this.kvPairs[key]; - }); - } - - Task IReliableDictionary.AddOrUpdateAsync(ITransaction tx, TKey key, TValue addValue, Func updateValueFactory, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.Run(() => - { - if (this.kvPairs.ContainsKey(key)) - { - this.kvPairs[key] = updateValueFactory.Invoke(key, this.kvPairs[key]); - } - else - { - this.kvPairs.Add(key, addValue); - } - - return this.kvPairs[key]; - }); - } - - Task IReliableDictionary.ClearAsync(TimeSpan timeout, CancellationToken cancellationToken) - { - this.kvPairs.Clear(); - return Task.CompletedTask; - } - - Task IReliableCollection>.ClearAsync() - { - this.kvPairs.Clear(); - return Task.CompletedTask; - } - - Task IReliableDictionary.ContainsKeyAsync(ITransaction tx, TKey key) - { - return Task.FromResult(this.kvPairs.ContainsKey(key)); - } - - Task IReliableDictionary.ContainsKeyAsync(ITransaction tx, TKey key, LockMode lockMode) - { - return Task.FromResult(this.kvPairs.ContainsKey(key)); - } - - Task IReliableDictionary.ContainsKeyAsync(ITransaction tx, TKey key, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.FromResult(this.kvPairs.ContainsKey(key)); - } - - Task IReliableDictionary.ContainsKeyAsync(ITransaction tx, TKey key, LockMode lockMode, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.FromResult(this.kvPairs.ContainsKey(key)); - } - - Task>> IReliableDictionary.CreateEnumerableAsync(ITransaction txn) - { - throw new NotImplementedException(); - } - - Task>> IReliableDictionary.CreateEnumerableAsync(ITransaction txn, EnumerationMode enumerationMode) - { - throw new NotImplementedException(); - } - - Task>> IReliableDictionary.CreateEnumerableAsync(ITransaction txn, Func filter, EnumerationMode enumerationMode) - { - throw new NotImplementedException(); - } - - Task> IReliableDictionary2.CreateKeyEnumerableAsync(ITransaction txn) - { - throw new NotImplementedException(); - } - - Task> IReliableDictionary2.CreateKeyEnumerableAsync(ITransaction txn, EnumerationMode enumerationMode) - { - throw new NotImplementedException(); - } - - Task> IReliableDictionary2.CreateKeyEnumerableAsync(ITransaction txn, EnumerationMode enumerationMode, TimeSpan timeout, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - Task IReliableCollection>.GetCountAsync(ITransaction tx) - { - return Task.FromResult((long)this.kvPairs.Count); - } - - Task IReliableDictionary.GetOrAddAsync(ITransaction tx, TKey key, Func valueFactory) - { - if (!this.kvPairs.ContainsKey(key)) - { - this.kvPairs.Add(key, valueFactory.Invoke(key)); - } - - return Task.FromResult(this.kvPairs[key]); - } - - Task IReliableDictionary.GetOrAddAsync(ITransaction tx, TKey key, TValue value) - { - if (!this.kvPairs.ContainsKey(key)) - { - this.kvPairs.Add(key, value); - } - - return Task.FromResult(this.kvPairs[key]); - } - - Task IReliableDictionary.GetOrAddAsync(ITransaction tx, TKey key, Func valueFactory, TimeSpan timeout, CancellationToken cancellationToken) - { - if (!this.kvPairs.ContainsKey(key)) - { - this.kvPairs.Add(key, valueFactory.Invoke(key)); - } - - return Task.FromResult(this.kvPairs[key]); - } - - Task IReliableDictionary.GetOrAddAsync(ITransaction tx, TKey key, TValue value, TimeSpan timeout, CancellationToken cancellationToken) - { - if (!this.kvPairs.ContainsKey(key)) - { - this.kvPairs.Add(key, value); - } - - return Task.FromResult(this.kvPairs[key]); - } - - Task IReliableDictionary.SetAsync(ITransaction tx, TKey key, TValue value) - { - this.kvPairs.Add(key, value); - return Task.CompletedTask; - } - - Task IReliableDictionary.SetAsync(ITransaction tx, TKey key, TValue value, TimeSpan timeout, CancellationToken cancellationToken) - { - this.kvPairs.Add(key, value); - return Task.CompletedTask; - } - - Task IReliableDictionary.TryAddAsync(ITransaction tx, TKey key, TValue value) - { - return Task.FromResult(this.kvPairs.TryAdd(key, value)); - } - - Task IReliableDictionary.TryAddAsync(ITransaction tx, TKey key, TValue value, TimeSpan timeout, CancellationToken cancellationToken) - { - return Task.FromResult(this.kvPairs.TryAdd(key, value)); - } - - Task> IReliableDictionary.TryGetValueAsync(ITransaction tx, TKey key) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task> IReliableDictionary.TryGetValueAsync(ITransaction tx, TKey key, LockMode lockMode) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task> IReliableDictionary.TryGetValueAsync(ITransaction tx, TKey key, TimeSpan timeout, CancellationToken cancellationToken) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task> IReliableDictionary.TryGetValueAsync(ITransaction tx, TKey key, LockMode lockMode, TimeSpan timeout, CancellationToken cancellationToken) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task> IReliableDictionary.TryRemoveAsync(ITransaction tx, TKey key) - { - if (this.kvPairs.Remove(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task> IReliableDictionary.TryRemoveAsync(ITransaction tx, TKey key, TimeSpan timeout, CancellationToken cancellationToken) - { - if (this.kvPairs.Remove(key, out var value)) - { - return Task.FromResult(new ConditionalValue(true, value)); - } - - return Task.FromResult(new ConditionalValue(false, default(TValue))); - } - - Task IReliableDictionary.TryUpdateAsync(ITransaction tx, TKey key, TValue newValue, TValue comparisonValue) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - if (comparisonValue.Equals(value)) - { - this.kvPairs[key] = newValue; - - return Task.FromResult(true); - } - } - - return Task.FromResult(false); - } - - Task IReliableDictionary.TryUpdateAsync(ITransaction tx, TKey key, TValue newValue, TValue comparisonValue, TimeSpan timeout, CancellationToken cancellationToken) - { - if (this.kvPairs.TryGetValue(key, out var value)) - { - if (comparisonValue.Equals(value)) - { - this.kvPairs[key] = newValue; - - return Task.FromResult(true); - } - } - - return Task.FromResult(false); - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockServicePartitionClient.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockServicePartitionClient.cs deleted file mode 100644 index 05df8def..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/MockTypes/MockServicePartitionClient.cs +++ /dev/null @@ -1,123 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes -{ - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration.Models; - using Microsoft.ServiceFabric.Services.Client; - using Microsoft.ServiceFabric.Services.Communication.Client; - - internal class MockServicePartitionClient : ServicePartitionClient - { - internal static readonly long EnumerationSize = 100; - - public MockServicePartitionClient( - ICommunicationClientFactory communicationClientFactory, - Uri serviceUri, - ServicePartitionKey partitionKey = null, - TargetReplicaSelector targetReplicaSelector = TargetReplicaSelector.Default, - string listenerName = null, - OperationRetrySettings retrySettings = null) - : base(communicationClientFactory, serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings) - { - } - - public override Task InvokeWithRetryAsync( - Func> func, - params Type[] doNotRetryExceptionTypes) - { - return this.InvokeWithRetryAsync(func, CancellationToken.None, doNotRetryExceptionTypes); - } - - public override async Task InvokeWithRetryAsync( - Func> func, - CancellationToken cancellationToken, - params Type[] doNotRetryExceptionTypes) - { - return await func.Invoke(this.GetMockCommunicationClient()); - } - - private HttpCommunicationClient GetMockCommunicationClient() - { - return new HttpCommunicationClient(new HttpClient(new MockHttpMessageHandler()) - { - BaseAddress = new Uri("http://testapp:8080/"), - }); - } - - private class MockHttpMessageHandler : HttpMessageHandler - { - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var requestUri = request.RequestUri.ToString(); - if (requestUri.EndsWith(MigrationConstants.EnumeratebySNEndpoint)) - { - var kvPairList = new List(); - for (int i = 0; i < EnumerationSize; i++) - { - kvPairList.Add(new KVSToRCMigration.Models.KeyValuePair - { - Version = i, - }); - } - - var response = new EnumerationResponse - { - EndSequenceNumberReached = true, - KeyValuePairs = kvPairList, - }; - - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - Content = new ByteArrayContent(SerializationUtility.Serialize(KvsActorStateProviderExtensions.ResponseSerializer, response)), - }); - } - else if (requestUri.EndsWith(MigrationConstants.GetStartSNEndpoint)) - { - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(1.ToString()), - }); - } - else if (requestUri.EndsWith(MigrationConstants.GetEndSNEndpoint)) - { - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(100.ToString()), - }); - } - else if (requestUri.EndsWith(MigrationConstants.StartDowntimeEndpoint)) - { - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - }); - } - else if (requestUri.EndsWith(MigrationConstants.AbortMigrationEndpoint)) - { - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - }); - } - - return Task.FromResult(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.NotFound, - }); - } - } - } -} diff --git a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/TargetMigrationOrchestratorTests.cs b/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/TargetMigrationOrchestratorTests.cs deleted file mode 100644 index 8aa8adb7..00000000 --- a/test/unittests/Microsoft.ServiceFabric.Actors.StateMigration.Tests/TargetMigrationOrchestratorTests.cs +++ /dev/null @@ -1,641 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. -// ------------------------------------------------------------ - -namespace Microsoft.ServiceFabric.Actors.StateMigration.Tests -{ - using System; - using System.Collections.Generic; - using System.Fabric; - using System.Numerics; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ServiceFabric.Actors.KVSToRCMigration; - using Microsoft.ServiceFabric.Actors.Migration; - using Microsoft.ServiceFabric.Actors.Migration.Exceptions; - using Microsoft.ServiceFabric.Actors.Runtime; - using Microsoft.ServiceFabric.Actors.Runtime.Migration; - using Microsoft.ServiceFabric.Actors.StateMigration.Tests.MockTypes; - using Microsoft.ServiceFabric.Services.Communication.Client; - using Moq; - using Xunit; - - /// - /// Target Migration orchestrator tests. - /// - public class TargetMigrationOrchestratorTests - { - /// - /// Auto Migration base case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task AutoMigrationBaseCase() - { - var orchestrator = GetOrchestrator(MigrationMode.Auto); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - var result = await orchestrator.GetResultAsync(CancellationToken.None); - - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - } - - /// - /// Auto Migration failover case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task AutoMigrationFailoverCase() - { - // Failover in Copy phase - var orchestrator = GetOrchestrator(MigrationMode.Auto); - var metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Copy.ToString(), (_, __) => MigrationPhase.Copy.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - var result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - - // Failover in catchup phase - orchestrator = GetOrchestrator(MigrationMode.Auto); - metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Catchup.ToString(), (_, __) => MigrationPhase.Catchup.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - - // Failover in downtime phase - orchestrator = GetOrchestrator(MigrationMode.Auto); - metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Downtime.ToString(), (_, __) => MigrationPhase.Downtime.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - } - - /// - /// Auto Migration abort case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task AutoMigrationAbortCase() - { - var tcs = new CancellationTokenSource(); - var orchestrator = GetOrchestrator(MigrationMode.Auto, MigrationPhase.Copy); - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(false, tcs.Token).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - await orchestrator.AbortMigrationAsync(true, CancellationToken.None); - break; - } - - try - { - await migrationTask; - } - catch (OperationCanceledException) - { - // Do nothing - } - - var result = await orchestrator.GetResultAsync(CancellationToken.None); - - Assert.Equal(MigrationState.Aborted, result.Status); - } - - /// - /// Manual Migration base case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task ManualMigrationBaseCase() - { - var orchestrator = GetOrchestrator(MigrationMode.Manual); - - // ActorService calling Start Migration when the service is up - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(true, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (InvalidMigrationOperationException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - if (result1.CurrentPhase == MigrationPhase.Catchup) - { - await orchestrator.StartDowntimeAsync(true, CancellationToken.None); - break; - } - - Thread.Sleep(TimeSpan.FromSeconds(1)); - } - - await migrationTask; - var result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - } - - /// - /// Manual Migration failover case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task ManualMigrationFailoverCase() - { - // Failover in Copy phase - var orchestrator = GetOrchestrator(MigrationMode.Manual); - var metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Copy.ToString(), (_, __) => MigrationPhase.Copy.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(false, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - if (result1.CurrentPhase == MigrationPhase.Catchup) - { - await orchestrator.StartDowntimeAsync(true, CancellationToken.None); - break; - } - - Thread.Sleep(TimeSpan.FromSeconds(1)); - } - - await migrationTask; - var result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - - // Failover in catchup phase - orchestrator = GetOrchestrator(MigrationMode.Manual); - metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Catchup.ToString(), (_, __) => MigrationPhase.Catchup.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(false, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - if (result1.CurrentPhase == MigrationPhase.Catchup) - { - await orchestrator.StartDowntimeAsync(true, CancellationToken.None); - break; - } - - Thread.Sleep(TimeSpan.FromSeconds(1)); - } - - await migrationTask; - result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - - // Failover in downtime phase - orchestrator = GetOrchestrator(MigrationMode.Manual); - metaDict = await ((KVStoRCMigrationActorStateProvider)orchestrator.GetMigrationActorStateProvider()).GetMetadataDictionaryAsync(); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentPhase, MigrationPhase.Downtime.ToString(), (_, __) => MigrationPhase.Downtime.ToString()); - await metaDict.AddOrUpdateAsync(orchestrator.Transaction, MigrationConstants.MigrationCurrentStatus, MigrationState.InProgress.ToString(), (_, __) => MigrationState.InProgress.ToString()); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - result = await orchestrator.GetResultAsync(CancellationToken.None); - Assert.Equal(MigrationPhase.Completed, result.CurrentPhase); - Assert.Equal(MigrationState.Completed, result.Status); - } - - /// - /// Manual Migration abort case - /// - /// representing the asynchronous unit test. - [Fact] - public async Task ManualMigrationAbortCase() - { - var tcs = new CancellationTokenSource(); - var orchestrator = GetOrchestrator(MigrationMode.Manual); - await orchestrator.StartMigrationAsync(false, tcs.Token); - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(true, tcs.Token).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - await orchestrator.AbortMigrationAsync(true, CancellationToken.None); - break; - } - - try - { - await migrationTask; - } - catch (OperationCanceledException) - { - // Do nothing - } - - var result = await orchestrator.GetResultAsync(CancellationToken.None); - - Assert.Equal(MigrationState.Aborted, result.Status); - } - - /// - /// Check if actor calls allowed. - /// - /// representing the asynchronous unit test. - [Fact] - public async Task AreActorCallsAllowedTest() - { - var orchestrator = GetOrchestrator(MigrationMode.Auto); - Assert.False(orchestrator.AreActorCallsAllowed()); - bool callbackInvoked = false; - orchestrator.RegisterCompletionCallback( - (_, __) => - { - callbackInvoked = true; - return Task.CompletedTask; - }); - - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - Assert.True(orchestrator.AreActorCallsAllowed()); - Assert.True(callbackInvoked); - - // Abort case - var tcs = new CancellationTokenSource(); - orchestrator = GetOrchestrator(MigrationMode.Manual); - Assert.False(orchestrator.AreActorCallsAllowed()); - callbackInvoked = false; - orchestrator.RegisterCompletionCallback( - (_, __) => - { - callbackInvoked = true; - return Task.CompletedTask; - }); - - await orchestrator.StartMigrationAsync(false, tcs.Token); - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(true, tcs.Token).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - Assert.True(result1.CurrentPhase <= MigrationPhase.Catchup); - Assert.True(result1.Status == MigrationState.InProgress); - await orchestrator.AbortMigrationAsync(true, CancellationToken.None); - break; - } - - try - { - await migrationTask; - } - catch (OperationCanceledException) - { - // Do nothing - } - - Assert.False(orchestrator.AreActorCallsAllowed()); - Assert.True(callbackInvoked); - } - - /// - /// Actor call forwarding test. - /// - /// representing the asynchronous unit test. - [Fact] - public async Task IsActorCallToBeForwardedTest() - { - var orchestrator = GetOrchestrator(MigrationMode.Manual); - - // Migration not initialized. Hence except exception. - Assert.Throws(() => orchestrator.IsActorCallToBeForwarded()); - - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - var migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(true, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - // Migration in progress. Call to be forwarded. - Assert.True(orchestrator.IsActorCallToBeForwarded()); - await orchestrator.StartDowntimeAsync(true, CancellationToken.None); - break; - } - - // Migration complete. Do not forward request. - await migrationTask; - Assert.False(orchestrator.IsActorCallToBeForwarded()); - - var tcs = new CancellationTokenSource(); - orchestrator = GetOrchestrator(MigrationMode.Manual); - await orchestrator.StartMigrationAsync(false, tcs.Token); - migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(true, tcs.Token).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - // Migration in progress. Call to be forwarded. - Assert.True(orchestrator.IsActorCallToBeForwarded()); - await orchestrator.AbortMigrationAsync(true, CancellationToken.None); - break; - } - - try - { - await migrationTask; - } - catch (OperationCanceledException) - { - // Do nothing - } - - // Migration aborted call to be forwarded. - Assert.True(orchestrator.IsActorCallToBeForwarded()); - - orchestrator = GetOrchestrator(MigrationMode.Auto, MigrationPhase.Downtime); - migrationTask = Task.Run(() => orchestrator.StartMigrationAsync(false, tcs.Token).ConfigureAwait(false).GetAwaiter().GetResult()); - while (true) - { - MigrationResult result1 = null; - try - { - result1 = await orchestrator.GetResultAsync(CancellationToken.None); - } - catch (MigrationFrameworkNotInitializedException e) - { - Console.WriteLine(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.None || result1.Status == MigrationState.None) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - continue; - } - - if (result1.CurrentPhase == MigrationPhase.Downtime) - { - // Downtime phase. Call should not be forwarded - Assert.False(orchestrator.IsActorCallToBeForwarded()); - break; - } - } - } - - /// - /// Invalid operations test. - /// - /// representing the asynchronous unit test. - [Fact] - public async Task InvalidOperationsTest() - { - var orchestrator = GetOrchestrator(MigrationMode.Auto); - await Assert.ThrowsAsync(() => orchestrator.AbortMigrationAsync(true, CancellationToken.None)); - await Assert.ThrowsAsync(() => orchestrator.StartDowntimeAsync(true, CancellationToken.None)); - await Assert.ThrowsAsync(() => orchestrator.GetResultAsync(CancellationToken.None)); - await orchestrator.StartMigrationAsync(false, CancellationToken.None); - await Assert.ThrowsAsync(() => orchestrator.AbortMigrationAsync(true, CancellationToken.None)); - await Assert.ThrowsAsync(() => orchestrator.StartDowntimeAsync(true, CancellationToken.None)); - await Assert.ThrowsAsync(() => orchestrator.StartMigrationAsync(true, CancellationToken.None)); - - orchestrator = GetOrchestrator(MigrationMode.Auto, invalidConfig: true); - await Assert.ThrowsAsync(() => orchestrator.StartMigrationAsync(false, CancellationToken.None)); - - orchestrator = GetOrchestrator(MigrationMode.Auto); - await Assert.ThrowsAsync(() => orchestrator.StartMigrationAsync(true, CancellationToken.None)); - } - - private static TargetMigrationOrchestrator GetOrchestrator(MigrationMode mode, MigrationPhase blockingPhase = MigrationPhase.None, bool invalidConfig = false) - { - var setting = new KVSToRCMigration.MigrationSettings - { - MigrationMode = mode, - CopyPhaseParallelism = 2, - ChunksPerEnumeration = 10, - KeyValuePairsPerChunk = 20, - SourceServiceUri = new Uri("fabric:/blah/blahblah"), - DowntimeThreshold = 1000, - }; - - var mockSp = new MockKVStoRCMigrationActorStateProvider(new MockReliableCollectionsStateProvider()); - var mockFactory = new Mock( - mockSp, - new ActorTypeInformation(), - new StatefulServiceContext( - new NodeContext("TestNode", new NodeId(new BigInteger(0), new BigInteger(0)), new BigInteger(0), "TestNodeType", "10.10.10.10"), - new Mock().Object, - "TestServiceType", - new Uri("fabric:/blah/blahblah"), - null, - Guid.NewGuid(), - 0L), - setting, - GetMockExFilter(setting), - new MockServicePartitionClient(null, new Uri("fabric:/Blah/BlahBlah")), - "TestTraceId"); - mockFactory.CallBase = true; - mockFactory.Setup(o => o.GetMigrationPhaseWorkload(It.IsAny(), It.IsAny())) - .Returns((phase, __) => - { - if (phase != MigrationPhase.Completed) - { - var res = GetMockPhaseWorkload(phase, blockingPhase != MigrationPhase.None && blockingPhase == phase); - var metaDict = mockSp.GetMetadataDictionaryAsync().GetAwaiter().GetResult(); - metaDict.AddOrUpdateAsync(mockSp.GetStateManager().CreateTransaction(), MigrationConstants.MigrationCurrentPhase, phase.ToString(), (_, __) => phase.ToString()).GetAwaiter().GetResult(); - return res; - } - - return null; - }); - mockFactory.Setup(o => o.ValidateConfigForMigrationAsync(It.IsAny())) - .Returns(_ => - { - if (invalidConfig) - { - throw new InvalidMigrationConfigException(); - } - - return Task.CompletedTask; - }); - - return mockFactory.Object; - } - - private static PartitionHealthExceptionFilter GetMockExFilter(KVSToRCMigration.MigrationSettings settings) - { - var exFilterFactory = new Mock(settings); - exFilterFactory.CallBase = false; - bool abortMigration = false; - bool rethrow = true; - exFilterFactory.Setup(ex => ex.ReportPartitionHealthIfNeeded(It.IsAny(), It.IsAny(), out abortMigration, out rethrow)); - return exFilterFactory.Object; - } - - private static IMigrationPhaseWorkload GetMockPhaseWorkload(MigrationPhase currentPhase, bool blockingCall = false) - { - var phaseFactory = new Mock(); - phaseFactory.Setup(pw => pw.StartOrResumeMigrationAsync(It.IsAny())) - .Returns(token => - { - token.ThrowIfCancellationRequested(); - if (blockingCall) - { - while (true) - { - token.ThrowIfCancellationRequested(); - Thread.Sleep(TimeSpan.FromSeconds(5)); - } - } - - var result = new PhaseResult - { - Status = MigrationState.Completed, - Phase = currentPhase, - StartDateTimeUTC = DateTime.UtcNow, - EndDateTimeUTC = DateTime.UtcNow, - StartSeqNum = 0, - EndSeqNum = 0, - LastAppliedSeqNum = 0, - Iteration = 1, - NoOfKeysMigrated = 0, - WorkerCount = 1, - }; - - return Task.FromResult(result); - }); - - phaseFactory.SetupGet(pw => pw.Phase).Returns(currentPhase); - - return phaseFactory.Object; - } - } -}