Package: lambda-cache
Lambda CacheSimple Caching for AWS LambdaIntroductionlambda-cachehelps you cache data in your Lambda functionacrossinvocations. The package utilizes the internal memory of the lambda function'sexecution contextto store data across multiple invocations. Doing this:Reduces load on back-end systemsReduces the execution time of the lambdaGuarantees functions will reference latest data after cache expiryReduces calls to APIs throttling limits or high chargeslambda-cacheprioritizes simplicity over performance and flexibility.The package is purpose-built for AWS Lambda functions, and currently supports SSM Parameters, Secrets from Secrets Manager and S3 Objects.InstallationInclude the package in your functions code zip-file using the following:$ pip install lambda-cache -t /path/of/functionRefer todocsfor how to include into your lambda function.UsageBelow we describe further features of the package. For more info refer to theuser guide.SSM - Parameter StoreCache single parameterChange cache expiryChange cache entry settingsCache multiple parametersDecorator stackingCache invalidationReturn ValuesSecrets ManagerCache single secretChange Cache expiryChange Cache entry settingsDecorator stackingCache InvalidationReturn ValuesS3Cache a single fileChange Cache expiryCheck file before downloadSSM - Parameter StoreCache single parameterTo cache a parameter from ssm, decorate your handler function like so:fromlambda_cacheimportssm@ssm.cache(parameter='/production/app/var')defhandler(event,context):var=getattr(context,'var')response=do_something(var)returnresponseAll invocations of this function over in the next minute will reference the parameter from the function's internal cache, rather than making a network call to ssm. After one minute has lapsed, the the next invocation will invokeget_parameterto refresh the cache. The parameter value will be injected into thecontextobject of your lambda handler.Change cache expiryThe defaultmax_age_in_secondssettings is 60 seconds (1 minute), it defines the maximum age of a parameter that is acceptable to the handler function. Cache entries older than this, will be refreshed. To set a longer cache duration (e.g 5 minutes), change the setting like so:fromlambda_cacheimportssm@ssm.cache(parameter='/production/app/var',max_age_in_seconds=300)defhandler(event,context):var=getattr(context,'var')response=do_something(var)returnresponseNote: The caching logic runs only at invocation, regardless of how long the function runs. A 15 minute lambda function will not refresh the parameter, unless explicitly refreshed usingget_entrymethod. The library is primary interested in caching 'across' invocation rather than 'within' an invocationChange cache entry settingsThe default name of the parameter is the string after the last slash('/') character of its name. This means/production/app/varandtest/app/varboth resolve to justvar. To over-ride this default, useentry_namesetting like so:fromlambda_cacheimportssm@ssm.cache(parameter='/production/app/var',entry_name='new_var')defhandler(event,context):var=getattr(context,'new_var')response=do_something(var)returnresponseCache multiple parametersTo cache multiple entries at once, pass a list of parameters to the parameter argument. This method groups all the parameter value under one python dictionary, stored in the Lambda Context under theentry_name.Note: When using this method,entry_nameis a required parameter, if not presentNoEntryNameErrorexception is thrown.fromlambda_cacheimportssm@ssm.cache(parameter=['/app/var1','/app/var2'],entry_name='parameters')defhandler(event,context):var1=getattr(context,'parameters').get('var1')var2=getattr(context,'parameters').get('var2')response=do_something(var)returnresponseUnder the hood, we use theget_parametersAPI call for boto3, which translate to a single network call for multiple parameters. You can group all parameters types in a single call, includingString,StringListandSecureString.StringListwill return as a list, while all other types will return as plain-text strings. The library does not support returningSecureStringparameters in encrypted form, and will only return plain-text strings regardless of String type.Note: for this method to work, ensure you have bothssm:GetParameterandssm:GetParameters(with the 's' at the end) in your function's permission policyDecorator stackingIf you wish to cache multiple parameters with different expiry times, stack the decorators. In this example,var1will be refreshed every 30 seconds,var2will be refreshed after 60.@ssm.cache(parameter='/production/app/var1',max_age_in_seconds=30)@ssm.cache(parameter='/production/app/var2',max_age_in_seconds=60)defhandler(event,context):var1=getattr(context,'var1')var2=getattr(context,'var2')response=do_something(var)returnresponseNote: Decorator stacking performs one API call per decorator, which might result is slower performanceCache invalidationIf you require a fresh value at some point of the code, you can force a refresh using thessm.get_entryfunction, and setting themax_age_in_secondsargument to 0.fromlambda_cacheimportssm@ssm.cache(parameter='/prod/var')defhandler(event,context):ifevent.get('refresh'):# refresh parametervar=ssm.get_entry(parameter='/prod/var',max_age_in_seconds=0)else:var=getattr(context,'var')response=do_something(var)returnresponseYou may also usessm.get_entryto get a parameter entry from anywhere in your functions code.To only get parameter once in the lifetime of the function, setmax_age_in_secondsto some arbitary large number ~36000 (10 hours).Return ValuesCaching supportsString,SecureStringandStringListparameters with no change required (ensure you havekms:Decryptpermission forSecureString). For simplicity,StringListparameters are automatically converted into list (delimited by comma), whileStringandSecureStringboth return the single string value of the parameter.Secrets ManagerCache single secretSecret support is similar, but uses thesecret.cachedecorator.fromlambda_cacheimportsecret@secret.cache(name='/prod/db/conn_string')defhandler(event,context):conn_string=getattr(context,'conn_string')returncontextChange Cache expiryThe defaultmax_age_in_secondssettings is 60 seconds (1 minute), it defines how long a parameter should be kept in cache before it is refreshed from ssm. To configure longer or shorter times, modify this argument like so:fromlambda_cacheimportsecret@secret.cache(name='/prod/db/conn_string',max_age_in_seconds=300)defhandler(event,context):var=getattr(context,'conn_string')response=do_something(var)returnresponseNote: The caching logic runs only at invocation, regardless of how long the function runs. A 15 minute lambda function will not refresh the parameter, unless explicitly refreshed using get_cache_ssm. The library is primary interested in caching 'across' invocation rather than 'within' an invocationChange Cache entry settingsThe name of the secret is simply shortened to the string after the last slash('/') character of the secret's name. This means/prod/db/conn_stringand/test/db/conn_stringresolve to justconn_string. To over-ride this default, useentry_name:fromlambda_cacheimportsecret@secret.cache(name='/prod/db/conn_string',entry_name='new_var')defhandler(event,context):var=getattr(context,'new_var')response=do_something(var)returnresponseDecorator stackingIf you wish to cache multiple secrets, you can use decorator stacking.@secret.cache(name='/prod/db/conn_string',max_age_in_seconds=30)@secret.cache(name='/prod/app/elk_username_password',max_age_in_seconds=60)defhandler(event,context):var1=getattr(context,'conn_string')var2=getattr(context,'elk_username_password')response=do_something(var)returnresponseNote: Decorator stacking performs one API call per decorator, which might result is slower performance.Cache InvalidationTo invalidate a secret, use theget_entry, setting themax_age_in_seconds=0.fromlambda_cacheimportsecret@secret.cache(name='/prod/db/conn_string')defhandler(event,context):ifevent.get('refresh'):var=secret.get_entry(name='/prod/db/conn_string',max_age_in_seconds=0)else:var=getattr(context,'conn_string')response=do_something(var)returnresponseReturn ValuesSecrets Manager supports both string and binary secrets. For simplicity we will cache the secret in the format it is stored. It is up to the calling application to process the return as Binary or Strings.S3S3 support is consideredexperimentalfor now, but withing the python community we see a lot of folks pull down files from S3 for use in AI/ML models.Files downloaded from s3 are automatically stored in the/tmpdirectory of the lambda function. This is the only writable directory within lambda, and has a 512MB of storage space.Cache a single fileTo download a file from S3 use the the same decorator pattern:fromlambda_cacheimports3@s3.cache(s3Uri='s3://bucket_name/path/to/object.json')defs3_download_entry_name(event,context):# Object from S3 automatically saved to /tmp directorywithopen("/tmp/object.json")asfile_data:status=json.loads(file_data.read())['status']returnstatusChange Cache expiryThe defaultmax_age_in_secondssettings is 60 seconds (1 minute), it defines how long a file should be kept in/tmpbefore it is refreshed from S3. To configure longer or shorter times, modify this argument like so:fromlambda_cacheimports3@s3.cache(s3Uri='s3://bucket_name/path/to/object.json',max_age_in_seconds=300)defs3_download_entry_name(event,context):withopen("/tmp/object.json")asfile_data:status=json.loads(file_data.read())['status']returnstatusNote: The caching logic runs only at invocation, regardless of how long the function runs. A 15 minute lambda function will not refresh the object, unless explicitly refreshed usings3.get_entry. The library is primary interested in caching 'across' invocation rather than 'within' an invocationCheck file before downloadBy default,lambda_cachewill download the file once at cache has expired, however, to save on network bandwidth (and possibly time), we can set thecheck_before_downloadparameter to True. This will check the age of the object in S3 and downloadonlyif the object has changed since the last download.fromlambda_cacheimports3@s3.cache(s3Uri='s3://bucket_name/path/to/object.json',max_age_in_seconds=300,check_before_download=True)defs3_download_entry_name(event,context):withopen("/tmp/object.json")asfile_data:status=json.loads(file_data.read())['status']returnstatusNote: we use the GetHead object call to verify the objectslast_modified_date. This simplifies the IAM policy of the function, as it still only requires thes3:GetObjectpermission. However, this is still a GET requests, and will be charged as such, for smaller objects it might be cheaper to just download the objectCreditProject inspired by:SSM-Cachemiddy