Skip to content

Rust workflow recompiles the full dependency tree once per function in a Cargo workspace (build time scales linearly with function count) #900

Description

@bnusunny

Description

This is a performance follow-up to #634. That issue fixed correctness for Cargo-workspace projects (binaries are now found), but the underlying build strategy still compiles each function in isolation, so shared dependencies are recompiled once per function.

For a workspace with N Rust functions that share heavy dependencies (e.g. the AWS SDK), sam build compiles the shared dependency tree N times. In a real project with 16 functions sharing the AWS SDK, this makes sam build take over an hour.

Root cause

In cargo_lambda.py (v1.65.0):

if not os.getenv("CARGO_TARGET_DIR"):
    # This results in the "target" dir being created under the member dir of a cargo workspace
    os.environ["CARGO_TARGET_DIR"] = "target"

CARGO_TARGET_DIR is set to the relative path target, and the build runs with cwd=self._source_dir (each function's own member directory — actions.py#L97). So each function gets an isolated <member>/target/, and cargo cannot reuse any compiled dependency across functions. The dependency graph is rebuilt from scratch for every function.

Expected behavior

For a workspace, compile once and package many. A single cargo lambda build --release [--arm64] at the workspace root builds every member binary in one pass, compiling shared deps only once.

Local measurement on the same 16-function workspace:

  • Current (per-function): > 1 hour
  • Single workspace build: ~10m cold, ~2m19s warm

Possible fix

The builder already supports selecting a single binary — build_command appends --bin <handler> (actions.py#L90). A workspace-aware path could:

  1. Detect the Cargo workspace root and run one cargo lambda build there (shared target/, deps compiled once).
  2. Resolve each function's binary from the shared target/lambda/<name>/ output by package/bin name, and copy it into that function's ARTIFACTS_DIR — instead of shelling out per CodeUri.

This keeps BuildMethod: rust-cargolambda transparent to users: no per-function Makefiles and no manual binary distribution. (cargo-lambda-cdk already handles the workspace case, as noted in #634.)

Current workaround

Switch each function to BuildMethod: makefile and drive a single workspace cargo lambda build yourself, distributing each bootstrap into its function dir before sam build packages it. Works, but pushes SAM's build responsibility into user-maintained Makefiles.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions