From f3c86c02c670f81cede29fb2c221ab9dfd655faa Mon Sep 17 00:00:00 2001
From: Selwin Ong <selwin.ong@gmail.com>
Date: Sun, 26 Jul 2020 17:49:09 +0700
Subject: [PATCH] Bump version to 1.5.0

---
 CHANGES.md          |  8 ++++++++
 README.md           | 28 +++++++++++++++++++++++++---
 docs/_config.yml    |  6 +++---
 docs/css/screen.css |  4 ++--
 docs/index.md       | 26 +++++++++++++++++++++-----
 rq/version.py       |  2 +-
 rq/worker.py        | 10 ++++------
 7 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0f23265..390505d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,11 @@
+### RQ 1.5.0 (2020-07-26)
+* Failed jobs can now be retries. Thanks @selwin!
+* Fixed scheduler on Python > 3.8.0. Thanks @selwin!
+* RQ is now aware of which version of Redis server it's running on. Thanks @aparcar!
+* RQ now uses `hset()` on redis-py >= 3.5.0. Thanks @aparcar!
+* Fix incorrect worker timeout calculation in SimpleWorker.execute_job(). Thanks @davidmurray!
+* Make horse handling logic more robust. Thanks @wevsty!
+
 ### RQ 1.4.3 (2020-06-28)
 * Added `job.get_position()` and `queue.get_job_position()`. Thanks @aparcar!
 * Longer TTLs for worker keys to prevent them from expiring inside the worker lifecycle. Thanks @selwin!
diff --git a/README.md b/README.md
index 1e32afc..52b708f 100644
--- a/README.md
+++ b/README.md
@@ -45,14 +45,36 @@ Then, create an RQ queue:
 from redis import Redis
 from rq import Queue
 
-q = Queue(connection=Redis())
+queue = Queue(connection=Redis())
 ```
 
 And enqueue the function call:
 
 ```python
 from my_module import count_words_at_url
-job = q.enqueue(count_words_at_url, 'http://nvie.com')
+job = queue.enqueue(count_words_at_url, 'http://nvie.com')
+```
+
+Scheduling jobs are also similarly easy:
+
+```python
+# Schedule job to run at 9:15, October 10th
+job = queue.enqueue_at(datetime(2019, 10, 8, 9, 15), say_hello)
+
+# Schedule job to run in 10 seconds
+job = queue.enqueue_in(timedelta(seconds=10), say_hello)
+```
+
+Retrying failed jobs is also supported:
+
+```python
+from rq import Retry
+
+# Retry up to 3 times, failed job will be requeued immediately
+queue.enqueue(say_hello, retry=Retry(max=3))
+
+# Retry up to 3 times, with configurable intervals between retries
+queue.enqueue(say_hello, retry=Retry(max=3, interval=[10, 30, 60]))
 ```
 
 For a more complete example, refer to the [docs][d].  But this is the essence.
@@ -64,7 +86,7 @@ To start executing enqueued function calls in the background, start a worker
 from your project's directory:
 
 ```console
-$ rq worker
+$ rq worker --with-scheduler
 *** Listening for work on default
 Got count_words_at_url('http://nvie.com') from default
 Job result = 818
diff --git a/docs/_config.yml b/docs/_config.yml
index e97aba3..6dd6c75 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -16,14 +16,14 @@ navigation:
     url: /docs/results/
   - text: Jobs
     url: /docs/jobs/
-  - text: Exceptions
+  - text: Exceptions & Retries
     url: /docs/exceptions/
   - text: Scheduling Jobs
     url: /docs/scheduling/
-  - text: Monitoring
-    url: /docs/monitoring/
   - text: Job Registries
     url: /docs/job_registries/
+    - text: Monitoring
+    url: /docs/monitoring/
   - text: Connections
     url: /docs/connections/  
   - text: Testing
diff --git a/docs/css/screen.css b/docs/css/screen.css
index add7179..a70e4d9 100644
--- a/docs/css/screen.css
+++ b/docs/css/screen.css
@@ -20,7 +20,7 @@ body
 header
 {
     background: url(../img/ribbon.png) no-repeat 50% 0;
-    max-width: 430px;
+    max-width: 630px;
     width: 100%;
     text-align: center;
 
@@ -86,7 +86,7 @@ header a:hover
 .container
 {
     margin: 0 auto;
-    max-width: 430px;
+    max-width: 630px;
     width: 100%;
 }
 
diff --git a/docs/index.md b/docs/index.md
index 64fddcf..0d3b919 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -36,14 +36,30 @@ And enqueue the function call:
 
 {% highlight python %}
 from my_module import count_words_at_url
-result = q.enqueue(
-             count_words_at_url, 'http://nvie.com')
+result = q.enqueue(count_words_at_url, 'http://nvie.com')
 {% endhighlight %}
 
-For a more complete example, refer to the [docs][d].  But this is the essence.
+Scheduling jobs are similarly easy:
 
-[d]: {{site.baseurl}}docs/
+{% highlight python %}
+# Schedule job to run at 9:15, October 10th
+job = queue.enqueue_at(datetime(2019, 10, 8, 9, 15), say_hello)
+
+# Schedule job to be run in 10 seconds
+job = queue.enqueue_in(timedelta(seconds=10), say_hello)
+{% endhighlight %}
+
+You can also ask RQ to retry failed jobs:
 
+{% highlight python %}
+from rq import Retry
+
+# Retry up to 3 times, failed job will be requeued immediately
+queue.enqueue(say_hello, retry=Retry(max=3))
+
+# Retry up to 3 times, with configurable intervals between retries
+queue.enqueue(say_hello, retry=Retry(max=3, interval=[10, 30, 60]))
+{% endhighlight %}
 
 ### The worker
 
@@ -51,7 +67,7 @@ To start executing enqueued function calls in the background, start a worker
 from your project's directory:
 
 {% highlight console %}
-$ rq worker
+$ rq worker --with-scheduler
 *** Listening for work on default
 Got count_words_at_url('http://nvie.com') from default
 Job result = 818
diff --git a/rq/version.py b/rq/version.py
index 03500dd..f41c8a0 100644
--- a/rq/version.py
+++ b/rq/version.py
@@ -2,4 +2,4 @@
 from __future__ import (absolute_import, division, print_function,
                         unicode_literals)
 
-VERSION = '1.4.3'
+VERSION = '1.5.0'
diff --git a/rq/worker.py b/rq/worker.py
index 463a00b..6868f75 100644
--- a/rq/worker.py
+++ b/rq/worker.py
@@ -843,25 +843,23 @@ class Worker(object):
                     self.connection,
                     job_class=self.job_class
                 )
-            
+
             # Requeue/reschedule if retry is configured
             if job.retries_left and job.retries_left > 0:
                 retry = True
                 retry_interval = job.get_retry_interval()
-                job.retries_left = job.retries_left - 1                
+                job.retries_left = job.retries_left - 1
             else:
                 retry = False
                 job.set_status(JobStatus.FAILED, pipeline=pipeline)
-            
+
             started_job_registry.remove(job, pipeline=pipeline)
 
             if not self.disable_default_exception_handler:
-
                 failed_job_registry = FailedJobRegistry(job.origin, job.connection,
                                                         job_class=self.job_class)
                 failed_job_registry.add(job, ttl=job.failure_ttl,
                                         exc_string=exc_string, pipeline=pipeline)
-                
 
             self.set_current_job_id(None, pipeline=pipeline)
             self.increment_failed_job_count(pipeline)
@@ -869,7 +867,7 @@ class Worker(object):
                 self.increment_total_working_time(
                     job.ended_at - job.started_at, pipeline
                 )
-            
+
             if retry:
                 if retry_interval:
                     scheduled_datetime = datetime.now(timezone.utc) + timedelta(seconds=retry_interval)