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

Implement SnapStart Runtime Hooks #701

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = [
"lambda-http",
"lambda-integration-tests",
Expand Down
1 change: 1 addition & 0 deletions examples/basic-runtime-hooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
19 changes: 19 additions & 0 deletions examples/basic-runtime-hooks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "basic-runtime-hooks"
version = "0.1.0"
edition = "2021"


# Use cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to manage dependencies.
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.136"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
uuid = { version = "1.4.1", features = ["v4"]}
11 changes: 11 additions & 0 deletions examples/basic-runtime-hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AWS Lambda Function example

## Build & Deploy

1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)
mbfreder marked this conversation as resolved.
Show resolved Hide resolved
2. Build the function with `cargo lambda build --release`
3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE`

## Build for ARM 64

Build the function with `cargo lambda build --release --arm64`
94 changes: 94 additions & 0 deletions examples/basic-runtime-hooks/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// This example demonstrates use of shared resources such as DB connections
// or local caches that can be initialized at the start of the runtime and
// reused by subsequent lambda handler calls.
// Run it with the following input:
// { "command": "do something" }

use lambda_runtime::{crac::Resource, service_fn, Error, LambdaEvent, Runtime};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use uuid::Uuid;

/// This is also a made-up example. Requests come into the runtime as unicode
/// strings in json format, which can map to any structure that implements `serde::Deserialize`
/// The runtime pays no attention to the contents of the request payload.
#[derive(Deserialize)]
struct Request {
command: String,
}

/// This is a made-up example of what a response structure may look like.
/// There is no restriction on what it can be. The runtime requires responses
/// to be serialized into json. The runtime pays no attention
/// to the contents of the response payload.
#[derive(Serialize)]
struct Response {
req_id: String,
msg: String,
secret: String,
}

struct SharedClient {
name: &'static str,
secret: RefCell<String>,
}

impl SharedClient {
fn new(name: &'static str, secret: String) -> Self {
Self {
name,
secret: RefCell::new(secret),
}
}

fn response(&self, req_id: String, command: String) -> Response {
Response {
req_id,
msg: format!("Command {} executed by {}.", command, self.name),
secret: self.secret.borrow().clone(),
}
}
}

impl Resource for SharedClient {
fn before_checkpoint(&self) -> Result<(), Error> {
// clear the secret before checkpointing
*self.secret.borrow_mut() = String::new();
tracing::info!("in before_checkpoint: secret={:?}", self.secret.borrow());
Ok(())
}
fn after_restore(&self) -> Result<(), Error> {
// regenerate the secret after restoring
let secret = Uuid::new_v4().to_string();
*self.secret.borrow_mut() = secret;
tracing::info!("in after_restore: secret={:?}", self.secret.borrow());
Ok(())
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();

let secret = Uuid::new_v4().to_string();
let client = SharedClient::new("Shared Client 1 (perhaps a database)", secret);
let client_ref = &client;
tracing::info!("In main function: secret={:?}", client_ref.secret.borrow());

Runtime::new()
.register(client_ref)
.run(service_fn(move |event: LambdaEvent<Request>| async move {
tracing::info!("In handler function: secret={:?}", client_ref.secret.borrow());
let command = event.payload.command;
Ok::<Response, Error>(client_ref.response(event.context.request_id, command))
}))
.await?;
Ok(())
}
1 change: 1 addition & 0 deletions examples/http-runtime-hooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
22 changes: 22 additions & 0 deletions examples/http-runtime-hooks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "http-runtime-hooks"
version = "0.1.0"
edition = "2021"

# Starting in Rust 1.62 you can use `cargo add` to add dependencies
# to your project.
#
# If you're using an older Rust version,
# download cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to install the `add` subcommand.
#
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
lambda_http = { path = "../../lambda-http" }
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
uuid = { version = "1.4.1", features = ["v4"]}
11 changes: 11 additions & 0 deletions examples/http-runtime-hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AWS Lambda Function example

## Build & Deploy

1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems https://www.cargo-lambda.info/guide/installation.html makes more sense here. There's no installation secion in this git

2. Build the function with `cargo lambda build --release`
3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE`

## Build for ARM 64

Build the function with `cargo lambda build --release --arm64`
115 changes: 115 additions & 0 deletions examples/http-runtime-hooks/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use lambda_http::{crac, service_fn, Body, Error, IntoResponse, Request, RequestExt, Response, Runtime};
use std::sync::Arc;
use std::sync::RwLock;
use uuid::Uuid;

struct SharedClient {
name: &'static str,
secret: Arc<RwLock<String>>,
}

impl SharedClient {
fn new(name: &'static str, secret: String) -> Self {
Self {
name,
secret: Arc::new(RwLock::new(secret)),
}
}

fn response(&self, req_id: String, first_name: &str) -> String {
format!("{}: Client ({}) invoked by {}.", req_id, self.name, first_name)
}
}

impl crac::Resource for SharedClient {
fn before_checkpoint(&self) -> Result<(), Error> {
// clear the secret before checkpointing
{
let mut write_lock = self.secret.write().unwrap();
*write_lock = String::new();
} // release the write lock

{
tracing::info!("in before_checkpoint: secret={:?}", self.secret.read().unwrap());
} // release the read lock

Ok(())
}
fn after_restore(&self) -> Result<(), Error> {
// regenerate the secret after restoring
{
let mut write_lock = self.secret.write().unwrap();
*write_lock = Uuid::new_v4().to_string();
} // release the write lock

{
tracing::info!("in after_restore: secret={:?}", self.secret.read().unwrap());
} // release the read lock

Ok(())
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();

// Create the "client" and a reference to it, so that we can pass this into the handler closure below.
let secret = Uuid::new_v4().to_string();
let shared_client = SharedClient::new("random_client_name_1", secret);
let shared_client_ref = &shared_client;
{
tracing::info!(
"In main function: secret={:?}",
shared_client_ref.secret.read().unwrap()
);
} // release the read lock

// Define a closure here that makes use of the shared client.
let handler_func_closure = move |event: Request| async move {
{
tracing::info!(
"In handler function: secret={:?}",
shared_client_ref.secret.read().unwrap()
);
} // release the read lock

Result::<Response<Body>, Error>::Ok(
match event
.query_string_parameters_ref()
.and_then(|params| params.first("first_name"))
{
Some(first_name) => {
shared_client_ref
.response(
event
.lambda_context_ref()
.map(|ctx| ctx.request_id.clone())
.unwrap_or_default(),
first_name,
)
.into_response()
.await
}
None => Response::builder()
.status(400)
.body("Empty first name".into())
.expect("failed to render response"),
},
)
};

// Pass the closure to the runtime here.
Runtime::new()
.register(shared_client_ref)
.run(service_fn(handler_func_closure))
.await?;
Ok(())
}
Loading