Skip to content

Commit 48b4780

Browse files
Merge pull request #1490 from redis/DOC-5065-python-prod-usage
DOC-5065 Python production usage page
2 parents e5f33a1 + 3220fc0 commit 48b4780

File tree

3 files changed

+178
-4
lines changed

3 files changed

+178
-4
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
categories:
3+
- docs
4+
- develop
5+
- stack
6+
- oss
7+
- rs
8+
- rc
9+
- oss
10+
- kubernetes
11+
- clients
12+
description: Get your `redis-py` app ready for production
13+
linkTitle: Production usage
14+
title: Production usage
15+
weight: 70
16+
---
17+
18+
This guide offers recommendations to get the best reliability and
19+
performance in your production environment.
20+
21+
## Checklist
22+
23+
Each item in the checklist below links to the section
24+
for a recommendation. Use the checklist icons to record your
25+
progress in implementing the recommendations.
26+
27+
{{< checklist "pyprodlist" >}}
28+
{{< checklist-item "#client-side-caching" >}}Client-side caching{{< /checklist-item >}}
29+
{{< checklist-item "#retries" >}}Retries{{< /checklist-item >}}
30+
{{< checklist-item "#health-checks" >}}Health checks{{< /checklist-item >}}
31+
{{< checklist-item "#exception-handling" >}}Exception handling{{< /checklist-item >}}
32+
{{< /checklist >}}
33+
34+
## Recommendations
35+
36+
The sections below offer recommendations for your production environment. Some
37+
of them may not apply to your particular use case.
38+
39+
### Client-side caching
40+
41+
[Client-side caching]({{< relref "/develop/clients/client-side-caching" >}})
42+
involves storing the results from read-only commands in a local cache. If the
43+
same command is executed again later, the results can be obtained from the cache,
44+
without contacting the server. This improves command execution time on the client,
45+
while also reducing network traffic and server load. See
46+
[Connect using client-side caching]({{< relref "/develop/clients/redis-py/connect#connect-using-client-side-caching" >}})
47+
for more information and example code.
48+
49+
### Retries
50+
51+
Redis connections and commands can often fail due to transient problems,
52+
such as temporary network outages or timeouts. When this happens,
53+
the operation will generally succeed after a few attempts, despite
54+
failing the first time.
55+
56+
`redis-py` can retry commands automatically when
57+
errors occur. From version 6.0.0 onwards, the default behavior is to
58+
attempt a failed command three times.
59+
The timing between successive attempts is calculated using
60+
[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)
61+
with some random "jitter" added to avoid two or more connections retrying
62+
commands in sync with each other.
63+
64+
You can override the default behavior using an instance of the `Retry` class to
65+
specify the number of times to retry after a failure along with your
66+
own choice of backoff strategy.
67+
Pass the `Retry` object in the `retry` parameter when you connect.
68+
For example, the connection in the code below uses an exponential backoff strategy
69+
(without jitter) that will make eight repeated attempts after a failure:
70+
71+
```py
72+
from redis.backoff import ExponentialBackoff
73+
from redis.retry import Retry
74+
75+
# Run 8 retries with exponential backoff strategy.
76+
retry = Retry(ExponentialBackoff(), 8)
77+
78+
r = Redis(
79+
retry=retry,
80+
.
81+
.
82+
)
83+
```
84+
85+
A retry is triggered when a command throws any exception
86+
listed in the `supported_errors` attribute of the `Retry` class.
87+
By default, the list only includes `ConnectionError` and `TimeoutError`,
88+
but you can set your own choice of exceptions when you create the
89+
instance:
90+
91+
```py
92+
# Only retry after a `TimeoutError`.
93+
retry = Retry(ExponentialBackoff(), 3, supported_errors=(TimeoutError,))
94+
```
95+
96+
You can also add extra exceptions to the default list using the `retry_on_error`
97+
parameter when you connect:
98+
99+
```py
100+
# Add `BusyLoadingError` to the default list of exceptions.
101+
from redis.exceptions import (
102+
BusyLoadingError,
103+
)
104+
.
105+
.
106+
107+
r = Redis(
108+
retry=retry,
109+
retry_on_error=[BusyLoadingError],
110+
.
111+
.
112+
)
113+
```
114+
115+
For a connection to a Redis cluster, you can specify a `retry` instance,
116+
but the list of exceptions is not configurable and is always set
117+
to `TimeoutError`, `ConnectionError`, and `ClusterDownError`.
118+
119+
### Health checks
120+
121+
If your code doesn't access the Redis server continuously then it
122+
might be useful to make a "health check" periodically (perhaps once
123+
every few seconds) to verify that the connection is working.
124+
Set the `health_check_interval` parameter during
125+
a connection (with either `Redis` or `ConnectionPool`) to specify
126+
an integer number of seconds. If the connection remains idle for
127+
longer than this interval, it will automatically issue a
128+
[`PING`]({{< relref "/commands/ping" >}}) command and check the
129+
response before continuing with any client commands.
130+
131+
```py
132+
# Issue a health check if the connection is idle for longer
133+
# than three seconds.
134+
r = Redis(
135+
health_check_interval = 3,
136+
.
137+
.
138+
)
139+
```
140+
141+
Health checks help to detect problems as soon as possible without
142+
waiting for a user to report them. Note that health checks, like
143+
other commands, will be [retried](#retries) using the strategy
144+
that you specified for the connection.
145+
146+
### Exception handling
147+
148+
Redis handles many errors using return values from commands, but there
149+
are also situations where exceptions can be thrown. In production code,
150+
you should handle exceptions wherever they can occur.
151+
152+
Import the exceptions you need to check from the `redis.exceptions`
153+
module. The list below describes some of the most common exceptions.
154+
155+
- `ConnectionError`: Thrown when a connection attempt fails
156+
(for example, when connection parameters are invalid or the server
157+
is unavailable) and sometimes when a [health check](#health-checks)
158+
fails. There is also a subclass, `AuthenticationError`, specifically
159+
for authentication failures.
160+
- `ResponseError`: Thrown when you attempt an operation that has no valid
161+
response. Examples include executing a command on the wrong type of key
162+
(as when you try an
163+
['LPUSH']({{< relref "/develop/data-types/lists#automatic-creation-and-removal-of-keys" >}})
164+
command on a string key), creating an
165+
[index]({{< relref "/develop/interact/search-and-query/indexing" >}})
166+
with a name that already exists, and using an invalid ID for a
167+
[stream entry]({{< relref "/develop/data-types/streams/#entry-ids" >}}).
168+
- `TimeoutError`: Thrown when a timeout persistently happens for a command,
169+
despite any [retries](#retries).
170+
- `WatchError`: Thrown when a
171+
[watched key]({{< relref "/develop/clients/redis-py/transpipe#watch-keys-for-changes" >}}) is
172+
modified during a transaction.

layouts/shortcodes/checklist-item.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<li>
2-
<select onchange="clChange('prodlist')">
2+
{{- with .Parent }}
3+
<select onchange="clChange('{{ .Get 0 }}')">
4+
{{- end }}
35
<option value="R">&#x274C;</option>
46
<option value="G">&#9989;</option>
57
<option value="A">&#x1F50D;</option>
68
<option value="X">&#x2205;</option>
79
</select>
8-
{{- if index .Params 0 -}}
10+
{{- if .Get 0 -}}
911
<a href="{{ index .Params 0 }}">{{ .Inner }}</a>
1012
{{- else -}}
1113
{{ .Inner }}

layouts/shortcodes/checklist.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{ $formId := index .Params 0 }}
1+
{{ $formId := .Get 0 }}
22
<form id="{{ $formId }}">
33
<ul style="list-style-type: none;padding-left: 0px;">
44
{{ .Inner }}
@@ -17,7 +17,7 @@
1717
document.addEventListener('DOMContentLoaded', () => {
1818
let itemString = localStorage.getItem("{{ $formId }}");
1919

20-
if (itemString !== "") {
20+
if (itemString) {
2121
setCLItemsFromString("{{ $formId }}", itemString);
2222
} else {
2323
clChange("{{ $formId }}");

0 commit comments

Comments
 (0)