public void RunJob(string xmlFile, string jobName, UnityLoader loader, bool shouldFail) { // Flush output file GetFileNamesOut().ForEach(s => { if (File.Exists(s)) { File.Delete(s); } }); // Prerequisites GetFileNamesIn().ForEach(s => Assert.IsTrue(new FileInfo(s).Exists, "Job input file " + s + " does not exist, job can't be run")); GetFileNamesOut().ForEach(s => Assert.IsFalse(new FileInfo(s).Exists, "Job output file " + s + " should have been deleted before test")); XmlJob job = XmlJobParser.LoadJob(xmlFile); IJobOperator jobOperator = BatchRuntime.GetJobOperator(loader, job); Assert.IsNotNull(jobOperator); long?executionId = jobOperator.StartNextInstance(jobName); Assert.IsNotNull(executionId); JobExecution jobExecution = ((SimpleJobOperator)jobOperator).JobExplorer.GetJobExecution((long)executionId); //job SHOULD BE FAILED because of rollback having occured if (shouldFail) { Assert.IsTrue(jobExecution.Status.IsUnsuccessful()); } else { Assert.IsFalse(jobExecution.Status.IsUnsuccessful()); } Assert.IsFalse(jobExecution.Status.IsRunning()); }
void ExtendedExample() { NativeArray <float> result = new NativeArray <float>(1, Allocator.Persistent); SimpleJob job = new SimpleJob() { result = result }; JobExecution execution = JobHelper.AddScheduledJob(job, job.Schedule(), (jobExecutor) => { /// OnJobComplete delegate returns 'jobExecutor' with usefull data. if (jobExecutor.FramesTaken == -1) { Debug.Log("Job has completed immediatelly!"); } else { Debug.LogFormat("Job has completed in {0}s and {1} frames!", jobExecutor.Duration, jobExecutor.FramesTaken); } // Result is available. LateUpdate() context. Debug.Log(result[0]); // You can dispose NativeContainers container within the jobexecution immediately after completion. // This is required if your set your container allocator as Temp or TempJob, dont Dispose if you use Persistent allocation. // Otherwise no need to call Dispose, the JobHelper will execute Dispose() on all completed jobs on OnDestroy() jobExecutor.Dispose(); }, // If you want to complete this job immediately in the LateUpdate() check this to true. // But make sure to schedule the job before, to leave some room for workers to do its work. completeImmediatelly: false); // There is also an option to complete the job whenever you want. Debug.Log(execution.Complete()); }
/// <summary> /// Handler of steps sequentially as provided, checking each one for success /// before moving to the next. Returns the last StepExecution /// successfully processed if it exists, and null if none were processed. /// </summary> /// <param name="execution"></param> protected override void DoExecute(JobExecution execution) { StepExecution stepExecution = null; foreach (IStep step in _steps) { stepExecution = HandleStep(step, execution); if (stepExecution.BatchStatus != BatchStatus.Completed) { break; } } // // Update the job status to be the same as the last step // if (stepExecution != null) { if (Logger.IsDebugEnabled) { Logger.Debug("Upgrading JobExecution status: {0}", stepExecution); } execution.UpgradeStatus(stepExecution.BatchStatus); execution.ExitStatus = stepExecution.ExitStatus; } }
/// <summary> /// @see IJobExecutionDao#SaveJobExecution. /// </summary> /// <param name="jobExecution"></param> public void SaveJobExecution(JobExecution jobExecution) { Assert.IsTrue(jobExecution.Id == null); jobExecution.Id = Interlocked.Increment(ref _currentId); jobExecution.IncrementVersion(); _executionsById[jobExecution.Id] = Copy(jobExecution); }
public void Initialize() { _jobInstance = new JobInstance(1, "testJob"); _jobExecution = new JobExecution(_jobInstance, new JobParameters()); _stepExecution1 = new StepExecution("testStep1", _jobExecution, 1); _stepExecution2 = new StepExecution("testStep2", _jobExecution, 2); }
/// <summary> /// Custom constructor using a job repository, a step hander and a job execution. /// </summary> /// <param name="jobRepository"></param> /// <param name="stepHandler"></param> /// <param name="execution"></param> public JobFlowExecutor(IJobRepository jobRepository, IStepHandler stepHandler, JobExecution execution) { _jobRepository = jobRepository; _stepHandler = stepHandler; _execution = execution; _stepExecutionHolder.Value = null; }
/// <summary> /// Call to listeners that might add some post execution behaviour. /// </summary> /// <param name="execution"></param> private void HandlePostExecution(JobExecution execution) { try { if (execution.Status.IsLessThanOrEqualTo(BatchStatus.Stopped) && !execution.StepExecutions.Any()) { ExitStatus exitStatus = execution.ExitStatus; ExitStatus newExitStatus = ExitStatus.Noop.AddExitDescription("All steps already completed or no steps configured for this job."); execution.ExitStatus = exitStatus.And(newExitStatus); } execution.EndTime = DateTime.Now; try { _listener.AfterJob(execution); } catch (Exception e) { Logger.Error(e, "Exception encountered in afterStep callback"); } JobRepository.Update(execution); Logger.Debug("Current job execution: {0}", execution); } finally { JobSynchronizationManager.Release(); } }
private Response HandleGetAllJobs() { Job[] all = jobs.All(with => { }); List <object> model = new List <object>(all.Length); foreach (Job job in all) { JobExecution execution = executions.GetByJob(job.Header).FirstOrDefault(); DateTime? nextRun = execution != null?execution.NextRun(job.Schedule) : job.Schedule.Next(null); job.Extract(data => { model.Add(new { id = job.Header.Identifier.ToHex(), version = job.Header.Version, name = data.Name, description = data.Description, executable = data.Executable, arguments = data.Arguments, blob = data.Blob.ToHex(), nextRun = nextRun }); }); } return(Response.AsJson(model)); }
/// <summary> /// Convert a JobAgent to AzureSqlDatabaseAgentJobExecutionModel /// </summary> /// <param name="resourceGroupName">The resource group the server is in</param> /// <param name="serverName">The server the agent is in</param> /// <param name="resp">The management client server response to convert</param> /// <returns>The converted agent model</returns> private static AzureSqlElasticJobExecutionModel CreateJobExecutionModelFromResponse( string resourceGroupName, string serverName, string agentName, string jobName, JobExecution resp) { AzureSqlElasticJobExecutionModel jobExecution = new AzureSqlElasticJobExecutionModel { ResourceGroupName = resourceGroupName, ServerName = serverName, AgentName = agentName, JobName = jobName, JobExecutionId = resp.JobExecutionId, CreateTime = resp.CreateTime, CurrentAttempts = resp.CurrentAttempts, CurrentAttemptStartTime = resp.CurrentAttemptStartTime, EndTime = resp.EndTime, JobVersion = resp.JobVersion, LastMessage = resp.LastMessage, Lifecycle = resp.Lifecycle, ProvisioningState = resp.ProvisioningState, ResourceId = resp.Id, StartTime = resp.StartTime, Type = resp.Type, }; return(jobExecution); }
public ExitStatus AfterStep(StepExecution stepExecution) { JobExecution job = stepExecution.JobExecution; var jobName = job.JobInstance.JobName; var filePathName = Path.Combine(TestDataDirectoryLogs, "1_" + jobName + "_" + job.StartTime.Value.Ticks + ".log"); FileInfo fInfo = new FileInfo(filePathName); using (StreamWriter logSW = new StreamWriter(fInfo.FullName, true)) { logSW.WriteLine("Job Name: " + jobName + ", Id: " + job.Id + " ended with BatchError."); for (int i = job.StepExecutions.Count - 1; i > 0; i--) { var stepName = job.StepExecutions.ElementAt(i).StepName; var exitCode = job.StepExecutions.ElementAt(i).ExitStatus.ExitCode; var batchStatus = job.StepExecutions.ElementAt(i).BatchStatus; var summary = job.StepExecutions.ElementAt(i).GetSummary(); ICollection <Exception> exceptions = job.StepExecutions.ElementAt(i).GetFailureExceptions(); logSW.WriteLine(summary); logSW.WriteLine("Step: " + stepName + ", Batch Status: " + batchStatus + ", Exit Status: " + exitCode); for (int j = 0; j < exceptions.Count; j++) { logSW.WriteLine("Exception @ Step[" + stepName + "]: " + exceptions.ElementAt(j).InnerException); } } } return(ExitStatus.Completed); }
public void Initialize() { _jobExecutionDao = new MapJobExecutionDao(); _instance = new JobInstance(1, "testJob"); _parameters = new JobParameters(); _execution = new JobExecution(_instance, _parameters); }
public void RunJobWithTaskletMergeCopy() { //prepare stuff if (!Directory.Exists(TestDataDirectoryToMerge)) { Directory.CreateDirectory(TestDataDirectoryToMerge); } FileInfo ofi = new FileInfo(Path.Combine(TestDataDirectoryToMerge, "report1_merged_copy.txt")); if (!ofi.Exists) { using (FileStream fs = File.OpenRead(Path.Combine(TestDataDirectoryIn, "report0.txt"))) using (FileStream ofs = File.OpenWrite(Path.Combine(TestDataDirectoryToMerge, "report1_merged_copy.txt"))) { fs.CopyTo(ofs); } } XmlJob job = XmlJobParser.LoadJob("Job1.xml"); IJobOperator jobOperator = BatchRuntime.GetJobOperator(new MyUnityLoaderJob1MergeCopy(), job); Assert.IsNotNull(jobOperator); long?executionId = jobOperator.StartNextInstance(job.Id); Assert.IsNotNull(executionId); JobExecution jobExecution = ((SimpleJobOperator)jobOperator).JobExplorer.GetJobExecution((long)executionId); Assert.IsFalse(jobExecution.Status.IsUnsuccessful()); Assert.IsFalse(jobExecution.Status.IsRunning()); }
/// <summary> /// @see IStepExecutionDao#GetStepExecution. /// </summary> /// <param name="jobExecution"></param> /// <param name="stepExecutionId"></param> /// <returns></returns> public StepExecution GetStepExecution(JobExecution jobExecution, long stepExecutionId) { StepExecution result; _executionsByStepExecutionId.TryGetValue(stepExecutionId, out result); return(result); }
/// <summary> /// Given a step and configuration, return true if the step should start, /// false if it should not, and throw an exception if the job should finish. /// </summary> /// <param name="lastStepExecution"></param> /// <param name="jobExecution"></param> /// <param name="step"></param> /// <returns></returns> /// <exception cref="JobRestartException"> </exception> /// <exception cref="StartLimitExceededException"> </exception> protected bool ShouldStart(StepExecution lastStepExecution, JobExecution jobExecution, IStep step) { var stepStatus = lastStepExecution == null ? BatchStatus.Starting : lastStepExecution.BatchStatus; if (stepStatus == BatchStatus.Unknown) { throw new JobRestartException("Cannot restart step from UNKNOWN status. " + "The last execution ended with a failure that could not be rolled back, " + "so it may be dangerous to proceed. Manual intervention is probably necessary."); } if (stepStatus == BatchStatus.Completed && (step.AllowStartIfComplete != null && !step.AllowStartIfComplete.Value) || stepStatus == BatchStatus.Abandoned) { // step is complete, false should be returned, indicating that the // step should not be started Logger.Info("Step already complete or not restartable, so no action to execute: {0}", lastStepExecution); return(false); } if (JobRepository.GetStepExecutionCount(jobExecution.JobInstance, step.Name) < step.StartLimit) { // step start count is less than start max, return true return(true); } else { // start max has been exceeded, throw an exception. throw new StartLimitExceededException( string.Format("Maximum start limit exceeded for step: {0} StartMax: {1}", step.Name, step.StartLimit)); } }
/// <summary> /// Persists a new job execution. /// The corresponding job instance must have been persisted. /// </summary> /// <param name="jobExecution">a job execution</param> public void SaveJobExecution(JobExecution jobExecution) { Assert.NotNull(jobExecution, "The job execution must not be null."); Assert.NotNull(jobExecution.GetJobId(), "The corresponding job id must not be null."); using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { jobExecution.IncrementVersion(); jobExecution.Id = _jobExecutionIncrementer.NextLong(); var parameters = new Dictionary <string, object> { { "executionId", jobExecution.Id }, { "jobId", jobExecution.GetJobId() }, { "startTime", (object)jobExecution.StartTime ?? DBNull.Value }, { "endTime", (object)jobExecution.EndTime ?? DBNull.Value }, { "status", jobExecution.Status.ToString() }, { "exitCode", jobExecution.ExitStatus.ExitCode }, { "exitMessage", jobExecution.ExitStatus.ExitDescription }, { "version", jobExecution.Version }, { "createTime", jobExecution.CreateTime }, { "lastUpdated", (object)jobExecution.LastUpdated ?? DBNull.Value }, { "jobConfigurationLocation", (object)jobExecution.JobConfigurationName ?? DBNull.Value } }; DbOperator.Update(InsertTablePrefix(SaveJobExecutionQuery), parameters); InsertJobParameters(jobExecution.Id, jobExecution.JobParameters); scope.Complete(); } }
/// <summary> /// registers both job and step contexts. /// </summary> /// <param name="stepExecution"></param> private void RegisterContexts(StepExecution stepExecution) { JobExecution jobExecution = stepExecution.JobExecution; JobContextManager.Context = jobExecution.ExecutionContext; StepContextManager.Context = stepExecution.ExecutionContext; }
public IHttpActionResult Create(JobExecution body) { var jobBody = new JobCreate { Description = body.Description, CallbackUrl = "http://localhost:31901/destination", HttpVerb = "POST", Payload = $"{{ \"description\": \"{body.Description}\" }}", ContentType = "application/json", Headers = "source=Jobbie.Sample.Client.WebApi.Host" }; var scheduleBody = new ScheduleCreate { Description = body.Description, StartUtc = DateTime.UtcNow.AddSeconds(10), Cron = body.Cron }; var schedule = _client .Root() .Post(Relationships.Job_Create, jobBody, Curies.Jobbie) .Post(Relationships.Schedule_Create, scheduleBody, Curies.Jobbie) .Item <Schedule>() .Data; return(Ok(schedule)); }
public async Task GivenValidRequest_WhenCreating_ShouldReturnCreated() { // Given var request = new CreateJobRequest { Subject = "test", Payload = "do things", RunAt = DateTime.UtcNow.AddDays(1), }; var job = Job.New(request.Subject, request.Payload, true, request.RunAt, request.StopAfter, null); var jobExecution = JobExecution.New(job, request.RunAt); Mock.Get(_mediator) .Setup(x => x.Send(It.IsAny <Core.Handlers.CreateJobRequest>(), CancellationToken.None)) .ReturnsAsync(new CreateJobResponse(job, jobExecution)); // When var result = await _controller.CreateAsync(request, CancellationToken.None); var actionResult = result.Result as ObjectResult; // Then actionResult.ShouldBeAssignableTo <ObjectResult>(); actionResult.StatusCode.ShouldBe(StatusCodes.Status201Created); }
/// <summary> /// Execute the job provided by delegating to the <see cref="IJobLauncher"/>to /// prevent duplicate executions. The job parameters will be generated by the /// <see cref="IJobParametersExtractor"/>provided (if any), otherwise empty. On a /// restart, the job parameters will be the same as the last (failed) execution. /// </summary> /// <param name="stepExecution"></param> protected override void DoExecute(StepExecution stepExecution) { ExecutionContext executionContext = stepExecution.ExecutionContext; executionContext.Put(StepConstants.StepTypeKey, GetType().Name); JobParameters jobParameters; if (executionContext.ContainsKey(JobParametersKey)) { jobParameters = (JobParameters)executionContext.Get(JobParametersKey); } else { jobParameters = _jobParametersExtractor.GetJobParameters(Job, stepExecution); executionContext.Put(JobParametersKey, jobParameters); } JobExecution jobExecution = JobLauncher.Run(Job, jobParameters); if (jobExecution.Status.IsUnsuccessful()) { // AbstractStep will take care of the step execution status throw new UnexpectedJobExecutionException("Step failure: the delegate Job failed in JobStep."); } }
public Task <bool> PublishAsync(JobExecution jobExecution, CancellationToken ctx) { using var _ = MessagingMetrics.TimePublishDuration(); try { if (!TryGetOrCreateModel(out var model) || model is null) { return(Task.FromResult(false)); } var job = jobExecution.Job; model.EnsureConfig(_options.JobsExchange, job.Subject); model.BasicPublish(_options.JobsExchange, job.Subject, true, null, Encoding.UTF8.GetBytes(job.Payload)); MessagingMetrics.MessagesPublished(job.Subject); return(Task.FromResult(model.WaitForConfirms())); } catch (Exception e) { _logger.LogError(e, "Failed to publish job {JobId} to RabbitMQ", jobExecution.Job.Id); return(Task.FromResult(false)); } }
/// <summary> /// @see IJobOperator#Stop . /// </summary> /// <param name="executionId"></param> /// <returns></returns> /// <exception cref="NoSuchJobException"> </exception> /// <exception cref="JobExecutionNotRunningException"> </exception> public bool Stop(long executionId) { JobExecution jobExecution = FindExecutionById(executionId); // Indicate the execution should be stopped by setting it's status to // 'STOPPING'. It is assumed that // the step implementation will check this status at chunk boundaries. BatchStatus status = jobExecution.Status; if (!(status == BatchStatus.Started || status == BatchStatus.Starting)) { throw new JobExecutionNotRunningException( string.Format("JobExecution must be running so that it can be stopped: {0}", jobExecution)); } jobExecution.Status = BatchStatus.Stopping; JobRepository.Update(jobExecution); try { IJob job = JobRegistry.GetJob(jobExecution.JobInstance.JobName); var locator = job as IStepLocator; if (locator != null) { //can only process as StepLocator is the only way to get the step object //get the current stepExecution foreach (StepExecution stepExecution in jobExecution.StepExecutions) { if (stepExecution.BatchStatus.IsRunning()) { try { //have the step execution that's running -> need to 'stop' it IStep step = locator.GetStep(stepExecution.StepName); var taskletStep = step as TaskletStep; if (taskletStep != null) { ITasklet tasklet = taskletStep.Tasklet; var stoppableTasklet = tasklet as IStoppableTasklet; if (stoppableTasklet != null) { StepSynchronizationManager.Register(stepExecution); stoppableTasklet.Stop(); StepSynchronizationManager.Release(); } } } catch (NoSuchStepException e) { _logger.Warn("Step not found {0}", e.Message); } } } } } catch (NoSuchJobException e) { _logger.Warn("Cannot find Job object {0}", e.Message); } return(true); }
/// <summary> /// Creates a row mapper that for step executions. /// </summary> /// <param name="jobExecution">the job execution to use when creating the step executions</param> /// <returns>a row mapper</returns> private static RowMapper <StepExecution> GetStepExecutionRowMapper(JobExecution jobExecution) { return((dataRecord, i) => { var wrapper = new DataRecordWrapper(dataRecord); var stepExecution = new StepExecution(wrapper.Get <string>((1)), jobExecution, wrapper.Get <long>((0))) { StartTime = wrapper.Get <DateTime>((2)), EndTime = wrapper.Get <DateTime>((3)), BatchStatus = BatchStatus.ValueOf(wrapper.Get <string>((4))), CommitCount = wrapper.Get <int>((5)), ReadCount = wrapper.Get <int>((6)), FilterCount = wrapper.Get <int>((7)), WriteCount = wrapper.Get <int>((8)), ExitStatus = new ExitStatus(wrapper.Get <string>((9)), wrapper.Get <string>(10)), ReadSkipCount = wrapper.Get <int>((11)), WriteSkipCount = wrapper.Get <int>((12)), ProcessSkipCount = wrapper.Get <int>((13)), RollbackCount = wrapper.Get <int>((14)), LastUpdated = wrapper.Get <DateTime?>(15), Version = wrapper.Get <int?>((16)) }; return stepExecution; }); }
/// <summary> /// Executes job. /// </summary> /// <param name="execution"></param> public void Execute(JobExecution execution) { Logger.Debug("Job execution starting: {0}", execution.JobInstance.JobName); JobSynchronizationManager.Register(execution); try { JobParametersValidator.Validate(execution.JobParameters); if (execution.Status != BatchStatus.Stopping) { HandleExecution(execution); } else { execution.Status = BatchStatus.Stopped; execution.ExitStatus = ExitStatus.Completed; Logger.Debug("Job execution was stopped: {0}", execution.JobInstance.JobName); } } catch (JobInterruptedException e) { HandleJobInterruptedException(execution, e); } catch (Exception t) { HandleException(execution, t); } finally { HandlePostExecution(execution); } }
public async Task GivenTwoPendingExecutions_WhenFailingToPublish_ShouldReScheduled() { // Given var request = new SchedulePendingRequest(); var executions = new[] { JobExecution.New(Job.New("test", "test payload", true, DateTime.UtcNow, DateTime.UtcNow, "* * * * *"), DateTime.UtcNow), JobExecution.New(Job.New("test", "test payload", true, DateTime.UtcNow, DateTime.UtcNow, "* * * * *"), DateTime.UtcNow), }; Mock.Get(_jobExecutionRepository) .SetupSequence(x => x.GetAndMarkPending(It.IsAny <int>(), It.IsAny <DateTime>(), CancellationToken.None)) .ReturnsAsync(executions) .ReturnsAsync(Enumerable.Empty <JobExecution>()); Mock.Get(_jobPublisher) .Setup(x => x.PublishManyAsync(It.IsAny <IEnumerable <JobExecution> >(), CancellationToken.None)) .ReturnsAsync(false); // When await _handler.Handle(request, CancellationToken.None); // Then Mock.Get(_jobExecutionRepo) .Verify(x => x.UpdateManyAsync(It.IsAny <IEnumerable <JobExecution> >(), CancellationToken.None), Times.Once); }
public async Task GivenTwoJobs_WhenPublishing_ThenShouldPublishBoth() { // Given var firstJob = Job.New("subject", "payload", true, DateTime.UtcNow, DateTime.UtcNow.AddHours(1), null); var secondJob = Job.New("subject", "payload", true, DateTime.UtcNow, DateTime.UtcNow.AddHours(1), null); var firstJobExecution = JobExecution.New(firstJob, firstJob.RunAt); var secondJobExecution = JobExecution.New(secondJob, secondJob.RunAt); _options.JobsExchange = "jobs"; Mock.Get(_model) .Setup(x => x.WaitForConfirms()) .Returns(true); var batchPublish = Mock.Of <IBasicPublishBatch>(); Mock.Get(_model) .Setup(x => x.CreateBasicPublishBatch()) .Returns(batchPublish); Mock.Get(_model) .Setup(x => x.BasicPublish(_options.JobsExchange, firstJob.Subject, true, null, It.IsAny <ReadOnlyMemory <byte> >())); Mock.Get(_model) .Setup(x => x.BasicPublish(_options.JobsExchange, secondJob.Subject, true, null, It.IsAny <ReadOnlyMemory <byte> >())); // When var result = await _publisher.PublishManyAsync(new [] { firstJobExecution, secondJobExecution }, CancellationToken.None); // Then result.ShouldBeTrue(); Mock.Get(batchPublish) .Verify(x => x.Publish(), Times.Once); }
/// <summary> /// Exception handling. /// </summary> /// <param name="execution"></param> /// <param name="e"></param> private void HandleException(JobExecution execution, Exception e) { Logger.Error(e, "Encountered fatal error executing job"); execution.ExitStatus = GetDefaultExitStatusForFailure(e, execution); execution.Status = BatchStatus.Failed; execution.AddFailureException(e); }
/// <summary> /// Finds all dependencies for a JobExecution, including JobInstance (which /// requires JobParameters) plus StepExecutions. /// </summary> /// <param name="jobExecution"></param> private void GetJobExecutionDependencies(JobExecution jobExecution) { JobInstance jobInstance = _jobInstanceDao.GetJobInstance(jobExecution); _stepExecutionDao.AddStepExecutions(jobExecution); jobExecution.JobInstance = jobInstance; jobExecution.ExecutionContext = _executionContextDao.GetExecutionContext(jobExecution); }
/// <summary> /// Job interruption handling. /// </summary> /// <param name="execution"></param> /// <param name="e"></param> private void HandleJobInterruptedException(JobExecution execution, JobInterruptedException e) { Logger.Info("Encountered interruption executing job: " + e.Message); Logger.Debug(e, "Full exception"); execution.ExitStatus = GetDefaultExitStatusForFailure(e, execution); execution.Status = BatchStatus.Max(BatchStatus.Stopped, e.Status); execution.AddFailureException(e); }
/// <summary> /// Updates JobExecution. /// </summary> /// <param name="jobExecution"></param> public void Update(JobExecution jobExecution) { Assert.NotNull(jobExecution, "JobExecution cannot be null."); Assert.NotNull(jobExecution.GetJobId(), "JobExecution must have a Job ID set."); Assert.NotNull(jobExecution.Id, "JobExecution must be already saved (have an id assigned)."); jobExecution.LastUpdated = DateTime.Now; _jobExecutionDao.SynchronizeStatus(jobExecution); _jobExecutionDao.UpdateJobExecution(jobExecution); }
/// <summary> /// @see IExecutionContextDao#UpdateExecutionContext . /// </summary> /// <param name="jobExecution"></param> public void UpdateExecutionContext(JobExecution jobExecution) { var executionContext = jobExecution.ExecutionContext; if (executionContext != null) { _contexts[jobExecution.GetContextKey()] = Copy(executionContext); } }
/// <summary> /// @see AbstractJob#DoExecute . /// </summary> /// <param name="execution"></param> /// <exception cref="JobExecutionException"> </exception> protected override void DoExecute(JobExecution execution) { try { JobFlowExecutor executor = new JobFlowExecutor(JobRepository, new SimpleStepHandler(JobRepository), execution); executor.UpdateJobExecutionStatus(Flow.Start(executor).Status); } catch (FlowExecutionException e) { var exception = e.InnerException as JobExecutionException; if (exception != null) { throw exception; } throw new JobExecutionException("Flow execution ended unexpectedly", e); } }
/// <summary> /// Copies through serialization/deserialization. /// </summary> /// <param name="original"></param> /// <returns></returns> private static JobExecution Copy(JobExecution original) { return original.Serialize().Deserialize<JobExecution>(); }
/// <summary> /// @see IJobExecutionDao#UpdateJobExecution /// </summary> /// <param name="jobExecution"></param> public void UpdateJobExecution(JobExecution jobExecution) { JobExecution persistedExecution; if (jobExecution.Id == null || !_executionsById.TryGetValue(jobExecution.Id, out persistedExecution)) { throw new ArgumentException("jobExecution should have already been saved"); } lock (jobExecution) { if (jobExecution.Version != persistedExecution.Version) { throw new ArgumentException(string.Format("Attempt to update job execution (id={0}) with version {1}, but current version is {2}.", jobExecution.Id, jobExecution.Version, persistedExecution.Version)); } jobExecution.IncrementVersion(); _executionsById[jobExecution.Id] = Copy(jobExecution); } }
private void UpdateStatus(JobExecution jobExecution, BatchStatus status) { jobExecution.Status = status; JobRepository.Update(jobExecution); }
/// <summary> /// Finds all dependencies for a JobExecution, including JobInstance (which /// requires JobParameters) plus StepExecutions. /// </summary> /// <param name="jobExecution">the given job execution</param> private void GetJobExecutionDependencies(JobExecution jobExecution) { JobInstance jobInstance = _jobInstanceDao.GetJobInstance(jobExecution); _stepExecutionDao.AddStepExecutions(jobExecution); jobExecution.JobInstance = jobInstance; jobExecution.ExecutionContext = _executionContextDao.GetExecutionContext(jobExecution); }
/// <summary> /// Updates the updates of a job execution. /// The job execution must have already been persisted. /// </summary> /// <param name="jobExecution">a job execution</param> public void UpdateJobExecution(JobExecution jobExecution) { Assert.NotNull(jobExecution, "The job execution must not be null."); Assert.NotNull(jobExecution.GetJobId(), "The corresponding job id must not be null."); Assert.NotNull(jobExecution.Id, "The job execution id must not be null. The execution must have already been saved."); Assert.NotNull(jobExecution.Version, "The job execution version must not be null. The execution must have already been saved."); lock (jobExecution) using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { var version = (jobExecution.Version ?? 0) + 1; var exitDescription = jobExecution.ExitStatus.ExitDescription; if (exitDescription != null && exitDescription.Length > _exitMessageLength) { exitDescription = exitDescription.Substring(0, _exitMessageLength); Logger.Debug("Truncating long message before updating job execution: {0}", jobExecution); } if (DbOperator.Query<int>(InsertTablePrefix(CheckJobExecutionExistsQuery), new Dictionary<string, object> { { "id", jobExecution.Id } }) != 1) { throw new NoSuchJobException(string.Format("Invalid job execution: no job execution with id {0} found.", jobExecution.Id)); } var parameters = new Dictionary<string, object> { { "startTime", (object) jobExecution.StartTime ?? DBNull.Value }, { "endTime", (object) jobExecution.EndTime ?? DBNull.Value }, { "status", jobExecution.Status.ToString() }, { "exitCode", jobExecution.ExitStatus.ExitCode }, { "exitMessage", exitDescription }, { "newVersion", version }, { "createTime", jobExecution.CreateTime }, { "lastUpdated", (object) jobExecution.LastUpdated ?? DBNull.Value }, { "id", jobExecution.Id }, { "version", jobExecution.Version } }; var count = DbOperator.Update(InsertTablePrefix(UpdateJobExecutionQuery), parameters); if (count == 0) { var currentVersion = DbOperator.Query<long>(InsertTablePrefix(CurrentVersionJobExecutionQuery), new Dictionary<string, object> { { "id", jobExecution.Id } }); throw new ArgumentException(string.Format("Attempt to update job id={0} with version {1} but current version is {2}", jobExecution.Id, jobExecution.Version, currentVersion)); } jobExecution.IncrementVersion(); scope.Complete(); } }
/// <summary> /// Creates a row mapper that for step executions. /// </summary> /// <param name="jobExecution">the job execution to use when creating the step executions</param> /// <returns>a row mapper</returns> private static RowMapper<StepExecution> GetStepExecutionRowMapper(JobExecution jobExecution) { return (dataRecord, i) => { var wrapper = new DataRecordWrapper(dataRecord); var stepExecution = new StepExecution(wrapper.Get<string>((1)), jobExecution, wrapper.Get<long>((0))) { StartTime = wrapper.Get<DateTime>((2)), EndTime = wrapper.Get<DateTime>((3)), BatchStatus = BatchStatus.ValueOf(wrapper.Get<string>((4))), CommitCount = wrapper.Get<int>((5)), ReadCount = wrapper.Get<int>((6)), FilterCount = wrapper.Get<int>((7)), WriteCount = wrapper.Get<int>((8)), ExitStatus = new ExitStatus(wrapper.Get<string>((9)), wrapper.Get<string>(10)), ReadSkipCount = wrapper.Get<int>((11)), WriteSkipCount = wrapper.Get<int>((12)), ProcessSkipCount = wrapper.Get<int>((13)), RollbackCount = wrapper.Get<int>((14)), LastUpdated = wrapper.Get<DateTime?>(15), Version = wrapper.Get<int?>((16)) }; return stepExecution; }; }
/// <summary> /// Adds persisted step executions to a job execution. /// </summary> /// <param name="jobExecution">a job execution</param> public void AddStepExecutions(JobExecution jobExecution) { using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { DbOperator.Select(InsertTablePrefix(GetStepExecutionsQuery), GetStepExecutionRowMapper(jobExecution), new Dictionary<string, object> { { "jobId", jobExecution.Id } }); scope.Complete(); } }
/// <param name="jobExecution">a job execution</param> /// <param name="stepExecutionId">a step execution id</param> /// <returns>the step execution with the given id in the given job execution</returns> public StepExecution GetStepExecution(JobExecution jobExecution, long stepExecutionId) { using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { var executions = DbOperator.Select(InsertTablePrefix(GetStepExecutionQuery), GetStepExecutionRowMapper(jobExecution), new Dictionary<string, object> { { "jobId", jobExecution.Id }, { "stepId", stepExecutionId } }); var result = executions.Count == 0 ? null : executions[0]; scope.Complete(); return result; } }
/// <summary> /// Actual job execution. Delegates to DoExecute. /// </summary> /// <param name="execution"></param> private void HandleExecution(JobExecution execution) { execution.StartTime = DateTime.Now; UpdateStatus(execution, BatchStatus.Started); _listener.BeforeJob(execution); try { Logger.Debug("Current job execution: {0}", execution); DoExecute(execution); Logger.Debug("Job execution complete: {0}", execution.JobInstance.JobName); } catch (RepeatException e) { throw e.InnerException; } }
/// <summary> /// @see IJobExecutionDao#SynchronizeStatus. /// </summary> /// <param name="jobExecution"></param> public void SynchronizeStatus(JobExecution jobExecution) { JobExecution persistedExecution; if (jobExecution.Id != null && _executionsById.TryGetValue(jobExecution.Id, out persistedExecution) && persistedExecution.Version != jobExecution.Version) { jobExecution.UpgradeStatus(persistedExecution.Status); jobExecution.Version = persistedExecution.Version; } }
/// <summary> /// Persists the status and version fields of a job execution. /// The job execution must have already been persisted. /// </summary> /// <param name="jobExecution"></param> public void SynchronizeStatus(JobExecution jobExecution) { using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { var currentVersion = DbOperator.Query<long>(InsertTablePrefix(CurrentVersionJobExecutionQuery), new Dictionary<string, object> { { "id", jobExecution.Id } }); if (currentVersion != jobExecution.Version) { var status = DbOperator.Query<string>(InsertTablePrefix(GetStatusQuery), new Dictionary<string, object> { { "id", jobExecution.Id } }); jobExecution.UpgradeStatus(BatchStatus.ValueOf(status)); jobExecution.Version = (int?)currentVersion; } scope.Complete(); } }
/// <summary> /// Actual job execution. /// To be implemented by sub-classes. /// </summary> /// <param name="execution"></param> /// <exception cref="JobExecutionException"> </exception> protected abstract void DoExecute(JobExecution execution);
/// <summary> /// Persists a new job execution. /// The corresponding job instance must have been persisted. /// </summary> /// <param name="jobExecution">a job execution</param> public void SaveJobExecution(JobExecution jobExecution) { Assert.NotNull(jobExecution, "The job execution must not be null."); Assert.NotNull(jobExecution.GetJobId(), "The corresponding job id must not be null."); using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionOptions)) { jobExecution.IncrementVersion(); jobExecution.Id = _jobExecutionIncrementer.NextLong(); var parameters = new Dictionary<string, object> { { "executionId", jobExecution.Id }, { "jobId", jobExecution.GetJobId() }, { "startTime",(object) jobExecution.StartTime ?? DBNull.Value }, { "endTime", (object) jobExecution.EndTime ?? DBNull.Value }, { "status", jobExecution.Status.ToString() }, { "exitCode", jobExecution.ExitStatus.ExitCode }, { "exitMessage", jobExecution.ExitStatus.ExitDescription }, { "version", jobExecution.Version }, { "createTime", jobExecution.CreateTime }, { "lastUpdated", (object) jobExecution.LastUpdated ?? DBNull.Value }, { "jobConfigurationLocation", (object) jobExecution.JobConfigurationName ?? DBNull.Value } }; DbOperator.Update(InsertTablePrefix(SaveJobExecutionQuery), parameters); InsertJobParameters(jobExecution.Id, jobExecution.JobParameters); scope.Complete(); } }
/// <summary> /// Custom constructor using a JobExecution. /// </summary> /// <param name="jobExecution"></param> public JobContext(JobExecution jobExecution) { Assert.NotNull(jobExecution, "A JobContext must have a non-null JobExecution"); _jobExecution = jobExecution; }
/// <summary> /// Action creation helper. /// Given a job, job parameters and a job execution, /// will wrap the execution of the job into a <see cref="System.Action"/>. /// </summary> /// <param name="job">the job to execute</param> /// <param name="jobParameters">the job parameters</param> /// <param name="jobExecution">the job execution</param> /// <returns></returns> private static Action CreateJobAction(IJob job, JobParameters jobParameters, JobExecution jobExecution) { Action jobAction = (() => { try { Logger.Info("Job: [{0} ] launched with the following parameters:[{1}]",job,jobParameters); job.Execute(jobExecution); Logger.Info("Job: [{0}] completed with the following parameters:[{1}] and the following status: [{2}]", job,jobParameters,jobExecution.Status); } catch (Exception exception) { Logger.Info("Job: [{0}] failed unexpectedly and fatally with the following parameters: [{1}]",job,exception); throw; } }); return jobAction; }
/// <summary> /// Manage last job execution if required. /// </summary> /// <param name="job"></param> /// <param name="lastExecution"></param> /// <exception cref="JobRestartException"> </exception> private static void HandleLastExecution(IJob job, JobExecution lastExecution) { //Last Execution handling if (lastExecution != null) { if (!job.Restartable) { throw new JobRestartException("JobInstance already exists and is not restartable !"); } foreach (StepExecution execution in lastExecution.StepExecutions) { if (execution.BatchStatus == BatchStatus.Unknown) { throw new JobRestartException(string.Format("Step [{0}] is of status UNKNOWN", execution.StepName)); } } } }
/// <summary> /// @see IStepHandler#HandleStep . /// </summary> /// <param name="step"></param> /// <param name="execution"></param> /// <returns></returns> /// <exception cref="JobInterruptedException"></exception> /// <exception cref="JobRestartException"></exception> /// <exception cref="StartLimitExceededException"></exception> public StepExecution HandleStep(IStep step, JobExecution execution) { if (execution.IsStopping()) { throw new JobInterruptedException("JobExecution interrupted."); } JobInstance jobInstance = execution.JobInstance; StepExecution lastStepExecution = JobRepository.GetLastStepExecution(jobInstance, step.Name); if (StepExecutionPartOfExistingJobExecution(execution, lastStepExecution)) { // If the last execution of this step was in the same job, it's // probably intentional so we want to run it again... Logger.Info("Duplicate step [{0}] detected in execution of job=[{1}]. " + "If either step fails, both will be executed again on restart.", step.Name, jobInstance.JobName ); lastStepExecution = null; } StepExecution currentStepExecution = lastStepExecution; if (ShouldStart(lastStepExecution, execution, step)) { currentStepExecution = execution.CreateStepExecution(step.Name); //Handle restart if needed HandleRestart(lastStepExecution, currentStepExecution); //Handle normal step execution HandleStepExecution(step, execution, currentStepExecution); if (currentStepExecution.BatchStatus == BatchStatus.Stopping || currentStepExecution.BatchStatus == BatchStatus.Stopped) { // Ensure that the job gets the message that it is stopping execution.Status = BatchStatus.Stopping; throw new JobInterruptedException("Job interrupted by step execution"); } } return currentStepExecution; }
/// <summary> /// Computes exit status depending on exception. /// </summary> /// <param name="ex"></param> /// <param name="execution"></param> /// <returns></returns> protected ExitStatus GetDefaultExitStatusForFailure(Exception ex, JobExecution execution) { ExitStatus exitStatus; if (ex is JobInterruptedException || ex.InnerException is JobInterruptedException) { exitStatus = ExitStatus.Stopped.AddExitDescription("JobInterruptedException"); } else if (ex is NoSuchJobException || ex.InnerException is NoSuchJobException) { exitStatus = new ExitStatus(ExitCodeMapperConstants.NoSuchJob, ex.GetType().Name); } else { exitStatus = ExitStatus.Failed.AddExitDescription(ex); } return exitStatus; }
/// <summary> /// Convenience method for subclasses to delegate the handling of a specific /// step in the context of the current <see cref="JobExecution"/> . Clients of this /// method do not need access to the <see cref="JobRepository"/>, nor do they need /// to worry about populating the execution context on a restart, nor /// detecting the interrupted state (in job or step execution). /// </summary> /// <param name="step">the step to execute</param> /// <param name="execution">the current job execution</param> /// <returns></returns> /// <exception cref="JobInterruptedException"> </exception> /// <exception cref="JobRestartException"> </exception> /// <exception cref="StartLimitExceededException"> </exception> protected StepExecution HandleStep(IStep step, JobExecution execution) { return _stepHandler.HandleStep(step, execution); }
private bool StepExecutionPartOfExistingJobExecution(JobExecution jobExecution, StepExecution stepExecution) { return stepExecution != null && stepExecution.GetJobExecutionId() != null && stepExecution.GetJobExecutionId().Equals(jobExecution.Id); }
/// <summary> /// Handle normal step execution. /// </summary> /// <param name="step"></param> /// <param name="execution"></param> /// <param name="currentStepExecution"></param> private void HandleStepExecution(IStep step, JobExecution execution, StepExecution currentStepExecution) { JobRepository.Add(currentStepExecution); Logger.Info("Executing step: [ {0} ]", step.Name); try { step.Execute(currentStepExecution); currentStepExecution.ExecutionContext.Put("batch.executed", true); } catch (JobInterruptedException) { // Ensure that the job gets the message that it is stopping // and can pass it on to other steps that are executing // concurrently. execution.Status = BatchStatus.Stopping; throw; } JobRepository.UpdateExecutionContext(execution); }
/// <summary> /// Given a step and configuration, return true if the step should start, /// false if it should not, and throw an exception if the job should finish. /// </summary> /// <param name="lastStepExecution"></param> /// <param name="jobExecution"></param> /// <param name="step"></param> /// <returns></returns> /// <exception cref="JobRestartException"></exception> /// <exception cref="StartLimitExceededException"></exception> protected bool ShouldStart(StepExecution lastStepExecution, JobExecution jobExecution, IStep step) { var stepStatus = lastStepExecution == null ? BatchStatus.Starting : lastStepExecution.BatchStatus; if (stepStatus == BatchStatus.Unknown) { throw new JobRestartException("Cannot restart step from UNKNOWN status. " + "The last execution ended with a failure that could not be rolled back, " + "so it may be dangerous to proceed. Manual intervention is probably necessary."); } if (stepStatus == BatchStatus.Completed && ( step.AllowStartIfComplete !=null && !step.AllowStartIfComplete.Value) || stepStatus == BatchStatus.Abandoned) { // step is complete, false should be returned, indicating that the // step should not be started Logger.Info("Step already complete or not restartable, so no action to execute: {0}",lastStepExecution); return false; } if (JobRepository.GetStepExecutionCount(jobExecution.JobInstance, step.Name) < step.StartLimit) { // step start count is less than start max, return true return true; } else { // start max has been exceeded, throw an exception. throw new StartLimitExceededException( string.Format("Maximum start limit exceeded for step: {0} StartMax: {1}", step.Name, step.StartLimit)); } }