private static bool AddFingerprintIfNotExists(IStorageConnection connection, Job job)
        {
            using (connection.AcquireDistributedLock(GetFingerprintLockKey(job), LockTimeout))
            {
                var fingerprint = connection.GetAllEntriesFromHash(GetFingerprintKey(job));

                DateTimeOffset timestamp;

                if (fingerprint != null &&
                    fingerprint.ContainsKey("Timestamp") &&
                    DateTimeOffset.TryParse(fingerprint["Timestamp"], null, DateTimeStyles.RoundtripKind, out timestamp) &&
                    DateTimeOffset.UtcNow <= timestamp.Add(FingerprintTimeout))
                {
                    // Actual fingerprint found, returning.
                    return(false);
                }

                // Fingerprint does not exist, it is invalid (no `Timestamp` key),
                // or it is not actual (timeout expired).
                connection.SetRangeInHash(GetFingerprintKey(job), new Dictionary <string, string>
                {
                    { "Timestamp", DateTimeOffset.UtcNow.ToString("o") }
                });

                return(true);
            }
        }
 private static void RemoveFingerprint(IStorageConnection connection, Job job)
 {
     using (connection.AcquireDistributedLock(GetFingerprintLockKey(job), LockTimeout))
         using (var transaction = connection.CreateWriteTransaction())
         {
             transaction.RemoveHash(GetFingerprintKey(job));
             transaction.Commit();
         }
 }
예제 #3
0
        private void RemoveFingerprint(IStorageConnection connection, Job job)
        {
            using (connection.AcquireDistributedLock(GetFingerprintLockKey(job), LockTimeout))
                using (IWriteOnlyTransaction transaction = connection.CreateWriteTransaction())
                {
                    string fingerprintKey = GetFingerprintKey(job);

                    transaction.RemoveHash(fingerprintKey);
                    transaction.Commit();
                }
        }
예제 #4
0
 /// <summary>
 /// Remove the fingerprint for the job.
 /// </summary>
 /// <param name="connection"></param>
 /// <param name="job"></param>
 private void RemoveFingerprint(IStorageConnection connection, Job job)
 {
     try
     {
         using (connection.AcquireDistributedLock(GetFingerprintWithPrefix(job, LockKeyPrefix), s_lockTimeout))
             using (var transaction = connection.CreateWriteTransaction())
             {
                 transaction.RemoveHash(GetFingerprintWithPrefix(job, FingerprintPrefix));
                 transaction.Commit();
             }
     }
     catch
     {
         // continue from exception
     }
 }
예제 #5
0
        public static IDisposable AcquireDistributedRecurringJobLock(
            [NotNull] this IStorageConnection connection,
            [NotNull] string recurringJobId,
            TimeSpan timeout)
        {
            if (connection == null)
            {
                throw new ArgumentNullException(nameof(connection));
            }
            if (recurringJobId == null)
            {
                throw new ArgumentNullException(nameof(recurringJobId));
            }

            return(connection.AcquireDistributedLock($"lock:recurring-job:{recurringJobId}", timeout));
        }
        public static IDisposable AcquireDistributedJobLock(
            [NotNull] this IStorageConnection connection,
            [NotNull] string jobId,
            TimeSpan timeout)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }
            if (jobId == null)
            {
                throw new ArgumentNullException("jobId");
            }

            return(connection.AcquireDistributedLock(
                       String.Format("job:{0}:state-lock", jobId),
                       timeout));
        }
예제 #7
0
        private bool AddFingerprintIfNotExists(IStorageConnection connection, Job job)
        {
            using var lck = connection.AcquireDistributedLock(GetFingerprintLockKey(job), LockTimeout);

            string fingerprintKey = GetFingerprintKey(job);
            Dictionary <string, string> fingerprint = connection.GetAllEntriesFromHash(fingerprintKey);

            if (fingerprint != null)
            {
                if (fingerprint.ContainsKey("Timestamp") &&
                    DateTimeOffset.TryParse(fingerprint["Timestamp"], null, DateTimeStyles.RoundtripKind, out DateTimeOffset timestamp))
                {
                    if (this.FingerprintTimeoutMinutes > 0)
                    {
                        var timestampWithTimeout = timestamp.Add(TimeSpan.FromMinutes(FingerprintTimeoutMinutes));

                        if (DateTimeOffset.UtcNow <= timestampWithTimeout)
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }
            }

            // Fingerprint does not exist, it is invalid (no `Timestamp` key),
            // or it is not actual (timeout expired).
            connection.SetRangeInHash(fingerprintKey, new Dictionary <string, string>
            {
                { "Timestamp", DateTimeOffset.UtcNow.ToString("o") },
                { "MethodName", job.Method.Name },
                { "TypeName", job.Type.Name }
            });

            return(true);
        }
        /// <summary>
        /// Sets <see cref="RecurringJobInfo"/> to storage which associated with <see cref="RecurringJob"/>.
        /// </summary>
        /// <param name="recurringJobInfo">The specified identifier of the RecurringJob.</param>
        public void SetJobData(RecurringJobInfo recurringJobInfo)
        {
            if (recurringJobInfo == null)
            {
                throw new ArgumentNullException(nameof(recurringJobInfo));
            }

            if (recurringJobInfo.JobData == null || recurringJobInfo.JobData.Count == 0)
            {
                return;
            }

            using (_connection.AcquireDistributedLock($"recurringjobextensions-jobdata:{recurringJobInfo.RecurringJobId}", LockTimeout))
            {
                var changedFields = new Dictionary <string, string>
                {
                    [nameof(RecurringJobInfo.Enable)]  = JobHelper.ToJson(recurringJobInfo.Enable),
                    [nameof(RecurringJobInfo.JobData)] = JobHelper.ToJson(recurringJobInfo.JobData)
                };

                _connection.SetRangeInHash($"recurring-job:{recurringJobInfo.RecurringJobId}", changedFields);
            }
        }
예제 #9
0
        public static void SetTrigger(this IStorageConnection connection, string triggerName)
        {
            var jsc        = (JobStorageConnection)connection;
            var triggerKey = GenerateTriggerKey(triggerName);
            var jobIds     = jsc.GetAllItemsFromList(triggerKey);

            var client = new BackgroundJobClient();

            try
            {
                using (connection.AcquireDistributedLock(triggerKey, TimeSpan.Zero))
                {
                    foreach (var jobId in jobIds)
                    {
                        client.ChangeState(jobId, new EnqueuedState());
                    }
                }
            }
            catch (DistributedLockTimeoutException)
            {
                // Assume already run
            }
        }
예제 #10
0
 private IDisposable AcquireDistributedSetLock(IStorageConnection connection, IEnumerable <object> args)
 {
     return(connection.AcquireDistributedLock(GetDistributedLockKey(args), DistributedLockTimeout));
 }
예제 #11
0
        public bool TryToChangeState(
            string jobId, IState toState, string[] fromStates)
        {
            if (jobId == null)
            {
                throw new ArgumentNullException("jobId");
            }
            if (toState == null)
            {
                throw new ArgumentNullException("toState");
            }
            if (fromStates != null && fromStates.Length == 0)
            {
                throw new ArgumentException("From states array should be null or non-empty.", "fromStates");
            }

            // To ensure that job state will be changed only from one of the
            // specified states, we need to ensure that other users/workers
            // are not able to change the state of the job during the
            // execution of this method. To guarantee this behavior, we are
            // using distributed application locks and rely on fact, that
            // any state transitions will be made only within a such lock.
            using (_connection.AcquireDistributedLock(
                       String.Format("job:{0}:state-lock", jobId),
                       JobLockTimeout))
            {
                var jobData = _connection.GetJobData(jobId);

                if (jobData == null)
                {
                    // The job does not exist. This may happen, because not
                    // all storage backends support foreign keys.
                    return(false);
                }

                if (fromStates != null && !fromStates.Contains(jobData.State, StringComparer.OrdinalIgnoreCase))
                {
                    return(false);
                }

                bool loadSucceeded = true;

                try
                {
                    jobData.EnsureLoaded();
                }
                catch (JobLoadException ex)
                {
                    // If the job type could not be loaded, we are unable to
                    // load corresponding filters, unable to process the job
                    // and sometimes unable to change its state (the enqueued
                    // state depends on the type of a job).

                    if (!toState.IgnoreJobLoadException)
                    {
                        toState = new FailedState(ex.InnerException)
                        {
                            Reason = String.Format(
                                "Can not change the state of a job to '{0}': target method was not found.",
                                toState.Name)
                        };

                        loadSucceeded = false;
                    }
                }

                var context      = new StateContext(jobId, jobData.Job, jobData.CreatedAt, _connection);
                var stateChanged = _stateChangeProcess.ChangeState(context, toState, jobData.State);

                return(loadSucceeded && stateChanged);
            }
        }