/// <summary> /// Gets the property groups. /// </summary> /// <param name="prefix">The prefix.</param> /// <returns></returns> public virtual string[] GetPropertyGroups(string prefix) { HashSet groups = new HashSet(10); if (!prefix.EndsWith(".")) { prefix += "."; } foreach (string key in props.Keys) { if (key.StartsWith(prefix)) { string groupName = key.Substring(prefix.Length, (key.IndexOf('.', prefix.Length)) - (prefix.Length)); groups.Add(groupName); } } return (string[]) groups.ToArray(typeof (string)); }
protected override TriggerPropertyBundle GetTriggerPropertyBundle(SimplePropertiesTriggerProperties props) { int repeatCount = (int)props.Long1; int interval = props.Int1; string intervalUnitStr = props.String1; string daysOfWeekStr = props.String2; string timeOfDayStr = props.String3; IntervalUnit intervalUnit = (IntervalUnit) Enum.Parse(typeof(IntervalUnit), intervalUnitStr, true); DailyTimeIntervalScheduleBuilder scheduleBuilder = DailyTimeIntervalScheduleBuilder.Create() .WithInterval(interval, intervalUnit) .WithRepeatCount(repeatCount); if (daysOfWeekStr != null) { ISet<DayOfWeek> daysOfWeek = new HashSet<DayOfWeek>(); String[] nums = daysOfWeekStr.Split(','); if (nums.Length > 0) { foreach (String num in nums) { daysOfWeek.Add((DayOfWeek) Int32.Parse(num)); } scheduleBuilder.OnDaysOfTheWeek(daysOfWeek); } } else { scheduleBuilder.OnDaysOfTheWeek(DailyTimeIntervalScheduleBuilder.AllDaysOfTheWeek); } if (timeOfDayStr != null) { string[] nums = timeOfDayStr.Split(','); TimeOfDay startTimeOfDay = null; if (nums.Length >= 3) { int hour = Int32.Parse(nums[0]); int min = Int32.Parse(nums[1]); int sec = Int32.Parse(nums[2]); startTimeOfDay = new TimeOfDay(hour, min, sec); } else { startTimeOfDay = TimeOfDay.HourMinuteAndSecondOfDay(0, 0, 0); } scheduleBuilder.StartingDailyAt(startTimeOfDay); TimeOfDay endTimeOfDay; if (nums.Length >= 6) { int hour = Int32.Parse(nums[3]); int min = Int32.Parse(nums[4]); int sec = Int32.Parse(nums[5]); endTimeOfDay = new TimeOfDay(hour, min, sec); } else { endTimeOfDay = TimeOfDay.HourMinuteAndSecondOfDay(23, 59, 59); } scheduleBuilder.EndingDailyAt(endTimeOfDay); } else { scheduleBuilder.StartingDailyAt(TimeOfDay.HourMinuteAndSecondOfDay(0, 0, 0)); scheduleBuilder.EndingDailyAt(TimeOfDay.HourMinuteAndSecondOfDay(23, 59, 59)); } int timesTriggered = props.Int2; String[] statePropertyNames = {"timesTriggered"}; Object[] statePropertyValues = {timesTriggered}; return new TriggerPropertyBundle(scheduleBuilder, statePropertyNames, statePropertyValues); }
/// <summary> /// Selects the paused trigger groups. /// </summary> /// <param name="conn">The DB Connection.</param> /// <returns></returns> public virtual ISet SelectPausedTriggerGroups(ConnectionAndTransactionHolder conn) { HashSet retValue = new HashSet(); using (IDbCommand cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlSelectPausedTriggerGroups))) { using (IDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) { string groupName = (string)dr[ColumnTriggerGroup]; retValue.Add(groupName); } } return retValue; } }
protected virtual void ClusterRecover(ConnectionAndTransactionHolder conn, IList failedInstances) { if (failedInstances.Count > 0) { long recoverIds = DateTime.UtcNow.Ticks; LogWarnIfNonZero(failedInstances.Count, "ClusterManager: detected " + failedInstances.Count + " failed or restarted instances."); try { foreach (SchedulerStateRecord rec in failedInstances) { Log.Info("ClusterManager: Scanning for instance \"" + rec.SchedulerInstanceId + "\"'s failed in-progress jobs."); IList firedTriggerRecs = Delegate.SelectInstancesFiredTriggerRecords(conn, rec.SchedulerInstanceId); int acquiredCount = 0; int recoveredCount = 0; int otherCount = 0; ISet triggerKeys = new HashSet(); foreach (FiredTriggerRecord ftRec in firedTriggerRecs) { Key tKey = ftRec.TriggerKey; Key jKey = ftRec.JobKey; triggerKeys.Add(tKey); // release blocked triggers.. if (ftRec.FireInstanceState.Equals(StateBlocked)) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey.Name, jKey.Group, StateWaiting, StateBlocked); } else if (ftRec.FireInstanceState.Equals(StatePausedBlocked)) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey.Name, jKey.Group, StatePaused, StatePausedBlocked); } // release acquired triggers.. if (ftRec.FireInstanceState.Equals(StateAcquired)) { Delegate.UpdateTriggerStateFromOtherState(conn, tKey.Name, tKey.Group, StateWaiting, StateAcquired); acquiredCount++; } else if (ftRec.JobRequestsRecovery) { // handle jobs marked for recovery that were not fully // executed.. if (JobExists(conn, jKey.Name, jKey.Group)) { DateTime tempAux = new DateTime(ftRec.FireTimestamp); SimpleTrigger rcvryTrig = new SimpleTrigger( "recover_" + rec.SchedulerInstanceId + "_" + Convert.ToString(recoverIds++, CultureInfo.InvariantCulture), SchedulerConstants.DefaultRecoveryGroup, tempAux); rcvryTrig.Volatile = ftRec.TriggerIsVolatile; rcvryTrig.JobName = jKey.Name; rcvryTrig.JobGroup = jKey.Group; rcvryTrig.MisfireInstruction = MisfireInstruction.SimpleTrigger.FireNow; rcvryTrig.Priority = ftRec.Priority; JobDataMap jd = Delegate.SelectTriggerJobDataMap(conn, tKey.Name, tKey.Group); jd.Put(SchedulerConstants.FailedJobOriginalTriggerName, tKey.Name); jd.Put(SchedulerConstants.FailedJobOriginalTriggerGroup, tKey.Group); jd.Put(SchedulerConstants.FailedJobOriginalTriggerFiretimeInMillisecoonds, Convert.ToString(ftRec.FireTimestamp, CultureInfo.InvariantCulture)); rcvryTrig.JobDataMap = jd; rcvryTrig.ComputeFirstFireTimeUtc(null); StoreTrigger(conn, null, rcvryTrig, null, false, StateWaiting, false, true); recoveredCount++; } else { Log.Warn("ClusterManager: failed job '" + jKey + "' no longer exists, cannot schedule recovery."); otherCount++; } } else { otherCount++; } // free up stateful job's triggers if (ftRec.JobIsStateful) { Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey.Name, jKey.Group, StateWaiting, StateBlocked); Delegate.UpdateTriggerStatesForJobFromOtherState(conn, jKey.Name, jKey.Group, StatePaused, StatePausedBlocked); } } Delegate.DeleteFiredTriggers(conn, rec.SchedulerInstanceId); // Check if any of the fired triggers we just deleted were the last fired trigger // records of a COMPLETE trigger. int completeCount = 0; foreach (Key triggerKey in triggerKeys) { if ( Delegate.SelectTriggerState(conn, triggerKey.Name, triggerKey.Group).Equals( StateComplete)) { IList firedTriggers = Delegate.SelectFiredTriggerRecords(conn, triggerKey.Name, triggerKey.Group); if (firedTriggers.Count == 0) { SchedulingContext schedulingContext = new SchedulingContext(); schedulingContext.InstanceId = instanceId; if (RemoveTrigger(conn, schedulingContext, triggerKey.Name, triggerKey.Group)) { completeCount++; } } } } LogWarnIfNonZero(acquiredCount, "ClusterManager: ......Freed " + acquiredCount + " acquired trigger(s)."); LogWarnIfNonZero(completeCount, "ClusterManager: ......Deleted " + completeCount + " complete triggers(s)."); LogWarnIfNonZero(recoveredCount, "ClusterManager: ......Scheduled " + recoveredCount + " recoverable job(s) for recovery."); LogWarnIfNonZero(otherCount, "ClusterManager: ......Cleaned-up " + otherCount + " other failed job(s)."); if (rec.SchedulerInstanceId.Equals(InstanceId) == false) { Delegate.DeleteSchedulerState(conn, rec.SchedulerInstanceId); } } } catch (Exception e) { throw new JobPersistenceException("Failure recovering jobs: " + e.Message, e); } } }
/// <summary> /// Select the distinct instance names of all fired-trigger records. /// </summary> /// <param name="conn">The conn.</param> /// <returns></returns> /// <remarks> /// This is useful when trying to identify orphaned fired triggers (a /// fired trigger without a scheduler state record.) /// </remarks> public ISet SelectFiredTriggerInstanceNames(ConnectionAndTransactionHolder conn) { ISet instanceNames = new HashSet(); using (IDbCommand cmd = PrepareCommand(conn, ReplaceTablePrefix(SqlSelectFiredTriggerInstanceNames))) { using (IDataReader rs = cmd.ExecuteReader()) { while (rs.Read()) { instanceNames.Add(rs[ColumnInstanceName]); } return instanceNames; } } }