Example #1
0
        public string CreateExpiredJob(
            InvocationData invocationData,
            string[] arguments,
            IDictionary<string, string> parameters, 
            TimeSpan expireIn)
        {
            var jobId = Guid.NewGuid().ToString();

            // Do not modify the original parameters.
            var storedParameters = new Dictionary<string, string>(parameters);
            storedParameters.Add("Type", invocationData.Type);
            storedParameters.Add("Method", invocationData.Method);
            storedParameters.Add("ParameterTypes", invocationData.ParameterTypes);
            storedParameters.Add("Arguments", JobHelper.ToJson(arguments));
            storedParameters.Add("CreatedAt", JobHelper.ToStringTimestamp(DateTime.UtcNow));

            using (var transaction = _redis.CreateTransaction())
            {
                transaction.QueueCommand(x => x.SetRangeInHash(
                    String.Format(RedisStorage.Prefix + "job:{0}", jobId),
                    storedParameters));

                transaction.QueueCommand(x => x.ExpireEntryIn(
                    String.Format(RedisStorage.Prefix + "job:{0}", jobId),
                    expireIn));

                // TODO: check return value
                transaction.Commit();
            }

            return jobId;
        }
Example #2
0
 public JobPayload(
     string id, string queue, InvocationData invocationData)
 {
     Id = id;
     Queue = queue;
     InvocationData = invocationData;
 }
Example #3
0
        public void Deserialize_WrapsAnException_WithTheJobLoadException()
        {
            var serializedData = new InvocationData(null, null, null);

            Assert.Throws<JobLoadException>(
                () => MethodData.Deserialize(serializedData));
        }
Example #4
0
        public void Deserialize_ThrowsAnException_WhenTypeCanNotBeFound()
        {
            var serializedData = new InvocationData(
                "NonExistingType",
                "Perform",
                "");

            Assert.Throws<JobLoadException>(
                () => MethodData.Deserialize(serializedData));
        }
        public void Deserialize_ThrowsAnException_WhenMethodCanNotBeFound()
        {
            var serializedData = new InvocationData(
                typeof(InvocationDataFacts).AssemblyQualifiedName,
                "NonExistingMethod",
                JobHelper.ToJson(new [] { typeof(string) }),
                "");

            Assert.Throws<JobLoadException>(
                () => serializedData.Deserialize());
        }
Example #6
0
        public void Deserialize_CorrectlyDeserializes_AllTheData()
        {
            var type = typeof(TestJob);
            var methodInfo = type.GetMethod("Perform");
            var serializedData = new InvocationData(
                type.AssemblyQualifiedName,
                methodInfo.Name,
                JobHelper.ToJson(new Type[0]));

            var method = MethodData.Deserialize(serializedData);

            Assert.Equal(type, method.Type);
            Assert.Equal(methodInfo, method.MethodInfo);
            Assert.False(method.OldFormat);
        }
Example #7
0
        public string CreateExpiredJob(
            InvocationData invocationData,
            string[] arguments,
            IDictionary<string, string> parameters, 
            TimeSpan expireIn)
        {
            if (invocationData == null) throw new ArgumentNullException("invocationData");
            if (arguments == null) throw new ArgumentNullException("arguments");
            if (parameters == null) throw new ArgumentNullException("parameters");

            const string createJobSql = @"
insert into HangFire.Job (InvocationData, Arguments, CreatedAt, ExpireAt)
values (@invocationData, @arguments, @createdAt, @expireAt);
SELECT CAST(SCOPE_IDENTITY() as int)";

            var jobId = _connection.Query<int>(
                createJobSql,
                new
                {
                    invocationData = JobHelper.ToJson(invocationData),
                    arguments = JobHelper.ToJson(arguments),
                    createdAt = DateTime.UtcNow,
                    expireAt = DateTime.UtcNow.Add(expireIn)
                }).Single().ToString();

            if (parameters.Count > 0)
            {
                var parameterArray = new object[parameters.Count];
                int parameterIndex = 0;
                foreach (var parameter in parameters)
                {
                    parameterArray[parameterIndex++] = new
                    {
                        jobId = jobId,
                        name = parameter.Key,
                        value = parameter.Value
                    };
                }

                const string insertParameterSql = @"
insert into HangFire.JobParameter (JobId, Name, Value)
values (@jobId, @name, @value)";

                _connection.Execute(insertParameterSql, parameterArray);
            }

            return jobId;
        }
        public void Deserialize_CorrectlyDeserializes_AllTheData()
        {
            var type = typeof(InvocationDataFacts);
            var methodInfo = type.GetMethod("Sample");

            var serializedData = new InvocationData(
                type.AssemblyQualifiedName,
                methodInfo.Name,
                JobHelper.ToJson(new [] { typeof(string) }),
                JobHelper.ToJson(new [] { "Hello" }));

            var job = serializedData.Deserialize();

            Assert.Equal(type, job.Type);
            Assert.Equal(methodInfo, job.Method);
            Assert.Equal("Hello", job.Arguments[0]);
        }
Example #9
0
        public JobPayload FetchNextJob(CancellationToken cancellationToken)
        {
            string jobId;
            string queueName;
            var queueIndex = 0;

            do
            {
                cancellationToken.ThrowIfCancellationRequested();

                queueIndex = (queueIndex + 1) % _queueNames.Count;
                queueName = _queueNames[queueIndex];

                var queueKey = RedisStorage.Prefix + String.Format("queue:{0}", queueName);
                var fetchedKey = RedisStorage.Prefix + String.Format("queue:{0}:dequeued", queueName);

                if (queueIndex == 0)
                {
                    jobId = _redis.BlockingPopAndPushItemBetweenLists(
                        queueKey,
                        fetchedKey,
                        _fetchTimeout);
                }
                else
                {
                    jobId = _redis.PopAndPushItemBetweenLists(
                        queueKey, fetchedKey);
                }
                
            } while (jobId == null);

            // The job was fetched by the server. To provide reliability,
            // we should ensure, that the job will be performed and acquired
            // resources will be disposed even if the server will crash 
            // while executing one of the subsequent lines of code.

            // The job's processing is splitted into a couple of checkpoints.
            // Each checkpoint occurs after successful update of the 
            // job information in the storage. And each checkpoint describes
            // the way to perform the job when the server was crashed after
            // reaching it.

            // Checkpoint #1-1. The job was fetched into the fetched list,
            // that is being inspected by the FetchedJobsWatcher instance.
            // Job's has the implicit 'Fetched' state.

            string type = null;
            string method = null;
            string parameterTypes = null;
            string arguments = null;
            string args = null;

            using (var pipeline = _redis.CreatePipeline())
            {
                pipeline.QueueCommand(x => x.SetEntryInHash(
                    String.Format(RedisStorage.Prefix + "job:{0}", jobId),
                    "Fetched",
                    JobHelper.ToStringTimestamp(DateTime.UtcNow)));

                // ServiceStack.Redis library could not queue a command,
                // that returns IDictionary, so, let's build it using MGET.
                pipeline.QueueCommand(
                    x => x.GetValuesFromHash(
                        RedisStorage.Prefix + String.Format("job:{0}", jobId),
                        new[] { "Type", "Args", "Method", "Arguments", "ParameterTypes" }),
                    x =>
                    {
                        type = x[0];
                        method = x[2];
                        parameterTypes = x[4];
                        args = x[1];
                        arguments = x[3];
                    });

                pipeline.Flush();
            }

            // Checkpoint #2. The job is in the implicit 'Fetched' state now.
            // This state stores information about fetched time. The job will
            // be re-queued when the JobTimeout will be expired.

            var invocationData = new InvocationData(type, method, parameterTypes);

            return new JobPayload(jobId, queueName, invocationData)
            {
                Args = args,
                Arguments = arguments
            };
        }
Example #10
0
        public StateAndInvocationData GetJobStateAndInvocationData(string id)
        {
            var jobData = _redis.GetAllEntriesFromHash(
                String.Format(RedisStorage.Prefix + "job:{0}", id));

            if (jobData.Count == 0) return null;

            string type = null;
            string method = null;
            string parameterTypes = null;

            if (jobData.ContainsKey("Type"))
            {
                type = jobData["Type"];
            }
            if (jobData.ContainsKey("Method"))
            {
                method = jobData["Method"];
            }
            if (jobData.ContainsKey("ParameterTypes"))
            {
                parameterTypes = jobData["ParameterTypes"];
            }

            var invocationData = new InvocationData(
                type,
                method,
                parameterTypes);

            return new StateAndInvocationData
            {
                InvocationData = invocationData,
                State = jobData.ContainsKey("State") ? jobData["State"] : null,
            };
        }
Example #11
0
        public JobData GetJobData(string id)
        {
            var storedData = Redis.GetAllEntriesFromHash(
                String.Format(RedisStorage.Prefix + "job:{0}", id));

            if (storedData.Count == 0) return null;

            string type = null;
            string method = null;
            string parameterTypes = null;
            string arguments = null;
            string createdAt = null;

            if (storedData.ContainsKey("Type"))
            {
                type = storedData["Type"];
            }
            if (storedData.ContainsKey("Method"))
            {
                method = storedData["Method"];
            }
            if (storedData.ContainsKey("ParameterTypes"))
            {
                parameterTypes = storedData["ParameterTypes"];
            }
            if (storedData.ContainsKey("Arguments"))
            {
                arguments = storedData["Arguments"];
            }
            if (storedData.ContainsKey("CreatedAt"))
            {
                createdAt = storedData["CreatedAt"];
            }

            Job job = null;
            JobLoadException loadException = null;

            var invocationData = new InvocationData(type, method, parameterTypes, arguments);

            try
            {
                job = invocationData.Deserialize();
            }
            catch (JobLoadException ex)
            {
                loadException = ex;
            }
            
            return new JobData
            {
                Job = job,
                State = storedData.ContainsKey("State") ? storedData["State"] : null,
                CreatedAt = JobHelper.FromNullableStringTimestamp(createdAt) ?? DateTime.MinValue,
                LoadException = loadException
            };
        }
Example #12
0
        public void Deserialize_ThrowsAnException_WhenMethodCanNotBeFound()
        {
            var serializedData = new InvocationData(
                typeof (TestJob).AssemblyQualifiedName,
                "NonExistingMethod",
                JobHelper.ToJson(new Type[0]));

            Assert.Throws<JobLoadException>(
                () => MethodData.Deserialize(serializedData));
        }
Example #13
0
        public void SerializedData_IsNotBeingChanged_DuringTheDeserialization()
        {
            var serializedData = new InvocationData(
                typeof (TestJob).AssemblyQualifiedName,
                null,
                null);

            MethodData.Deserialize(serializedData);
            Assert.Null(serializedData.Method);
        }
Example #14
0
        public void Deserialization_FromTheOldFormat_CorrectlySerializesBothTypeAndMethod()
        {
            var serializedData = new InvocationData(
                typeof (TestJob).AssemblyQualifiedName,
                null,
                null);

            var method = MethodData.Deserialize(serializedData);
            Assert.Equal(typeof(TestJob), method.Type);
            Assert.Equal(typeof(TestJob).GetMethod("Perform"), method.MethodInfo);
            Assert.True(method.OldFormat);
        }