Skip to main content

What a namespace is

A namespace is a routing value that the engine stores with a function id and a worker name. It is not part of either value. For example state::get can exist as the same function id in default, orders, and analytics namespaces. The engine uses these registry keys:
  • (namespace, function_id) for functions.
  • (namespace, worker_name) for workers.
A worker connection has one namespace. Its function and service registrations, and the observers it creates, use that namespace.

The two namespaces an observer names

An observer names two functions, and each one resolves in a namespace:
  • The source’s namespace is where the observed function is found. Absent, it is default for engine::* and configuration::* functions, and the observing worker’s namespace for every other function.
  • The target’s namespace is where the target runs when the source runs. Absent, it is the observing worker’s namespace.
The engine hosts its own event sources in the default namespace. A worker in any namespace observes engine::* and configuration::* sources without naming one. For observability::* and stream::* sources, a worker in another namespace names namespace: "default". Every other observed function resolves only in the observer’s namespace (the one it names, or the observing worker’s): a worker in orders that observes http::on-request reaches the http worker in orders, and names namespace: "default" to reach the one in default.

What namespaces enable

Namespaces let you ship one reusable worker package and deploy it for more than one tenant on the same engine. Each tenant can run a worker named state and expose state::get. The worker package does not need tenant-specific names.
Without namespaces, the two deployments claim the same worker name and function id. The engine cannot route a call to the correct tenant. With namespaces, the tenant namespace selects the deployment.

Why the namespace is not a prefix

iii does not change state::get to orders/state::get. A function id is part of the worker contract. It appears in schemas, skills, console output, commands, and documentation. A deployment value must not change that contract. The worker author defines the function id. The operator selects the namespace when they deploy the worker. These two values stay separate.

Why routing is strict

A call with namespace: "orders" resolves in orders only. A call with no namespace resolves in the namespace of the calling worker. The engine does not search other namespaces after a miss. A fallback could send a call to another tenant. Instead, the engine returns function_not_found and lists the namespaces where the function id exists. The discovery functions are less strict when no namespace is present. For engine::functions::info and engine::workers::info, a default entry has priority. If there is no default entry, a name that exists in only one namespace resolves there. A name that exists in multiple namespaces returns an ambiguity. An explicit namespace always uses strict resolution.

Why the engine rejects a collision

Only one live worker can own a worker name in a namespace. Only one live worker can own a function id in a namespace. The engine rejects a second owner instead of replacing the current owner. A worker-name conflict is fatal. The engine closes the new connection because the worker cannot use its declared identity. A function-id conflict rejects only that function. The worker stays connected and serves its other functions. Worker registration and function registration are separate operations. A connected worker does not confirm that all its functions are registered. The SDK reports a function conflict as a warning. A restart does not conflict with its own connection after teardown starts. The new connection can reclaim the same worker name.

How a connection gets its namespace

A worker declares its namespace in the engine::workers::register call. The engine starts a namespace timer when the WebSocket connection opens. The default timeout is 5000 ms. A client can send registration messages before engine::workers::register. The engine holds RegisterFunction, UnregisterFunction, RegisterService, Observe, and Unobserve messages until it knows the namespace. It does not register them in default and move them later. If engine::workers::register arrives first, the engine sets the declared namespace. An absent value selects default. The engine then processes the held messages in arrival order. If the timer expires first, the engine sets the connection namespace to default and processes the held messages. A later worker registration cannot change that namespace. To change the timeout, use registration_namespace_grace_ms or III_NAMESPACE_GRACE_MS. See Registration namespace timeout.

Use namespaces

For deployment configuration, SDK examples, cross-namespace calls, observer targets, discovery, and registration errors, see Use namespaces.