<jupyter_start><jupyter_text>Cold start download latenciesThis notebook focuses on the latencies of **a single download on a DownloadCache**. We test the differences between downloads:- in a completely fresh Lambda container (cold start)- subsequent invocations on the already initialized container<jupyter_code>import os

region_name="us-east-2"
binary_name="lambda"
aws_profile=os.environ["AWS_PROFILE"] # Specify the profile you want to use from your .aws/credentials file with the AWS_PROFILE env variable

MEGA = 1024*1024
lambda_name = !docker run \
    --rm \
    -v $HOME/.aws/credentials:/creds:ro \
    -v cloud-reader-tf:/mnt/state_vol \
    cloudfuse/cloud-reader-terraform output lambda_arn
lambda_name = lambda_name[0][1:len(lambda_name[0])-1]
print('lambda_name:', lambda_name)
import boto3
import json
import base64
from datetime import datetime
import time
import pandas as pd

session = boto3.Session(profile_name=aws_profile)
client = session.client('lambda', region_name = region_name)

# Changing the description of the Lambda trashes the old containers
def reset_lambda():
    response = client.update_function_configuration(
        FunctionName = lambda_name,
        Description = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')
    )
    # print('Lambda function update status:', response['LastUpdateStatus'])

def bench_setup_1(max_parallel, ranges):
    inputParams = {
        "region": region_name,
        "bucket": "cloudfuse-taxi-data",
        "key": "tpch/tbl-s1/lineitem.tbl",
        "size": 759863287,
        "ranges": ranges,
        "max_parallel": max_parallel,
    }
    response = client.invoke(
        FunctionName = lambda_name,
        InvocationType = 'RequestResponse',
        Payload = json.dumps(inputParams),
        LogType='Tail'
    )
    # print(base64.b64decode(response['LogResult']).decode("utf-8") )
    return json.load(response['Payload'])<jupyter_output><empty_output><jupyter_text>Show that reset works<jupyter_code>print(bench_setup_1(32, [{"start": 0, "length": 10*MEGA}]))
print()
print(bench_setup_1(32, [{"start": 0, "length": 10*MEGA}]))
print()
reset_lambda()
print()
print(bench_setup_1(32, [{"start": 0, "length": 10*MEGA}]))<jupyter_output>{'downloaded_bytes': [{'dl_duration': 239, 'size': 10485760, 'wait_duration': 0}], 'init_duration': 18, 'range_durations': [239], 'run_count': 1}

{'downloaded_bytes': [{'dl_duration': 122, 'size': 10485760, 'wait_duration': 0}], 'init_duration': 1, 'range_durations': [122], 'run_count': 2}


{'downloaded_bytes': [{'dl_duration': 114, 'size': 10485760, 'wait_duration': 0}], 'init_duration': 17, 'range_durations': [114], 'run_count': 1}<jupyter_text>---------------------------------------------------Note above how the `run_count` is reseted to 1 when calling `reset_lambda()`**Example log from the lambda:**```START RequestId: 63589778-b1e8-434a-94d1-bf7398616f40 Version: $LATESTEND RequestId: 63589778-b1e8-434a-94d1-bf7398616f40REPORT RequestId: 63589778-b1e8-434a-94d1-bf7398616f40	Duration: 146.11 ms	Billed Duration: 176 ms	Memory Size: 3008 MB	Max Memory Used: 41 MB	Init Duration: 29.86 ms	```The overall init duration is:- ~30ms to create the new container- ~20ms to crate the cache and submit the downloadsThese durations are mostly neglectable compared to other S3 related latencies.Note that even though we re-create the cache completely between invocations, ulterior creations are much faster.We can see big differences between download latency of different runs. We try to caracterize those below.  Run multiple containers with 5 consecutive invocations each  10 MB chunks<jupyter_code>measurements = []
reset_lambda()
for i in range(50):
    reset_lambda()
    time.sleep(5) # sleep a bit for the change to propagate
    for j in range(5):
        measurements.append(bench_setup_1(32, [{"start": 0, "length": 10*MEGA}]))

df = pd.DataFrame.from_records(measurements)      
df.explode('range_durations').boxplot(column='range_durations', by='run_count', figsize = (10, 6))<jupyter_output><empty_output><jupyter_text>100 MB chunks<jupyter_code>measurements = []
reset_lambda()
for i in range(50):
    reset_lambda()
    time.sleep(5) # sleep a bit for the change to propagate
    for j in range(5):
        measurements.append(bench_setup_1(32, [{"start": 0, "length": 100*MEGA}]))

df = pd.DataFrame.from_records(measurements)      
df.explode('range_durations').boxplot(column='range_durations', by='run_count', figsize = (10, 6))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13866666666666666, 'fraction_numerical': 0.052444444444444446, 'mean_word_length': 4.868318122555411, 'pattern_counts': {'":': 16, '<': 16, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '561745', 'license': 'MIT', 'n_tokens_mistral': 1608, 'n_tokens_neox': 1440, 'n_words': 386, 'path': 'notebooks/cold_latencies.ipynb', 'repo_name': 'cloudfuse-io/cloud-readers-rs', 'size': '4500'}