-
Notifications
You must be signed in to change notification settings - Fork 421
Add support for MemoryDB/ElasticCache/Redis as Idempotency backend #1181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm putting myself available to tackle this issue. |
That would be awesome! I think vanilla Redis makes more sense than MemoryDB
as it’s still early to know how many customers would want it.
FYI - I’m back on Sep 27th and will review the PR by then.
Muito obrigado Guilherme ;-)
…On Wed, 1 Sep 2021 at 22:15, Guilherme Martins Crocetti < ***@***.***> wrote:
I'm putting myself available to tackle this issue.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<https://github.com/awslabs/aws-lambda-powertools-python/issues/629#issuecomment-910989625>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAZPQBE4ZZNMF4I3EIW7ZYDT73FZHANCNFSM5CRLKANQ>
.
|
Isn't it meant to be the same client implementation for both? |
Yep, though I haven’t had a chance to look into it yet for any caveats.
…On Sat, 4 Sep 2021 at 07:46, Daniel Loader ***@***.***> wrote:
Isn't it meant to be the same client implementation for both?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<https://github.com/awslabs/aws-lambda-powertools-python/issues/629#issuecomment-912952211>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAZPQBDWNMV65RQC5BQXWDDUAH2GRANCNFSM5CRLKANQ>
.
|
Just read the docs, you’re right Daniel, it’s fully compatible — we could
use redis-py-cluster as an optional dependency. Depending on the
implementation, it’d be easy to also provide Redis support for Parameters
and Feature Flag.
https://github.com/Grokzen/redis-py-cluster
…On Sat, 4 Sep 2021 at 07:51, Heitor Lessa ***@***.***> wrote:
Yep, though I haven’t had a chance to look into it yet for any caveats.
On Sat, 4 Sep 2021 at 07:46, Daniel Loader ***@***.***>
wrote:
> Isn't it meant to be the same client implementation for both?
>
> —
> You are receiving this because you commented.
>
>
> Reply to this email directly, view it on GitHub
> <https://github.com/awslabs/aws-lambda-powertools-python/issues/629#issuecomment-912952211>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAZPQBDWNMV65RQC5BQXWDDUAH2GRANCNFSM5CRLKANQ>
> .
>
|
Redis in general would be fairly rad to have available via an extra requirement. I wasn't entirely sure if a broad implementation in aws-lambda-powertools for redis support would ultimately support both ElasticCache and MemoryDB. |
@heitorlessa . Things have changed, won't have bandwidth to tackle this issue :/. I hope someone else will volunteer. Enjoy your time ! Obrigado 👍 |
Hi all! I was looking at some issues and opportunities to start contributing to Lambda PowerTools and came across this issue. This implementation looks very interesting and adding a new backend to persist Idempotency data seems like a great idea. Even DynamoDB is a great lightweight option, not everyone wants to create a new table and/or manage a new resource on AWS. I'll try to summarize some information before I start writing code and tests. 1 - As everyone mentioned, MemoryDB is fully compatible with the Redis protocol and we can confirm that here https://docs.aws.amazon.com/memorydb/latest/devguide/memorydb-guide.pdf.pdf (page 1) 2 - @heitorlessa I'm not sure if the https://github.com/Grokzen/redis-py-cluster library is a good choice at this point. It might be a good choice when this thread starts, but now they suggest migrating to the official RedisLabs library (https://github.com/redis/redis-py) 3 - The official library supports connection and operations on a single node or cluster. https://github.com/redis/redis-py#cluster-mode 4 - Even the Idempotency utility will write and read few keys in Redis, a performance test is indicated to see if it can impact somewhere. I'll take care of that and share the results as I write the code. 5 - I create a new class to test how we can implement this feature and it is already saving and getting keys from Redis. Of course it's missing a lot of things like tests, parameters, code optimization, comments and other things, but I think it can be a start and I can start working on this. 6 - I will try to cover most scenarios in the first version. I know this project is frozen until next month I think. So there will be plenty of time to write code and test. Thanks for reading and suggestions are welcome. |
Quick update: @Vandita2020 is working on this |
Hey all! To give visibility to everyone of the work that @Vandita2020 and I are doing so that everyone can follow and give feedback.
We're making progress and hope to have this code ready for merge soon. |
I've been writing code to connect to Redis Sentinel and it changes parameters and options a lot, so I thought of a new UX to make everything simpler. The way it is programmed now is: from aws_lambda_powertools.utilities.idempotency import (
idempotent,
RedisCachePersistenceLayer,
IdempotencyConfig
)
persistence_layer = RedisCachePersistenceLayer(host="192.168.68.112", port="6379", user="xxx", password="xxxx", db_index=0, static_pk_value="test",...) The way this would be more readable to create a connection would be: Standalone from aws_lambda_powertools.utilities.idempotency.redis.connection import RedisStandalone
from aws_lambda_powertools.utilities.idempotency import RedisCachePersistenceLayer
conn_config = RedisStandalone(host.. pass.. user)
persistence_layer = RedisCachePersistenceLayer(connection=conn_config, ...) Cluster from aws_lambda_powertools.utilities.idempotency.redis.connection import RedisCluster
from aws_lambda_powertools.utilities.idempotency import RedisCachePersistenceLayer
conn_config = RedisCluster(host... startup_nodes)
persistence_layer = RedisCachePersistenceLayer(connection=conn_config, ...) Sentinel from aws_lambda_powertools.utilities.idempotency.redis.connection import RedisSentinel
from aws_lambda_powertools.utilities.idempotency import RedisCachePersistenceLayer
conn_config = RedisSentinel(sentinels..socket…)
persistence_layer = RedisCachePersistenceLayer(connection=conn_config, ....) Makes sense? I would like to hear feedback on this. |
We're reviewing the last edge case on concurrency and docs tomorrow - hoping to get this released next week |
|
This is now released under 2.32.0 version! |
To enable other runtimes and customers to benefit from the research and implementation carried out in this pull request, I have provided the Redis implementation text below. Many thanks to @roger-zhangg for the partnership and deep dive into this extensive and fantastic work, which undoubtedly raised the bar for this project! API DesignWe kept the same user experience when switching from DynamoDB to Redis. This is very important because one of the core principles of Powertools is to ensure the developer experience is as seamless as possible. By keeping the persistence abstraction layer interchangeable between DynamoDB and Redis, we enabled a smooth transition that required minimal code changes for developers. The interfaces remained consistent, reducing friction when migrating the data storage technology. ConnectionIn the initial version, we provided a dedicated Connection Class to assist our customers in creating Redis connections. This class wrapped the Redis client, both standalone and cluster, enabling customers to provide their Redis connection details (host, port, passwords) for establishing connections. Once the connection was established, this class could be passed to the Idempotency Layer for use. However, this design had a few significant drawbacks. Firstly, it was challenging for this design to support Redis sentinel connections, as sentinel connections are set up differently from standalone and cluster connections. Secondly, the default Redis client had more than 40 parameters. In our connection design, we chose to support only the most commonly used ones, passing all other parameters using Keeping these considerations in mind, we drafted the second version of the Redis Connection. We made a few changes to the logic in the Idempotency Layer class so that it can now accept an established Redis client. With this design, customers can pass in any Redis client they prefer, as long as it adheres to the schema defined in the protocol class. Additionally, using the original Redis clients allows customers to leverage their prior experience with Redis and easily transfer and adapt their existing code. However, after some discussions, we concluded that this design may not be user-friendly for individuals without prior Redis experience. Therefore, we believe it's still beneficial to provide a helper class for creating connections to assist such users. In the third and final design, we have opted to implement a helper connection class that assists customers in creating Redis connections with only the most commonly used Redis parameters (host, port, username, password, db_index, url, ssl). Simultaneously, we enable our customers to bring their own Redis connection if they prefer. One common use case, for example, is when customers want to use Redis with their certificates. This added flexibility allows them to establish secure Redis connections using their custom certificates while benefiting from the simplified connection setup provided by our helper class. Examplefrom redis import Redis
from aws_lambda_powertools.utilities.idempotency.persistence.redis import (
RedisCachePersistenceLayer,
)
client = Redis(
host="host",
port=6379,
ssl=True,
ssl_certfile=ssl_certfile,
ssl_keyfile=ssl_keyfile,
ssl_cert_reqs="required",
ssl_ca_certs=ssl_ca_certs,
)
persistence_layer = RedisCachePersistenceLayer(client=client) Orphan RecordsEach idempotency record includes attributes such as However, due to factors like Lambda handler timeouts, exceptions, or potential Redis expiration issues, there may be instances where idempotency records persist in Redis even after the current time has exceeded It's important to note that the method we implement to address these orphan records must be executed with caution to avoid potential race conditions. Further details on this issue will be elaborated upon in the following two paragraphs. Redis HSET vs SETIn the idempotency workflow, we need to store idempotency records with multiple attributes in Redis. This can be achieved by using In the initial design, we employed However, there are two major drawbacks using this method.
During our experiments, we used
Due to Redis Race conditionThere are two potential race conditions in the Redis Idempotency workflow. Although the probability of these race conditions occurring is low, if not addressed effectively, certain payloads may bypass the idempotency layer. This could lead to the underlying Lambda handler executing more than once for identical payloads, which is an undesirable scenario for our customers when utilizing our idempotency layer. The first potential race condition occurs when two Lambda handlers simultaneously perform This race condition can be resolved by adding The second potential race condition is similar to the first one but more complicated. This scenario occurs when two lambda handers are both trying to fix the same orphan record they found at the same time. In our idempotency workflow, if a Lambda handler encounters an existing idempotency record and identifies it as a corrupted or orphaned record, it proceeds to overwrite it with a valid record. However, in this case, we are attempting to overwrite a record in Redis, so we cannot use the without lockwith lockTestsWe had challenges while writing tests to test the Redis interface and functionality, primarily because testing the Protocol without establishing a real connection is a little bit hard. Our initial approach was create an integration testing by running Redis locally in Docker containers, but spinning up container environments locally has some downsides, such as:
Our solution was to shift towards more functional testing by injecting a fake "Redis client" class instead of using a real Redis connection. This approach offers the following advantages:
Thanks |
Updated topic to mention ElasticCache and general Redis as well.
https://aws.amazon.com/about-aws/whats-new/2021/08/amazon-memorydb-redis/
The text was updated successfully, but these errors were encountered: