Skip to content

Call a Function

View Markdown

A Function has three ways to start a call. Each call is a FunctionCall object, so you can look it up later, wait for it from another process or cancel it.

Call What it does
f.remote(x) Runs one call and blocks until it returns its result.
f.spawn(x) Starts one call and returns a handle: handle.get(timeout=...) waits, handle.cancel() stops it.
f.map(xs) Starts one call per input and yields the results in input order.
with app.run():
print(square.remote(7)) # 49
handle = square.spawn(8)
print(handle.get(timeout=600)) # 64
print(list(square.map(range(1000)))) # 1,000 results, in order

.map() creates its calls in batches of up to 1,000 per request, and a batch is all or nothing: a request that fails leaves no calls behind, and sending it again does not create them twice. Results come back in input order whichever worker finishes first. order_outputs=False yields them as they finish, and return_exceptions=True yields a failed input’s exception instead of stopping the loop.

results = list(process.map(files, order_outputs=False, return_exceptions=True))

Several callers can map into one Function at the same time. Workers take calls from each caller in turn, so a caller that submits 10 inputs is not stuck behind another caller’s 10,000.

Arguments and results are serialized with cloudpickle. Values up to 64 KiB travel inside the call. Larger ones are uploaded once, by content hash, and the call carries a reference, so a large argument costs nothing extra to send again. The caller and the worker image must run the same Python minor version, 3.10 to 3.13.

An exception raised inside the Function is raised again in your process as its own type when that type can be imported there. Otherwise you get nodus.errors.RemoteError. Either way the remote traceback is attached.

try:
divide.remote(1, 0)
except ZeroDivisionError:
...

Two things can send a call back to the queue, and they are counted separately:

  • The function raised. With retries=nodus.Retries(max_retries=3) the call is retried with exponential backoff, up to ten times. Without retries the exception ends the call.
  • The worker was lost. A worker that disappears or is replaced does not count as a retry. Its calls go back to the queue and run on another worker, up to recovery.maxAttempts times (eight by default), then end as Failed with the reason RecoveryLimitExceeded.

A call can finish only once. A result that arrives from a worker that no longer holds the call is refused, so a call that was re-dispatched never ends with two results.

call = nodus.FunctionCall.from_name("fn-warm-ping-bcdfghjklm")
print(call.get(timeout=60))
Terminal window
$ nodus get functioncalls -l nodus.dev/map=<map id>
$ nodus get functioncall <name> -o yaml # phase, result reference, retries, recoveries, costUSD

A call keeps its result for seven days after it ends. Each call reports status.costUSD, the worker time attributed to it; the billing page explains how it relates to what you pay.