/// <summary>
        /// Uninstalls InitializationProcessInProgress neighborhood action that was installed by InstallInitializationProcessInProgress.
        /// </summary>
        /// <param name="FollowerId">Identifier of the follower.</param>
        /// <returns>true if the function suceeds, false otherwise.</returns>
        public async Task <bool> UninstallInitializationProcessInProgressAsync(int ActionId)
        {
            log.Trace("(ActionId:{0})", ActionId);

            bool res = false;

            DatabaseLock lockObject = UnitOfWork.NeighborhoodActionLock;
            await unitOfWork.AcquireLockAsync(lockObject);

            try
            {
                NeighborhoodAction action = (await GetAsync(a => a.Id == ActionId)).FirstOrDefault();
                if (action != null)
                {
                    Delete(action);
                    await unitOfWork.SaveThrowAsync();

                    res = true;
                }
                else
                {
                    log.Error("Action ID {0} not found.", ActionId);
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            unitOfWork.ReleaseLock(lockObject);

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 2
0
        async Task ExecuteSqlScriptAsync(string scriptName, DatabaseLock dbLock)
        {
            // We don't actually use the lock here, but want to make sure the caller is holding it.
            if (dbLock == null)
            {
                throw new ArgumentNullException(nameof(dbLock));
            }

            if (!dbLock.IsHeld)
            {
                throw new ArgumentException("This database lock has already been released!", nameof(dbLock));
            }

            string schemaCommands = await GetScriptTextAsync(scriptName);

            // Reference: https://stackoverflow.com/questions/650098/how-to-execute-an-sql-script-file-using-c-sharp
            using SqlConnection scriptRunnerConnection = this.settings.CreateConnection();
            var serverConnection = new ServerConnection(scriptRunnerConnection);

            Stopwatch latencyStopwatch = Stopwatch.StartNew();

            try
            {
                // NOTE: Async execution is not supported by this library
                serverConnection.ExecuteNonQuery(schemaCommands);
            }
            finally
            {
                latencyStopwatch.Stop();
                this.traceHelper.ExecutedSqlScript(scriptName, latencyStopwatch);
            }
        }
        /// <summary>
        /// Updates LastRefreshTime of a remote server.
        /// </summary>
        /// <param name="NetworkId">Identifier of the remote server to update.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> UpdateLastRefreshTimeAsync(byte[] NetworkId)
        {
            log.Trace("(NetworkId:'{0}')", NetworkId.ToHex());

            bool res = false;

            DatabaseLock lockObject = GetTableLock();
            await unitOfWork.AcquireLockAsync(lockObject);

            try
            {
                T remoteServer = (await GetAsync(n => n.NetworkId == NetworkId)).FirstOrDefault();
                if (remoteServer != null)
                {
                    remoteServer.LastRefreshTime = DateTime.UtcNow;
                    Update(remoteServer);
                    await unitOfWork.SaveThrowAsync();
                }
                else
                {
                    // Between the check couple of lines above and here, the requesting server stop being our neighbor/follower
                    // we can ignore it now and proceed as this does no harm.
                    log.Error("Remote server ID '{0}' is no longer our {1}.", NetworkId.ToHex(), remoteServer is Neighbor ? "neighbor" : "follower");
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred while trying to update LastRefreshTime of server ID '{0}': {1}", NetworkId.ToHex(), e.ToString());
            }

            unitOfWork.ReleaseLock(lockObject);

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Saves profiles of a neighbor from the memory to the database. This is done when the neighborhood initialization process is finished.
        /// </summary>
        /// <param name="IdentityDatabase">List of identities received from the neighbor.</param>
        /// <param name="NeighborId">Network ID of the neighbor.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> SaveNeighborhoodInitializationProfilesAsync(Dictionary <byte[], NeighborIdentity> IdentityDatabase, byte[] NeighborId)
        {
            log.Trace("(IdentityDatabase.Count:{0},NeighborId:'{1}')", IdentityDatabase.Count, NeighborId.ToHex());

            bool error   = false;
            bool success = false;

            DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.NeighborIdentityLock, UnitOfWork.NeighborLock };
            using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
            {
                try
                {
                    Neighbor neighbor = (await GetAsync(n => n.NetworkId == NeighborId)).FirstOrDefault();
                    if (neighbor != null)
                    {
                        // The neighbor is now initialized and is allowed to send us updates.
                        neighbor.LastRefreshTime = DateTime.UtcNow;
                        neighbor.Initialized     = true;
                        neighbor.SharedProfiles  = IdentityDatabase.Count;
                        Update(neighbor);

                        // Insert all its activities.
                        foreach (NeighborIdentity identity in IdentityDatabase.Values)
                        {
                            await unitOfWork.NeighborIdentityRepository.InsertAsync(identity);
                        }

                        await unitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        success = true;
                    }
                    else
                    {
                        log.Error("Unable to find neighbor ID '{0}'.", NeighborId.ToHex());
                    }
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!success)
                {
                    log.Warn("Rolling back transaction.");
                    unitOfWork.SafeTransactionRollback(transaction);
                    error = true;
                }

                unitOfWork.ReleaseLock(lockObjects);
            }


            bool res = !error;

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 5
0
        public async Task DeleteSchemaAsync()
        {
            // Prevent other create or delete operations from executing at the same time.
            await using DatabaseLock dbLock = await this.AcquireDatabaseLockAsync();

            await this.DropSchemaAsync(dbLock);

            await dbLock.CommitAsync();
        }
        public async Task InstallScriptFor(int entryOrGuid, SmartScriptType type, IList <ISmartScriptLine> script)
        {
            using var writeLock = await DatabaseLock.WriteLock();

            await using var model = Database();

            await model.BeginTransactionAsync(IsolationLevel.ReadCommitted);

            foreach (var pair in script.Select(l => (l.ScriptSourceType, l.EntryOrGuid))
                     .Concat(new (int ScriptSourceType, int EntryOrGuid)[] { ((int)type, entryOrGuid) })
Exemplo n.º 7
0
        /// <summary>
        /// Finds and deletes neighbor actions for which there is no existing neighbor.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        private bool DeleteInvalidNeighborActions()
        {
            log.Info("()");

            bool res = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                // Disable change tracking for faster multiple deletes.
                unitOfWork.Context.ChangeTracker.AutoDetectChangesEnabled = false;

                DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.NeighborActivityLock, UnitOfWork.NeighborLock };
                unitOfWork.AcquireLock(lockObjects);
                try
                {
                    List <byte[]>           neighborIds        = unitOfWork.NeighborRepository.Get(null, null, true).Select(n => n.NeighborId).ToList();
                    HashSet <byte[]>        neighborIdsHashSet = new HashSet <byte[]>(neighborIds, StructuralEqualityComparer <byte[]> .Default);
                    List <NeighborActivity> identities         = unitOfWork.NeighborActivityRepository.Get(null, null, true).ToList();

                    bool saveDb        = false;
                    int  deleteCounter = 0;
                    foreach (NeighborActivity identity in identities)
                    {
                        if (!neighborIdsHashSet.Contains(identity.PrimaryServerId))
                        {
                            unitOfWork.NeighborActivityRepository.Delete(identity);
                            saveDb = true;
                            deleteCounter++;
                        }
                    }

                    if (saveDb)
                    {
                        log.Debug("Removing {0} identities without existing neighbor server.", deleteCounter);

                        unitOfWork.SaveThrow();
                    }
                    else
                    {
                        log.Debug("No identities without existing neighbor server found.");
                    }
                    res = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                unitOfWork.ReleaseLock(lockObjects);
            }

            log.Info("(-):{0}", res);
            return(res);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Removes neighbor servers from database that we failed to finish the neighborhood initialization process with.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        private bool DeleteUninitializedNeighbors()
        {
            log.Info("()");

            bool res = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                List <Neighbor> neighborsToDelete = null;

                DatabaseLock lockObject = UnitOfWork.NeighborLock;
                unitOfWork.AcquireLock(lockObject);
                try
                {
                    neighborsToDelete = unitOfWork.NeighborRepository.Get(n => n.LastRefreshTime == null).ToList();
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }
                unitOfWork.ReleaseLock(lockObject);

                // Delete neighbor completely.
                if (neighborsToDelete.Count > 0)
                {
                    bool error = false;
                    log.Debug("Removing {0} uninitialized neighbors.", neighborsToDelete.Count);
                    foreach (Neighbor neighbor in neighborsToDelete)
                    {
                        Task <bool> task = unitOfWork.NeighborRepository.DeleteNeighbor(unitOfWork, neighbor.NeighborId);
                        if (!task.Result)
                        {
                            log.Error("Unable to delete neighbor ID '{0}' from the database.", neighbor.NeighborId.ToHex());
                            error = true;
                            break;
                        }
                    }

                    res = !error;
                }
                else
                {
                    res = true;
                    log.Debug("No uninitialized neighbors found.");
                }
            }

            log.Info("(-):{0}", res);
            return(res);
        }
Exemplo n.º 9
0
        public async Task InstallConditions(IEnumerable <IConditionLine> conditionLines,
                                            IDatabaseProvider.ConditionKeyMask keyMask,
                                            IDatabaseProvider.ConditionKey?manualKey = null)
        {
            using var writeLock = await DatabaseLock.WriteLock();

            await using var model = new TrinityDatabase();

            var conditions = conditionLines?.ToList() ?? new List <IConditionLine>();
            List <(int SourceType, int?SourceGroup, int?SourceEntry, int?SourceId)> keys = conditions.Select(c =>
                                                                                                             (c.SourceType, keyMask.HasFlag(IDatabaseProvider.ConditionKeyMask.SourceGroup) ? (int?)c.SourceGroup : null,
                                                                                                              keyMask.HasFlag(IDatabaseProvider.ConditionKeyMask.SourceEntry) ? (int?)c.SourceEntry : null,
                                                                                                              keyMask.HasFlag(IDatabaseProvider.ConditionKeyMask.SourceId) ? (int?)c.SourceId : null))
                                                                                           .Union(manualKey.HasValue
                    ? new[]
        /// <summary>
        /// Sets srNeighborPort of a follower to null.
        /// </summary>
        /// <param name="UnitOfWork">Unit of work instance.</param>
        /// <param name="FollowerId">Identifier of the follower server.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> ResetSrNeighborPort(UnitOfWork UnitOfWork, byte[] FollowerId)
        {
            log.Trace("(FollowerId:'{0}')", FollowerId.ToHex());

            bool         res        = false;
            bool         dbSuccess  = false;
            DatabaseLock lockObject = UnitOfWork.FollowerLock;

            using (IDbContextTransaction transaction = await UnitOfWork.BeginTransactionWithLockAsync(lockObject))
            {
                try
                {
                    Follower follower = (await GetAsync(f => f.FollowerId == FollowerId)).FirstOrDefault();
                    if (follower != null)
                    {
                        follower.SrNeighborPort = null;
                        Update(follower);

                        await UnitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        res = true;
                    }
                    else
                    {
                        log.Error("Unable to find follower ID '{0}'.", FollowerId.ToHex());
                    }

                    dbSuccess = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!dbSuccess)
                {
                    log.Warn("Rolling back transaction.");
                    UnitOfWork.SafeTransactionRollback(transaction);
                }

                UnitOfWork.ReleaseLock(lockObject);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Sets NeighborPort of a server to null.
        /// </summary>
        /// <param name="NetworkId">Identifier of the remote server.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> ResetNeighborPortAsync(byte[] NetworkId)
        {
            log.Trace("(NetworkId:'{0}')", NetworkId.ToHex());

            bool         res        = false;
            bool         dbSuccess  = false;
            DatabaseLock lockObject = GetTableLock();

            using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObject))
            {
                try
                {
                    T remoteServer = (await GetAsync(f => f.NetworkId == NetworkId)).FirstOrDefault();
                    if (remoteServer != null)
                    {
                        remoteServer.NeighborPort = null;
                        Update(remoteServer);

                        await unitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        res = true;
                    }
                    else
                    {
                        log.Error("Unable to find {0} ID '{1}'.", remoteServer is Neighbor ? "neighbor" : "follower", NetworkId.ToHex());
                    }

                    dbSuccess = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!dbSuccess)
                {
                    log.Warn("Rolling back transaction.");
                    unitOfWork.SafeTransactionRollback(transaction);
                }

                unitOfWork.ReleaseLock(lockObject);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Sets a new value to CanObjectHash of an identity's profile.
        /// </summary>
        /// <param name="UunitOfWork">Unit of work instance.</param>
        /// <param name="IdentityId">Network identifier of the identity to set the value to.</param>
        /// <param name="NewValue">Value to set identity's CanObjectHash to.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> SetCanObjectHashAsync(UnitOfWork UnitOfWork, byte[] IdentityId, byte[] NewValue)
        {
            log.Trace("(IdentityId:'{0}',NewValue:'{1}')", IdentityId.ToHex(), NewValue != null ? NewValue.ToBase58() : "");

            bool res = false;

            DatabaseLock lockObject = UnitOfWork.HostedIdentityLock;

            using (IDbContextTransaction transaction = await UnitOfWork.BeginTransactionWithLockAsync(lockObject))
            {
                try
                {
                    HostedIdentity identity = (await GetAsync(i => i.IdentityId == IdentityId)).FirstOrDefault();
                    if (identity != null)
                    {
                        identity.CanObjectHash = NewValue;
                        Update(identity);
                        await UnitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        res = true;
                    }
                    else
                    {
                        log.Error("Identity ID '{0}' not found.", IdentityId.ToHex());
                    }
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!res)
                {
                    log.Warn("Rolling back transaction.");
                    UnitOfWork.SafeTransactionRollback(transaction);
                }
            }

            UnitOfWork.ReleaseLock(lockObject);

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Saves values related to the profile server contact information to the database.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> SaveProfileServerContactInformation()
        {
            log.Trace("()");

            bool res = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                DatabaseLock lockObject = UnitOfWork.SettingsLock;
                await unitOfWork.AcquireLockAsync(lockObject);

                try
                {
                    string addr = Config.Configuration.ExternalServerAddress.ToString();
                    string port = Config.Configuration.ServerRoles.GetRolePort((uint)ServerRole.Primary).ToString();
                    string hash = canContactInformationHash.ToBase58();
                    log.Debug("Saving contact information values to database: {0}:{1}, '{2}'", addr, port, hash);

                    Setting primaryPort           = new Setting("PrimaryPort", port);
                    Setting externalServerAddress = new Setting("ExternalServerAddress", addr);
                    Setting canProfileServerContactInformationHash = new Setting("CanProfileServerContactInformationHash", hash);

                    await unitOfWork.SettingsRepository.AddOrUpdate(externalServerAddress);

                    await unitOfWork.SettingsRepository.AddOrUpdate(primaryPort);

                    await unitOfWork.SettingsRepository.AddOrUpdate(canProfileServerContactInformationHash);

                    await unitOfWork.SaveThrowAsync();

                    res = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                unitOfWork.ReleaseLock(lockObject);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Installs InitializationProcessInProgress neighborhood action that will prevent
        /// the proximity server from sending updates to a new follower.
        /// </summary>
        /// <param name="FollowerId">Identifier of the follower to block updates to.</param>
        /// <returns>Action ID of the newly installed action, or -1 if the function fails.</returns>
        public async Task <int> InstallInitializationProcessInProgressAsync(byte[] FollowerId)
        {
            log.Trace("(FollowerId:'{0}')", FollowerId.ToHex());

            int res = -1;

            DatabaseLock lockObject = UnitOfWork.NeighborhoodActionLock;
            await unitOfWork.AcquireLockAsync(lockObject);

            try
            {
                // This action will make sure the proximity server will not send updates to the new follower
                // until the neighborhood initialization process is complete.
                NeighborhoodAction action = new NeighborhoodAction()
                {
                    ServerId              = FollowerId,
                    Type                  = NeighborhoodActionType.InitializationProcessInProgress,
                    TargetActivityId      = 0,
                    TargetActivityOwnerId = null,
                    Timestamp             = DateTime.UtcNow,
                    AdditionalData        = null,

                    // This will cause other actions to this follower to be postponed for 20 minutes from now.
                    ExecuteAfter = DateTime.UtcNow.AddMinutes(20)
                };
                await InsertAsync(action);

                await unitOfWork.SaveThrowAsync();

                res = action.Id;
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            unitOfWork.ReleaseLock(lockObject);

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Removes follower servers from database that failed to finish the neighborhood initialization process.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        private bool DeleteUninitializedFollowers()
        {
            log.Info("()");

            bool res = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                DatabaseLock lockObject = UnitOfWork.FollowerLock;
                unitOfWork.AcquireLock(lockObject);
                try
                {
                    List <Follower> followers = unitOfWork.FollowerRepository.Get(f => f.LastRefreshTime == null).ToList();
                    if (followers.Count > 0)
                    {
                        log.Debug("Removing {0} uninitialized followers.", followers.Count);
                        foreach (Follower follower in followers)
                        {
                            unitOfWork.FollowerRepository.Delete(follower);
                        }

                        res = unitOfWork.Save();
                    }
                    else
                    {
                        res = true;
                        log.Debug("No uninitialized followers found.");
                    }
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                unitOfWork.ReleaseLock(lockObject);
            }

            log.Info("(-):{0}", res);
            return(res);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Saves GPS location of the server to database settings.
        /// </summary>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        private async Task <bool> SaveLocationToSettings()
        {
            log.Trace("()");

            bool res = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                DatabaseLock lockObject = UnitOfWork.SettingsLock;
                await unitOfWork.AcquireLockAsync(lockObject);

                try
                {
                    Setting locLocationLatitude = new Setting("LocLocationLatitude", Location.Latitude.ToString(CultureInfo.InvariantCulture));
                    unitOfWork.SettingsRepository.Insert(locLocationLatitude);

                    Setting locLocationLongitude = new Setting("LocLocationLongitude", Location.Longitude.ToString(CultureInfo.InvariantCulture));
                    unitOfWork.SettingsRepository.Insert(locLocationLongitude);

                    log.Debug("Saving new GPS location [{0}] to database.", Location);

                    await unitOfWork.SettingsRepository.AddOrUpdate(locLocationLatitude);

                    await unitOfWork.SettingsRepository.AddOrUpdate(locLocationLongitude);

                    await unitOfWork.SaveThrowAsync();

                    res = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                unitOfWork.ReleaseLock(lockObject);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Finds and deletes expired activities.
        /// </summary>
        public async Task DeleteExpiredActivitiesAsync()
        {
            log.Trace("()");

            bool     isPrimaryRepository = this is PrimaryActivityRepository;
            DateTime now = DateTime.UtcNow;

            DatabaseLock lockObject = isPrimaryRepository ? UnitOfWork.PrimaryActivityLock : UnitOfWork.NeighborActivityLock;
            await unitOfWork.AcquireLockAsync(lockObject);

            try
            {
                List <T> expiredActivities = (await GetAsync(i => i.ExpirationTime < now, null, true)).ToList();
                if (expiredActivities.Count > 0)
                {
                    log.Debug("There are {0} expired primary activities.", expiredActivities.Count);
                    foreach (T activity in expiredActivities)
                    {
                        Delete(activity);
                        log.Debug("{0} activity ID {1}, owner ID '{2}' expired and will be deleted.", isPrimaryRepository ? "Primary" : "Neighbor", activity.ActivityId, activity.OwnerIdentityId.ToHex());
                    }

                    await unitOfWork.SaveThrowAsync();

                    log.Debug("{0} expired {1} activities were deleted.", expiredActivities.Count, isPrimaryRepository ? "primary" : "neighbor");
                }
                else
                {
                    log.Debug("No expired activities found.");
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            unitOfWork.ReleaseLock(lockObject);
            log.Trace("(-)");
        }
Exemplo n.º 18
0
        async Task ExecuteSqlScriptAsync(string scriptName, DatabaseLock dbLock)
        {
            if (dbLock == null)
            {
                throw new ArgumentNullException(nameof(dbLock));
            }

            if (!dbLock.IsHeld)
            {
                throw new ArgumentException("This database lock has already been released!", nameof(dbLock));
            }

            string scriptText = await GetScriptTextAsync(scriptName);

            // Split script into distinct executeable commands
            IEnumerable <string> scriptCommands = Regex.Split(scriptText, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase)
                                                  .Where(x => x.Trim().Length > 0);

            Stopwatch latencyStopwatch = Stopwatch.StartNew();

            try
            {
                foreach (string commandText in scriptCommands)
                {
                    using SqlCommand command = dbLock.CreateCommand();
                    command.CommandText      = commandText;

                    await command.ExecuteNonQueryAsync();
                }
            }
            finally
            {
                latencyStopwatch.Stop();
                this.traceHelper.ExecutedSqlScript(scriptName, latencyStopwatch);
            }
        }
        /// <summary>
        /// Safely deletes action from the database.
        /// </summary>
        /// <param name="ActionId">Database ID of the action to delete.</param>
        /// <returns>true if the action was deleted, false otherwise.</returns>
        public async Task <bool> DeleteAsync(int ActionId)
        {
            log.Trace("(ActionId:{0})", ActionId);

            bool         res        = false;
            DatabaseLock lockObject = UnitOfWork.NeighborhoodActionLock;
            await unitOfWork.AcquireLockAsync(lockObject);

            try
            {
                NeighborhoodAction action = (await GetAsync(a => a.Id == ActionId)).FirstOrDefault();
                if (action != null)
                {
                    Delete(action);
                    log.Trace("Action ID {0} will be removed from database.", ActionId);

                    if (await unitOfWork.SaveAsync())
                    {
                        res = true;
                    }
                }
                else
                {
                    log.Info("Unable to find action ID {0} in the database, it has probably been removed already.", ActionId);
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            unitOfWork.ReleaseLock(lockObject);

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Deletes a follower entry from the database when the follower disconnected before the neighborhood initialization process completed.
        /// </summary>
        /// <returns>true if the function succeeds, false othewise.</returns>
        private async Task <bool> DeleteUnfinishedFollowerInitialization()
        {
            log.Trace("()");

            bool res = false;

            byte[] followerId = IdentityId;
            bool   success    = false;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.FollowerLock, UnitOfWork.NeighborhoodActionLock };
                using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
                {
                    try
                    {
                        bool saveDb = false;

                        // Delete the follower itself.
                        Follower existingFollower = (await unitOfWork.FollowerRepository.GetAsync(f => f.NetworkId == followerId)).FirstOrDefault();
                        if (existingFollower != null)
                        {
                            unitOfWork.FollowerRepository.Delete(existingFollower);
                            log.Debug("Follower ID '{0}' will be removed from the database.", followerId.ToHex());
                            saveDb = true;
                        }
                        else
                        {
                            log.Error("Follower ID '{0}' not found.", followerId.ToHex());
                        }

                        // Delete all its neighborhood actions.
                        List <NeighborhoodAction> actions = (await unitOfWork.NeighborhoodActionRepository.GetAsync(a => a.ServerId == followerId)).ToList();
                        foreach (NeighborhoodAction action in actions)
                        {
                            if (action.IsProfileAction())
                            {
                                log.Debug("Action ID {0}, type {1}, serverId '{2}' will be removed from the database.", action.Id, action.Type, followerId.ToHex());
                                unitOfWork.NeighborhoodActionRepository.Delete(action);
                                saveDb = true;
                            }
                        }


                        if (saveDb)
                        {
                            await unitOfWork.SaveThrowAsync();

                            transaction.Commit();
                        }

                        success = true;
                        res     = true;
                    }
                    catch (Exception e)
                    {
                        log.Error("Exception occurred: {0}", e.ToString());
                    }

                    if (!success)
                    {
                        log.Warn("Rolling back transaction.");
                        unitOfWork.SafeTransactionRollback(transaction);
                    }

                    unitOfWork.ReleaseLock(lockObjects);
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
Exemplo n.º 21
0
        private static void ProcessThreadedCommands()
        {
            if ((Core.Flags & StartupFlags.SingleThreaded) == StartupFlags.SingleThreaded)
            {
                throw new InvalidOperationException("ProcessThreadedCommands should never be called in single threaded mode.");
            }

            IndividualCommandThread = new Thread(ProcessCommandsWorkerThread);
            IndividualCommandThread.Start();

            while (!ShuttingDown)
            {
                System.Threading.Thread.Sleep(10);
                DatabaseLock.WaitOne();
                Heartbeat();
                DatabaseLock.ReleaseMutex();

                while (PendingCommands.Count > 0 && !ShuttingDown)
                {
                    PendingCommand PendingCommand = null;

                    PendingCommandLock.WaitOne();

                    try
                    {
                        PendingCommand = PendingCommands.FirstOrDefault(pc =>
                        {
                            return(true);
                            //if (pc.Actor.ConnectedClient == null) return true;
                            //else return (DateTime.Now - pc.Actor.ConnectedClient.TimeOfLastCommand).TotalMilliseconds > SettingsObject.AllowedCommandRate;
                        });
                        if (PendingCommand != null)
                        {
                            PendingCommands.Remove(PendingCommand);
                        }
                    }
                    catch (Exception e)
                    {
                        LogCommandError(e);
                        PendingCommand = null;
                    }

                    PendingCommandLock.ReleaseMutex();

                    if (PendingCommand != null)
                    {
                        DatabaseLock.WaitOne();

                        NextCommand = PendingCommand;

                        //Reset flags that the last command may have changed
                        CommandTimeoutEnabled = true;
                        SilentFlag            = false;
                        GlobalRules.LogRules(null);

                        CommandReadyHandle.Set(); //Signal worker thread to proceed.
                        if (!CommandFinishedHandle.WaitOne(SettingsObject.CommandTimeOut))
                        {
                            if (!CommandTimeoutEnabled) //Timeout is disabled, go ahead and wait for infinity.
                            {
                                CommandFinishedHandle.WaitOne();
                            }
                            else
                            {
                                //Kill the command processor thread.
                                IndividualCommandThread.Abort();
                                ClearPendingMessages();
                                if (PendingCommand.Actor.ConnectedClient != null)
                                {
                                    PendingCommand.Actor.ConnectedClient.Send("Command timeout.\r\n");
                                    LogError(String.Format("Command timeout. {0} - {1}", PendingCommand.Actor.ConnectedClient.ConnectionDescription, PendingCommand.RawCommand));
                                }
                                else
                                {
                                    LogError(String.Format("Command timeout [No client] - {1}", PendingCommand.RawCommand));
                                }
                                IndividualCommandThread = new Thread(ProcessCommandsWorkerThread);
                                IndividualCommandThread.Start();
                            }
                        }

                        if (PendingCommand.ProcessingCompleteCallback != null)
                        {
                            PendingCommand.ProcessingCompleteCallback();
                        }

                        DatabaseLock.ReleaseMutex();
                    }
                }
            }

            IndividualCommandThread.Abort();
            if (Core.OnShutDown != null)
            {
                Core.OnShutDown();
            }
        }
Exemplo n.º 22
0
        public async Task CreateOrUpgradeSchemaAsync(bool recreateIfExists)
        {
            // Prevent other create or delete operations from executing at the same time.
            await using DatabaseLock dbLock = await this.AcquireDatabaseLockAsync(this.settings.CreateDatabaseIfNotExists);

            var currentSchemaVersion = new SemanticVersion(0, 0, 0);

            if (recreateIfExists)
            {
                await this.DropSchemaAsync(dbLock);
            }
            else
            {
                // If the database already has the latest schema, then skip
                using SqlCommand command = dbLock.CreateCommand();
                command.CommandText      = "dt._GetVersions";
                command.CommandType      = CommandType.StoredProcedure;

                try
                {
                    using DbDataReader reader = await SqlUtils.ExecuteReaderAsync(command, this.traceHelper);

                    if (await reader.ReadAsync())
                    {
                        // The first result contains the latest version
                        currentSchemaVersion = SqlUtils.GetSemanticVersion(reader);
                        if (currentSchemaVersion >= DTUtils.ExtensionVersion)
                        {
                            // The schema is already up-to-date.
                            return;
                        }
                    }
                }
                catch (SqlException e) when(e.Number == 2812 /* Could not find stored procedure */)
                {
                    // Ignore - this is expected for new databases
                }
            }

            // SQL schema setup scripts are embedded resources in the assembly, making them immutable post-build.
            Assembly             assembly          = typeof(SqlOrchestrationService).Assembly;
            IEnumerable <string> createSchemaFiles = assembly.GetManifestResourceNames()
                                                     .Where(name => name.Contains(".schema-") && name.EndsWith(".sql"));

            var versionedFiles = new Dictionary <SemanticVersion, string>();

            foreach (string name in createSchemaFiles)
            {
                // Attempt to parse the semver-like string from the resource name.
                // This version number tells us whether to execute the script for this extension version.
                const string RegexExpression = @"schema-(\d+.\d+.\d+(?:-\w+)?).sql$";
                Match        match           = Regex.Match(name, RegexExpression);
                if (!match.Success || match.Groups.Count < 2)
                {
                    throw new InvalidOperationException($"Failed to find version information in resource name '{name}'. The resource name must match the regex expression '{RegexExpression}'.");
                }

                SemanticVersion version = SemanticVersion.Parse(match.Groups[1].Value);
                if (!versionedFiles.TryAdd(version, match.Value))
                {
                    throw new InvalidOperationException($"There must not be more than one script resource with the same version number! Found {version} multiple times.");
                }
            }

            // Sort by the version numbers to ensure that we run them in the correct order
            foreach ((SemanticVersion version, string name) in versionedFiles.OrderBy(pair => pair.Key))
            {
                // Skip past versions that are already present in the database
                if (version > currentSchemaVersion)
                {
                    await this.ExecuteSqlScriptAsync(name, dbLock);

                    currentSchemaVersion = version;
                }
            }

            // Add or update stored procedures, functions, and views
            await this.ExecuteSqlScriptAsync("logic.sql", dbLock);

            // Configure security roles, permissions, etc.
            await this.ExecuteSqlScriptAsync("permissions.sql", dbLock);

            // Insert the current extension version number into the database and commit the transaction.
            // The extension version is used instead of the schema version to more accurately track whether
            // we need to update the sprocs or views.
            using (SqlCommand command = dbLock.CreateCommand())
            {
                command.CommandText = "dt._UpdateVersion";
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.Add("@SemanticVersion", SqlDbType.NVarChar, 100).Value = DTUtils.ExtensionVersion.ToString();

                await SqlUtils.ExecuteNonQueryAsync(command, this.traceHelper);
            }

            await dbLock.CommitAsync();
        }
Exemplo n.º 23
0
 Task DropSchemaAsync(DatabaseLock dbLock) => this.ExecuteSqlScriptAsync("drop-schema.sql", dbLock);
        public async Task InstallScriptFor(int entryOrGuid, SmartScriptType type, IEnumerable <ISmartScriptLine> script)
        {
            using var writeLock = await DatabaseLock.WriteLock();

            await using var model = Database();

            await model.BeginTransactionAsync(IsolationLevel.ReadCommitted);

            await model.SmartScript.Where(x => x.EntryOrGuid == entryOrGuid && x.ScriptSourceType == (int)type).DeleteAsync();

            switch (type)
            {
            case SmartScriptType.Creature:
            {
                uint entry = 0;
                if (entryOrGuid < 0)
                {
                    var template = await model.Creature.Where(p => p.Guid == (uint)-entryOrGuid).FirstOrDefaultAsync();

                    if (template == null)
                    {
                        throw new Exception(
                                  $"Trying to install creature script for guid {-entryOrGuid}, but this guid doesn't exist in creature table, so entry cannot be determined.");
                    }
                    entry = template.Entry;
                }
                else
                {
                    entry = (uint)entryOrGuid;
                }
                await model.CreatureTemplate.Where(p => p.Entry == entry)
                .Set(p => p.AIName, currentCoreVersion.Current.SmartScriptFeatures.CreatureSmartAiName)
                .Set(p => p.ScriptName, "")
                .UpdateAsync();

                break;
            }

            case SmartScriptType.GameObject:
            {
                uint entry = 0;
                if (entryOrGuid < 0)
                {
                    var template = await model.GameObject.Where(p => p.Guid == (uint)-entryOrGuid).FirstOrDefaultAsync();

                    if (template == null)
                    {
                        throw new Exception(
                                  $"Trying to install gameobject script for guid {-entryOrGuid}, but this guid doesn't exist in gameobject table, so entry cannot be determined.");
                    }
                    entry = template.Entry;
                }
                else
                {
                    entry = (uint)entryOrGuid;
                }
                await model.GameObjectTemplate.Where(p => p.Entry == entry)
                .Set(p => p.AIName, currentCoreVersion.Current.SmartScriptFeatures.GameObjectSmartAiName)
                .Set(p => p.ScriptName, "")
                .UpdateAsync();

                break;
            }

            case SmartScriptType.Quest:
                var addonExists = await model.QuestTemplateAddon.Where(p => p.Entry == (uint)entryOrGuid).AnyAsync();

                if (!addonExists)
                {
                    await model.QuestTemplateAddon.InsertAsync(() => new MySqlQuestTemplateAddon()
                    {
                        Entry = (uint)entryOrGuid
                    });
                }
                await model.QuestTemplateAddonWithScriptName
                .Where(p => p.Entry == (uint)entryOrGuid)
                .Set(p => p.ScriptName, "SmartQuest")
                .UpdateAsync();

                break;

            case SmartScriptType.AreaTrigger:
                await model.AreaTriggerScript.Where(p => p.Id == entryOrGuid).DeleteAsync();

                await model.AreaTriggerScript.InsertAsync(() => new MySqlAreaTriggerScript()
                {
                    Id = entryOrGuid, ScriptName = "SmartTrigger"
                });

                break;

            case SmartScriptType.AreaTriggerEntity:
                await model.AreaTriggerTemplate.Where(p => p.Id == (uint)entryOrGuid && p.IsServerSide == false)
                .Set(p => p.ScriptName, "SmartAreaTriggerAI")
                .UpdateAsync();

                break;

            case SmartScriptType.AreaTriggerEntityServerSide:
                await model.AreaTriggerTemplate.Where(p => p.Id == (uint)entryOrGuid && p.IsServerSide == true)
                .Set(p => p.ScriptName, "SmartAreaTriggerAI")
                .UpdateAsync();

                break;
            }

            await model.SmartScript.BulkCopyAsync(script.Select(l => new MySqlSmartScriptLine(l)));

            await model.CommitTransactionAsync();
        }
        /// <summary>
        /// Obtains IP address and neighbor port from the server's network identifier.
        /// </summary>
        /// <param name="NetworkId">Network identifer of the server.</param>
        /// <param name="NotFound">If the function fails, this is set to true if the reason for failure was that the remote server was not found.</param>
        /// <param name="IgnoreDbPortValue">If set to true, the function will ignore NeighborPort value of the server even if it is set in the database
        /// and will contact the server on its primary port and then update NeighborPort in the database, if it successfully gets its value.</param>
        /// <returns>End point description or null if the function fails.</returns>
        public async Task <IPEndPoint> GetServerContactAsync(byte[] NetworkId, StrongBox <bool> NotFound, bool IgnoreDbPortValue = false)
        {
            log.Trace("(NetworkId:'{0}',IgnoreDbPortValue:{1})", NetworkId.ToHex(), IgnoreDbPortValue);

            IPEndPoint   res        = null;
            DatabaseLock lockObject = null;
            bool         unlock     = false;

            try
            {
                T remoteServer = (await GetAsync(n => n.NetworkId == NetworkId)).FirstOrDefault();
                if (remoteServer != null)
                {
                    log.Trace("{0} server found in the database.", remoteServer is Neighbor ? "Neighbor" : "Follower");
                    IPAddress addr = new IPAddress(remoteServer.IpAddress);
                    if (!IgnoreDbPortValue && (remoteServer.NeighborPort != null))
                    {
                        res = new IPEndPoint(addr, remoteServer.NeighborPort.Value);
                    }
                    else
                    {
                        NeighborhoodActionProcessor neighborhoodActionProcessor = (NeighborhoodActionProcessor)Base.ComponentDictionary[NeighborhoodActionProcessor.ComponentName];

                        // We do not know neighbor port of this server yet (or we ignore it), we have to connect to its primary port and get that information.
                        int neighborPort = await neighborhoodActionProcessor.GetServerRolePortFromPrimaryPort(addr, remoteServer.PrimaryPort, ServerRoleType.Neighbor);

                        if (neighborPort != 0)
                        {
                            lockObject = GetTableLock();
                            await unitOfWork.AcquireLockAsync(lockObject);

                            unlock = true;

                            remoteServer.NeighborPort = neighborPort;
                            Update(remoteServer);
                            if (!await unitOfWork.SaveAsync())
                            {
                                log.Error("Unable to save new neighbor port information {0} of {1} ID '{2}' to the database.", neighborPort, remoteServer is Neighbor ? "neighbor" : "follower", NetworkId.ToHex());
                            }

                            res = new IPEndPoint(addr, neighborPort);
                        }
                        else
                        {
                            log.Debug("Unable to obtain srNeighbor port from primary port of {0} ID '{1}'.", remoteServer is Neighbor ? "neighbor" : "follower", NetworkId.ToHex());
                        }
                    }
                }
                else
                {
                    log.Error("Unable to find {0} ID '{1}' in the database.", remoteServer is Neighbor ? "neighbor" : "follower", NetworkId.ToHex());
                    NotFound.Value = true;
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            if (unlock)
            {
                unitOfWork.ReleaseLock(lockObject);
            }

            log.Trace("(-):{0}", res != null ? res.ToString() : "null");
            return(res);
        }
Exemplo n.º 26
0
        public async Task InstallScriptFor(int entryOrGuid, SmartScriptType type, IEnumerable <ISmartScriptLine> script)
        {
            using var writeLock = await DatabaseLock.WriteLock();

            await using var model = new TrinityDatabase();

            await model.BeginTransactionAsync(IsolationLevel.ReadCommitted);

            await model.SmartScript.Where(x => x.EntryOrGuid == entryOrGuid && x.ScriptSourceType == (int)type).DeleteAsync();

            switch (type)
            {
            case SmartScriptType.Creature:
                await model.CreatureTemplate.Where(p => p.Entry == (uint)entryOrGuid)
                .Set(p => p.AIName, "SmartAI")
                .Set(p => p.ScriptName, "")
                .UpdateAsync();

                break;

            case SmartScriptType.GameObject:
                await model.GameObjectTemplate.Where(p => p.Entry == (uint)entryOrGuid)
                .Set(p => p.AIName, "SmartGameObjectAI")
                .Set(p => p.ScriptName, "")
                .UpdateAsync();

                break;

            case SmartScriptType.Quest:
                var addonExists = await model.QuestTemplateAddon.Where(p => p.Entry == (uint)entryOrGuid).AnyAsync();

                if (!addonExists)
                {
                    await model.QuestTemplateAddon.InsertAsync(() => new MySqlQuestTemplateAddon()
                    {
                        Entry = (uint)entryOrGuid
                    });
                }
                await model.QuestTemplateAddonWithScriptName
                .Where(p => p.Entry == (uint)entryOrGuid)
                .Set(p => p.ScriptName, "SmartQuest")
                .UpdateAsync();

                break;

            case SmartScriptType.AreaTrigger:
                await model.AreaTriggerScript.Where(p => p.Id == entryOrGuid).DeleteAsync();

                await model.AreaTriggerScript.InsertAsync(() => new MySqlAreaTriggerScript()
                {
                    Id = entryOrGuid, ScriptName = "SmartTrigger"
                });

                break;

            case SmartScriptType.AreaTriggerEntity:
                await model.AreaTriggerTemplate.Where(p => p.Id == (uint)entryOrGuid && p.IsServerSide == false)
                .Set(p => p.ScriptName, "SmartAreaTriggerAI")
                .UpdateAsync();

                break;

            case SmartScriptType.AreaTriggerEntityServerSide:
                await model.AreaTriggerTemplate.Where(p => p.Id == (uint)entryOrGuid && p.IsServerSide == true)
                .Set(p => p.ScriptName, "SmartAreaTriggerAI")
                .UpdateAsync();

                break;
            }

            await model.SmartScript.BulkCopyAsync(script.Select(l => new MySqlSmartScriptLine(l)));

            await model.CommitTransactionAsync();
        }
Exemplo n.º 27
0
        /// <summary>
        /// Deletes neighbor server, all its activities and all neighborhood actions for it from the database.
        /// </summary>
        /// <param name="UnitOfWork">Unit of work instance.</param>
        /// <param name="NeighborId">Identifier of the neighbor server to delete.</param>
        /// <param name="ActionId">If there is a neighborhood action that should NOT be deleted, this is its ID, otherwise it is -1.</param>
        /// <param name="HoldingLocks">true if the caller is holding NeighborLock and NeighborhoodActionLock.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> DeleteNeighbor(UnitOfWork UnitOfWork, byte[] NeighborId, int ActionId = -1, bool HoldingLocks = false)
        {
            log.Trace("(NeighborId:'{0}',ActionId:{1},HoldingLocks:{2})", NeighborId.ToHex(), ActionId, HoldingLocks);

            bool res     = false;
            bool success = false;


            // Delete neighbor from the list of neighbors.
            DatabaseLock lockObject = UnitOfWork.NeighborLock;

            if (!HoldingLocks)
            {
                await UnitOfWork.AcquireLockAsync(lockObject);
            }
            try
            {
                Neighbor neighbor = (await GetAsync(n => n.NeighborId == NeighborId)).FirstOrDefault();
                if (neighbor != null)
                {
                    Delete(neighbor);
                    await UnitOfWork.SaveThrowAsync();

                    log.Debug("Neighbor ID '{0}' deleted from database.", NeighborId.ToHex());
                }
                else
                {
                    // If the neighbor does not exist, we set success to true as the result of the operation is as we want it
                    // and we gain nothing by trying to repeat the action later.
                    log.Warn("Neighbor ID '{0}' not found.", NeighborId.ToHex());
                }

                success = true;
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }
            if (!HoldingLocks)
            {
                UnitOfWork.ReleaseLock(lockObject);
            }

            // Delete neighbor's activities from the database.
            if (success)
            {
                success = false;

                // Disable change tracking for faster multiple deletes.
                UnitOfWork.Context.ChangeTracker.AutoDetectChangesEnabled = false;

                lockObject = UnitOfWork.NeighborActivityLock;
                await UnitOfWork.AcquireLockAsync(lockObject);

                try
                {
                    List <NeighborActivity> activities = (await UnitOfWork.NeighborActivityRepository.GetAsync(i => i.PrimaryServerId == NeighborId)).ToList();
                    if (activities.Count > 0)
                    {
                        log.Debug("There are {0} activities of removed neighbor ID '{1}'.", activities.Count, NeighborId.ToHex());
                        foreach (NeighborActivity activity in activities)
                        {
                            UnitOfWork.NeighborActivityRepository.Delete(activity);
                        }

                        await UnitOfWork.SaveThrowAsync();

                        log.Debug("{0} identities hosted on neighbor ID '{1}' deleted from database.", activities.Count, NeighborId.ToHex());
                    }
                    else
                    {
                        log.Trace("No profiles hosted on neighbor ID '{0}' found.", NeighborId.ToHex());
                    }

                    success = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                UnitOfWork.ReleaseLock(lockObject);

                UnitOfWork.Context.ChangeTracker.AutoDetectChangesEnabled = true;
            }

            if (success)
            {
                success    = false;
                lockObject = UnitOfWork.NeighborhoodActionLock;
                if (!HoldingLocks)
                {
                    await UnitOfWork.AcquireLockAsync(lockObject);
                }
                try
                {
                    // Do not delete the current action, it will be deleted just after this method finishes.
                    List <NeighborhoodAction> actions = UnitOfWork.NeighborhoodActionRepository.Get(a => (a.ServerId == NeighborId) && (a.Id != ActionId)).ToList();
                    if (actions.Count > 0)
                    {
                        foreach (NeighborhoodAction action in actions)
                        {
                            log.Debug("Action ID {0}, type {1}, serverId '{2}' will be removed from the database.", action.Id, action.Type, NeighborId.ToHex());
                            UnitOfWork.NeighborhoodActionRepository.Delete(action);
                        }

                        await UnitOfWork.SaveThrowAsync();
                    }
                    else
                    {
                        log.Debug("No neighborhood actions for neighbor ID '{0}' found.", NeighborId.ToHex());
                    }

                    success = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!HoldingLocks)
                {
                    UnitOfWork.ReleaseLock(lockObject);
                }
            }

            res = success;


            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Processes NeighbourhoodChangedNotificationRequest message from LOC server.
        /// <para>Adds, changes, or deletes neighbor and possibly adds new neighborhood action to the database.</para>
        /// </summary>
        /// <param name="Client">TCP client who received the message.</param>
        /// <param name="RequestMessage">Full request message.</param>
        /// <returns>Response message to be sent to the client.</returns>
        public async Task <LocProtocolMessage> ProcessMessageNeighbourhoodChangedNotificationRequestAsync(LocClient Client, LocProtocolMessage RequestMessage)
        {
            log.Trace("()");

            LocProtocolMessage res     = Client.MessageBuilder.CreateErrorInternalResponse(RequestMessage);
            bool signalActionProcessor = false;

            NeighbourhoodChangedNotificationRequest neighbourhoodChangedNotificationRequest = RequestMessage.Request.LocalService.NeighbourhoodChanged;

            using (UnitOfWork unitOfWork = new UnitOfWork())
            {
                DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.NeighborLock, UnitOfWork.NeighborhoodActionLock };
                using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
                {
                    bool success = false;
                    bool saveDb  = false;
                    try
                    {
                        int neighborhoodSize = await unitOfWork.NeighborRepository.CountAsync();

                        foreach (NeighbourhoodChange change in neighbourhoodChangedNotificationRequest.Changes)
                        {
                            // We do ignore errors here for each individual change and just continue processing the next item from the list.
                            log.Trace("Neighborhood change type is {0}.", change.ChangeTypeCase);
                            switch (change.ChangeTypeCase)
                            {
                            case NeighbourhoodChange.ChangeTypeOneofCase.AddedNodeInfo:
                            case NeighbourhoodChange.ChangeTypeOneofCase.UpdatedNodeInfo:
                            {
                                bool     isAdd    = change.ChangeTypeCase == NeighbourhoodChange.ChangeTypeOneofCase.AddedNodeInfo;
                                NodeInfo nodeInfo = isAdd ? change.AddedNodeInfo : change.UpdatedNodeInfo;

                                // Check whether a proximity server is running on this node.
                                // If not, it is not interesting for us at all, skip it.
                                int    proximityServerPort;
                                byte[] proximityServerId;
                                if (!HasProximityServerService(nodeInfo, out proximityServerPort, out proximityServerId))
                                {
                                    break;
                                }

                                NodeContact            contact   = nodeInfo.Contact;
                                IPAddress              ipAddress = new IPAddress(contact.IpAddress.ToByteArray());
                                Iop.Locnet.GpsLocation location  = nodeInfo.Location;
                                int latitude  = location.Latitude;
                                int longitude = location.Longitude;

                                AddOrChangeNeighborResult addChangeRes = await AddOrChangeNeighborAsync(unitOfWork, proximityServerId, ipAddress, proximityServerPort, latitude, longitude, neighborhoodSize);

                                neighborhoodSize = addChangeRes.NeighborhoodSize;

                                if (addChangeRes.SaveDb)
                                {
                                    saveDb = true;
                                }

                                if (addChangeRes.SignalActionProcessor)
                                {
                                    signalActionProcessor = true;
                                }

                                break;
                            }

                            case NeighbourhoodChange.ChangeTypeOneofCase.RemovedNodeId:
                            {
                                byte[] serverId = change.RemovedNodeId.ToByteArray();

                                bool serverIdValid = serverId.Length == ProtocolHelper.NetworkIdentifierLength;
                                if (!serverIdValid)
                                {
                                    log.Error("Received invalid neighbor server ID '{0}' from LOC server.", serverId.ToHex());
                                    break;
                                }

                                // Data processing.
                                Neighbor existingNeighbor = (await unitOfWork.NeighborRepository.GetAsync(n => n.NetworkId == serverId)).FirstOrDefault();
                                if (existingNeighbor != null)
                                {
                                    log.Trace("Creating neighborhood action to deleting neighbor ID '{0}' from the database.", serverId.ToHex());

                                    string neighborInfo = JsonConvert.SerializeObject(existingNeighbor);

                                    // Delete neighbor completely.
                                    // This will cause our proximity server to erase all activities of the neighbor that has been removed.
                                    bool deleted = await unitOfWork.NeighborRepository.DeleteNeighborAsync(serverId, -1, true);

                                    if (deleted)
                                    {
                                        // Add action that will contact the neighbor and ask it to stop sending updates.
                                        // Note that the neighbor information will be deleted by the time this action
                                        // is executed and this is why we have to fill in AdditionalData.
                                        NeighborhoodAction stopUpdatesAction = new NeighborhoodAction()
                                        {
                                            ServerId              = serverId,
                                            Timestamp             = DateTime.UtcNow,
                                            Type                  = NeighborhoodActionType.StopNeighborhoodUpdates,
                                            TargetActivityId      = 0,
                                            TargetActivityOwnerId = null,
                                            ExecuteAfter          = DateTime.UtcNow,
                                            AdditionalData        = neighborInfo
                                        };
                                        await unitOfWork.NeighborhoodActionRepository.InsertAsync(stopUpdatesAction);

                                        signalActionProcessor = true;
                                        saveDb = true;
                                    }
                                    else
                                    {
                                        log.Error("Failed to remove neighbor ID '{0}' from the database.", serverId.ToHex());
                                        // This is actually bad, we failed to remove a record from the database, which should never happen.
                                        // We try to insert action to remove this neighbor later, but adding the action might fail as well.
                                        NeighborhoodAction action = new NeighborhoodAction()
                                        {
                                            ServerId              = serverId,
                                            Timestamp             = DateTime.UtcNow,
                                            Type                  = NeighborhoodActionType.RemoveNeighbor,
                                            TargetActivityId      = 0,
                                            TargetActivityOwnerId = null,
                                            AdditionalData        = null
                                        };
                                        await unitOfWork.NeighborhoodActionRepository.InsertAsync(action);

                                        signalActionProcessor = true;
                                        saveDb = true;
                                    }
                                }
                                else
                                {
                                    log.Debug("Neighbor ID '{0}' not found, can not be removed.", serverId.ToHex());
                                    // It can be the case that this node has not an associated proximity server, so in that case we should ignore it.
                                    // If the node has an associated proximity server, then nothing bad really happens here if we have activities
                                    // of such a neighbor in NeighborActivity table. Those entries will expire and will be deleted.
                                }
                                break;
                            }

                            default:
                                log.Error("Invalid neighborhood change type '{0}'.", change.ChangeTypeCase);
                                break;
                            }
                        }

                        if (saveDb)
                        {
                            await unitOfWork.SaveThrowAsync();

                            transaction.Commit();
                        }
                        success = true;
                        res     = Client.MessageBuilder.CreateNeighbourhoodChangedNotificationResponse(RequestMessage);
                    }
                    catch (Exception e)
                    {
                        log.Error("Exception occurred: {0}", e.ToString());
                    }

                    if (!success)
                    {
                        log.Warn("Rolling back transaction.");
                        unitOfWork.SafeTransactionRollback(transaction);
                    }

                    unitOfWork.ReleaseLock(lockObjects);
                }
            }

            if (signalActionProcessor)
            {
                NeighborhoodActionProcessor neighborhoodActionProcessor = (NeighborhoodActionProcessor)Base.ComponentDictionary[NeighborhoodActionProcessor.ComponentName];
                neighborhoodActionProcessor.Signal();
            }

            log.Trace("(-):*.Response.Status={0}", res.Response.Status);
            return(res);
        }
        /// <summary>
        /// Processes GetNeighbourNodesByDistanceResponse message received from LOC server.
        /// <para>This message contains information about proximity server's neighbors, with which it should share its activity database.</para>
        /// </summary>
        /// <param name="ResponseMessage">Full response message.</param>
        /// <param name="IsInitialization">true if the response was received to the request during the LOC initialization, false if it was received to the refresh request after the initialization.</param>
        /// <returns>true if the connection to the LOC server should remain open, false if it should be closed.</returns>
        public async Task <bool> ProcessMessageGetNeighbourNodesByDistanceResponseAsync(LocProtocolMessage ResponseMessage, bool IsInitialization)
        {
            log.Trace("(IsInitialization:{0})", IsInitialization);

            bool res = false;
            bool signalActionProcessor = false;

            GetNeighbourNodesByDistanceResponse getNeighbourNodesByDistanceResponse = ResponseMessage.Response.LocalService.GetNeighbourNodes;

            if (getNeighbourNodesByDistanceResponse.Nodes.Count > 0)
            {
                using (UnitOfWork unitOfWork = new UnitOfWork())
                {
                    DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.NeighborLock, UnitOfWork.NeighborhoodActionLock };
                    using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
                    {
                        bool success = false;
                        bool saveDb  = false;
                        try
                        {
                            int neighborhoodSize = await unitOfWork.NeighborRepository.CountAsync();

                            foreach (NodeInfo nodeInfo in getNeighbourNodesByDistanceResponse.Nodes)
                            {
                                // Check whether a proximity server is running on this node.
                                // If not, it is not interesting for us at all, skip it.
                                int    proximityServerPort;
                                byte[] proximityServerId;
                                if (!HasProximityServerService(nodeInfo, out proximityServerPort, out proximityServerId))
                                {
                                    continue;
                                }

                                NodeContact contact   = nodeInfo.Contact;
                                byte[]      ipBytes   = contact.IpAddress.ToByteArray();
                                IPAddress   ipAddress = new IPAddress(ipBytes);

                                int latitude  = nodeInfo.Location.Latitude;
                                int longitude = nodeInfo.Location.Longitude;

                                AddOrChangeNeighborResult addChangeRes = await AddOrChangeNeighborAsync(unitOfWork, proximityServerId, ipAddress, proximityServerPort, latitude, longitude, neighborhoodSize);

                                neighborhoodSize = addChangeRes.NeighborhoodSize;

                                if (addChangeRes.SaveDb)
                                {
                                    saveDb = true;
                                }

                                if (addChangeRes.SignalActionProcessor)
                                {
                                    signalActionProcessor = true;
                                }

                                // We do ignore errors here and just continue processing another item from the list.
                            }

                            if (saveDb)
                            {
                                await unitOfWork.SaveThrowAsync();

                                transaction.Commit();
                            }
                            success = true;
                            res     = true;
                        }
                        catch (Exception e)
                        {
                            log.Error("Exception occurred: {0}", e.ToString());
                        }

                        if (!success)
                        {
                            log.Warn("Rolling back transaction.");
                            unitOfWork.SafeTransactionRollback(transaction);
                        }

                        unitOfWork.ReleaseLock(lockObjects);
                    }
                }
            }
            else
            {
                log.Debug("No neighbors announced by LOC server.");
                res = true;
            }

            if (signalActionProcessor)
            {
                NeighborhoodActionProcessor neighborhoodActionProcessor = (NeighborhoodActionProcessor)Base.ComponentDictionary[NeighborhoodActionProcessor.ComponentName];
                neighborhoodActionProcessor.Signal();
            }

            if (res && IsInitialization)
            {
                log.Debug("LOC component is now considered in sync with LOC server.");
                serverComponent.SetLocServerInitialized(true);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
        /// <summary>
        /// Deletes follower server and all neighborhood actions for it from the database.
        /// </summary>
        /// <param name="FollowerId">Identifier of the follower server to delete.</param>
        /// <param name="ActionId">If there is a neighborhood action that should NOT be deleted, this is its ID, otherwise it is -1.</param>
        /// <returns>Status.Ok if the function succeeds, Status.ErrorNotFound if the function fails because a follower of the given ID was not found,
        /// Status.ErrorInternal if the function fails for any other reason.</returns>
        public async Task <Status> DeleteFollowerAsync(byte[] FollowerId, int ActionId = -1)
        {
            log.Trace("(FollowerId:'{0}',ActionId:{1})", FollowerId.ToHex(), ActionId);

            Status res       = Status.ErrorInternal;
            bool   dbSuccess = false;

            DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.FollowerLock, UnitOfWork.NeighborhoodActionLock };
            using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
            {
                try
                {
                    Follower existingFollower = (await unitOfWork.FollowerRepository.GetAsync(f => f.NetworkId == FollowerId)).FirstOrDefault();
                    if (existingFollower != null)
                    {
                        Delete(existingFollower);

                        List <NeighborhoodAction> actions = (await unitOfWork.NeighborhoodActionRepository.GetAsync(a => (a.ServerId == FollowerId) && (a.Id != ActionId))).ToList();
                        if (actions.Count > 0)
                        {
                            foreach (NeighborhoodAction action in actions)
                            {
                                if (action.IsProfileAction())
                                {
                                    log.Debug("Action ID {0}, type {1}, serverId '{2}' will be removed from the database.", action.Id, action.Type, FollowerId.ToHex());
                                    unitOfWork.NeighborhoodActionRepository.Delete(action);
                                }
                            }
                        }
                        else
                        {
                            log.Debug("No neighborhood actions for follower ID '{0}' found.", FollowerId.ToHex());
                        }

                        await unitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        res = Status.Ok;
                    }
                    else
                    {
                        log.Warn("Follower ID '{0}' not found.", FollowerId.ToHex());
                        res = Status.ErrorNotFound;
                    }

                    dbSuccess = true;
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }


                if (!dbSuccess)
                {
                    log.Warn("Rolling back transaction.");
                    unitOfWork.SafeTransactionRollback(transaction);
                }

                unitOfWork.ReleaseLock(lockObjects);
            }

            log.Trace("(-):{0}", res);
            return(res);
        }