Example #1
0
        private static void HandleSyncError(SyncException sex)
        {
            switch (sex.SyncStage)
            {
            case SyncStage.BeginSession:
                Logger.Current.Info(sex.ToString());
                break;

            case SyncStage.EnsureMetadata:
                break;

            case SyncStage.SelectedChanges:
                break;

            case SyncStage.ApplyingChanges:
                break;

            case SyncStage.ApplyingInserts:
                break;

            case SyncStage.ApplyingUpdates:
                break;

            case SyncStage.ApplyingDeletes:
                break;

            case SyncStage.WriteMetadata:
                break;

            case SyncStage.EndSession:
                break;

            case SyncStage.CleanupMetadata:
                break;

            default:
                break;
            }

            // try to end sessions on both
        }
Example #2
0
        /// <summary>
        /// Main action : Launch the synchronization
        /// </summary>
        public async Task <SyncContext> SynchronizeAsync(CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(this.scopeName))
            {
                throw new Exception("Scope Name is mandatory");
            }

            // Context, used to back and forth data between servers
            SyncContext context = new SyncContext(Guid.NewGuid());

            // set start time
            context.StartTime = DateTime.Now;

            this.SessionState = SyncSessionState.Synchronizing;
            this.SessionStateChanged?.Invoke(this, this.SessionState);
            ScopeInfo localScopeInfo = null, serverScopeInfo = null, serverLocalScopeReferenceInfo = null;

            // Stats computed
            ChangesStatistics changesStatistics = new ChangesStatistics();

            try
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Setting the cancellation token
                this.LocalProvider.SetCancellationToken(cancellationToken);
                this.RemoteProvider.SetCancellationToken(cancellationToken);

                // Begin Session / Read the adapters
                context = await this.RemoteProvider.BeginSessionAsync(context);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                context = await this.LocalProvider.BeginSessionAsync(context);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
                // ----------------------------------------
                // 1) Read scope info
                // ----------------------------------------

                // get the scope from local provider
                List <ScopeInfo> localScopes;
                List <ScopeInfo> serverScopes;
                (context, localScopes) = await this.LocalProvider.EnsureScopesAsync(context, scopeName);

                if (localScopes.Count != 1)
                {
                    throw new Exception("On Local provider, we should have only one scope info");
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                localScopeInfo = localScopes[0];

                (context, serverScopes) = await this.RemoteProvider.EnsureScopesAsync(context, scopeName, localScopeInfo.Id);

                if (serverScopes.Count != 2)
                {
                    throw new Exception("On Remote provider, we should have two scopes (one for server and one for client side)");
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                serverScopeInfo = serverScopes.First(s => s.Id != localScopeInfo.Id);
                serverLocalScopeReferenceInfo = serverScopes.First(s => s.Id == localScopeInfo.Id);

                // ----------------------------------------
                // 2) Build Configuration Object
                // ----------------------------------------

                // Get Configuration from remote provider
                (context, this.serviceConfiguration) = await this.RemoteProvider.EnsureConfigurationAsync(context, this.serviceConfiguration);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Invert policy on the client
                var configurationLocale = this.serviceConfiguration.Clone();
                var policy = this.serviceConfiguration.ConflictResolutionPolicy;
                if (policy == ConflictResolutionPolicy.ServerWins)
                {
                    configurationLocale.ConflictResolutionPolicy = ConflictResolutionPolicy.ClientWins;
                }
                if (policy == ConflictResolutionPolicy.ClientWins)
                {
                    configurationLocale.ConflictResolutionPolicy = ConflictResolutionPolicy.ServerWins;
                }

                // Apply on local Provider
                ServiceConfiguration configuration;
                (context, configuration) = await this.LocalProvider.EnsureConfigurationAsync(context, configurationLocale);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // ----------------------------------------
                // 3) Ensure databases are ready
                // ----------------------------------------

                // Server should have already the schema
                context = await this.RemoteProvider.EnsureDatabaseAsync(context, serverScopeInfo, DbBuilderOption.CreateOrUseExistingSchema | DbBuilderOption.CreateOrUseExistingTrackingTables);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Client could have, or not, the tables
                context = await this.LocalProvider.EnsureDatabaseAsync(context, localScopeInfo, DbBuilderOption.CreateOrUseExistingSchema | DbBuilderOption.CreateOrUseExistingTrackingTables);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // ----------------------------------------
                // 5) Get changes and apply them
                // ----------------------------------------
                BatchInfo         clientBatchInfo;
                BatchInfo         serverBatchInfo;
                ChangesStatistics clientStatistics    = null;
                ChangesStatistics serverStatistics    = null;
                ChangesStatistics tmpClientStatistics = null;
                ChangesStatistics tmpServerStatistics = null;


                // Generate the client batchinfo with all files involved
                (context, clientBatchInfo, clientStatistics) = await this.LocalProvider.GetChangeBatchAsync(context, localScopeInfo);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Apply on the Server Side
                // Since we are on the server, we need to check the server client timestamp (not the client timestamp which is completely different)
                (context, serverStatistics) = await this.RemoteProvider.ApplyChangesAsync(context, serverLocalScopeReferenceInfo, clientBatchInfo);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Get changes from server
                (context, serverBatchInfo, tmpServerStatistics) = await this.RemoteProvider.GetChangeBatchAsync(context, serverScopeInfo);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Update server stats
                if (serverStatistics == null)
                {
                    serverStatistics = tmpServerStatistics;
                }
                else
                {
                    clientStatistics.SelectedChanges = tmpServerStatistics.SelectedChanges;
                }

                // Apply local changes
                (context, tmpClientStatistics) = await this.LocalProvider.ApplyChangesAsync(context, serverScopeInfo, serverBatchInfo);

                if (clientStatistics == null)
                {
                    clientStatistics = tmpClientStatistics;
                }
                else
                {
                    clientStatistics.AppliedChanges = tmpClientStatistics.AppliedChanges;
                }

                context.TotalChangesDownloaded = clientStatistics.TotalAppliedChanges;
                context.TotalChangesUploaded   = serverStatistics.TotalAppliedChanges;
                context.TotalSyncErrors        = clientStatistics.TotalAppliedChangesFailed;

                long serverTimestamp, clientTimestamp;

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                (context, serverTimestamp) = await this.RemoteProvider.GetLocalTimestampAsync(context);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                (context, clientTimestamp) = await this.LocalProvider.GetLocalTimestampAsync(context);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                context.CompleteTime = DateTime.Now;

                serverScopeInfo.IsNewScope = false;
                serverLocalScopeReferenceInfo.IsNewScope = false;
                localScopeInfo.IsNewScope = false;

                serverScopeInfo.LastSync = context.CompleteTime;
                serverLocalScopeReferenceInfo.LastSync = context.CompleteTime;
                localScopeInfo.LastSync = context.CompleteTime;

                serverScopeInfo.IsLocal = true;
                serverLocalScopeReferenceInfo.IsLocal = false;

                context = await this.RemoteProvider.WriteScopesAsync(context, new List <ScopeInfo> {
                    serverScopeInfo, serverLocalScopeReferenceInfo
                });

                serverScopeInfo.IsLocal = false;
                localScopeInfo.IsLocal  = true;

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                context = await this.LocalProvider.WriteScopesAsync(context, new List <ScopeInfo> {
                    localScopeInfo, serverScopeInfo
                });

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                // Begin Session / Read the adapters
                context = await this.RemoteProvider.EndSessionAsync(context);

                context = await this.LocalProvider.EndSessionAsync(context);
            }
            catch (OperationCanceledException oce)
            {
                var error = SyncException.CreateOperationCanceledException(context.SyncStage, oce);
                HandleSyncError(error);
                context.Error = error;
            }
            catch (SyncException sex)
            {
                HandleSyncError(sex);
                context.Error = sex;
            }
            catch (Exception ex)
            {
                var error = SyncException.CreateUnknowException(context.SyncStage, ex);
                HandleSyncError(error);
                context.Error = error;
            }
            finally
            {
                // if EndSessionAsync() was never called, try a last time
                try
                {
                    context = await this.RemoteProvider.EndSessionAsync(context);

                    context = await this.LocalProvider.EndSessionAsync(context);
                }
                catch (Exception)
                {
                    // no raise
                }

                this.SessionState = SyncSessionState.Ready;
                this.SessionStateChanged?.Invoke(this, this.SessionState);
            }

            return(context);
        }
Example #3
0
        internal static SyncException CreateOperationCanceledException(SyncStage syncStage, OperationCanceledException oce)
        {
            SyncException syncException = new SyncException("Operation canceled.", syncStage, SyncExceptionType.OperationCanceled);

            return(syncException);
        }
Example #4
0
        internal static Exception CreateInProgressException(SyncStage syncStage)
        {
            SyncException syncException = new SyncException("Session already in progress", syncStage, SyncExceptionType.Rollback);

            return(syncException);
        }
Example #5
0
        internal static SyncException CreateUnknowException(SyncStage stage, Exception ex)
        {
            SyncException syncException = new SyncException("Unknown error has occured", stage, ex, SyncExceptionType.Unknown);

            return(syncException);
        }
Example #6
0
        /// <summary>
        /// Create a rollback exception to rollback the Sync session
        /// </summary>
        /// <param name="context"></param>
        internal static SyncException CreateRollbackException(SyncStage stage)
        {
            SyncException syncException = new SyncException("User rollback action.", stage, SyncExceptionType.Rollback);

            return(syncException);
        }