Skip to content
Closed

Lambda #6123

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
78 changes: 78 additions & 0 deletions .github/workflows/publish_lambda.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# This workflow creates a new release for a quickwit search aws lambda.
# The artifact is a zip file containing a binary for ARM 64,
# ready to be deployed by the deployer.
#
# See quickwit-lambda-client/README.md
name: Release Lambda binary

on:
push:
tags:
- 'lambda-*'
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g., v0.8.0)'
required: false
default: 'dev'

permissions:
contents: read

jobs:
build-lambda:
name: Build Lambda ARM64
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set version
run: |
if [ "${{ github.ref_type }}" = "tag" ]; then
# Extract version from tag (e.g., lambda-v0.8.0 -> v0.8.0)
echo "ASSET_VERSION=${GITHUB_REF_NAME#lambda-}" >> $GITHUB_ENV
elif [ -n "${{ github.event.inputs.version }}" ] && [ "${{ github.event.inputs.version }}" != "dev" ]; then
echo "ASSET_VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
else
echo "ASSET_VERSION=dev-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
fi

- name: Install rustup
run: curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain none -y

- name: Install cross
run: cargo install cross

- name: Retrieve and export commit date, hash, and tags
run: |
echo "QW_COMMIT_DATE=$(TZ=UTC0 git log -1 --format=%cd --date=format-local:%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_ENV
echo "QW_COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV
echo "QW_COMMIT_TAGS=$(git tag --points-at HEAD | tr '\n' ',')" >> $GITHUB_ENV

- name: Build Lambda binary
run: cross build --release --features lambda-release --target aarch64-unknown-linux-gnu -p quickwit-lambda-server --bin quickwit-aws-lambda-leaf-search
env:
QW_COMMIT_DATE: ${{ env.QW_COMMIT_DATE }}
QW_COMMIT_HASH: ${{ env.QW_COMMIT_HASH }}
QW_COMMIT_TAGS: ${{ env.QW_COMMIT_TAGS }}
working-directory: ./quickwit

- name: Create Lambda zip
run: |
cd quickwit/target/aarch64-unknown-linux-gnu/release
cp quickwit-aws-lambda-leaf-search bootstrap
zip quickwit-aws-lambda-${{ env.ASSET_VERSION }}-aarch64.zip bootstrap
mv quickwit-aws-lambda-${{ env.ASSET_VERSION }}-aarch64.zip ../../../../

- name: Upload to GitHub release
uses: quickwit-inc/upload-to-github-release@v1

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Release Lambda binary' step
Uses Step
uses 'quickwit-inc/upload-to-github-release' with ref 'v1', not a pinned commit hash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
file: quickwit-aws-lambda-${{ env.ASSET_VERSION }}-aarch64.zip
overwrite: true
draft: true
tag_name: ${{ env.ASSET_VERSION }}
8 changes: 8 additions & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ arrayvec,https://github.com/bluss/arrayvec,MIT OR Apache-2.0,bluss
assert-json-diff,https://github.com/davidpdrsn/assert-json-diff,MIT,David Pedersen <david.pdrsn@gmail.com>
async-compression,https://github.com/Nullus157/async-compression,MIT OR Apache-2.0,"Wim Looman <wim@nemo157.com>, Allen Bui <fairingrey@gmail.com>"
async-speed-limit,https://github.com/tikv/async-speed-limit,MIT OR Apache-2.0,The TiKV Project Developers
async-stream,https://github.com/tokio-rs/async-stream,MIT,Carl Lerche <me@carllerche.com>
async-stream-impl,https://github.com/tokio-rs/async-stream,MIT,Carl Lerche <me@carllerche.com>
async-trait,https://github.com/dtolnay/async-trait,MIT OR Apache-2.0,David Tolnay <dtolnay@gmail.com>
atomic-waker,https://github.com/smol-rs/atomic-waker,Apache-2.0 OR MIT,"Stjepan Glavina <stjepang@gmail.com>, Contributors to futures-rs"
aws-config,https://github.com/smithy-lang/smithy-rs,Apache-2.0,"AWS Rust SDK Team <aws-sdk-rust@amazon.com>, Russell Cohen <rcoh@amazon.com>"
aws-credential-types,https://github.com/smithy-lang/smithy-rs,Apache-2.0,AWS Rust SDK Team <aws-sdk-rust@amazon.com>
aws-lc-rs,https://github.com/aws/aws-lc-rs,ISC AND (Apache-2.0 OR ISC),AWS-LibCrypto
aws-lc-sys,https://github.com/aws/aws-lc-rs,ISC AND (Apache-2.0 OR ISC) AND OpenSSL,AWS-LC
aws-runtime,https://github.com/smithy-lang/smithy-rs,Apache-2.0,AWS Rust SDK Team <aws-sdk-rust@amazon.com>
aws-sdk-lambda,https://github.com/awslabs/aws-sdk-rust,Apache-2.0,"AWS Rust SDK Team <aws-sdk-rust@amazon.com>, Russell Cohen <rcoh@amazon.com>"
aws-sdk-s3,https://github.com/awslabs/aws-sdk-rust,Apache-2.0,"AWS Rust SDK Team <aws-sdk-rust@amazon.com>, Russell Cohen <rcoh@amazon.com>"
aws-sdk-sso,https://github.com/awslabs/aws-sdk-rust,Apache-2.0,"AWS Rust SDK Team <aws-sdk-rust@amazon.com>, Russell Cohen <rcoh@amazon.com>"
aws-sdk-ssooidc,https://github.com/awslabs/aws-sdk-rust,Apache-2.0,"AWS Rust SDK Team <aws-sdk-rust@amazon.com>, Russell Cohen <rcoh@amazon.com>"
Expand Down Expand Up @@ -235,6 +238,8 @@ jiff-static,https://github.com/BurntSushi/jiff,Unlicense OR MIT,Andrew Gallant <
jobserver,https://github.com/rust-lang/jobserver-rs,MIT OR Apache-2.0,Alex Crichton <alex@alexcrichton.com>
js-sys,https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys,MIT OR Apache-2.0,The wasm-bindgen Developers
json_comments,https://github.com/tmccombs/json-comments-rs,Apache-2.0,Thayne McCombs <astrothayne@gmail.com>
lambda_runtime,https://github.com/awslabs/aws-lambda-rust-runtime,Apache-2.0,"David Calavera <dcalaver@amazon.com>, Harold Sun <sunhua@amazon.com>"
lambda_runtime_api_client,https://github.com/awslabs/aws-lambda-rust-runtime,Apache-2.0,"David Calavera <dcalaver@amazon.com>, Harold Sun <sunhua@amazon.com>"
lazy_static,https://github.com/rust-lang-nursery/lazy-static.rs,MIT OR Apache-2.0,Marvin Löbel <loebel.marvin@gmail.com>
levenshtein_automata,https://github.com/tantivy-search/levenshtein-automata,MIT,Paul Masurel <paul.masurel@gmail.com>
libc,https://github.com/rust-lang/libc,MIT OR Apache-2.0,The Rust Project Developers
Expand Down Expand Up @@ -424,6 +429,7 @@ serde_core,https://github.com/serde-rs/serde,MIT OR Apache-2.0,"Erick Tryzelaar
serde_derive,https://github.com/serde-rs/serde,MIT OR Apache-2.0,"Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
serde_json,https://github.com/serde-rs/json,MIT OR Apache-2.0,"Erick Tryzelaar <erick.tryzelaar@gmail.com>, David Tolnay <dtolnay@gmail.com>"
serde_json_borrow,https://github.com/PSeitz/serde_json_borrow,MIT,Pascal Seitz <pascal.seitz@gmail.com>
serde_path_to_error,https://github.com/dtolnay/path-to-error,MIT OR Apache-2.0,David Tolnay <dtolnay@gmail.com>
serde_qs,https://github.com/samscott89/serde_qs,MIT OR Apache-2.0,Sam Scott <sam@osohq.com>
serde_spanned,https://github.com/toml-rs/toml,MIT OR Apache-2.0,The serde_spanned Authors
serde_urlencoded,https://github.com/nox/serde_urlencoded,MIT OR Apache-2.0,Anthony Ramine <n.oxyde@gmail.com>
Expand Down Expand Up @@ -523,9 +529,11 @@ unicode-width,https://github.com/unicode-rs/unicode-width,MIT OR Apache-2.0,"kwa
unit-prefix,https://codeberg.org/commons-rs/unit-prefix,MIT,"Fabio Valentini <decathorpe@gmail.com>, Benjamin Sago <ogham@bsago.me>"
unsafe-libyaml,https://github.com/dtolnay/unsafe-libyaml,MIT,David Tolnay <dtolnay@gmail.com>
untrusted,https://github.com/briansmith/untrusted,ISC,Brian Smith <brian@briansmith.org>
ureq-proto,https://github.com/algesten/ureq-proto,MIT OR Apache-2.0,Martin Algesten <martin@algesten.se>
url,https://github.com/servo/rust-url,MIT OR Apache-2.0,The rust-url developers
urlencoding,https://github.com/kornelski/rust_urlencoding,MIT,"Kornel <kornel@geekhood.net>, Bertram Truong <b@bertramtruong.com>"
username,https://pijul.org/darcs/user,MIT OR Apache-2.0,Pierre-Étienne Meunier <pierre-etienne.meunier@aalto.fi>
utf-8,https://github.com/SimonSapin/rust-utf8,MIT OR Apache-2.0,Simon Sapin <simon.sapin@exyr.org>
utf8-ranges,https://github.com/BurntSushi/utf8-ranges,Unlicense OR MIT,Andrew Gallant <jamslam@gmail.com>
utf8_iter,https://github.com/hsivonen/utf8_iter,Apache-2.0 OR MIT,Henri Sivonen <hsivonen@hsivonen.fi>
utf8parse,https://github.com/alacritty/vte,Apache-2.0 OR MIT,"Joe Wilm <joe@jwilm.com>, Christian Duerr <contact@christianduerr.com>"
Expand Down
225 changes: 225 additions & 0 deletions docs/configuration/lambda-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
---
title: Lambda configuration
sidebar_position: 6
---

Quickwit supports offloading leaf search operations to AWS Lambda for horizontal scaling. When the local search queue becomes saturated, overflow splits are automatically sent to Lambda functions for processing.

:::note
Lambda offloading is currently only supported on AWS.
:::

## How it works

Lambda offloading is **only active when a `lambda` configuration section is present** under `searcher` in your node configuration. When configured:

1. Quickwit monitors the local search queue depth
2. When pending searches exceed the `offload_threshold`, new splits are sent to Lambda instead of being queued locally
3. Lambda returns per-split search results that are cached and merged with local results

This allows Quickwit to handle traffic spikes without provisioning additional searcher nodes.

## Startup validation

When a `lambda` configuration is defined, Quickwit performs a **dry run invocation** at startup to verify that:
- The Lambda function exists
- The function version matches the embedded binary
- The invoker has permission to call the function

If this validation fails, **Quickwit will fail to start**. This ensures that Lambda offloading works correctly before the node begins serving traffic.

## Configuration

Add a `lambda` section under `searcher` in your node configuration:

```yaml
searcher:
lambda:
offload_threshold: 100
auto_deploy:
execution_role_arn: arn:aws:iam::123456789012:role/quickwit-lambda-role
memory_size: 5 GiB
invocation_timeout_secs: 15
```

### Lambda configuration options

| Property | Description | Default value |
| --- | --- | --- |
| `function_name` | Name of the AWS Lambda function to invoke. | `quickwit-lambda-search` |
| `max_splits_per_invocation` | Maximum number of splits to send in a single Lambda invocation. Must be at least 1. | `10` |
| `offload_threshold` | Number of pending local searches before offloading to Lambda. A value of `0` offloads everything to Lambda. | `100` |
| `auto_deploy` | Auto-deployment configuration. If set, Quickwit automatically deploys or updates the Lambda function at startup. | (none) |

### Auto-deploy configuration options

| Property | Description | Default value |
| --- | --- | --- |
| `execution_role_arn` | **Required.** IAM role ARN for the Lambda function's execution role. | |
| `memory_size` | Memory allocated to the Lambda function. More memory provides more CPU. | `5 GiB` |
| `invocation_timeout_secs` | Timeout for Lambda invocations in seconds. | `15` |

## Deployment options

### Automatic deployment (recommended)

With `auto_deploy` configured, Quickwit automatically:
1. Creates the Lambda function if it doesn't exist
2. Updates the function code if the embedded binary has changed
3. Publishes a new version with a unique identifier
4. Garbage collects old versions (keeps current + 5 most recent)

This is the recommended approach as it ensures the Lambda function always matches the Quickwit binary version.

### Manual deployment

You can deploy the Lambda function manually without `auto_deploy`:
1. Download the Lambda zip from [GitHub releases](https://github.com/quickwit-oss/quickwit/releases)
2. Create or update the Lambda function using AWS CLI, Terraform, or the AWS Console
3. Publish a version with description format `quickwit:{version}-{sha1}` (e.g., `quickwit:0_8_0-fa752891`)

The description must match the format Quickwit expects, or it won't find the function version.

## IAM permissions

### Permissions for the Quickwit node

The IAM role or user running Quickwit needs the following permissions to invoke Lambda:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": "arn:aws:lambda:*:*:function:quickwit-lambda-search:*"
}
]
}
```

If using `auto_deploy`, additional permissions are required for deployment:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:CreateFunction",
"lambda:GetFunction",
"lambda:UpdateFunctionCode",
"lambda:PublishVersion",
"lambda:ListVersionsByFunction",
"lambda:DeleteFunction"
],
"Resource": "arn:aws:lambda:*:*:function:quickwit-lambda-search"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/quickwit-lambda-role",
"Condition": {
"StringEquals": {
"iam:PassedToService": "lambda.amazonaws.com"
}
}
}
]
}
```

### Lambda execution role

The Lambda function requires an execution role with S3 read access to your index data. CloudWatch logging permissions are not required.

Example policy:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-index-bucket/*"
}
]
}
```

The execution role must also have a trust policy allowing Lambda to assume it:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
```

## Versioning

Quickwit uses content-based versioning for Lambda:
- A SHA1 hash of the Lambda binary is computed at build time
- This hash is embedded in the Lambda function description as `quickwit:{version}-{sha1_short}`
- When Quickwit starts, it searches for a version matching this description
- Different Quickwit builds with the same Lambda binary share the same Lambda version
- Updating the Lambda binary automatically triggers a new deployment

## Example configuration

Minimal configuration (manual deployment):

```yaml
searcher:
lambda:
function_name: {}
```


Minimal configuration (auto deployment):

```yaml
searcher:
lambda:
auto_deploy:
execution_role_arn: arn:aws:iam::123456789012:role/quickwit-lambda-role
```


Full configuration (auto-deployment):

```yaml
searcher:
lambda:
function_name: quickwit-lambda-search
max_splits_per_invocation: 10
offload_threshold: 100
auto_deploy:
execution_role_arn: arn:aws:iam::123456789012:role/quickwit-lambda-role
memory_size: 5 GiB
invocation_timeout_secs: 15
```

Aggressive offloading (send everything to Lambda):

```yaml
searcher:
lambda:
function_name: quickwit-lambda-search
offload_threshold: 0
auto_deploy:
execution_role_arn: arn:aws:iam::123456789012:role/quickwit-lambda-role
```
16 changes: 16 additions & 0 deletions quickwit/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Quickwit Claude Guidelines

## Code Formatting

Run `make fmt` to check and fix code formatting. This command performs three checks:

1. **Rust formatting**: Ensures Rust code is properly formatted (via `cargo fmt`)
2. **License headers**: Checks that files are prepended with the correct LICENSE header
3. **Log format policy**: Checks that log statements follow our format rules:
- No trailing punctuation in log messages
- No uppercase for the first character of log messages
- See `scripts/check_log_format.sh` for details

### Quick Fix

Use `/fmt` to automatically run format checks and see issues.
Loading
Loading