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

how to initialize SDK client outside of function handler #616

Closed
peterborkuti opened this issue Mar 12, 2023 · 7 comments
Closed

how to initialize SDK client outside of function handler #616

peterborkuti opened this issue Mar 12, 2023 · 7 comments

Comments

@peterborkuti
Copy link
Contributor

Dear Developers,

I created an example for s3:
peterborkuti@dbc5d3d

It works, but as I read aws best practices for lambda:
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html
"Initialize SDK clients and database connections outside of the function handler"

I could not do it (I am absolute beginner in Rust but I have solid knowledge in Java and JavaScript)

What I tried

creating static/const but client creation is async

const client: Client = get_client().await;

static mut

static mut client : Option<Client> = None;
...
// in main:
client = Some(get_client().await); // but it is unsafe

using lambdas context in main

pub(crate) async fn function_handler<F1, Fut1, F2, Fut2>(fn_get_file: F1, fn_put_file: F2, event: LambdaEvent<S3Event>) -> Result<(), Error> 
    where
        F1: Fn(&str, &str) -> Fut1,
        Fut1: Future<Output = Option<Cursor<Vec<u8>>>>,
        F2: Fn(&str, &str, ByteStream) -> Fut2,
        Fut2: Future<Output = ()>
     {

(With the above handler testing would be very easy, I think, because the client is "embedded" in the fn_get_file and fn_put_file functions, so when testing we do not need sdk client at all)

in main I tried to binding the client parameters to fn_get_file and fn_put_file:

    let client = get_client().await;

    let clientRef = |x:Client| &x;

    let clientBroker = move || clientRef(client); 

    let fn_get_file = move |bucket: &str, key: &str| get_file(&clientBroker(), bucket, key);
    let fn_put_file = |bucket: &str, key: &str, bytes: ByteStream| put_file(&clientBroker(), bucket, key, bytes);
    let fn_function_handler = |event: LambdaEvent<S3Event>| function_handler(fn_get_file, fn_put_file, event);  

    run(service_fn(fn_function_handler)).await
    

First I had lifetime issues (tried to solve with "move" and now I got:"expected a closure that implements the Fn trait, but this closure only implements FnOnce":

Do you have any ideas how to do it?

Thank you in advance
Péter

@greenwoodcm
Copy link
Contributor

the following example does something similar, can you model your approach off of this?

https://github.com/awslabs/aws-lambda-rust-runtime/blob/main/examples/basic-shared-resource/src/main.rs

@peterborkuti
Copy link
Contributor Author

Thank you for your answer, but I could not do it.
I made a very basic example (60+ lines):
peterborkuti@142090f

#[tokio::main]
async fn main() -> Result<(), Error> {
    let s3_client = get_client().await;
    let client = move || &SharedClient::new(&s3_client);

    let client_ref = client();

    run(service_fn(
        move |_event: LambdaEvent<S3Event>| async move {
            Ok::<Response, Error>(client_ref.put_file_x().await)
        }
    ))
    .await?;
    Ok(())
}

It does not compile:

2:07 $ cargo build
   Compiling basic-s3-test v0.1.0 (/home/peter/repo/aws-lambda-rust-runtime/examples/basic-s3-test)
error[E0515]: cannot return reference to temporary value
  --> src/main.rs:54:26
   |
54 |     let client = move || &SharedClient::new(&s3_client);
   |                          ^-----------------------------
   |                          ||
   |                          |temporary value created here
   |                          returns a reference to data owned by the current function

error: lifetime may not live long enough
  --> src/main.rs:54:27
   |
54 |     let client = move || &SharedClient::new(&s3_client);
   |                  -------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'static`
   |                  |
   |                  lifetime `'1` represents this closure's body
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

For more information about this error, try `rustc --explain E0515`.

Unfortunately I am far from the level to solve this or to decide where to go.

@korostelevm
Copy link

korostelevm commented Mar 14, 2023

Another rust beginner here, same question. Would be helpful to add a more complete example.

Edit:
Found this was merged recently, looks promising - will try this out
https://github.com/awslabs/aws-lambda-rust-runtime/pull/582/files

@greenwoodcm
Copy link
Contributor

I just submitted a PR for a basic SDK example. probably worth having one of those in the repo to show sharing of the client along with an example of how to test code that interacts with the SDK. does this help clarify the recommended approach?

#619

@greenwoodcm greenwoodcm mentioned this issue Mar 14, 2023
2 tasks
@peterborkuti
Copy link
Contributor Author

Thank you, it works. I clean up my code a bit and send a PR.

@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for the maintainers of this repository to see.
If you need more assistance, please open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@peterborkuti
Copy link
Contributor Author

For reference:
I compared instantiating s3 client in the main (once) and in the handler (in every run), the difference was noticeable:
#621 (comment)

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

No branches or pull requests

3 participants