private void StartAndAddTimer(ReminderEntry entry) { // it might happen that we already have a local reminder with a different eTag // if so, stop the local timer for the old reminder, and start again with new info // Note: it can happen here that we restart a reminder that has the same eTag as what we just registered ... its a rare case, and restarting it doesn't hurt, so we don't check for it var key = new ReminderIdentity(entry.GrainRef, entry.ReminderName); LocalReminderData prevReminder; if (localReminders.TryGetValue(key, out prevReminder)) // if found locally { if (Logger.IsVerbose) { Logger.Verbose(ErrorCode.RS_LocalStop, "Localy stopping reminder {0} as it is different than newly registered reminder {1}", prevReminder, entry); } prevReminder.StopReminder(Logger); localReminders.Remove(prevReminder.Identity); } var newReminder = new LocalReminderData(entry); localTableSequence++; newReminder.LocalSequenceNumber = localTableSequence; localReminders.Add(newReminder.Identity, newReminder); newReminder.StartTimer(this.RuntimeClient.Scheduler, AsyncTimerCallback, Logger); if (Logger.IsVerbose) { Logger.Verbose(ErrorCode.RS_Started, "Started reminder {0}.", entry.ToString()); } }
internal long LocalSequenceNumber; // locally, we use this for resolving races between the periodic table reader, and any concurrent local register/unregister requests internal LocalReminderData(ReminderEntry entry) { Identity = new ReminderIdentity(entry.GrainRef, entry.ReminderName); firstTickTime = entry.StartAt; period = entry.Period; remindable = entry.GrainRef.Cast <IRemindable>(); ETag = entry.ETag; LocalSequenceNumber = -1; }
internal long LocalSequenceNumber; // locally, we use this for resolving races between the periodic table reader, and any concurrent local register/unregister requests internal LocalReminderData(ReminderEntry entry, ILoggerFactory loggerFactory) { Identity = new ReminderIdentity(entry.GrainRef, entry.ReminderName); firstTickTime = entry.StartAt; period = entry.Period; remindable = entry.GrainRef.Cast <IRemindable>(); ETag = entry.ETag; LocalSequenceNumber = -1; this.timerLogger = loggerFactory.CreateLogger <GrainTimer>(); }
internal LocalReminderData(ReminderEntry entry, LocalReminderService reminderService) { Identity = new ReminderIdentity(entry.GrainRef, entry.ReminderName); firstTickTime = entry.StartAt; period = entry.Period; remindable = entry.GrainRef.Cast <IRemindable>(); ETag = entry.ETag; LocalSequenceNumber = -1; this.reminderService = reminderService; this.timer = reminderService.asyncTimerFactory.Create(period, ""); }
// stop without removing it. will remove later. private bool TryStopPreviousTimer(GrainReference grainRef, string reminderName) { // we stop the locally running timer for this reminder var key = new ReminderIdentity(grainRef, reminderName); LocalReminderData localRem; if (!localReminders.TryGetValue(key, out localRem)) { return(false); } // if we have it locally localTableSequence++; // move to next sequence localRem.LocalSequenceNumber = localTableSequence; localRem.StopReminder(Logger); return(true); }
private async Task ReadTableAndStartTimers(IRingRange range, int rangeSerialNumberCopy) { if (Logger.IsVerbose) { Logger.Verbose("Reading rows from {0}", range.ToString()); } localTableSequence++; long cachedSequence = localTableSequence; try { var srange = (SingleRange)range; ReminderTableData table = await reminderTable.ReadRows(srange.Begin, srange.End); // get all reminders, even the ones we already have if (rangeSerialNumberCopy < RangeSerialNumber) { if (Logger.IsVerbose) { Logger.Verbose($"My range changed while reading from the table, ignoring the results. Another read has been started. RangeSerialNumber {RangeSerialNumber}, RangeSerialNumberCopy {rangeSerialNumberCopy}."); } return; } if (StoppedCancellationTokenSource.IsCancellationRequested) { return; } // if null is a valid value, it means that there's nothing to do. if (null == table && reminderTable is MockReminderTable) { return; } var remindersNotInTable = localReminders.Where(r => range.InRange(r.Key.GrainRef)).ToDictionary(r => r.Key, r => r.Value); // shallow copy if (Logger.IsVerbose) { Logger.Verbose("For range {0}, I read in {1} reminders from table. LocalTableSequence {2}, CachedSequence {3}", range.ToString(), table.Reminders.Count, localTableSequence, cachedSequence); } foreach (ReminderEntry entry in table.Reminders) { var key = new ReminderIdentity(entry.GrainRef, entry.ReminderName); LocalReminderData localRem; if (localReminders.TryGetValue(key, out localRem)) { if (cachedSequence > localRem.LocalSequenceNumber) // info read from table is same or newer than local info { if (localRem.Timer != null) // if ticking { if (Logger.IsVerbose2) { Logger.Verbose2("In table, In local, Old, & Ticking {0}", localRem); } // it might happen that our local reminder is different than the one in the table, i.e., eTag is different // if so, stop the local timer for the old reminder, and start again with new info if (!localRem.ETag.Equals(entry.ETag)) // this reminder needs a restart { if (Logger.IsVerbose2) { Logger.Verbose2("{0} Needs a restart", localRem); } localRem.StopReminder(Logger); localReminders.Remove(localRem.Identity); StartAndAddTimer(entry); } } else // if not ticking { // no-op if (Logger.IsVerbose2) { Logger.Verbose2("In table, In local, Old, & Not Ticking {0}", localRem); } } } else // cachedSequence < localRem.LocalSequenceNumber ... // info read from table is older than local info { if (localRem.Timer != null) // if ticking { // no-op if (Logger.IsVerbose2) { Logger.Verbose2("In table, In local, Newer, & Ticking {0}", localRem); } } else // if not ticking { // no-op if (Logger.IsVerbose2) { Logger.Verbose2("In table, In local, Newer, & Not Ticking {0}", localRem); } } } } else // exists in table, but not locally { if (Logger.IsVerbose2) { Logger.Verbose2("In table, Not in local, {0}", entry); } // create and start the reminder StartAndAddTimer(entry); } // keep a track of extra reminders ... this 'reminder' is useful, so remove it from extra list remindersNotInTable.Remove(key); } // foreach reminder read from table int remindersCountBeforeRemove = localReminders.Count; // foreach reminder that is not in global table, but exists locally foreach (var reminder in remindersNotInTable.Values) { if (cachedSequence < reminder.LocalSequenceNumber) { // no-op if (Logger.IsVerbose2) { Logger.Verbose2("Not in table, In local, Newer, {0}", reminder); } } else // cachedSequence > reminder.LocalSequenceNumber { if (Logger.IsVerbose2) { Logger.Verbose2("Not in table, In local, Old, so removing. {0}", reminder); } // remove locally reminder.StopReminder(Logger); localReminders.Remove(reminder.Identity); } } if (Logger.IsVerbose) { Logger.Verbose($"Removed {localReminders.Count - remindersCountBeforeRemove} reminders from local table"); } } catch (Exception exc) { Logger.Error(ErrorCode.RS_FailedToReadTableAndStartTimer, "Failed to read rows from table.", exc); throw; } }