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

Feature request: Add support for multi-variant AWS AppConfig feature flags #3754

Open
2 tasks done
HaaLeo opened this issue Mar 21, 2025 · 7 comments
Open
2 tasks done
Labels
discussing The issue needs to be discussed, elaborated, or refined feature-request This item refers to a feature request for an existing or new utility need-customer-feedback Requires more customers feedback before making or revisiting a decision parameters This item relates to the Parameters Utility

Comments

@HaaLeo
Copy link
Contributor

HaaLeo commented Mar 21, 2025

Use case

AWS AppConfig multi-variant feature flags enable users to implement use cases such as user segmentation or traffic splitting. Those use cases are described in more detail here.

Solution/User Experience

Taking the example found here one could introduce an optional context option of type Record<string,string|number|boolean>. Then one could fetch a variant like this:

import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';

const result = await getAppConfig('ConfigName', {
    environment: 'dev',
    application: 'MyApp',
    context: {
        email: 'jane_doe@example.org',
        opted_in_to_beta: false
    }
});

Alternative solutions

Acknowledgment

Future readers

Please react with 👍 and your use case to help us understand customer demand.

@HaaLeo HaaLeo added feature-request This item refers to a feature request for an existing or new utility triage This item has not been triaged by a maintainer, please wait labels Mar 21, 2025
@dreamorosi
Copy link
Contributor

Hi @HaaLeo, thank you for opening this issue.

Looking at the link you shared, and also at the docs, it looks like this feature is only available when interacting with AppConfig via the Lambda extension - if it's not the case, please let me know.

At the moment our Parameters utility is based on the AWS SDK AppConfigDataClient - which doesn't seem to support this type of retrieval.

We have had some initial conversations with the AppConfig team earlier this year, but we haven't made any concrete plans yet - although the option is very much on the table.

To help us with the decision, besides the functional use case that is covered in the issue & links, could you speak a bit more about how you think Powertools could improve or do different than the experience described in the docs (aka using the agent directly?).

I have some assumptions/ideas that I'd like to validate before sharing. Thank you!

@dreamorosi dreamorosi added parameters This item relates to the Parameters Utility need-customer-feedback Requires more customers feedback before making or revisiting a decision discussing The issue needs to be discussed, elaborated, or refined and removed triage This item has not been triaged by a maintainer, please wait labels Mar 21, 2025
@HaaLeo
Copy link
Contributor Author

HaaLeo commented Mar 21, 2025

Hi @dreamorosi,
thx for the swift reply. I wasn't aware that this is limited to the usage of the agent. I guess the major concern about using the agent directly is the impact on cold starts and the lambda's bundle size. However, we haven't evaluated the impact on that yet.

I think also the developer experience would benefit from adding that feature to powertools directly. I guess we are an example here: Instead of configuring/enabling the agent on the lambda function a developer can just pass an additional option to getAppConfig() call.

@dreamorosi
Copy link
Contributor

dreamorosi commented Mar 21, 2025

I see, thank you.

I wasn't aware either, but the second link you shared has a note that says this:

Image

I also checked the docs of the AWS CLI AppConfigData client and it doesn't seem to have the field.


We haven't done any benchmarks, so I can't speak to the performance overhead of adding that layer.

Regarding the developer experience and functionality, it'll still require you to add the layer to the function. Our space of operation is what happens within the Lambda function.

To be very honest with you, I haven't really played with this context feature of extension yet - so I don't know for sure, but my guess is that we can maybe streamline the HTTP request and calling this local API that is exposed by the extension, and perhaps do a Feature Flag utility similar to the one that Powertools for AWS Lambda (Python) has, but that's pretty much it.

Before moving forward I'd like us to test the extension further and see how it works and based on the results, if we think we have a compelling value add, consider doing something in this space.

In the meantime, I'd also encourage others who come across this issue to leave a 👍 to the original post above or leave a comment describing what you'd like to see us do in this area.

@leandrodamascena
Copy link
Contributor

leandrodamascena commented Mar 21, 2025

Hey @dreamorosi and @HaaLeo I'd like to add my 2 cents in this discussion!

We have an issue open in Python to add support for the AppConfig extension (the one that brings the agent in) to enable access to some features that are not available through the AppConfig/AppConfigData SDK features. It is also true that the extension adds some cold start penalties - this is kind normal for loading extensions - but it seems that this is more performant than SDK calls (read here), especially in Python functions where boto3 needs to load json and other things.

The experience for customers remains the same when using the parameter/feature flags utility, but under the hood we essentially encapsulate the HTTP call to call the extension and unlock some new features. To be honest I think this should be a valid improvement for this utility.

I guess the major concern about using the agent directly is the impact on cold starts and the lambda's bundle size.

Yeah, thats true that cold start can be higher than without extension, but in theory you don't need to worry with the Lambda's bundle size when deploying because you can use the public layer provided by AppConfig team. But of course this will count a few megabytes towards the total size of the Lambda package - which limit is 250MB.

perhaps do a Feature Flag utility similar to the one that Powertools for AWS Lambda (Python) has

A current limitation of the Python Feature Flags utility is that it only supports Freeform configuration, it does not support FeatureFlags configuration, we need to improve this. If you decide to do this in TS, keep this in mind.

I think if we get more positive feedback here we could consider adding support for the AppConfig extension.

Thanks

@dreamorosi
Copy link
Contributor

Yea, 100% agree and thanks for providing more info about the perf impact.

I looked at the contents of the extension and it's a single binary around ~9MB so all in all it shouldn't be too bad, but to do a more fair comparison I'd like us/someone to run tests that compare something like this:

import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';

const config = await getAppConfig({...}); // async outside of handler

export const handler = async () => {}

Versus doing the same thing with the extension + agent combination. This way we could observe the actual cold start impact and decide

@leandrodamascena
Copy link
Contributor

I have this performance test somewhere. Let me find it in my AWS account and I will run it again on Monday with the latest version of the extension. I will post the result here.

@leandrodamascena
Copy link
Contributor

leandrodamascena commented Mar 23, 2025

Hello, I come with some test results. I'm focusing this test on Python and because of that ColdStart might be a bit different on NodeJS due to the way things are built + AWS SDK, but I understand that the idea of ​​this issue is to check this in a broader view, so I'm running these tests using Python. The code will be provided in the final step, so you can reproduce it on your own if you want.
I'm creating the AppConfigProvider Constructor for Powertools and requests.Session for extension outside the handler, but I do the fetch configuration inside the handle, since a configuration can be retrieved N times using the same container.

Fetching configuration

Both Powertools and the AppConfig extension support built-in caching when fetching the configuration profile from AppConfig. In Powertools, customers can disable this caching - even though it is not common - while when using the extension, it must cache for at least 1 second. Another difference is that Powertools stores the cache directly in Lambda's memory (it's valid for Python and TS), avoiding any HTTP call, while the extensions store it in the extension (using memory somehow) but require the Lambda code to make an HTTP call to the extension. This make some difference in scenarios where Lambda is reusing the same container for process new requests.

In this test, I'm using a 1s cache for both, so we have the closest scenario for testing. While I'm confident with this test, but I know It can affect a little bit the tests because the extension refreshes this cache async, while powertools does sync.

Cache with Powertools

Image

Cache with Extension

Image

Loading
graph TD
    A[AppConfig Configuration] --> B{Retrieval Method}
    
    B --> |Powertools| C[Cache stored in Lambda memory]
    C --> G[Check Lambda Memory Cache]
    G --> H[Return Configuration Instantly]
    
    B --> |AppConfig Extension| D[Cache stored in Extension memory]
    D --> I[Lambda sends HTTP request to Extension]
    I --> J[Extension checks its memory cache]
    J --> K[Extension returns cached configuration]
    
    style G fill:#90EE90
    style H fill:#90EE90
    style I fill:#ADD8E6
    style J fill:#ADD8E6
    style K fill:#ADD8E6

ColdStart duration

The AppConfig team has improved the extension + agent, and while ColdStart is still a thing, but based on testing there isn't much difference when using the extension or bringing boto3 to a Lambda with 128MB of config. Of course this might change a bit with more memory, but I think it makes sense to use the baseline config for testing.
Just sampling some data, we can observe that ColdStart with extension is around 1.2s while with boto3 (powertools) is around 1.1s

CW Insights Query + Init duration

first column = log group - the log group using extension contains extension in its name and using powertools contains powertools in its name.
second column = initDuration when ColdStart happens
third column = Execution duration when ColdStart happens

fields @log, @initDuration, @billedDuration
| filter @type = "REPORT" and @initDuration > 0
| sort @initDuration desc
| limit 10000
@log,@initDuration,@billedDuration
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1333.98,98
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1288.63,476
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1243.31,108
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1215.59,454
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1209.17,92
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1191.73,495
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1186.04,489
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1184.87,123
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1182.77,474
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1182.34,97
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1173.19,497
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1169.91,178
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1169.59,464
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1161.41,169
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1137.46,104
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1135.02,493
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1134.49,492
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1133.02,493
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1127.53,134
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1125.72,493
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1124.22,88
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1121.46,439
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1121.19,128
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1119.61,490
533568316194:/aws/lambda/appconfig-with-extension-test-HelloWorldFunction-m9emB1J4zccb,1119.19,103
533568316194:/aws/lambda/appconfig-with-powertools-test-HelloWorldFunction-59UIHxzrpvAv,1118,498

Fetching with ColdStart

Base on the previous data, when a ColdStart occurs, we can observe that fetching the configuration with the extension is at least 4x faster than fetching it with boto3. That's why while the extension loads the agent and establishes a connection, Powertools (using boto3) needs to make two calls with boto3: StartConfigurationSession and GetLatestConfiguration.

ColdStart - fetch with Powertools

Image

ColdStart - fetch with Extension

Image

Additional features

Using the extension, customers can use some advanced features that are not supported by boto3, so we cannot add this support in Powertools. Features such as: multivariant, prefetch list, multiple account recovery using manifest, others.
Here we have to consider that we have different programming models, I am not ranking which is better or worse, just considering points that should be considered.

Conclusion

I believe we can think about adding a new provider - I don't know the name yet - that uses the AppConfig extension as a backend and we use the same experience as Powertools, which greatly simplifies the customer's development environment.

Please let me know if you have any additional questions.


Code

Powertools + boto3
import time

from aws_lambda_powertools.utilities import parameters
from aws_lambda_powertools import Metrics, Tracer
from aws_lambda_powertools.metrics import MetricUnit, MetricResolution
from aws_lambda_powertools.utilities.typing import LambdaContext

metrics = Metrics(namespace="AppConfigLoadTest")
tracer = Tracer()

app_name = "freeform"
env = "dev"
app_profile = "profile-free"

appconf_provider = parameters.AppConfigProvider(environment=env, application=app_name)

@tracer.capture_lambda_handler
@metrics.log_metrics(capture_cold_start_metric=True)
def lambda_handler(event, context: LambdaContext):
    start_time = int(time.time() * 1000)

    config = appconf_provider.get(app_profile, max_age=1)
    print(config)
    # End time measurement
    end_time = int(time.time() * 1000)

    # Calculate and print execution time
    execution_time = end_time - start_time

    metrics.add_metric(name="GETWITHPOWERTOOLS", unit=MetricUnit.Milliseconds, value=execution_time, resolution=MetricResolution.High)
    return {
        "statusCode": 200,
        "body": "OK"
    }
Extension
import time
import requests

from aws_lambda_powertools import Metrics, Tracer
from aws_lambda_powertools.metrics import MetricUnit, MetricResolution
from aws_lambda_powertools.utilities.typing import LambdaContext

metrics = Metrics(namespace="AppConfigLoadTest")
tracer = Tracer()

# Create a global session object
session = requests.Session()

app_name = "freeform"
env = "dev"
app_profile = "profile-free"

@tracer.capture_lambda_handler
@metrics.log_metrics(capture_cold_start_metric=True)
def lambda_handler(event, context: LambdaContext):
    start_time = int(time.time() * 1000)

    url = f"http://localhost:2772/applications/{app_name}/environments/{env}/configurations/{app_profile}"
    
    response = session.get(url)
    config = response.content
    print(config)
    # End time measurement
    end_time = int(time.time() * 1000)

    # Calculate and print execution time
    execution_time = end_time - start_time

    metrics.add_metric(name="GETWITHEXTENSION", unit=MetricUnit.Milliseconds, value=execution_time, resolution=MetricResolution.High)
    return {
        "statusCode": 200,
        "body": "OK"
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussing The issue needs to be discussed, elaborated, or refined feature-request This item refers to a feature request for an existing or new utility need-customer-feedback Requires more customers feedback before making or revisiting a decision parameters This item relates to the Parameters Utility
Projects
Development

No branches or pull requests

3 participants