public IBulbCacheReadObject SetSignal(Guid bulbId, BulbSignal signal) { var statusData = ProlongOrChangeStatus(signal, bulbId); UpdateParentByChild(statusData); return(statusData); }
protected void ProlongStatus(BulbSignal signal, BulbCacheWriteObject data) { // проверим пробел var nextStartDate = GetStartDate(data, signal); bool hasSpace = data.ActualDate < nextStartDate; if (hasSpace) { throw new Exception("Тут не должно быть пробела"); } // разрыва нет // у виртуальных колбасок нет смысла увеличивать счетчик, т.к. это приводит только к лишнему сохранению объектов if (data.IsLeaf) { data.Count++; } data.HasSignal = signal.HasSignal; data.Message = signal.Message; if (signal.EventId != Guid.Empty) { data.LastEventId = signal.EventId; } data.LastChildBulbId = signal.ChildBulbId; // у листьев время завершения меняется, // а у родителей время завершения меняется только когда начинется новый статус if (data.IsLeaf) { // это нужно, например, чтобы у проверок и метрик EndDate показывал время последнего выполнения data.EndDate = signal.ProcessDate; // актуальность проверки и метрики определяется последним значением if (data.EventCategory == EventCategory.MetricStatus || data.EventCategory == EventCategory.UnitTestStatus) { data.ActualDate = signal.ActualDate; } else if (data.EventCategory == EventCategory.ComponentEventsStatus) { if (signal.ActualDate > data.ActualDate) { data.ActualDate = signal.ActualDate; } } else { throw new Exception("Неизвестный тип лампочки " + data.EventCategory); } } else { // у виртуальных лампочек время актуальности и завершения не меняется при продлении data.ActualDate = EventHelper.InfiniteActualDate; } CreateOrUpdateStatusEvent(signal.AccountId, data); }
public IBulbCacheReadObject SetUnknownStatus(Guid accountId, Guid statusId, DateTime processDate) { var signal = BulbSignal.CreateUnknown(accountId, processDate); var data = ProlongOrChangeStatus(signal, statusId); UpdateParentByChild(data); return(data); }
protected void CalculateComponentEventsStatus(Guid accountId, Guid eventsStatusId) { var processDate = DateTime.Now; var eventService = Context.EventService; var statusService = Context.BulbService; var request = new AccountCacheRequest() { AccountId = accountId, ObjectId = eventsStatusId }; // получаем объект для записи, чтобы заблокировать доступ, но ничего не пишем using (var data = AllCaches.StatusDatas.Write(request)) { if (data.Actual(processDate)) { // событий не было вообще if (data.LastEventId == null) { return; } // проверим, что событие актуально var lastEvent = eventService.GetEventCacheOrNullById(accountId, data.LastEventId.Value); if (lastEvent != null) { bool isActual = EventImportanceHelper.Get(data.Status) == lastEvent.Importance && lastEvent.ActualDate >= processDate; if (isActual) { // ничего не изменилось, статус актуален return; } } } // ищем событие, которое пересекается со статусом var nextEvent = eventService.GetDangerousEvent( accountId, data.ComponentId.Value, processDate); if (nextEvent != null) { var noSignalImportance = Api.EventImportance.Unknown; var signal = BulbSignal.Create(processDate, nextEvent, noSignalImportance, accountId); statusService.SetSignal(data.Id, signal); return; } // нет актуальных событий var unknownSignal = BulbSignal.CreateUnknown(accountId, processDate); statusService.SetSignal(eventsStatusId, unknownSignal); } }
protected void UpdateParentByChild(IBulbCacheReadObject childBulb) { if (childBulb == null) { return; } var parentId = childBulb.GetParentId(); if (parentId == null) { return; } var parentRequest = new AccountCacheRequest() { AccountId = childBulb.AccountId, ObjectId = parentId.Value }; IBulbCacheReadObject rParent = null; using (var parent = AllCaches.StatusDatas.Write(parentRequest)) { // менять выключенного родителя нельзя, его надо сначала включить if (parent.Status == MonitoringStatus.Disabled) { return; } var processDate = DateTime.Now; BulbSignal signal = null; if (childBulb.Status > parent.Status) { // 1. если дочерний опаснее signal = BulbSignal.CreateFromChild(processDate, childBulb); rParent = ProlongOrChangeStatus(signal, parent.Id); } else { // 2. если дочерний менее опасный и он был последней причиной if (childBulb.Status < parent.Status && parent.LastChildBulbId == childBulb.Id) { // нужно расчитать занова CalculateByChilds(parent); } } } if (rParent != null) { UpdateParentByChild(rParent); } }
protected IBulbCacheReadObject ProlongOrChangeStatus(BulbSignal signal, Guid statusDataId) { var request = new AccountCacheRequest() { AccountId = signal.AccountId, ObjectId = statusDataId }; IBulbCacheReadObject rBulb = null; using (var wStatusData = AllCaches.StatusDatas.Write(request)) { rBulb = wStatusData; // если сигнал протух, то игнорируем его if (signal.ActualDate < wStatusData.EndDate) { return(rBulb); } // вставим пробел var startTime = GetStartDate(wStatusData, signal); if (wStatusData.ActualDate < startTime) { var noSignal = BulbSignal.CreateNoSignal(signal.AccountId, wStatusData.ActualDate, signal.NoSignalImportance); ChangeStatus(noSignal, wStatusData); } // теперь лампочка актуальная, обработаем сигнал if (wStatusData.Status == signal.Status && wStatusData.HasSignal == signal.HasSignal) { // продлим ProlongStatus(signal, wStatusData); } else { // изменим ChangeStatus(signal, wStatusData); } // запомним к какому статусу привело событие // сервис юнит-тестов сам обновит LastStatusId (в целях оптимизации) // if (wStatusData.EventCategory != EventCategory.UnitTestStatus) { UpdateLastStatusId(signal.AccountId, signal.EventId, wStatusData); } wStatusData.BeginSave(); } return(rBulb); }
public static BulbSignal CreateDisable(Guid accountId, DateTime processTime) { var signal = new BulbSignal() { AccountId = accountId, ActualDate = EventHelper.InfiniteActualDate, // чтобы статус был актуален вечно StartDate = processTime, ProcessDate = processTime, Status = MonitoringStatus.Disabled, Message = "Объект выключен", NoSignalImportance = EventImportance.Unknown // при выключении пробел будет серым }; return(signal); }
public static BulbSignal CreateUnknown(Guid accountId, DateTime startDate) { var signal = new BulbSignal() { AccountId = accountId, StartDate = startDate, ProcessDate = startDate, ActualDate = EventHelper.InfiniteActualDate, Message = "Нет данных", Status = MonitoringStatus.Unknown, NoSignalImportance = EventImportance.Unknown, IsSpace = true // todo ??? пробел ли это ??? }; return(signal); }
public IBulbCacheReadObject GetActual( Guid accountId, Guid statusId, DateTime processDate, EventImportance noSignalImportance) { var request = new AccountCacheRequest() { AccountId = accountId, ObjectId = statusId }; var data = AllCaches.StatusDatas.Find(request); // статус актуальный if (data.Actual(processDate)) { return(data); } // статус протух IBulbCacheReadObject rStatus = null; using (var rStatus2 = AllCaches.StatusDatas.Write(request)) { rStatus = rStatus2; if (rStatus.Actual(processDate)) { return(rStatus); } var signal = BulbSignal.CreateNoSignal(accountId, data.ActualDate, noSignalImportance); //var signal = new BulbSignal() //{ // AccountId = accountId, // IsSpace = true, // ActualDate = EventHelper.InfiniteActualDate, // ProcessDate = processDate, // StartDate = data.ActualDate, // data.ActualDate всегда меньше processDate // Status = noSignalStatus, // Message = "Нет данных", // NoSignalImportance = noSignalImportance //}; ProlongOrChangeStatus(signal, statusId); } UpdateParentByChild(rStatus); return(rStatus); }
public static BulbSignal CreateNoSignal(Guid accountId, DateTime startDate, EventImportance noSignalImportance) { var noSignalStatus = MonitoringStatusHelper.Get(noSignalImportance); var signal = new BulbSignal() { AccountId = accountId, StartDate = startDate, ProcessDate = startDate, ActualDate = EventHelper.InfiniteActualDate, Message = "Нет сигнала", Status = noSignalStatus, NoSignalImportance = EventImportance.Unknown, IsSpace = true }; return(signal); }
public static BulbSignal CreateFromChild(DateTime processDate, IBulbCacheReadObject child) { var signal = new BulbSignal() { AccountId = child.AccountId, Status = child.Status, NoSignalImportance = EventImportance.Unknown, StartDate = processDate, ProcessDate = processDate, ActualDate = EventHelper.InfiniteActualDate, EventId = child.StatusEventId, IsSpace = child.IsSpace, Message = child.Message, ChildBulbId = child.Id }; return(signal); }
protected IBulbCacheReadObject UpdateEnableOrDisableStatusData(IMetricCacheReadObject metric) { var statusService = Context.BulbService; IBulbCacheReadObject data = null; var processDate = DateTime.Now; if (metric.CanProcess) { var unknownSignal = BulbSignal.CreateUnknown(metric.AccountId, processDate); data = statusService.SetSignal(metric.StatusDataId, unknownSignal); } else { var disableSignal = BulbSignal.CreateDisable(metric.AccountId, processDate); data = statusService.SetSignal(metric.StatusDataId, disableSignal); } //Context.ComponentService.CalculateAllStatuses(metric.AccountId, metric.ComponentId); return(data); }
public void CalculateByChilds(Guid accountId, Guid statusId, List <Guid> childs) { var processDate = DateTime.Now; var request = new AccountCacheRequest() { AccountId = accountId, ObjectId = statusId }; IBulbCacheReadObject rBulb = null; using (var statusData = AllCaches.StatusDatas.Write(request)) { var childsList = new List <IBulbCacheReadObject>(); foreach (var childId in childs) { var childRequest = new AccountCacheRequest() { AccountId = accountId, ObjectId = childId }; var child = AllCaches.StatusDatas.Find(childRequest); // Disabled и Unknown не влияют на родителей if (child != null && child.Status != MonitoringStatus.Disabled && child.Status != MonitoringStatus.Unknown) { childsList.Add(child); } } var mostDandger = childsList.OrderByDescending(x => x.Status).FirstOrDefault(); if (mostDandger == null) { var signal = BulbSignal.CreateUnknown(accountId, processDate); rBulb = ProlongOrChangeStatus(signal, statusId); } else { var signal = BulbSignal.CreateFromChild(processDate, mostDandger); rBulb = ProlongOrChangeStatus(signal, statusId); } } UpdateParentByChild(rBulb); }
protected DateTime GetStartDate(IBulbCacheReadObject bulb, BulbSignal signal) { var startDate = signal.StartDate; if (signal.Status > bulb.Status) { // более важные события могут обнулять длительность предыдущих // обычно statusData.EndDate = statusData.StartDate startDate = DateTimeHelper.Max(startDate, bulb.EndDate); } else { // менее важные не могут затирать более важные startDate = DateTimeHelper.Max(startDate, bulb.ActualDate); } // время начала нового статуса НЕ может быть больше текущей даты startDate = DateTimeHelper.Min(signal.ProcessDate, startDate); return(startDate); }
public IBulbCacheReadObject Enable(Guid accountId, Guid unitTestId) { var cache = new AccountCache(accountId); IUnitTestCacheReadObject unitTestRead = null; // изменим юнит-тест using (var unitTest = cache.UnitTests.Write(unitTestId)) { unitTestRead = unitTest; unitTest.Enable = true; unitTest.BeginSave(); } // обновим колбаски var statusService = Context.BulbService; var processDate = DateTime.Now; var unknownSignal = BulbSignal.CreateUnknown(accountId, processDate); var data = statusService.SetSignal(unitTestRead.StatusDataId, unknownSignal); Context.ComponentService.CalculateAllStatuses(accountId, unitTestRead.ComponentId); return(data); }
protected void ChangeStatus(BulbSignal signal, BulbCacheWriteObject data) { // проверим пробел var startDate = GetStartDate(data, signal); if (startDate > data.ActualDate) { throw new Exception("startDate > data.ActualDate"); } // обновим статус data.PreviousStatus = data.Status; // запомним предыдущий if (signal.EventId != Guid.Empty) { data.FirstEventId = signal.EventId; data.LastEventId = signal.EventId; } data.Count = 1; data.Message = signal.Message; data.Status = signal.Status; data.LastChildBulbId = signal.ChildBulbId; if (data.IsLeaf) { data.ActualDate = signal.ActualDate; } else { // время актуальности колбасок, которые НЕ являются листьями дерева, бесконечо data.ActualDate = EventHelper.InfiniteActualDate; } data.StartDate = startDate; data.HasSignal = signal.HasSignal; data.EndDate = startDate; // меняется только при создании нового статуса var ownerId = data.GetOwnerId(); var statusEvent = AddStatusEvent(signal.AccountId, data, ownerId); }
public void EnableMetric(Guid accountId, Guid metricId) { var cache = new AccountCache(accountId); IMetricCacheReadObject metricRead = null; // изменим метрику using (var metric = cache.Metrics.Write(metricId)) { metricRead = metric; metric.Enable = true; metric.BeginSave(); } // обновим колбаски var statusService = Context.BulbService; var processDate = DateTime.Now; var unknownSignal = BulbSignal.CreateUnknown(accountId, processDate); var data = statusService.SetSignal(metricRead.StatusDataId, unknownSignal); Context.ComponentService.CalculateAllStatuses(accountId, metricRead.ComponentId); // return data; }
protected IComponentCacheReadObject RecalculateComponentStatuses(IComponentCacheReadObject component) { var accountId = component.AccountId; var cache = new AccountCache(accountId); IComponentCacheReadObject result = component; // обновим ParentEnable if (component.ParentId.HasValue) { var parent = cache.Components.Read(component.ParentId.Value); if (parent.CanProcess != component.ParentEnable) { using (var componentWrite = cache.Components.Write(component)) { componentWrite.ParentEnable = parent.CanProcess; componentWrite.BeginSave(); component = componentWrite; } UpdateParentEnableFlags(component, true); } } // если время выключения истекло, то включим компонент if (component.Enable == false && component.DisableToDate.HasValue && component.DisableToDate < DateTime.Now) { using (var componentWrite = cache.Components.Write(component)) { componentWrite.Enable = true; componentWrite.BeginSave(); component = componentWrite; result = component; } UpdateParentEnableFlags(component, true); } var statusService = Context.BulbService; var allStatusesIds = component.GetAllStatusesIds(); var allStatuses = allStatusesIds.Select(cache.StatusDatas.Read).ToList(); // если надо выключить if (component.CanProcess == false && allStatuses.Any(x => x.Status != Api.MonitoringStatus.Disabled)) { // выключим все колбаски // выжно выключить их в этом порядке, чтобы не создать лишних событий колбасок-родителей var disableSignal = BulbSignal.CreateDisable(accountId, DateTime.Now); statusService.SetSignal(component.ExternalStatusId, disableSignal); statusService.SetSignal(component.ChildComponentsStatusId, disableSignal); statusService.SetSignal(component.InternalStatusId, disableSignal); statusService.SetSignal(component.EventsStatusId, disableSignal); statusService.SetSignal(component.UnitTestsStatusId, disableSignal); statusService.SetSignal(component.MetricsStatusId, disableSignal); } // обновим тесты var allUnitTestsStatusDataIds = new List <Guid>(); foreach (var unitTestRef in component.UnitTests.GetAll()) { using (var unitTest = cache.UnitTests.Write(unitTestRef.Id)) { if (unitTest.IsDeleted) { continue; } allUnitTestsStatusDataIds.Add(unitTest.StatusDataId); if (unitTest.ParentEnable != component.CanProcess) { unitTest.ParentEnable = component.CanProcess; unitTest.BeginSave(); } } Context.UnitTestService.GetUnitTestResult(component.AccountId, unitTestRef.Id); } // обновим метрики var allMetricsStatusDataIds = new List <Guid>(); Guid metricTypeId; foreach (var metricRef in component.Metrics.GetAll()) { using (var metric = cache.Metrics.Write(metricRef.Id)) { if (metric.IsDeleted) { continue; } metricTypeId = metric.MetricTypeId; allMetricsStatusDataIds.Add(metric.StatusDataId); if (metric.ParentEnable != component.CanProcess) { metric.ParentEnable = component.CanProcess; metric.BeginSave(); } } Context.MetricService.GetActualMetric(component.AccountId, component.Id, metricTypeId); } // проверим, нужно ли обновить детей var childComponentsStatusDataIds = new List <Guid>(); foreach (var childRef in component.Childs.GetAll()) { using (var child = cache.Components.Write(childRef.Id)) { if (child.IsDeleted) { continue; } childComponentsStatusDataIds.Add(child.ExternalStatusId); if (child.ParentEnable != component.CanProcess) { child.ParentEnable = component.CanProcess; child.BeginSave(); } } GetComponentExternalState(component.AccountId, childRef.Id); } if (component.CanProcess) { // обновим колбасу тестов явно (вдруг нет ни одного теста) statusService.CalculateByChilds(accountId, component.UnitTestsStatusId, allUnitTestsStatusDataIds); // обновим колбасу метрик явно (вдруг нет ни одной метрики) statusService.CalculateByChilds(accountId, component.MetricsStatusId, allMetricsStatusDataIds); // обновим колбасу событий CalculateComponentEventsStatus(accountId, component.EventsStatusId); // обновим внутренний статус var childsForInternal = new List <Guid>() { component.EventsStatusId, component.UnitTestsStatusId, component.MetricsStatusId }; statusService.CalculateByChilds(accountId, component.InternalStatusId, childsForInternal); // обновим колбасу дочерних statusService.CalculateByChilds(accountId, component.ChildComponentsStatusId, childComponentsStatusDataIds); // обновим внешний статус var childsForExternal = new List <Guid>() { component.InternalStatusId, component.ChildComponentsStatusId }; statusService.CalculateByChilds(accountId, component.ExternalStatusId, childsForExternal); } return(result); }
protected IBulbCacheReadObject SaveResultEvent( DateTime processDate, IUnitTestCacheReadObject unitTest, Event newEvent) { var request = new AccountCacheRequest() { AccountId = unitTest.AccountId, ObjectId = unitTest.StatusDataId }; using (var statusData = AllCaches.StatusDatas.Write(request)) { // сохраним результаты var eventService = Context.EventService; IEventCacheReadObject lastEvent = null; if (statusData.LastEventId.HasValue) { lastEvent = eventService.GetEventCacheOrNullById( unitTest.AccountId, statusData.LastEventId.Value); } // для системных проверок пробелы должны быть серыми, // чтобы из-за простоев агента зидиума не красить пользовательские проверки var noSignalImportance = unitTest.IsSystemType ? EventImportance.Unknown : EventImportance.Alarm; if (lastEvent == null) { // todo не понятно нужно ли в результаты проверок вставлять пробелы? // TryAddSpaceResultEvent(unitTest, newEvent, noSignalImportance); } else { // например, чтобы обрезать бесконечную актуальность у события "нет сигнала" if (lastEvent.ActualDate > newEvent.StartDate) { if (AllCaches.Events.ExistsInStorage(lastEvent)) { using (var wLastEvent = AllCaches.Events.Write(lastEvent)) { wLastEvent.ActualDate = newEvent.StartDate; wLastEvent.BeginSave(); } } } } // синхронная вставка // асинхронное создание объектов сильно усложняет код, поэтому будет создавать всё синхронно eventService.Add(unitTest.AccountId, newEvent); // асинхронная вставка //var newEventCache = EventCacheWriteObject.CreateForAdd(newEvent); //AllCaches.Events.AddNew(newEventCache); using (var wUnitTest = AllCaches.UnitTests.Write(unitTest)) { wUnitTest.LastExecutionDate = newEvent.EndDate; // расчитаем время следующего выполнения if (unitTest.PeriodSeconds > 0) { var nextTime = wUnitTest.NextDate ?? processDate; var period = TimeSpan.FromSeconds(wUnitTest.PeriodSeconds.Value); while (nextTime <= processDate) { nextTime = nextTime + period; } wUnitTest.NextDate = nextTime; } wUnitTest.BeginSave(); } // обновим статус var statusService = Context.BulbService; var signal = BulbSignal.Create(processDate, newEvent, noSignalImportance, unitTest.AccountId); var newStatus = statusService.SetSignal(unitTest.StatusDataId, signal); return(newStatus); } }
protected IBulbCacheReadObject SetMetricValue( IMetricTypeCacheReadObject metricType, MetricCacheWriteObject metric, double?value, DateTime processDate, DateTime actualDate, MonitoringStatus status, string message, bool hasSignal) { // Обновим текущие значения var equal = (value == null && metric.Value == null) || (value == metric.Value); if (!equal) { metric.BeginDate = processDate; } metric.ActualDate = actualDate; metric.Value = value; // Обновим статус метрики var statusService = Context.BulbService; var noSignalColor = metric.NoSignalColor ?? metricType.NoSignalColor ?? ObjectColor.Red; var noSignalImportance = EventImportanceHelper.Get(noSignalColor); var signal = new BulbSignal() { AccountId = metric.AccountId, ActualDate = metric.ActualDate, StartDate = metric.BeginDate, IsSpace = !hasSignal, EventId = Guid.Empty, Message = message, NoSignalImportance = noSignalImportance, ProcessDate = processDate, Status = status }; var statusData = statusService.SetSignal(metric.StatusDataId, signal); // сохраним историю var color = ObjectColorHelper.Get(status); var history = new MetricHistory() { ComponentId = metric.ComponentId, MetricTypeId = metric.MetricTypeId, Value = value, BeginDate = processDate, ActualDate = actualDate, Color = color, StatusEventId = statusData.StatusEventId, HasSignal = hasSignal }; var accountDbContext = Context.GetAccountDbContext(metric.AccountId); var historyRepository = accountDbContext.GetMetricHistoryRepository(); historyRepository.Add(history); Context.SaveChanges(); return(statusData); }
public void ProcessEvent(Guid accountId, Guid componentId, IEventCacheReadObject eventObj) { var categories = new[] { Api.EventCategory.ApplicationError, Api.EventCategory.ComponentEvent }; if (categories.Contains(eventObj.Category) == false) { return; } var accountDbContext = Context.GetAccountDbContext(accountId); var component = accountDbContext.GetComponentRepository().GetById(componentId); // если выключен, то ничего не делаем if (component.CanProcess == false) { return; } // отфильтруем события, которые не могут влиять на статус // если событие НЕ длится if (eventObj.StartDate == eventObj.ActualDate) { // возможно такие события могут продлить EndDate до eventObj.StartDate // пока будем их игнорировать, чтобы сделать алгоритм проще return; } var statusId = component.EventsStatusId; var statusService = Context.BulbService; var noSignalImportance = Api.EventImportance.Unknown; // получаем или создаем статус (колбаску) var request = new AccountCacheRequest() { AccountId = accountId, ObjectId = statusId }; using (var data = AllCaches.StatusDatas.Write(request)) { DateTime processDate = DateTime.Now; // событие из будущего (завершается через 5 минут) if (eventObj.EndDate > processDate + TimeSpan.FromMinutes(5)) { // пусть будет, эту проблему должен решать отправитель //return UpdateStatusResult.Canceled(); } // события из прошлого игнорируем, иначе будет ерунда при обновлении родительской колбасы // т.е. будет несоответствие наложения дочерних колбасок на родительскую if (eventObj.ActualDate <= processDate) { return; } // если событие протухло if (eventObj.ActualDate <= data.EndDate) { return; } var status = MonitoringStatusHelper.Get(eventObj.Importance); var signal = BulbSignal.Create(processDate, eventObj, noSignalImportance); // 0. если это первый сигнал после пробела if (data.HasSignal == false) { statusService.SetSignal(data.Id, signal); return; } // 1. событие менее важное, чем текущий статус if (status < data.Status) { // событие поменяло важность, нодо расчитать занова! if (data.LastEventId == eventObj.Id) { CalculateComponentEventsStatus(accountId, component.EventsStatusId); return; } // текущий статус актуальней if (data.ActualDate >= eventObj.ActualDate) { return; } // на актуальный статус менее важные события не влияют if (data.Actual(processDate)) { return; } // статус протух, а событие актуальное // нужно расчитать, т.к. могут быть более важные актуальные события CalculateComponentEventsStatus(accountId, component.EventsStatusId); return; } // 2. событие такой же важности как статус if (status == data.Status) { // продлеваем statusService.SetSignal(data.Id, signal); return; } // 3. событие более важное, чем текущий статус statusService.SetSignal(data.Id, signal); } }