/// <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); }
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); }
/// <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); }
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) })
/// <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); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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("(-)"); }
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); }
/// <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); }
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(); } }
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(); }
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); }
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(); }
/// <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); }