Esempio n. 1
0
        public bool EnqueueNextScheduledJob()
        {
            using (var connection = _storage.GetConnection())
            {
                var timestamp = JobHelper.ToTimestamp(DateTime.UtcNow);

                // TODO: it is very slow. Add batching.
                var jobId = connection
                    .GetFirstByLowestScoreFromSet("schedule", 0, timestamp);

                if (String.IsNullOrEmpty(jobId))
                {
                    return false;
                }

                var stateMachine = new StateMachine(connection);
                var enqueuedState = new EnqueuedState
                {
                    Reason = "Enqueued as a scheduled job"
                };

                stateMachine.TryToChangeState(jobId, enqueuedState, new [] { ScheduledState.StateName });

                return true;
            }
        }
Esempio n. 2
0
        private void PerformJob(IStorageConnection connection, JobPayload payload)
        {
            if (payload == null)
            {
                return;
            }

            var stateMachine = new StateMachine(connection);
            var processingState = new ProcessingState(_context.ServerName);

            if (!stateMachine.TryToChangeState(
                payload.Id, 
                processingState, 
                new [] { EnqueuedState.StateName, ProcessingState.StateName }))
            {
                return;
            }

            // Checkpoint #3. Job is in the Processing state. However, there are
            // no guarantees that it was performed. We need to re-queue it even
            // it was performed to guarantee that it was performed AT LEAST once.
            // It will be re-queued after the JobTimeout was expired.

            State state;

            try
            {
                IJobPerformStrategy performStrategy;

                var methodData = MethodData.Deserialize(payload.InvocationData);
                if (methodData.OldFormat)
                {
                    // For compatibility with the Old Client API.
                    // TODO: remove it in version 1.0
                    var arguments = JobHelper.FromJson<Dictionary<string, string>>(
                        payload.Args);

                    performStrategy = new JobAsClassPerformStrategy(
                        methodData, arguments);
                }
                else
                {
                    var arguments = JobHelper.FromJson<string[]>(payload.Arguments);

                    performStrategy = new JobAsMethodPerformStrategy(
                        methodData, arguments);
                }
                
                var performContext = new PerformContext(_context, connection, payload.Id, methodData);
                _context.PerformancePipeline.Run(performContext, performStrategy);

                state = new SucceededState();
            }
            catch (JobPerformanceException ex)
            {
                state = new FailedState(ex.InnerException)
                {
                    Reason = ex.Message
                };
            }
            catch (Exception ex)
            {
                state = new FailedState(ex)
                {
                    Reason = "Internal HangFire Server exception occurred. Please, report it to HangFire developers."
                };
            }

            // TODO: check return value
            stateMachine.TryToChangeState(payload.Id, state, new [] { ProcessingState.StateName });
        }
Esempio n. 3
0
        private bool RequeueJobIfTimedOut(IRedisClient redis, string jobId, string queue)
        {
            var flags = redis.GetValuesFromHash(
                String.Format("hangfire:job:{0}", jobId),
                "Fetched",
                "Checked");

            var fetched = flags[0];
            var @checked = flags[1];

            if (String.IsNullOrEmpty(fetched) && String.IsNullOrEmpty(@checked))
            {
                // If the job does not have these flags set, then it is
                // in the implicit 'Fetched' state. This state has no 
                // information about the time it was fetched. So we
                // can not do anything with the job in this state, because
                // there are two options:

                // 1. It is going to move to the implicit 'Fetched' state
                //    in a short time.
                // 2. It will stay in the 'Fetched' state forever due to
                //    its processing server is dead.

                // To ensure its server is dead, we'll move the job to
                // the implicit 'Checked' state with the current timestamp
                // and will not do anything else at this pass of the watcher.
                // If job's state will still be 'Checked' on the later passes
                // and after the CheckedTimeout expired, then the server
                // is dead, and we'll re-queue the job.

                redis.SetEntryInHash(
                    String.Format("hangfire:job:{0}", jobId),
                    "Checked",
                    JobHelper.ToStringTimestamp(DateTime.UtcNow));

                // Checkpoint #1-2. The job is in the implicit 'Checked' state.
                // It will be re-queued after the CheckedTimeout will be expired.
            }
            else
            {
                if (TimedOutByFetchedTime(fetched) || TimedOutByCheckedTime(fetched, @checked))
                {
                    var stateMachine = new StateMachine(new RedisConnection(_storage, redis));
                    var state = new EnqueuedState{
                        Reason = "Re-queued due to time out"
                    };

                    stateMachine.TryToChangeState(
                        jobId, 
                        state, 
                        new [] { EnqueuedState.StateName, ProcessingState.StateName });

                    RedisConnection.RemoveFromFetchedList(redis, queue, jobId);

                    return true;
                }
            }

            return false;
        }