iii

Create an Ephemeral Function

How to create a temporary worker that completes a task and gracefully shuts down.

Goal

Create a script that connects to the engine, waits to process a task (e.g., generating a heavy report), and then gracefully shuts down.

Context

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.

Steps

1. Initialize and Register

Connect to iii and register the reports::generate function handler to process the heavy CPU task.

worker.ts
import { registerWorker, Logger } from 'iii-sdk'

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 } }
})
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)
worker.rs
use iii_sdk::{Logger, RegisterFunctionMessage, TriggerRequest, register_worker, InitOptions};
use serde_json::json;

#[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 }
            }))
        },
    );

2. Trigger and Graceful Shutdown

The main thread connects, triggers the function, awaits its completion, then calls shutdown() to cleanly close the WebSocket and exit the process.

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)
worker.py
print("Worker ready, triggering task...")

# Trigger the task and await its completion
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...")

# Cleanly close the WebSocket connection
iii.shutdown()
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(())
}

Result

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.

On this page