import%20marimo%0A%0A__generated_with%20%3D%20%220.18.1%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Lambda%20Layer%20Merging%0A%0A%20%20%20%20A%20Lambda%20layer%20is%20a%20.zip%20file%20archive%20that%20contains%20supplementary%20code%20or%20data%2C%0A%20%20%20%20e.g.%20dependencies%2C%20a%20custom%20runtime%20or%20configuration%20files.%0A%0A%20%20%20%20If%20you%20associate%20multiple%20layers%20with%20a%20function%2C%20how%20are%20they%20merged%3F%0A%20%20%20%20For%20example%2C%20what%20happens%20if%3A%0A%20%20%20%20-%20layer1%20contains%20%60cool-pkg%201.0%60%20and%20layer2%20contains%20%60cool-pkg%202.0%60%3F%0A%20%20%20%20-%20layer1%20contains%20%60conf.txt%60%20with%20content%20%22foo%22%20and%20layer2%20contains%20%60conf.txt%60%20with%20content%20%22bar%22%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Stack%0A%0A%20%20%20%20Two%20layers%20associated%20to%20a%20lambda.%0A%20%20%20%20One%20layer%20contains%20%60requests%202.30%60.%0A%20%20%20%20The%20other%20contains%20%60requests%202.31%60.%0A%0A%20%20%20%20I%20got%20%60requests%60%20locally%20via%20%60uv%20pip%20install%20requests%20-t%20lib%2Fresources%2Flayers%2Frequests-%3Cmajor%3E-%3Cminor%3E%2Fpython%60.%0A%0A%20%20%20%20The%20lambda%20handler%20returns%20import-related%20stuff%2C%20to%20reveal%20how%20layers%20are%20merged.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Results%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20boto3%0A%0A%20%20%20%20lambda_%20%3D%20boto3.client(%22lambda%22)%0A%20%20%20%20return%20boto3%2C%20lambda_%0A%0A%0A%40app.cell%0Adef%20_(lambda_)%3A%0A%20%20%20%20invoke_response%20%3D%20lambda_.invoke(FunctionName%3D%22layer_merging%22)%0A%20%20%20%20return%20(invoke_response%2C)%0A%0A%0A%40app.cell%0Adef%20_(invoke_response)%3A%0A%20%20%20%20import%20json%0A%0A%20%20%20%20json.loads(invoke_response%5B%22Payload%22%5D.read().decode(%22utf-8%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20How%20layers%20are%20merged%0A%0A%20%20%20%20Our%20layers%20got%20put%20in%20%60%2Fopt%2Fpython%60.%0A%0A%20%20%20%20%60%2Fopt%2Fpython%60%20is%20in%20%60sys.path%60%2C%20the%20module%20search%20path.%0A%20%20%20%20But%20%60PYTHONPATH%60%20is%20set%20to%20%60%2Fvar%2Fruntime%60.%0A%20%20%20%20So%20%60%2Fopt%2Fpython%60%20got%20in%20%60sys.path%60%20by%20some%20other%20means%2C%0A%20%20%20%20and%20**%60PYTHONPATH%60%20is%20being%20used%20for%20a%20non-layer%20purpose.**%0A%0A%20%20%20%20The%20%60%2Fopt%2Fpython%60%20listing%20reveals%20**how%20the%20layers%20are%20merged%3A%20rsync-style!**%0A%20%20%20%20Both%20%60requests-2.30.0.dist-info%60%20and%20%60requests-2.31.0.dist-info%60%20are%20listed.%0A%20%20%20%20So%20it's%20not%20that%20one%20version%20is%20selected.%0A%20%20%20%20Rather%2C%20they're%20just%20merged%20as%20directories%2C%20rsync-style.%0A%20%20%20%20No%20package%20manager%20magic.%0A%0A%20%20%20%20I%20guess%20this%20makes%20sense.%0A%20%20%20%20Layers%20can%20contain%20any%20kinds%20of%20files%2C%20not%20just%20dependencies.%0A%20%20%20%20Even%20if%20a%20package%20manager%20got%20involved%2C%20how%20could%20it%20sensibly%20pick%20a%20winner%3F%0A%20%20%20%20So%20rsync-style%20merging%20is%20the%20only%20option.%0A%0A%20%20%20%20Still%2C%20couldn't%20it%20lead%20to%20Dr%20Moreau%20packages%20and%20mad%20bugs%3F%0A%20%20%20%20**If%20layers%20contain%20different%20versions%20of%20a%20package%2C%0A%20%20%20%20you%20end%20up%20with%20a%20hybrid.**%0A%20%20%20%20In%20this%20case%20it's%20harmless%2C%0A%20%20%20%20but%20couldn't%20it%20be%20harmful%20in%20some%20cases%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Merging%20order%0A%0A%20%20%20%20The%202.31%20layer%20was%20merged%20into%20the%202.30%20layer.%0A%20%20%20%20So%20we%20effectively%20get%202.31.%0A%20%20%20%20That's%20confirmed%20by%20%60requests.__version__%60%20in%20the%20handler.%0A%0A%20%20%20%20How%20come%3F%0A%0A%20%20%20%20In%20the%20cdk%20it's%20%60layers%3D%5Brequests_2_31_layer%2C%20requests_2_30_layer%5D%60.%0A%0A%20%20%20%20In%20the%20template%2C%20it's%20the%20other%20way%20round%3A%202.30%20then%202.31.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(boto3)%3A%0A%20%20%20%20cf%20%3D%20boto3.client(%22cloudformation%22)%0A%0A%20%20%20%20template_response%20%3D%20cf.get_template(StackName%3D%22LambdaLayerMergingStack%22)%0A%20%20%20%20resources%20%3D%20template_response%5B%22TemplateBody%22%5D%5B%22Resources%22%5D%0A%20%20%20%20function_resource%20%3D%20next(props%20for%20props%20in%20resources.values()%20if%20props%5B%22Type%22%5D%20%3D%3D%20%22AWS%3A%3ALambda%3A%3AFunction%22)%0A%20%20%20%20function_resource%5B%22Properties%22%5D%5B%22Layers%22%5D%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20It%20turns%20out%20that%20cdk%20sorts%20your%20list%20of%20layers%20in-place%20%5B1%5D%3A%0A%0A%20%20%20%20%60%60%60typescript%0A%20%20%20%20private%20renderLayers()%20%7B%0A%20%20%20%20%20%20%20%20if%20(!this._layers%20%7C%7C%20this._layers.length%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%20undefined%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(FeatureFlags.of(this).isEnabled(LAMBDA_RECOGNIZE_LAYER_VERSION))%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._layers.sort()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20return%20this._layers.map(layer%20%3D%3E%20layer.layerVersionArn)%3B%0A%20%20%20%20%7D%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%60LAMBDA_RECOGNIZE_LAYER_VERSION%60%20defaults%20to%20%60true%60%20when%20you%20run%20%60cdk%20init%60.%0A%20%20%20%20So%20cdk%20moved%202.31%20_after_%202.30.%0A%20%20%20%20That's%20why%202.31%20won.%0A%0A%20%20%20%20Why%20does%20cdk%20sort%20layers%3F%20The%20docs%20%5B2%5D%3A%0A%0A%20%20%20%20%3EAn%20additional%20update%20to%20the%20hashing%20logic%20fixes%20two%20issues%20surrounding%20layers.%20Prior%20to%20this%20change%2C%20updating%20the%20lambda%20layer%20version%20would%20have%20no%20effect%20on%20the%20function%20version.%20Also%2C%20the%20order%20of%20lambda%20layers%20provided%20to%20the%20function%20was%20unnecessarily%20baked%20into%20the%20hash.%0A%20%20%20%20%3E%0A%20%20%20%20%3EThis%20has%20been%20fixed%20in%20the%20AWS%20CDK%20starting%20with%20version%202.27.%20If%20you%20ran%20cdk%20init%20with%20an%20earlier%20version%2C%20you%20will%20need%20to%20opt-in%20via%20a%20feature%20flag.%20If%20you%20run%20cdk%20init%20with%20v2.27%20or%20later%2C%20this%20fix%20will%20be%20opted%20in%2C%20by%20default.%0A%0A%20%20%20%20The%20first%20issue%20makes%20sense%3A%20bumping%20a%20layer%20version%20should%20bump%20the%20function%20version.%0A%20%20%20%20But%20I'm%20puzzled%20by%20the%20second%20issue.%0A%20%20%20%20This%20comment%20seems%20spot-on%20to%20me%20%5B3%5D%3A%0A%0A%20%20%20%20%3EBut%20because%20layers%20can%20overwrite%20each%20other%2C%20the%20order%20in%20which%20they're%20extracted%20is%20crucial%20and%20Lambda%20functions%20which%20register%20the%20same%20layers%20in%20a%20different%20order%20should%20always%20have%20a%20different%20hash.%0A%20%20%20%20%3E%0A%20%20%20%20%3ERegardless%20of%20the%20previous%20point%2C%20why%20is%20it%20necessary%20to%20mutate%20the%20layers%20array%20to%20calculate%20the%20Lambda%20function%20hash%3F%20A%20sorted%20copy%20can%20be%20used%20to%20calculate%20the%20same%20hash.%20This%20would%20cause%20the%20layers%20to%20maintain%20the%20original%20ordering%20and%20be%20extracted%20in%20the%20expected%20order.%0A%0A%20%20%20%20Beware%3A%20**layer%20order%20matters%2C%20but%20by%20default%20cdk%20sorts%20your%20list%20in%20place.**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20References%0A%0A%20%20%20%20%5B1%5D%20https%3A%2F%2Fgithub.com%2Faws%2Faws-cdk%2Fblob%2Fv2.243.0%2Fpackages%2Faws-cdk-lib%2Faws-lambda%2Flib%2Ffunction.ts%23L1562%0A%0A%20%20%20%20%5B2%5D%20https%3A%2F%2Fdocs.aws.amazon.com%2Fcdk%2Fapi%2Fv2%2Fdocs%2Faws-cdk-lib.aws_lambda-readme.html%23currentversion-updated-hashing-logic-for-layer-versions%0A%0A%20%20%20%20%5B3%5D%20https%3A%2F%2Fgithub.com%2Faws%2Faws-cdk%2Fdiscussions%2F26395%0A%0A%20%20%20%20%5B4%5D%20https%3A%2F%2Fdamianjanik.com%2Fblog%2Faws-cdk-lambda-layer-merge-order%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
5865afae46b1b2179ad9b998e7582d1c