internal void PauseSessionUntilConflictsResolved(SessionWorker worker) { bool unresolvedConflictsFound = false; while (m_syncStateMachine.CurrentState == PipelineState.PausedByConflict) { if (ConflictManager.DoesSessionHaveUnresolvedConflicts(worker.LeftMigrationSourceid, worker.RightMigrationSourceid)) { if (!unresolvedConflictsFound) { TraceManager.TraceWarning("Session paused due to blocking conflict(s); resolve conflicts for this session to resume"); unresolvedConflictsFound = true; } // sleep at "PausedByConflict" Thread.Sleep(ResumeAfterConflictPollingMilliSecs); } else { if (unresolvedConflictsFound) { TraceManager.TraceInformation("Resuming session {0} that was previously paused by one or more conflicts", m_syncStateMachine.OwnerUniqueId); } break; } } m_syncStateMachine.TryTransit(PipelineSyncCommand.START_NEW_TRIP); }
private void Run() { TraceManager.TraceInformation(String.Format("Session worker thread [{0}] started", m_thread.Name)); bool sessionFinishesSuccessfully = false; while (!sessionFinishesSuccessfully) { try { MarkSessionRunning(); ProcessPipeline(WorkFlowType); sessionFinishesSuccessfully = true; } catch (Exception ex) { TraceManager.TraceError(String.Format("Session worker thread [{0}] exception: {1}", m_thread.Name, ex is MissingErrorRouterException ? ex.InnerException : ex)); TraceManager.TraceInformation(string.Format("Restarting session in session worker thread [{0}]", m_thread.Name)); } } TraceManager.TraceInformation(String.Format("Session worker thread [{0}] completed", m_thread.Name)); // Signal SyncOrchestrator that this thread is done Event.Set(); }
private void GenerateDeltaForForceSync( IForceSyncItemService forceSyncItemService, IForceSyncAnalysisProvider forceSyncAnalysisProvider) { int batchSize = 100; List <string> forceSyncItemIds = new List <string>(); foreach (string forceSyncItemId in forceSyncItemService.GetItemsForForceSync()) { TraceManager.TraceInformation(String.Format(CultureInfo.InvariantCulture, "Will attempt to force sync work item {0} from migration source {1}", forceSyncItemId, forceSyncItemService.MigrationSourceid)); forceSyncItemIds.Add(forceSyncItemId); if (forceSyncItemIds.Count == batchSize) { forceSyncAnalysisProvider.GenerateDeltaForForceSync(forceSyncItemIds); forceSyncItemIds.Clear(); } } if (forceSyncItemIds.Count > 0) { forceSyncAnalysisProvider.GenerateDeltaForForceSync(forceSyncItemIds); } }
public static ICollection <ProviderHandler> LoadProvider(params DirectoryInfo[] probingDirectories) { if (s_providers == null || s_providers.Count == 0) { // Initialize a list that will contain all plugin types discovered s_providers = new List <ProviderHandler>(); if (probingDirectories != null) { // Iterate over the probing directories and look for plugins foreach (DirectoryInfo directory in probingDirectories) { Debug.Assert(directory.Exists, string.Format("Plugins directory does not exist: {0}", directory.FullName)); if (directory.Exists) { // Try to load plugins from each dll foreach (FileInfo file in directory.GetFiles("*.dll")) { try { // Load the dll into an assembly Assembly assembly = Assembly.LoadFrom(file.FullName); // Iterate over all types contained in the assembly foreach (Type type in assembly.GetTypes()) { // Only consider public, concrete types that implement IProvider if (type.IsPublic && !type.IsAbstract && (type.GetInterface(typeof(IProvider).Name) != null)) { ProviderHandler handler = ProviderHandler.FromType(type); if (null == handler || handler.ProviderId == null || handler.ProviderId == Guid.Empty) { continue; } s_providers.Add(handler); TraceManager.TraceInformation("Provider {0} {1} is available", handler.ProviderName, handler.ProviderId.ToString()); } } } catch (ReflectionTypeLoadException) { TraceManager.TraceInformation("Provider {0} is unavailable. One or more of the requested types cannot be loaded.", file.FullName); } catch (Exception) { // We try to load all .dll files and some of them just are not providers. // Just log a warning and move along TraceManager.TraceWarning("Failed to load the possible provider file: {0}", file.FullName); } } } } } } return(s_providers); }
/// <summary> /// Remove all change groups in current session which are in progress. /// For delta table, this includes status of Delta, DeltaPending. /// For migration instruction, this includes Pending or Inprogress. /// </summary> private void RemoveInProgressChangeGroupsInSession(Guid sessionId) { using (RuntimeEntityModel context = RuntimeEntityModel.CreateInstance()) { TraceManager.TraceInformation( "Deleting cached data for session '{0}'", sessionId); context.DeleteSessionCachedData(sessionId); } }
/// <summary> /// PostProcessing Delta Table entries after GeneratingMigrationInstruction /// </summary> /// <param name="targetSystemId">the target system SourceId of the current run through the pipeline</param> /// <param name="isBidirectional">true if the current session is bi-directional</param> internal void PostProcessDeltaTableEntries(Guid targetSystemId, bool isBidirectional) { Debug.Assert( DeltaTableMaintenanceService != null, "DeltaTableMaintenanceService is not properly initialized."); if (!isBidirectional) { TraceManager.TraceInformation("Marking as 'DeltaComplete' the target-side delta table for uni-directional session"); DeltaTableMaintenanceService.BatchMarkDeltaTableEntriesAsDeltaCompleted(targetSystemId); } }
/// <summary> /// Discovers providers under the specified directories and loads them if used in MigrationSources /// defined in the configuration. It instantiates a unique provider instance for each MigrationSource. /// </summary> public Dictionary <Guid, ProviderHandler> LoadProvider(params DirectoryInfo[] probingDirectories) { IEnumerable <ProviderHandler> providers = Utility.LoadProvider(probingDirectories); // Initialize a list that will contain all plugin types discovered Dictionary <Guid, ProviderHandler> providerHandlers = new Dictionary <Guid, ProviderHandler>(); foreach (ProviderHandler handler in providers) { try { #region Register Add-Ins IProvider provider = handler.Provider; IAddin addIn = provider.GetService(typeof(IAddin)) as IAddin; if (null != addIn) { m_AddinManagementService.RegisterAddin(addIn); } #endregion Guid[] sourceIds = GetMigrationSourceId(handler.ProviderId); if (sourceIds == null || sourceIds.Length == 0) { continue; } // try persist provider information to db handler.FindSaveProvider(); // create a unique provider instance for each migration source foreach (Guid migrationSource in sourceIds) { ProviderHandler providerHandler = new ProviderHandler(handler); if (!providerHandlers.ContainsKey(migrationSource)) { providerHandlers.Add(migrationSource, providerHandler); TraceManager.TraceInformation("Provider {0} {1} is loaded", providerHandler.ProviderName, handler.ProviderId.ToString()); } } } catch (Exception ex) { TraceManager.TraceError("A failure occurred while trying to load the {0} Provider: {1}{2}", handler.ProviderName, Environment.NewLine, ex.ToString()); } } return(providerHandlers); }
/// <summary> /// Try saving the managed configuration (always check whether the configuration can be saved or not) /// </summary> /// <returns>The Id of the saved configuration</returns> /// <exception cref="Microsoft.TeamFoundation.Migration.BusinessModel.DuplicateConfigurationException" /> public int TrySave() { if (CanEditAndSave) { return(SaveConfigWithoutCanSaveCheck()); } else { var pipeProxy = new MigrationServiceClient(); bool sessionWasRunningBeforeSavingConfig = false; try { var runningGroups = pipeProxy.GetRunningSessionGroups(); sessionWasRunningBeforeSavingConfig = runningGroups.Contains(Configuration.SessionGroupUniqueId); if (sessionWasRunningBeforeSavingConfig) { pipeProxy.StopSessionGroup(Configuration.SessionGroupUniqueId); if (!WaitForSessionGroupToStop(pipeProxy, TimeSpan.TicksPerMinute * 10)) { throw new SavingUnsavableConfigurationException(Configuration); } } } catch (MigrationServiceEndpointNotFoundException) { // WCF service is not active - session group shouldn't be running either } int saveResult = SaveConfigWithoutCanSaveCheck(); if (sessionWasRunningBeforeSavingConfig) { try { pipeProxy.StartSessionGroup(Configuration.SessionGroupUniqueId); } catch (MigrationServiceEndpointNotFoundException) { TraceManager.TraceInformation("Cannot restart Session Group '{0}' after updating its configuration.", Configuration.SessionGroupUniqueId.ToString()); } } return(saveResult); } }
internal SessionWorker( Guid sessionId, ManualResetEvent sessionEvent, AnalysisEngine analysisEngine, MigrationEngine migrationEngine, LinkEngine linkEngine, Guid leftMigrationSourceId, Guid rightMigrationSourceId, WorkFlowType workFlowType, int secondsSyncWaitInterval, string threadName) { SessionId = sessionId; Event = sessionEvent; AnalysisEngine = analysisEngine; MigrationEngine = migrationEngine; LinkEngine = linkEngine; LeftMigrationSourceid = leftMigrationSourceId; RightMigrationSourceid = rightMigrationSourceId; WorkFlowType = workFlowType; m_threadName = threadName; SqlSyncStateManager manager = SqlSyncStateManager.GetInstance(); m_syncStateManager = manager; m_syncCommandQueue = manager; m_syncStateMachine = new SyncStateMachine(PipelineState.Default, new SyncStateTransitionAlgorithm(), OwnerType.Session, sessionId, m_syncStateManager); m_orchPolicy = new SessionOrchestrationPolicy(WorkFlowType, m_syncStateMachine); try { checked { MilliSecondsSyncWaitInterval = secondsSyncWaitInterval * 1000; } } catch (OverflowException) { MilliSecondsSyncWaitInterval = int.MaxValue; TraceManager.TraceInformation( "The speicified interval of {0} minutes is too long for the system to handle. The interval is now changed to {1} minutes.", secondsSyncWaitInterval / 60, (int)(MilliSecondsSyncWaitInterval / 1000 / 60)); } }
private void DetectBasicConflicts(ChangeGroupService targetChangeGroupService, Guid targetSystemId, Guid sourceSystemId) { TraceManager.TraceInformation("Starting basic conflict detection"); if (null == m_basicConflictAnalysisService) { return; } m_basicConflictAnalysisService.Configuration = m_session.Configuration; m_basicConflictAnalysisService.TargetChangeGroupService = targetChangeGroupService; m_basicConflictAnalysisService.ConflictManager = m_serviceContainers[targetSystemId].GetService(typeof(ConflictManager)) as ConflictManager; m_basicConflictAnalysisService.TranslationService = m_translationService; m_basicConflictAnalysisService.TargetSystemId = targetSystemId; m_basicConflictAnalysisService.SourceSystemId = sourceSystemId; m_basicConflictAnalysisService.Analyze(); TraceManager.TraceInformation("Finishing basic conflict detection"); }
private void ProviderDetectConflicts(Guid targetSystemId, ChangeGroupService targetChangeGroupService) { Debug.Assert(m_serviceContainers.ContainsKey(targetSystemId), string.Format(MigrationToolkitResources.UnknownSourceId, targetSystemId)); IAnalysisProvider targetAnalysisProvider; if (!m_analysisProviders.TryGetValue(targetSystemId, out targetAnalysisProvider)) { throw new MigrationException(string.Format( MigrationToolkitResources.Culture, MigrationToolkitResources.UnknownSourceId, targetSystemId)); } try { int pageNumber = 0; IEnumerable <ChangeGroup> changeGroups; do { TraceManager.TraceInformation("Loading {0} ChangeGroup(s)", m_pageSize); changeGroups = targetChangeGroupService.NextMigrationInstructionTablePage(pageNumber++, m_pageSize, true, false); foreach (ChangeGroup nextChangeGroup in changeGroups) { TraceManager.TraceInformation("Target AnalysisProvider detecting conflicts in ChangeGroup #{0}", nextChangeGroup.ChangeGroupId); targetAnalysisProvider.DetectConflicts(nextChangeGroup); } }while (changeGroups.Count() == m_pageSize); } catch (MigrationUnresolvedConflictException) { // We have already created an unresolved conflict, just return. return; } catch (Exception e) { ConflictManager manager = m_serviceContainers[targetSystemId].GetService(typeof(ConflictManager)) as ConflictManager; ErrorManager.TryHandleException(e, manager); } }
/// <summary> /// Remove all change groups in current session which are in progress. /// For delta table, this includes status of Delta, DeltaPending. /// For migration instruction, this includes Pending or Inprogress. /// </summary> public override void RemoveInProgressChangeGroups() { Guid sessionUniqueId = new Guid(Session.SessionUniqueId); using (RuntimeEntityModel context = RuntimeEntityModel.CreateInstance()) { IQueryable <RTChangeGroup> query; query = (from cg in context.RTChangeGroupSet where cg.SessionUniqueId.Equals(sessionUniqueId) && ((cg.SourceUniqueId.Equals(SourceId) && (cg.Status == (int)ChangeStatus.Delta || cg.Status == (int)ChangeStatus.DeltaPending)) || (cg.SourceUniqueId.Equals(OtherSideChangeGroupManager.SourceId) && (cg.Status == (int)ChangeStatus.InProgress || cg.Status == (int)ChangeStatus.Pending || cg.Status == (int)ChangeStatus.PendingConflictDetection || cg.Status == (int)ChangeStatus.AnalysisMigrationInstruction))) select cg); foreach (RTChangeGroup rtChangeGroup in query) { rtChangeGroup.Status = (int)ChangeStatus.Obsolete; TraceManager.TraceInformation("In-progress changegroup {0} will be marked as obselete", rtChangeGroup.Id); } context.TrySaveChanges(); } }
private void UpdatePerItemLinkChangeGroupsByCheckingRelatedItemRecords( string sourceItemUri, LinkChangeGroup group, ILinkProvider linkProvider, bool createDeleteActionImplicitly) { var queryByItem = QueryByItem(sourceItemUri); var perItemExistingLinks = from link in queryByItem where link.RelationshipExistsOnServer select link; if (perItemExistingLinks.Count() == 0) { // no link existed before, all 'add' link actions should be pushed to the other side // AND we are going to record these links as existing on this side now AddLinks(group.Actions); } else { // check the delta link change actions // this list contains all the actions that do not need to push into the pipeline List <LinkChangeAction> actionThatEstablishExistingLinkRelationship = new List <LinkChangeAction>(); foreach (LinkChangeAction action in group.Actions) { if (action.Link.LinkType.GetsActionsFromLinkChangeHistory) { // For link types that can provide a history of link changes, we don't need to keep track of related artifact metadata // so continue to the next LinkChangeAction continue; } Debug.Assert(sourceItemUri.Equals(action.Link.SourceArtifact.Uri), "link of different action exists in the same group"); var linkQuery = from l in perItemExistingLinks where l.Relationship.Equals(action.Link.LinkType.ReferenceName) && l.RelatedArtifactId.Equals(action.Link.TargetArtifact.Uri) select l; if (action.ChangeActionId.Equals(WellKnownChangeActionId.Add)) { if (linkQuery.Count() > 0) { if (!linkQuery.First().OtherProperty.HasValue) { // link lock property is not available - required by backward-compability UpdateLink(action, true); } else { bool linkInStoreHasLock = (((WorkItemLinkStore.LinkLockStatus)linkQuery.First().OtherProperty.Value) == WorkItemLinkStore.LinkLockStatus.Locked); if (action.Link.IsLocked == linkInStoreHasLock) { // link already exist and lock-property matches - no need to push to the other side actionThatEstablishExistingLinkRelationship.Add(action); } else { UpdateLink(action, true); } } } else { // link does not exist, keep it and push through the pipeline // AND we are going to record these links as existing on this side now UpdateLink(action, true); } } else // delete { if (linkQuery.Count() > 0) { // link exists, so we will mark in our store that it no longer exists UpdateLink(action, false); } else { // link does not exist, no need to migrate this action actionThatEstablishExistingLinkRelationship.Add(action); } } } // make sure we generate "Delete Link" action for ones that exist in the our recorded link table but is not included // in delta link actions List <LinkChangeAction> deletionActions = new List <LinkChangeAction>(); if (createDeleteActionImplicitly) { foreach (var recordedExistingLink in perItemExistingLinks) { Debug.Assert(linkProvider.SupportedLinkTypes.ContainsKey(recordedExistingLink.Relationship), "linkProvider.SupportedLinkTypes.ContainsKey(recordedExistingLink.Relationship) returns false"); LinkType linkType = linkProvider.SupportedLinkTypes[recordedExistingLink.Relationship]; if (linkType.GetsActionsFromLinkChangeHistory) { // The link type is one that support link change history, so we ignore the contents of the // RelatedArtifactTable and rely on the link change history instead. continue; } bool recordedActionInGroup = false; foreach (LinkChangeAction action in group.Actions) { if (action.Link.LinkType.ReferenceName.Equals(recordedExistingLink.Relationship, StringComparison.OrdinalIgnoreCase) && action.Link.TargetArtifact.Uri.Equals(recordedExistingLink.RelatedArtifactId, StringComparison.OrdinalIgnoreCase)) { recordedActionInGroup = true; break; } } if (!recordedActionInGroup) { TraceManager.TraceInformation("Link '{0}'->'{1}' ({2}) appears to have been deleted - generating link deletion action to be migrated", recordedExistingLink.ItemId, recordedExistingLink.RelatedArtifactId, recordedExistingLink.Relationship); LinkChangeAction linkDeleteAction = linkType.CreateLinkDeletionAction( recordedExistingLink.ItemId, recordedExistingLink.RelatedArtifactId, recordedExistingLink.Relationship); if (null != linkDeleteAction) { deletionActions.Add(linkDeleteAction); } recordedExistingLink.RelationshipExistsOnServer = false; } } } if (actionThatEstablishExistingLinkRelationship.Count > 0) { foreach (LinkChangeAction actionToDelete in actionThatEstablishExistingLinkRelationship) { group.DeleteChangeAction(actionToDelete); } } if (deletionActions.Count > 0) { group.PrependActions(deletionActions); } } m_context.TrySaveChanges(); }
internal void Migrate(Guid targetSideSourceId, SessionOrchestrationPolicy orchPolicy) { try { Debug.Assert(m_serviceContainers.ContainsKey(targetSideSourceId), string.Format(MigrationToolkitResources.UnknownSourceId, targetSideSourceId)); ChangeGroupService changegroupService = (ChangeGroupService)m_serviceContainers[targetSideSourceId].GetService( typeof(ChangeGroupService)); Debug.Assert(changegroupService != null, string.Format("Change group service on {0} is not loaded", targetSideSourceId)); changegroupService.DemoteInProgressActionsToPending(); int pageNumber = 0; IEnumerable <ChangeGroup> changeGroups = null; long?firstConflictedChangeGroupId = null; if (StopMigrationEngineOnBasicConflict) { firstConflictedChangeGroupId = changegroupService.GetFirstConflictedChangeGroup(ChangeStatus.Pending); } do { // NOTE: we do not increment pageNumber here, because the processed ChangeGroups are marked "Complete" and no longer // appear in the table TraceManager.TraceInformation("Loading {0} ChangeGroup(s)", m_pageSize); changeGroups = changegroupService.NextMigrationInstructionTablePage(pageNumber, m_pageSize, false, false); foreach (ChangeGroup nextChangeGroup in changeGroups) { if (firstConflictedChangeGroupId.HasValue && firstConflictedChangeGroupId <= nextChangeGroup.ChangeGroupId) { // we should not process any conflicted change group or the following ones // if StopMigrationEngineOnBasicConflict is the policy return; } //ToDo Session.OnMigratingChangeStarting(args); TraceManager.TraceInformation("Processing ChangeGroup #{0}", nextChangeGroup.ChangeGroupId); ProcessMigrInstructionTableEntry(nextChangeGroup, targetSideSourceId); nextChangeGroup.UpdateStatus(ChangeStatus.InProgress); if (NoActiveMigrationInstructionInChangeGroup(nextChangeGroup)) { nextChangeGroup.Complete(); continue; } ConversionResult result; try { result = m_migrationProviders[targetSideSourceId].ProcessChangeGroup(nextChangeGroup); } catch (MigrationUnresolvedConflictException) { // We have already created an unresolved conflict, just return. return; } catch (Exception e) { ConflictManager manager = m_serviceContainers[targetSideSourceId].GetService(typeof(ConflictManager)) as ConflictManager; ErrorManager.TryHandleException(e, manager); return; } if (!result.ContinueProcessing) { return; } if (!string.IsNullOrEmpty(result.ChangeId)) { FinishChangeGroupMigration(nextChangeGroup, result); InvokePostChangeGroupMigrationAddins(targetSideSourceId, nextChangeGroup); } orchPolicy.Check(); } }while (changeGroups.Count() == m_pageSize); } catch (Microsoft.TeamFoundation.Migration.Toolkit.SessionOrchestrationPolicy.StopSingleTripException) { throw; } catch (Microsoft.TeamFoundation.Migration.Toolkit.SessionOrchestrationPolicy.StopSessionException) { throw; } catch (Exception e) { ConflictManager manager = m_serviceContainers[targetSideSourceId].GetService(typeof(ConflictManager)) as ConflictManager; ErrorManager.TryHandleException(e, manager); } }
private void ProcessPipeline(WorkFlowType workFlowType) { bool bidirection = IsBidirectionalWorkFlowType(workFlowType); bool leftToRightContextSyncNeeded = IsLeftToRightContextSyncNeeded(workFlowType); bool rightToLeftContextSyncNeeded = IsRightToLeftContextSyncNeeded(workFlowType); do { // always notifying the listeners the pipeline is (re)starting SessionControlResume(this, new SessionControlEventArgs()); try { #region ----- LEFT TO RIGHT ----- OneDirectionProcessPipeline(true, LeftMigrationSourceid, RightMigrationSourceid, leftToRightContextSyncNeeded, bidirection); #endregion TraceManager.TraceInformation(""); #region ----- RIGHT TO LEFT ----- if (bidirection) { OneDirectionProcessPipeline(false, RightMigrationSourceid, LeftMigrationSourceid, rightToLeftContextSyncNeeded, bidirection); } else { CleanupDeltaTable(RightMigrationSourceid); } #endregion #region ----- The current round-trip is stopping ----- if (m_syncStateMachine.TryTransit(PipelineSyncCommand.STOP_CURRENT_TRIP)) { m_syncStateMachine.CommandTransitFinished(PipelineSyncCommand.STOP_CURRENT_TRIP); } #endregion } catch (SessionOrchestrationPolicy.StopSessionException) { TraceManager.TraceInformation("{0}: Session aborted!", m_thread.Name); return; } catch (SessionOrchestrationPolicy.StopSingleTripException) { // When Stop is requested, we continue and let the policy to decide // whether it should start another round trip or not TraceManager.TraceInformation("{0}: Session stopped!", m_thread.Name); continue; } catch (Exception) { throw; } switch (workFlowType.Frequency) { case Frequency.ContinuousAutomatic: TraceManager.TraceInformation("{0}: Sync is done!", m_thread.Name); TraceManager.TraceInformation("{0}: Waiting {1} seconds before next synchronization", m_thread.Name, MilliSecondsSyncWaitInterval / 1000); break; case Frequency.ContinuousManual: case Frequency.OneTime: TraceManager.TraceInformation("{0}: Migration is done!", m_thread.Name); MigrationSessionCompleted(); break; default: throw new ArgumentException("m_orchPolicy.WorkFlowType.Frequency"); } } while (m_orchPolicy.TryStartNextRoundTrip(MilliSecondsSyncWaitInterval)); }
private void OneDirectionProcessPipeline( bool isLeftToRight, Guid sourceMigrationSourceId, Guid targetMigrationSourceId, bool contextSyncNeeded, bool bidirection) { try { m_orchPolicy.Check(); AnalysisEngine.SourceMigrationSourceId = sourceMigrationSourceId; AnalysisEngine.InvokePreAnalysisAddins(sourceMigrationSourceId); if (!AnalysisEngine.InvokeProceedToAnalysisOnAnalysisAddins(sourceMigrationSourceId)) { // In case any of the AnalysisAddins perform cleanup in the PostAnalysis method AnalysisEngine.InvokePostAnalysisAddins(sourceMigrationSourceId); return; } TraceManager.TraceInformation("Pipeline flow from {0} to {1}", sourceMigrationSourceId, targetMigrationSourceId); if (contextSyncNeeded) { TraceManager.TraceInformation("Generating context info tables for the migration source {0}", sourceMigrationSourceId); AnalysisEngine.GenerateContextInfoTables(sourceMigrationSourceId); m_orchPolicy.Check(); } TraceManager.TraceInformation("Generating delta tables for the migration source {0}", sourceMigrationSourceId); AnalysisEngine.GenerateDeltaTables(sourceMigrationSourceId); m_orchPolicy.Check(); AnalysisEngine.InvokePostDeltaComputationAddins(sourceMigrationSourceId); TraceManager.TraceInformation("Generating linking delta for the migration source {0}", sourceMigrationSourceId); LinkEngine.GenerateLinkDelta(SessionId, sourceMigrationSourceId); m_orchPolicy.Check(); AnalysisEngine.InvokePostAnalysisAddins(sourceMigrationSourceId); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Above: data collection at source side // // Below: data migration/submission at target side // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MigrationEngine.InvokePreMigrationAddins(targetMigrationSourceId); if (contextSyncNeeded) { TraceManager.TraceInformation("Establishing context for the migration source {0}", targetMigrationSourceId); MigrationEngine.EstablishContext(targetMigrationSourceId); m_orchPolicy.Check(); } if (!AnalysisEngine.DisableTargetAnalysis) { TraceManager.TraceInformation("Generating delta tables for the migration source {0}", targetMigrationSourceId); AnalysisEngine.GenerateDeltaTables(targetMigrationSourceId); m_orchPolicy.Check(); } TraceManager.TraceInformation("Generating migration instructions for the migration source {0}", targetMigrationSourceId); AnalysisEngine.GenerateMigrationInstructions(targetMigrationSourceId); m_orchPolicy.Check(); TraceManager.TraceInformation("Post-processing delta table entries from the migration source {0}", targetMigrationSourceId); AnalysisEngine.PostProcessDeltaTableEntries(targetMigrationSourceId, bidirection); m_orchPolicy.Check(); TraceManager.TraceInformation("Migrating to the migration source {0}", targetMigrationSourceId); MigrationEngine.Migrate(targetMigrationSourceId, m_orchPolicy); m_orchPolicy.Check(); MigrationEngine.InvokePostMigrationAddins(targetMigrationSourceId); TraceManager.TraceInformation("Processing linking delta"); LinkEngine.AnalyzeLinkDelta(SessionId, sourceMigrationSourceId, bidirection); m_orchPolicy.Check(); TraceManager.TraceInformation("Migrating links to the migration source {0}", targetMigrationSourceId); LinkEngine.MigrateLinks(SessionId, targetMigrationSourceId); m_orchPolicy.Check(); } finally { // Record a sync point in the database try { RecordSyncPoint(isLeftToRight, sourceMigrationSourceId, targetMigrationSourceId); } catch (Exception ex) { TraceManager.TraceWarning("{0}: Unable to record SyncPoint data due to exception: {1}", m_thread.Name, ex.ToString()); } } }
/// <summary> /// Generate migration instructions /// </summary> internal void GenerateMigrationInstructions(Guid targetSystemId) { try { // Given target system, find change group service for source and for ourselves... ConfigurationService configurationService = m_serviceContainers[targetSystemId].GetService(typeof(ConfigurationService)) as ConfigurationService; // ToDo, not sure, we can probably just pass in ource system id to let target change group service to load it. But source/target may be different, not sqlchangegroupmanager ChangeGroupService sourceChangeGroupService = m_serviceContainers[configurationService.MigrationPeer].GetService(typeof(ChangeGroupService)) as ChangeGroupService; ChangeGroupService targetChangeGroupService = m_serviceContainers[targetSystemId].GetService(typeof(ChangeGroupService)) as ChangeGroupService; // CopySourceDeltaTableToTarget //ChangeGroup deltaTableEntry; if (StopMigrationEngineOnBasicConflict) { // if one of the delta table entry on source side is conflicted, we stop long?firstConflictedChangeGroupId = sourceChangeGroupService.GetFirstConflictedChangeGroup(ChangeStatus.DeltaPending); if (firstConflictedChangeGroupId.HasValue) { return; } // if one of the migration instruction for target side is conflict, we also stop firstConflictedChangeGroupId = targetChangeGroupService.GetFirstConflictedChangeGroup(ChangeStatus.Pending); if (firstConflictedChangeGroupId.HasValue) { return; } } ChangeActionRegistrationService changeActionRegistrationService = m_serviceContainers[targetSystemId].GetService(typeof(ChangeActionRegistrationService)) as ChangeActionRegistrationService; int pageNumber = 0; IEnumerable <ChangeGroup> changeGroups; do { // NOTE: we do not increment pageNumber here, because the processed ChangeGroups are marked "DeltaComplete" and no longer // appear in the delta table changeGroups = sourceChangeGroupService.NextDeltaTablePage(pageNumber, m_pageSize, false); foreach (ChangeGroup deltaTableEntry in changeGroups) { TraceManager.TraceInformation(string.Format( "Generating migration instruction for ChangeGroup {0}", deltaTableEntry.ChangeGroupId)); ChangeGroup migrationInstructionChangeGroup = targetChangeGroupService.CreateChangeGroupForMigrationInstructionTable(deltaTableEntry); // NOTE: // migration instruction change group is created using the target change group manager/service // however, the MigrationItems in it are created by the source-side adapter // by setting the UseOtherSideMigrationItemSerializers flag, we tell this change group to use the source-side change group manager // to find the registered IMigrationItem serializer to persist these MigrationItems migrationInstructionChangeGroup.UseOtherSideMigrationItemSerializers = true; migrationInstructionChangeGroup.ReflectedChangeGroupId = deltaTableEntry.ChangeGroupId; foreach (MigrationAction action in deltaTableEntry.Actions) { try { BeforeCopyDeltaTableEntryToMigrationInstructionTable(action, configurationService.MigrationPeer); } catch (UnmappedWorkItemTypeException unmappedWITEx) { ConflictManager conflictManager = m_serviceContainers[configurationService.MigrationPeer].GetService(typeof(ConflictManager)) as ConflictManager; var conflict = WITUnmappedWITConflictType.CreateConflict(unmappedWITEx.SourceWorkItemType, action); List <MigrationAction> actions; var result = conflictManager.TryResolveNewConflict(conflictManager.SourceId, conflict, out actions); if (!result.Resolved) { continue; } else { if (result.ResolutionType == ConflictResolutionType.SkipConflictedChangeAction) { action.State = ActionState.Skipped; continue; } else { // NOTE: // So far this conflict can only be: // 1. manually resolved (skipped) AFTER // the configuration is updated with the requirement WIT mapping; // 2. skipping the conflicted migration action (i.e. not migrating the source // Work Item type. Debug.Assert( false, string.Format("WITUnmappedWITConflict is auto-resolved. Skipping this assertion will SKIP the original conflicted action '{0}'.", action.ActionId.ToString())); action.State = ActionState.Skipped; continue; } } } if (action.State == ActionState.Skipped || action.ChangeGroup.ContainsBackloggedAction) { continue; } ChangeActionHandler actionHandler; if (changeActionRegistrationService.TryGetChangeActionHandler(action.Action, action.ItemTypeReferenceName, out actionHandler)) { try { actionHandler(action, migrationInstructionChangeGroup); } catch (MigrationUnresolvedConflictException) { // We have already created an unresolved conflict, just return. return; } catch (Exception e) { ConflictManager manager = m_serviceContainers[targetSystemId].GetService(typeof(ConflictManager)) as ConflictManager; ErrorManager.TryHandleException(e, manager); } } else { string analysisProviderName; IAnalysisProvider analysisProvider; if (m_analysisProviders.TryGetValue(targetSystemId, out analysisProvider)) { analysisProviderName = analysisProvider.GetType().ToString(); } else { Debug.Fail("Unable to find IAnalysisProvider with Id: " + targetSystemId); analysisProviderName = "Unknown"; } throw new MigrationException( string.Format(MigrationToolkitResources.Culture, MigrationToolkitResources.UnknownChangeAction, action.Action.ToString(), analysisProviderName)); } } if (!migrationInstructionChangeGroup.ContainsBackloggedAction && migrationInstructionChangeGroup.Actions.Count > 0) { ChangeStatus status = migrationInstructionChangeGroup.Status; migrationInstructionChangeGroup.Status = ChangeStatus.ChangeCreationInProgress; migrationInstructionChangeGroup.Owner = deltaTableEntry.Owner; // owner may be translated too // Save the partial Change group into DB. migrationInstructionChangeGroup.Save(); // Commit the status change together. migrationInstructionChangeGroup.Status = status; deltaTableEntry.Status = ChangeStatus.DeltaComplete; migrationInstructionChangeGroup.Manager.BatchUpdateStatus( new ChangeGroup[] { migrationInstructionChangeGroup, deltaTableEntry }); } else { // If all change actions in the delta table entry are skipped. // Just mark the delta table entry as completed. deltaTableEntry.UpdateStatus(ChangeStatus.DeltaComplete); } if (this.StopRequested) { return; } } }while (changeGroups.Count() == m_pageSize); DetectBasicConflicts(targetChangeGroupService, targetSystemId, configurationService.MigrationPeer); if (this.StopRequested) { return; } ProviderDetectConflicts(targetSystemId, targetChangeGroupService); if (this.StopRequested) { return; } // dispose the target side delta table entries after we've done all conflict analysis targetChangeGroupService.BatchMarkMigrationInstructionsAsPending(); } catch (Exception e) { ConflictManager manager = m_serviceContainers[targetSystemId].GetService(typeof(ConflictManager)) as ConflictManager; ErrorManager.TryHandleException(e, manager); } }
// Insert a row into the SYNC_POINT table private void RecordSyncPoint( bool isLeftToRight, Guid sourceMigrationSourceId, Guid targetMigrationSourceId) { using (RuntimeEntityModel context = RuntimeEntityModel.CreateInstance()) { // Get the current source high water mark var highWaterMarkQuery = (from h in context.RTHighWaterMarkSet where (h.SessionUniqueId == SessionId && h.SourceUniqueId == sourceMigrationSourceId) select h).Take(1); if (highWaterMarkQuery.Count() > 0) { RTHighWaterMark sourceHighWaterMark = highWaterMarkQuery.First(); // Get the corresponding target item MigrationItemId lastMigratedTargetItem = MigrationEngine.TranslationService.GetLastMigratedItemId(targetMigrationSourceId); // find last ChangeGroupId as of the sync point var lastChangeGroupQuery = (from g in context.RTChangeGroupSet where g.SessionUniqueId.Equals(this.SessionId) && (g.SourceUniqueId.Equals(sourceMigrationSourceId) || g.SourceUniqueId.Equals(targetMigrationSourceId)) orderby g.Id descending select g.Id).Take(1); long lastChangeGroupId = (lastChangeGroupQuery.Count() > 0 ? lastChangeGroupQuery.First() : 0); // Use the previous sync point's values lastMigratedTargetItem the sync point if there is no value for lastMigratedTargetItem.ItemId if (string.IsNullOrEmpty(lastMigratedTargetItem.ItemId)) { IQueryable<RTSyncPoint> lastChangeSyncPointQuery = (from syncPt in context.RTSyncPointSet where syncPt.SessionUniqueId.Equals(this.SessionId) && syncPt.SourceUniqueId.Equals(sourceMigrationSourceId) && syncPt.SourceHighWaterMarkName.Equals(sourceHighWaterMark.Name) orderby syncPt.Id descending select syncPt).Take(1); if (lastChangeSyncPointQuery.Count() > 0) { RTSyncPoint previousSyncPoint = lastChangeSyncPointQuery.First(); lastMigratedTargetItem.ItemId = previousSyncPoint.LastMigratedTargetItemId; lastMigratedTargetItem.ItemVersion = previousSyncPoint.LastMigratedTargetItemVersion; } } // Don't write the sync point if there is still no value for lastMigratedTargetItem.ItemId if (!string.IsNullOrEmpty(lastMigratedTargetItem.ItemId)) { // Populate and save the SyncPoint info RTSyncPoint syncPoint = RTSyncPoint.CreateRTSyncPoint( 0, // Id SessionId, sourceMigrationSourceId, sourceHighWaterMark.Name, lastMigratedTargetItem.ItemId, lastMigratedTargetItem.ItemVersion ); syncPoint.SourceHighWaterMarkValue = sourceHighWaterMark.Value; syncPoint.LastChangeGroupId = lastChangeGroupId; context.AddToRTSyncPointSet(syncPoint); context.TrySaveChanges(); TraceManager.TraceInformation("Recorded sync point for migration source {0} of session {1} with Source High Water Mark '{2}' value of '{3}'", sourceMigrationSourceId, SessionId, syncPoint.SourceHighWaterMarkName, syncPoint.SourceHighWaterMarkValue); } } } }