docs/how-to/create-ephemeral-worker.mdx
Create a script that connects to the engine, waits to process a task (e.g., generating a heavy report), and then gracefully shuts down.
This pattern is useful for executing resource-intensive or one-off tasks on serverless container platforms (like AWS Fargate or Kubernetes Jobs) where you want to minimize execution time and infrastructure costs.
Connect to iii and register the reports::generate function handler to process the heavy CPU task.
const iii = registerWorker(process.env.III_URL ?? 'ws://localhost:49134')
iii.registerFunction({ id: 'reports::generate' }, async (req) => { const logger = new Logger() const reportId = req.body?.reportId ?? 'unknown'
logger.info('Generating heavy report...', { reportId })
// ... perform resource-intensive work here ... await new Promise(resolve => setTimeout(resolve, 5000)) // example 5 second delay
logger.info('Report generated', { reportId })
return { status_code: 200, body: { success: true, reportId } } })
</Tab>
<Tab title="Python">
```python title="worker.py"
import os
import time
from iii import Logger, register_worker
iii = register_worker(os.environ.get("III_URL", "ws://localhost:49134"))
def generate_report(req):
logger = Logger()
report_id = req.get("body", {}).get("reportId", "unknown")
logger.info("Generating heavy report...", {"reportId": report_id})
# ... perform resource-intensive work here ...
time.sleep(5) # example 5 second delay
logger.info("Report generated", {"reportId": report_id})
return {"status_code": 200, "body": {"success": True, "reportId": report_id}}
iii.register_function({"id": "reports::generate"}, generate_report)
#[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let url = std::env::var("III_URL").unwrap_or_else(|_| "ws://127.0.0.1:49134".to_string()); let iii = register_worker(&url, InitOptions::default());
iii.register_function(
RegisterFunctionMessage {
id: "reports::generate".into(),
description: None,
request_format: None,
response_format: None,
metadata: None,
invocation: None,
},
|req| async move {
let logger = Logger::new();
let report_id = req["body"]["reportId"].as_str().unwrap_or("unknown");
logger.info("Generating heavy report...", Some(json!({ "reportId": report_id })));
// ... perform resource-intensive work here ...
tokio::time::sleep(std::time::Duration::from_secs(5)).await; // example 5 second delay
logger.info("Report generated", Some(json!({ "reportId": report_id })));
Ok(json!({
"status_code": 200,
"body": { "success": true, "reportId": report_id }
}))
},
);
</Tab>
</Tabs>
### 2. Trigger and Graceful Shutdown
The main thread connects, triggers the function, awaits its completion, then calls `shutdown()` (or `shutdown_async().await` in Rust for guaranteed telemetry flush) to cleanly close the WebSocket and exit the process.
<Tabs>
<Tab title="Node / TypeScript">
```typescript title="worker.ts"
async function main() {
console.log("Worker ready, triggering task...")
// Trigger the task and await its completion
const result = await iii.trigger({
function_id: 'reports::generate',
payload: { body: { reportId: '123' } },
})
console.log("Task completed", result.status_code, result.body)
console.log("Task completed. Shutting down worker...")
// Cleanly close the WebSocket connection
await iii.shutdown()
process.exit(0)
}
main().catch(console.error)
result = iii.trigger({ "function_id": "reports::generate", "payload": {"body": {"reportId": "123"}}, }) print(f"Task completed: status_code={result.get('status_code')} body={result.get('body')}")
print("Task completed. Shutting down worker...")
iii.shutdown()
</Tab>
<Tab title="Rust">
```rust title="worker.rs"
println!("Worker ready, triggering task...");
// Trigger the task and await its completion
let result = iii
.trigger(TriggerRequest {
function_id: "reports::generate".into(),
payload: json!({ "body": { "reportId": "123" } }),
action: None,
timeout_ms: None,
})
.await?;
println!("Task completed: {}", result);
println!("Task completed. Shutting down worker...");
// Cleanly close the WebSocket connection
iii.shutdown_async().await;
Ok(())
}
When the script runs, it connects to the Engine, waits for a single invocation of reports::generate, executes it, and cleanly disconnects and exits immediately after. This prevents zombie processes and automatically scales your infrastructure down once the heavy lifting is done.