Skip to content
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

chore(v2): Split powertools-idempotency module to sub-modules and add redis implementation #1513

Closed
wants to merge 33 commits into from

Conversation

eldimi
Copy link
Contributor

@eldimi eldimi commented Nov 13, 2023

Issue #1467

This started with PR #1504. The branch was moved from the forked repository eldimi:chore/v2-split-idempotency-mod to the aws-powertools:chore/v2-split-idempotency-mod.
Reference the previous PR #1504, for older discussions.
PR #1504 will be closed without merge now.

Description of changes:

The idempotency module is split to sub-modules so that the various implementations (currently only dynamodb), are provided as pluggable extensions

Also, a Redis implementation is provided. The Redis deployment can be either standalone or cluster and the authentication supported is username/password. Certificate authentication is not included.
The e2e tests are implemented using an ElastiCache standalone deployment. This means also VPC, segurity groups and subnets are created to support it.

We can also use https://upstash.com/ which will basically mean, set-up an account and a server in upstash.com which should be maintained. This makes test execution more quick, but has the drawback of mainting an external service - and not maintaining a test case that consistency testing on Amazon ElastiCache.
One thought could be to have both tests and enable/disbale them with a flag according to needs.

Amazon ElastiCache Serveless doesn't seem to be supported with the current implementation, most probably because it gets created by default with encryption in transit and there is no option to modify it.

Checklist

Breaking change checklist

RFC issue #:

  • Migration process documented
  • Implement warnings (if it can live side by side)

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Copy link

github-actions bot commented Nov 13, 2023

💾 Artifacts Size Report

Module Version Size (KB)
powertools-common 2.0.0-SNAPSHOT 9.59
powertools-serialization 2.0.0-SNAPSHOT 17.69
powertools-logging 2.0.0-SNAPSHOT 20.74
powertools-logging-log4j 2.0.0-SNAPSHOT 21.78
powertools-logging-logback 2.0.0-SNAPSHOT 20.19
powertools-tracing 2.0.0-SNAPSHOT 14.00
powertools-metrics 2.0.0-SNAPSHOT 14.05
powertools-parameters 2.0.0-SNAPSHOT 17.47
powertools-validation 2.0.0-SNAPSHOT 20.79
powertools-cloudformation 2.0.0-SNAPSHOT 16.99
powertools-idempotency-core 2.0.0-SNAPSHOT 35.59
powertools-idempotency-dynamodb 2.0.0-SNAPSHOT 12.91
powertools-idempotency-redis 2.0.0-SNAPSHOT 14.42
powertools-large-messages 2.0.0-SNAPSHOT 17.47
powertools-batch 2.0.0-SNAPSHOT 16.89
powertools-parameters-ssm 2.0.0-SNAPSHOT 10.64
powertools-parameters-secrets 2.0.0-SNAPSHOT 9.86
powertools-parameters-dynamodb 2.0.0-SNAPSHOT 11.90
powertools-parameters-appconfig 2.0.0-SNAPSHOT 11.93

@eldimi
Copy link
Contributor Author

eldimi commented Nov 13, 2023

This started with PR #1504. The branch was moved from the forked repository eldimi:chore/v2-split-idempotency-mod to the aws-powertools:chore/v2-split-idempotency-mod.
Reference the previous PR #1504, for older discussions.
PR #1504 will be closed without merge now.

@codecov-commenter
Copy link

codecov-commenter commented Nov 13, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

❗ No coverage uploaded for pull request base (v2@fb14bcf). Click here to learn what that means.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@          Coverage Diff          @@
##             v2    #1513   +/-   ##
=====================================
  Coverage      ?   89.79%           
  Complexity    ?      406           
=====================================
  Files         ?       44           
  Lines         ?     1274           
  Branches      ?      165           
=====================================
  Hits          ?     1144           
  Misses        ?       88           
  Partials      ?       42           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jeromevdl jeromevdl changed the title chore: Split powertools-idempotency module to sub-modules chore(v2): Split powertools-idempotency module to sub-modules and add redis implementation Nov 15, 2023
import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache;

import java.time.Duration;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you don't have checkstyle applied.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be fixed now. I think it is because we only format uncommitted and if I forget to format before commit, I am missing some files..

@eldimi eldimi marked this pull request as ready for review December 8, 2023 08:54
@eldimi
Copy link
Contributor Author

eldimi commented Dec 27, 2023

Verified that it is also compatible with Amazon MemoryDB for Redis and ElastiCache Serverless. Also, used ElastiCache Serverless in the e2e test.


!!! warning "Warning: Large responses with Redis persistence layer"
When using this utility with Redis your function's responses must be smaller than 512MB.
Persisting larger items cannot might cause exceptions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

problem in this sentence...

```java hl_lines="2-11 13 18"
public App() {
JedisConfig jedisConfig = JedisConfig.Builder.builder()
.withHost(redisCluster.getHost())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does redisCluster come from? Would be great to have the full detail here as it's not super easy...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c&p from code. I will replace that

.build())
.build();

JedisPooled jedisPooled = new JedisPooled(new HostAndPort("host",6789), jedisConfig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to define the host / port twice ? (line 973 and here)

}
```

!!! info "Default configuration is the following:"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "default configuration", it is what we use as default ? If that's the case, does it make sense? Not sure anyone is using the user "default" with no password...

I think we should ask for all the required fields to create a JedisClient ourself:

  • host (mandatory)
  • port (default: 6379)
  • user (mandatory)
  • password (mandatory)
  • ssl (default: false)
  • database ?
  • something else ?

@scottgerring wdyt ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default here means, as provided by the jedis client default config.
In Redis before version 6, you could only require password for auth .
Also, by default all access is unathenticated and a user with name "default" is created. So, by default (that is the redis default), you can execute commands either without any user/pass or with user:"default" and an empty pass. The user "default" is created even after redis version 6, for backwards compatibility purposes.
See ACL redis doc.
For these reasons, I am not sure we should make them mandatory.

Actually I have to update the powertools documentation, since user/pass, by default, in JedisClient are null.

Copy link
Contributor Author

@eldimi eldimi Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found also this extract here from redis.conf

The special username "default" is used for new connections. If this user
has the "nopass" rule, then new connections will be immediately authenticated
as the "default" user without the need of any password provided via the
AUTH command. Otherwise if the "default" user is not flagged with "nopass"
the connections will start in not authenticated state, and will require
AUTH (or the HELLO command AUTH option) in order to be authenticated and
start to work.

Function function = Function.Builder
.create(stack, functionName)

final SecurityGroup lambdaSecurityGroup = SecurityGroup.Builder.create(this.stack, "Lambda-SG")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done only if (isRedisDeployment)

List<String> subnets = vpc.getPublicSubnets().stream().map(subnet ->
subnet.getSubnetId()).collect(Collectors.toList());

securityGroup = SecurityGroup.Builder.create(stack, "ElastiCache-SG-" + stackName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
securityGroup = SecurityGroup.Builder.create(stack, "ElastiCache-SG-" + stackName)
redisSecurityGroup = SecurityGroup.Builder.create(stack, "ElastiCache-SG-" + stackName)

private String ddbStreamsTableName;
private String functionName;
private Object cfnTemplate;
private String cfnAssetDirectory;
private SubnetSelection subnetSelection;
private CfnSubnetGroup cfnSubnetGroup;
private SecurityGroup securityGroup;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private SecurityGroup securityGroup;
private SecurityGroup redisSecurityGroup;

.allowAllOutbound(true)
.description("Lambda SecurityGroup")
.build();
securityGroup.addIngressRule(Peer.securityGroupId(lambdaSecurityGroup.getSecurityGroupId()), Port.tcp(6379),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
securityGroup.addIngressRule(Peer.securityGroupId(lambdaSecurityGroup.getSecurityGroupId()), Port.tcp(6379),
redisSecurityGroup.addIngressRule(Peer.securityGroupId(lambdaSecurityGroup.getSecurityGroupId()), Port.tcp(6379),

throw new IllegalStateException("Utility class");
}

public static final String REDIS_CLUSTER_MODE = "REDIS_CLUSTER_MODE";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move this to the RedisPersistenceStore config ?
to have everything at the same place:

RedisPersistenceStore.builder() 
  .withHost(System.getenv("REDIS_HOST")) 
  .withPort(System.getenv("REDIS_PORT")) // optional, default 6379 
  .withUsername(secretsProvider.getValue("REDIS_USERNAME")) 
  .withPassword(secretsProvider.getValue("REDIS_PASSWORD")) 
  .withSsl(true) // optional, default false ? or should it be true as default 
  .withClusterMode(true) // optional, default false
  .build();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this lua code for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for conditional updates. See the usage here

And the relevant discussion here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned above, I think we should hide this, but maybe I miss something... @scottgerring

Copy link
Contributor

@jeromevdl jeromevdl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost good, just one thing about the developer experience for the configuration...

@jeromevdl jeromevdl dismissed their stale review January 2, 2024 10:31

review done

Copy link

sonarcloud bot commented Jan 22, 2024

Quality Gate Failed Quality Gate failed

Failed conditions

1 Security Hotspot

See analysis details on SonarCloud

@scottgerring
Copy link
Contributor

Closing for now in favor of #1559. We can pick up the redis changes here in a later branch, but let's get the base module split into v2!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants