Reputation: 755
I have a windows service that ticks over every 4 minutes. When the timer ticks if runs a DataImporter, the DataImporter has a number of "Jobs" that it can run on each tick, for example, there is a ProcessData job and a RetreiveData job:
As soon as the DataImporter is run it checks a DB table called ScheduledJob - this has a number of scheduling functionality such as FrequencyInterval, ActiveStart/Stop times, StartedLastRun time. The ScheduledJob table has flag called "InProgress", this flag will stop the DataImport picking up that job when it's already running.
There is a continuous issue where a job is picked up twice, a few seconds apart from each other and then both run simultaneously which cause a number of DB constraints when trying to insert identical records. I am not really sure how it can pick two jobs up at the same time, the tick is 4 minutes apart, so in theory it shouldn't be able to even look at the potential jobs to run, how can it run them both a few seconds apart?
Both the RetrieveData and ProcessData jobs need to be able to run in parallel so I can't pause the Timer whilst I execute the job.
Service:
public partial class DataImport : ServiceBase
{
private int _eventId = 0;
readonly Timer _serviceTimer = new Timer(240000);
public DataImport()
{
InitializeComponent();
ImportServiceEventLog.Source = ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource(); ;
}
protected override void OnStart(string[] args)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " started", EventLogEntryType.Information, _eventId++);
_serviceTimer.AutoReset = true;
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " timer interval = " + _serviceTimer.Interval / 1000 + " seconds", EventLogEntryType.Information, _eventId++);
_serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer);
_serviceTimer.Start();
}
protected override void OnStop()
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " stopped", EventLogEntryType.Information, _eventId++);
}
public void OnTimer(object sender, ElapsedEventArgs args)
{
try
{
Run();
}
catch (System.Exception ex)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " error: " + ex.ToString(), EventLogEntryType.Information, _eventId++);
}
}
public void Run()
{
using (var dataImportController = new DataImportController())
{
dataImportController.Run();
}
}
}
DataImportController:
public class DataImportController
{
public void Run()
{
// Gets all the jobs from the ScheduledJob table in the DB
var jobs = GetJobsToRun();
//Get all Processes (from DB)
foreach (var job in jobs)
{
//Check the time it was last run - do this for each process
if (RunJob(job))
{
_messaging.EventMessage("Process " + job.Name + " finished : " + DateTime.Now, ServiceSource.DATA_IMPORT_SERVICE);
}
}
}
public bool RunJob(ScheduledJob job)
{
// Checks if the job is ready to run, i.e. is the InProgress flag set to false and the interval long enough since the StartedLastRun DateTime
if (!job.IsReadyToRun())
{
return false;
}
// Set job to in progress
job.InProgress = true;
job.StartedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
try
{
switch (job.Name.ToUpper())
{
case "RetreiveData":
// RUN JOB
break;
case "ProcessData":
// RUN JOB
break;
}
job.InProgress = false;
job.EndedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
}
catch (Exception exception)
{
_messaging.ReportError("Error occured whilst checking we are ready to run " + exception.Message, exception, null, 0, ServiceSource.DATA_IMPORT_SERVICE);
}
return true;
}
}
EDIT:
Include Program.cs
static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataImport()
};
ServiceBase.Run(ServicesToRun);
}
}
Upvotes: 0
Views: 502
Reputation: 1025
You subsribe to timer event in OnStart
, and didn't unsubscribe in OnStop
.
Move _serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer);
and initialization of AutoReset to constructor. Stop timer in OnStop
. That should fix your issue. I believe your service is started (restarted) more than once.
Upvotes: 0
Reputation: 120380
If overlapping is a concern, ditch the timer and make an async loop, leveraging Task.Delay
:
async Task SomeFunc(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
DoWork();
await Task.Delay(timeInterval, token);
}
}
Upvotes: 1
Reputation: 129
try to stop the timer inside the OnTimer function then re-start timer after it has finished executing your task.
Upvotes: 0