/// <summary>
        /// Resume an active job or trigger new job from job store.
        /// and execute the job.
        /// </summary>
        /// <param name="cancellationToken">cancellation token.</param>
        /// <returns>Completed task.</returns>
        public async Task RunAsync(CancellationToken cancellationToken = default)
        {
            _logger.LogInformation("Job starts running.");

            // Acquire an active job from the job store.
            var job = await _jobStore.AcquireActiveJobAsync(cancellationToken) ?? await CreateNewJobAsync(cancellationToken);

            if (job == null)
            {
                _logger.LogWarning("Job has been scheduled to end.");

                // release job lock
                Dispose();
            }
            else
            {
                _logger.LogInformation($"The running job id is {job.Id}");

                // Update the running job to job store.
                // For new/resume job, add the created job to job store; For active job, update the last heart beat.
                await _jobStore.UpdateJobAsync(job, cancellationToken);

                try
                {
                    job.Status = JobStatus.Running;
                    await _jobExecutor.ExecuteAsync(job, cancellationToken);

                    job.Status = JobStatus.Succeeded;
                    await _jobStore.CompleteJobAsync(job, cancellationToken);
                }
                catch (Exception exception)
                {
                    job.Status       = JobStatus.Failed;
                    job.FailedReason = exception.ToString();
                    await _jobStore.CompleteJobAsync(job, cancellationToken);

                    _logger.LogError(exception, "Process job '{jobId}' failed.", job.Id);
                    throw;
                }
            }
        }