Skip to content

PROP-239 - Add latent tasks for on-demand FaaS execution - #252

Open
JeffMboya wants to merge 13 commits into
absmach:mainfrom
JeffMboya:PROP-239
Open

PROP-239 - Add latent tasks for on-demand FaaS execution#252
JeffMboya wants to merge 13 commits into
absmach:mainfrom
JeffMboya:PROP-239

Conversation

@JeffMboya

Copy link
Copy Markdown
Contributor

What type of PR is this?

Feature: adds latent FaaS tasks that precompile on deploy and instantiate on demand.

What does this do?

Deploys a task that precompiles its WASM component without running, then instantiates it per request in parallel.

Which issue(s) does this PR fix/relate to?

Resolves #239

Have you included tests for your changes?

Yes. Proplet precompile/invoke and concurrent-invocation tests, plus a manager InvokeTask service test.

Did you document any new/modified features?

No README/API docs changed; usage is the new tasks invoke <id> CLI command.

Notes

Latent flag persists only in memory/badger stores, matching existing Priority and Schedule handling.

@JeffMboya JeffMboya changed the title feat: add latent tasks for on-demand FaaS execution PROP-239 - Add latent tasks for on-demand FaaS execution Jul 2, 2026

@rodneyosodo rodneyosodo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Metrics observations while reviewing latent tasks

Comment thread proplet/src/service.rs
Comment thread proplet/src/service.rs
Comment thread proplet/src/service.rs
Comment thread manager/service.go
…ocation

Latent tasks pull and precompile their WASM component on deploy but do
not execute it. The compiled component is cached and instantiated on
demand when an invoke request arrives; concurrent invocations each get a
fresh instance so they run in parallel.

- Runtime gains precompile() and invoke() (default: unsupported)
- WasmtimeRuntime caches precompiled components and reuses the shared
  component run paths for both start and invoke
- StartRequest gains a latent flag; latent starts precompile and return
- InvokeRequest and a control/manager/invoke handler drive invocations,
  publishing results to control/proplet/invoke_results
Adds a Latent flag on tasks that is forwarded to the proplet in the
start payload, so a task can be deployed without executing. A new
InvokeTask service method and POST /tasks/{taskID}/invoke endpoint
publish invocations to control/manager/invoke; per-invocation results
arrive on control/proplet/invoke_results and are recorded without
marking the deployed task terminal.

- Service.InvokeTask + publishInvoke + updateInvokeResultsHandler
- ActionInvoke plugin authorization and middleware wrappers
- invoke-task HTTP endpoint, decoder and mock
Expose InvokeTask on the SDK and a "tasks invoke <id> [inputs...]"
CLI command so latent tasks can be triggered on demand.
Store the precompiled component's wasm bytes once behind an Arc instead
of deep-copying the whole StartConfig (including the wasm binary) on
every invocation. invoke now clones only an Arc refcount and an empty
config; run_component_export takes the bytes as a parameter, removing a
second per-call copy on both the start and invoke paths.
Invoke now always targets exactly one proplet: a pinned task uses its
deployed proplet, and a broadcast-deployed latent task (present on every
active proplet) is load-balanced via the round-robin scheduler instead
of fanning the request out to all of them. This distributes request load
across proplets and gives each invocation a single responder.
Invoke is now request/response instead of fire-and-forget. The manager
tags each invocation with an id, registers a waiter, and blocks until
the proplet publishes the matching result on control/proplet/invoke_results
(or the request times out). The proplet echoes the invocation id back in
an InvokeResultMessage. InvokeTask, the SDK, the CLI and POST
/tasks/{id}/invoke now surface the function's output to the caller, so an
external HTTP client gets the result directly.
SelectProplet mutates LastProplet without synchronization. The latent
invoke path now calls it concurrently (load-balancing broadcast tasks),
racing with concurrent task starts. Add a mutex around the counter and a
concurrent regression test.
InvokeTask used time.After, which leaks a 30s timer every time the
result or context case wins the select — the common path on the FaaS
hot path. Use time.NewTimer with defer Stop().
updateInvokeResultsHandler only set the task error on failure, so a
successful invocation after a failed one left a stale error on the task.
Set it unconditionally so the persisted result and error always reflect
the latest invocation.
The synchronous invoke path already returns each result to the caller
via the pending-invocation waiter, so writing the last invocation's
result back onto the task was pure overhead: a task-repo write per
invocation plus a last-write-wins race with concurrent invokes. The
invoke-results handler now only routes the result to the waiting caller.
Without it the SDK dropped the field, so a latent task could only be
created by hand-rolling the REST payload even though the CLI already
had an invoke command.
The proplet already merged InvokeRequest.env into the precompiled
task config, but nothing populated the field: publishInvoke never
sent it and the REST decoder never read it, so the whole path was
dead. Thread env from the CLI and SDK through the invoke endpoint
into the MQTT payload.
publishInvoke added a third occurrence of the literal, which trips
goconst on the golangci-lint version CI pins.
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

Successfully merging this pull request may close these issues.

Feature: Latent tasks to provide FaaS

2 participants