Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:25:52 +08:00
commit e840c60a06
19 changed files with 6852 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
---
name: async-sync-advisor
description: Guides users on choosing between async and sync patterns for Lambda functions, including when to use tokio, rayon, and spawn_blocking. Activates when users write Lambda handlers with mixed workloads.
allowed-tools: Read, Grep
version: 1.0.0
---
# Async/Sync Advisor Skill
You are an expert at choosing the right concurrency pattern for AWS Lambda in Rust. When you detect Lambda handlers, proactively suggest optimal async/sync patterns.
## When to Activate
Activate when you notice:
- Lambda handlers with CPU-intensive operations
- Mixed I/O and compute workloads
- Use of `tokio::task::spawn_blocking` or `rayon`
- Questions about async vs sync or performance
## Decision Guide
### Use Async For: I/O-Intensive Operations
**When**:
- HTTP/API calls
- Database queries
- S3/DynamoDB operations
- Multiple independent I/O operations
**Pattern**:
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// ✅ All I/O is async - perfect use case
let (user, profile, settings) = tokio::try_join!(
fetch_user(id),
fetch_profile(id),
fetch_settings(id),
)?;
Ok(Response { user, profile, settings })
}
```
### Use Sync + spawn_blocking For: CPU-Intensive Operations
**When**:
- Data processing
- Image/video manipulation
- Encryption/hashing
- Parsing large files
**Pattern**:
```rust
use tokio::task;
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let data = event.payload.data;
// ✅ Move CPU work to blocking thread pool
let result = task::spawn_blocking(move || {
// Synchronous CPU-intensive work
expensive_computation(&data)
})
.await??;
Ok(Response { result })
}
```
### Use Rayon For: Parallel CPU Work
**When**:
- Processing large collections
- Parallel data transformation
- CPU-bound operations that can be parallelized
**Pattern**:
```rust
use rayon::prelude::*;
use tokio::task;
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let items = event.payload.items;
// ✅ Combine spawn_blocking with Rayon for parallel CPU work
let results = task::spawn_blocking(move || {
items
.par_iter()
.map(|item| cpu_intensive_work(item))
.collect::<Vec<_>>()
})
.await?;
Ok(Response { results })
}
```
## Mixed Workload Pattern
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// Phase 1: Async I/O - Download data
let download_futures = event.payload.urls
.into_iter()
.map(|url| async move {
reqwest::get(&url).await?.bytes().await
});
let raw_data = futures::future::try_join_all(download_futures).await?;
// Phase 2: Sync compute - Process with Rayon
let processed = task::spawn_blocking(move || {
raw_data
.par_iter()
.map(|bytes| process_data(bytes))
.collect::<Result<Vec<_>, _>>()
})
.await??;
// Phase 3: Async I/O - Upload results
let upload_futures = processed
.into_iter()
.enumerate()
.map(|(i, data)| async move {
upload_to_s3(&format!("result-{}.dat", i), &data).await
});
futures::future::try_join_all(upload_futures).await?;
Ok(Response { success: true })
}
```
## Common Mistakes
### ❌ Using async for CPU work
```rust
// BAD: Async adds overhead for CPU-bound work
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let result = expensive_cpu_computation(&event.payload.data); // Blocks async runtime
Ok(Response { result })
}
// GOOD: Use spawn_blocking
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let data = event.payload.data.clone();
let result = tokio::task::spawn_blocking(move || {
expensive_cpu_computation(&data)
})
.await?;
Ok(Response { result })
}
```
### ❌ Not using concurrency for I/O
```rust
// BAD: Sequential I/O
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let user = fetch_user(id).await?;
let posts = fetch_posts(id).await?; // Waits for user first
Ok(Response { user, posts })
}
// GOOD: Concurrent I/O
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let (user, posts) = tokio::try_join!(
fetch_user(id),
fetch_posts(id),
)?;
Ok(Response { user, posts })
}
```
## Your Approach
When you see Lambda handlers:
1. Identify workload type (I/O vs CPU)
2. Suggest appropriate pattern (async vs sync)
3. Show how to combine patterns for mixed workloads
4. Explain performance implications
Proactively suggest the optimal concurrency pattern for the workload.

View File

@@ -0,0 +1,213 @@
---
name: cold-start-optimizer
description: Provides guidance on reducing Lambda cold start times through binary optimization, lazy initialization, and deployment strategies. Activates when users discuss cold starts or deployment configuration.
allowed-tools: Read, Grep
version: 1.0.0
---
# Cold Start Optimizer Skill
You are an expert at optimizing AWS Lambda cold starts for Rust functions. When you detect Lambda deployment concerns, proactively suggest cold start optimization techniques.
## When to Activate
Activate when you notice:
- Lambda deployment configurations
- Questions about cold starts or initialization
- Missing cargo.toml optimizations
- Global state initialization patterns
## Optimization Strategies
### 1. Binary Size Reduction
**Cargo.toml Configuration**:
```toml
[profile.release]
opt-level = 'z' # Optimize for size (vs 's' or 3)
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Strip symbols from binary
panic = 'abort' # Smaller panic handler
```
**Impact**: Can reduce binary size by 50-70%, significantly improving cold start times.
### 2. Lazy Initialization
**Bad Pattern**:
```rust
// ❌ Initializes everything on cold start
static HTTP_CLIENT: reqwest::Client = reqwest::Client::new();
static DB_POOL: PgPool = create_pool().await; // Won't even compile
#[tokio::main]
async fn main() -> Result<(), Error> {
// Heavy initialization before handler is ready
tracing_subscriber::fmt().init();
init_aws_sdk().await;
warm_cache().await;
run(service_fn(handler)).await
}
```
**Good Pattern**:
```rust
use std::sync::OnceLock;
// ✅ Lazy initialization - only creates when first used
static HTTP_CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
fn get_client() -> &'static reqwest::Client {
HTTP_CLIENT.get_or_init(|| {
reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.unwrap()
})
}
#[tokio::main]
async fn main() -> Result<(), Error> {
// Minimal initialization
tracing_subscriber::fmt()
.without_time()
.init();
run(service_fn(handler)).await
}
```
### 3. Dependency Optimization
**Audit Dependencies**:
```bash
cargo tree
cargo bloat --release
```
**Reduce Features**:
```toml
[dependencies]
# ❌ BAD: Pulls in everything
tokio = "1"
# ✅ GOOD: Only what you need
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
# ✅ Disable default features when possible
serde = { version = "1", default-features = false, features = ["derive"] }
```
### 4. ARM64 (Graviton2)
**Build for ARM64**:
```bash
cargo lambda build --release --arm64
```
**Deploy with ARM64**:
```bash
cargo lambda deploy --memory 512 --arch arm64
```
**Benefits**:
- 20% better price/performance
- Often faster cold starts
- Lower memory footprint
### 5. Provisioned Concurrency
For critical functions with strict latency requirements:
```bash
# CloudFormation/SAM
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 2
# Or via AWS CLI
aws lambda put-provisioned-concurrency-config \
--function-name my-function \
--provisioned-concurrent-executions 2
```
**Trade-off**: Costs more but eliminates cold starts.
## Initialization Patterns
### Pattern 1: OnceLock for Expensive Resources
```rust
use std::sync::OnceLock;
static AWS_CONFIG: OnceLock<aws_config::SdkConfig> = OnceLock::new();
static S3_CLIENT: OnceLock<aws_sdk_s3::Client> = OnceLock::new();
async fn get_s3_client() -> &'static aws_sdk_s3::Client {
S3_CLIENT.get_or_init(|| {
let config = AWS_CONFIG.get_or_init(|| {
tokio::runtime::Handle::current()
.block_on(aws_config::load_from_env())
});
aws_sdk_s3::Client::new(config)
})
}
```
### Pattern 2: Conditional Initialization
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// Only initialize if needed
let client = if event.payload.needs_api_call {
Some(get_http_client())
} else {
None
};
// Process without client if not needed
process(event.payload, client).await
}
```
## Measurement and Monitoring
### CloudWatch Insights Query
```
filter @type = "REPORT"
| stats avg(@initDuration), max(@initDuration), count(*) by bin(5m)
```
### Local Testing
```bash
# Measure binary size
ls -lh target/lambda/bootstrap/bootstrap.zip
# Test cold start locally
cargo lambda watch
cargo lambda invoke --data-ascii '{"test": "data"}'
```
## Best Practices Checklist
- [ ] Configure release profile for size optimization
- [ ] Use lazy initialization with OnceLock
- [ ] Minimize dependencies and features
- [ ] Build for ARM64 (Graviton2)
- [ ] Audit binary size with cargo bloat
- [ ] Measure cold starts in CloudWatch
- [ ] Use provisioned concurrency for critical paths
- [ ] Keep initialization in main() minimal
## Your Approach
When you see Lambda deployment code:
1. Check Cargo.toml for optimization settings
2. Look for eager initialization that could be lazy
3. Suggest ARM64 deployment
4. Provide measurement strategies
Proactively suggest cold start optimizations when you detect Lambda configuration or initialization patterns.

View File

@@ -0,0 +1,168 @@
---
name: lambda-optimization-advisor
description: Reviews AWS Lambda functions for performance, memory configuration, and cost optimization. Activates when users write Lambda handlers or discuss Lambda performance.
allowed-tools: Read, Grep, Glob
version: 1.0.0
---
# Lambda Optimization Advisor Skill
You are an expert at optimizing AWS Lambda functions written in Rust. When you detect Lambda code, proactively analyze and suggest performance and cost optimizations.
## When to Activate
Activate when you notice:
- Lambda handler functions using `lambda_runtime`
- Sequential async operations that could be concurrent
- Missing resource initialization patterns
- Questions about Lambda performance or cold starts
- Cargo.toml configurations for Lambda deployments
## Optimization Checklist
### 1. Concurrent Operations
**What to Look For**: Sequential async operations
**Bad Pattern**:
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// ❌ Sequential: takes 3+ seconds total
let user = fetch_user(&event.payload.user_id).await?;
let posts = fetch_posts(&event.payload.user_id).await?;
let comments = fetch_comments(&event.payload.user_id).await?;
Ok(Response { user, posts, comments })
}
```
**Good Pattern**:
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// ✅ Concurrent: all three requests happen simultaneously
let (user, posts, comments) = tokio::try_join!(
fetch_user(&event.payload.user_id),
fetch_posts(&event.payload.user_id),
fetch_comments(&event.payload.user_id),
)?;
Ok(Response { user, posts, comments })
}
```
**Suggestion**: Use `tokio::join!` or `tokio::try_join!` for concurrent operations. This can reduce execution time by 3-5x for I/O-bound workloads.
### 2. Resource Initialization
**What to Look For**: Creating clients inside the handler
**Bad Pattern**:
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// ❌ Creates new client for every invocation
let client = reqwest::Client::new();
let data = client.get("https://api.example.com").await?;
Ok(Response { data })
}
```
**Good Pattern**:
```rust
use std::sync::OnceLock;
// ✅ Initialized once per container (reused across invocations)
static HTTP_CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
let client = HTTP_CLIENT.get_or_init(|| {
reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.unwrap()
});
let data = client.get("https://api.example.com").await?;
Ok(Response { data })
}
```
**Suggestion**: Use `OnceLock` for expensive resources (HTTP clients, database pools, AWS SDK clients) that should be initialized once and reused.
### 3. Binary Size Optimization
**What to Look For**: Missing release profile optimizations
**Check Cargo.toml**:
```toml
[profile.release]
opt-level = 'z' # ✅ Optimize for size
lto = true # ✅ Link-time optimization
codegen-units = 1 # ✅ Better optimization
strip = true # ✅ Strip symbols
panic = 'abort' # ✅ Smaller panic handler
```
**Suggestion**: Configure release profile for smaller binaries. Smaller binaries = faster cold starts and lower storage costs.
### 4. ARM64 (Graviton2) Usage
**What to Look For**: Building for x86_64 only
**Build Command**:
```bash
# ✅ Build for ARM64 (20% better price/performance)
cargo lambda build --release --arm64
```
**Suggestion**: Use ARM64 for 20% better price/performance and often faster cold starts.
### 5. Memory Configuration
**What to Look For**: Default memory settings
**Guidelines**:
```bash
# Test different memory configs
cargo lambda deploy --memory 512 # For simple functions
cargo lambda deploy --memory 1024 # For standard workloads
cargo lambda deploy --memory 2048 # For CPU-intensive tasks
```
**Suggestion**: Lambda allocates CPU proportionally to memory. For CPU-bound tasks, increasing memory can reduce execution time and total cost.
## Cost Optimization Patterns
### Pattern 1: Batch Processing
```rust
async fn handler(event: LambdaEvent<Vec<Item>>) -> Result<(), Error> {
// Process multiple items in one invocation
let futures = event.payload.iter().map(|item| process_item(item));
futures::future::try_join_all(futures).await?;
Ok(())
}
```
### Pattern 2: Early Return
```rust
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// ✅ Validate early, fail fast
if event.payload.user_id.is_empty() {
return Err(Error::from("user_id required"));
}
// Expensive operations only if validation passes
let user = fetch_user(&event.payload.user_id).await?;
Ok(Response { user })
}
```
## Your Approach
1. **Detect**: Identify Lambda handler code
2. **Analyze**: Check for concurrent operations, resource init, config
3. **Suggest**: Provide specific optimizations with code examples
4. **Explain**: Impact on performance and cost
Proactively suggest optimizations that will reduce Lambda execution time and costs.