From a49e65682c17d338ba4343154bc5e4b23e58068f Mon Sep 17 00:00:00 2001 From: Chayim Date: Wed, 2 Aug 2023 14:03:30 +0300 Subject: [PATCH] RESP3 connection examples (#2863) --- README.md | 12 +- docs/examples/asyncio_examples.ipynb | 210 ++++++++++++++++-------- docs/examples/connection_examples.ipynb | 65 ++++++-- 3 files changed, 203 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index e97119a888..e4e0debe03 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ The table below higlights version compatibility of the most-recent library versi | Library version | Supported redis versions | |-----------------|-------------------| | 3.5.3 | <= 6.2 Family of releases | -| >= 4.1.0 | Version 5.0 to current | +| >= 4.5.0 | Version 5.0 to 7.0 | +| >= 5.0.0 | Versiond 5.0 to current | ## Usage @@ -63,6 +64,15 @@ b'bar' The above code connects to localhost on port 6379, sets a value in Redis, and retrieves it. All responses are returned as bytes in Python, to receive decoded strings, set *decode_responses=True*. For this, and more connection options, see [these examples](https://redis.readthedocs.io/en/stable/examples.html). + +#### RESP3 Support +To enable support for RESP3, ensure you have at least version 5.0 of the client, and change your connection object to include *protocol=3* + +``` python +>>> import redis +>>> r = redis.Redis(host='localhost', port=6379, db=0, protocol=3) +``` + ### Connection Pools By default, redis-py uses a connection pool to manage connections. Each instance of a Redis class receives its own connection pool. You can however define your own [redis.ConnectionPool](https://redis.readthedocs.io/en/stable/connections.html#connection-pools). diff --git a/docs/examples/asyncio_examples.ipynb b/docs/examples/asyncio_examples.ipynb index 855255c88d..7fdcc36bc5 100644 --- a/docs/examples/asyncio_examples.ipynb +++ b/docs/examples/asyncio_examples.ipynb @@ -21,6 +21,12 @@ { "cell_type": "code", "execution_count": 1, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -36,29 +42,29 @@ "connection = redis.Redis()\n", "print(f\"Ping successful: {await connection.ping()}\")\n", "await connection.close()" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "If you supply a custom `ConnectionPool` that is supplied to several `Redis` instances, you may want to disconnect the connection pool explicitly. Disconnecting the connection pool simply disconnects all connections hosted in the pool." - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "If you supply a custom `ConnectionPool` that is supplied to several `Redis` instances, you may want to disconnect the connection pool explicitly. Disconnecting the connection pool simply disconnects all connections hosted in the pool." + ] }, { "cell_type": "code", "execution_count": 2, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "import redis.asyncio as redis\n", @@ -67,16 +73,36 @@ "await connection.close()\n", "# Or: await connection.close(close_connection_pool=False)\n", "await connection.connection_pool.disconnect()" - ], + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By default, this library uses version 2 of the RESP protocol. To enable RESP version 3, you will want to set `protocol` to 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import redis.asyncio as redis\n", + "\n", + "connection = redis.Redis(protocol=3)\n", + "await connection.close()\n", + "await connection.ping()" + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%%\n" + "name": "#%% md\n" } - } - }, - { - "cell_type": "markdown", + }, "source": [ "## Transactions (Multi/Exec)\n", "\n", @@ -85,17 +111,17 @@ "The commands will not be reflected in Redis until execute() is called & awaited.\n", "\n", "Usually, when performing a bulk operation, taking advantage of a “transaction” (e.g., Multi/Exec) is to be desired, as it will also add a layer of atomicity to your bulk operation." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + ] }, { "cell_type": "code", "execution_count": 3, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "import redis.asyncio as redis\n", @@ -105,31 +131,31 @@ " ok1, ok2 = await (pipe.set(\"key1\", \"value1\").set(\"key2\", \"value2\").execute())\n", "assert ok1\n", "assert ok2" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "## Pub/Sub Mode\n", - "\n", - "Subscribing to specific channels:" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## Pub/Sub Mode\n", + "\n", + "Subscribing to specific channels:" + ] }, { "cell_type": "code", "execution_count": 4, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -170,29 +196,29 @@ " await r.publish(\"channel:1\", STOPWORD)\n", "\n", " await future" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Subscribing to channels matching a glob-style pattern:" - ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Subscribing to channels matching a glob-style pattern:" + ] }, { "cell_type": "code", "execution_count": 5, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [ { "name": "stdout", @@ -234,16 +260,16 @@ " await r.publish(\"channel:1\", STOPWORD)\n", "\n", " await future" - ], + ] + }, + { + "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%%\n" + "name": "#%% md\n" } - } - }, - { - "cell_type": "markdown", + }, "source": [ "## Sentinel Client\n", "\n", @@ -252,17 +278,17 @@ "Calling aioredis.sentinel.Sentinel.master_for or aioredis.sentinel.Sentinel.slave_for methods will return Redis clients connected to specified services monitored by Sentinel.\n", "\n", "Sentinel client will detect failover and reconnect Redis clients automatically." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "import asyncio\n", @@ -277,13 +303,61 @@ "assert ok\n", "val = await r.get(\"key\")\n", "assert val == b\"value\"" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connecting to Redis instances by specifying a URL scheme.\n", + "Parameters are passed to the following schems, as parameters to the url scheme.\n", + "\n", + "Three URL schemes are supported:\n", + "\n", + "- `redis://` creates a TCP socket connection. \n", + "- `rediss://` creates a SSL wrapped TCP socket connection. \n", + "- ``unix://``: creates a Unix Domain Socket connection.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "metadata": {}, + "output_type": "display_data" } - } + ], + "source": [ + "import redis.asyncio as redis\n", + "url_connection = redis.from_url(\"redis://localhost:6379?decode_responses=True\")\n", + "url_connection.ping()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To enable the RESP 3 protocol, append `protocol=3` to the URL." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import redis.asyncio as redis\n", + "\n", + "url_connection = redis.from_url(\"redis://localhost:6379?decode_responses=Trueprotocol=3\")\n", + "url_connection.ping()" + ] } ], "metadata": { @@ -307,4 +381,4 @@ }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +} diff --git a/docs/examples/connection_examples.ipynb b/docs/examples/connection_examples.ipynb index d15d964af7..e6d147c920 100644 --- a/docs/examples/connection_examples.ipynb +++ b/docs/examples/connection_examples.ipynb @@ -41,7 +41,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### by default Redis return binary responses, to decode them use decode_responses=True" + "### By default Redis return binary responses, to decode them use decode_responses=True" ] }, { @@ -67,6 +67,25 @@ "decoded_connection.ping()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "### by default this library uses the RESP 2 protocol. To eanble RESP3, set protocol=3." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "import redis\n", + "\n", + "r = redis.Redis(protocol=3)\n", + "rcon.ping()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -99,14 +118,15 @@ }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Connecting to a redis instance with username and password credential provider" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "import redis\n", @@ -114,19 +134,19 @@ "creds_provider = redis.UsernamePasswordCredentialProvider(\"username\", \"password\")\n", "user_connection = redis.Redis(host=\"localhost\", port=6379, credential_provider=creds_provider)\n", "user_connection.ping()" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Connecting to a redis instance with standard credential provider" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from typing import Tuple\n", @@ -158,19 +178,19 @@ "user_connection = redis.Redis(host=\"localhost\", port=6379,\n", " credential_provider=creds_provider)\n", "user_connection.ping()" - ], - "metadata": {} + ] }, { "cell_type": "markdown", + "metadata": {}, "source": [ "## Connecting to a redis instance first with an initial credential set and then calling the credential provider" - ], - "metadata": {} + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from typing import Union\n", @@ -194,8 +214,7 @@ " return self.username, self.password\n", "\n", "cred_provider = InitCredsSetCredentialProvider(username=\"init_user\", password=\"init_pass\")" - ], - "metadata": {} + ] }, { "cell_type": "markdown", @@ -357,7 +376,23 @@ ], "source": [ "url_connection = redis.from_url(\"redis://localhost:6379?decode_responses=True&health_check_interval=2\")\n", - "\n", + "url_connection.ping()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connecting to Redis instances by specifying a URL scheme and the RESP3 protocol.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "url_connection = redis.from_url(\"redis://localhost:6379?decode_responses=True&health_check_interval=2&protocol=3\")\n", "url_connection.ping()" ] }, @@ -404,4 +439,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +}