Service Commands
Service commands let you control running Strata services at runtime without restarting them. You can adjust concurrency, trigger graceful shutdowns, query service info, and tune event loop settings -- all by sending a message over the same backend your services already use.
Commands use a pub/sub model, not request/response. They are best-effort delivery: the backend does not retry if a service is unavailable, and delivery is not guaranteed. For the Redis backends, commands are sent via Redis pub/sub.
How Commands Work
Commands are lightweight JSON messages published to a target channel. Services subscribe to these channels on startup and handle incoming commands automatically.
Command Targeting
Commands can be sent at three levels of specificity:
| Target Pattern | Scope |
|---|---|
Services | Every service connected to this backend |
Services:<ServiceGroup> | Every instance in a specific service group |
Services:<ServiceGroup>:<ServiceID> | A single service instance |
This gives you fine-grained control. Broadcast a shutdown to an entire service group, or adjust concurrency on one specific instance.
Command Envelope
Every command is a JSON object with this structure:
interface ServiceCommand
{
id : string; // Unique command ID
command : ValidCommandName; // 'info' | 'concurrency' | 'shutdown' | 'toobusy'
payload ?: Record<string, unknown>; // Command-specific data
}The info command adds a responseChannel field so the service knows where to send the reply:
interface StrataInfoCommand extends ServiceCommand
{
command : 'info';
responseChannel : string; // Channel for the response
}Built-in Commands
Strata defines four built-in commands. These are the only valid command names (ValidCommandName).
info
Requests service information from any service that receives the command. This is the only command that returns a response -- the service sends back a ServiceInfo payload containing its ID, version, concurrency, registered contexts, and more.
const response = await client.command('info', 'Services:UserService');
console.log(response?.payload);
// {
// id: 'l7aNdfrRDZW8nt0FBaSS',
// serviceName: 'User Service',
// serviceGroup: 'UserService',
// version: '1.2.0',
// strataVersion: '2.0.0',
// concurrency: 32,
// outstanding: 3,
// hostname: 'worker-01',
// contexts: { service: ['info'], users: ['get', 'create'] },
// ...
// }TIP
The info command targets a specific instance and gets a direct response. The service/info operation (available via client.request()) goes through the normal request queue and is handled by whichever instance picks it up first. Use the command when you need info from a specific instance; use the operation for general health checks.
concurrency
Changes the maximum number of concurrent requests a service will process. Takes effect immediately.
// Set concurrency to 50 for all instances in UserService
await client.command('concurrency', 'Services:UserService', { concurrency: 50 });
// Set concurrency on a specific instance
await client.command('concurrency', 'Services:UserService:abc123', { concurrency: 10 });The payload requires a concurrency field with a number greater than or equal to 0.
shutdown
Initiates a shutdown of the target service(s).
// Graceful shutdown (default) -- finishes in-flight requests, then exits with code 0
await client.command('shutdown', 'Services:UserService');
// Graceful shutdown with a custom exit code
await client.command('shutdown', 'Services:UserService', {
graceful: true,
exitCode: 0,
});
// Immediate shutdown -- process exits immediately
await client.command('shutdown', 'Services:UserService', {
graceful: false,
exitCode: 1,
});| Payload Field | Type | Default | Description |
|---|---|---|---|
graceful | boolean | true | If false, calls process.exit() immediately |
exitCode | number | 0 | Exit code for the process |
If the payload is omitted entirely, the service performs a graceful shutdown with exit code 0.
toobusy
Adjusts the node-toobusy parameters that control dynamic concurrency. This lets you tune how aggressively Strata backs off when the event loop is under load.
await client.command('toobusy', 'Services:UserService', {
maxLag: 70,
interval: 500,
smoothingFactorOnRise: 0.33,
smoothingFactorOnFall: 0.75,
});| Payload Field | Type | Description |
|---|---|---|
maxLag | number | Maximum event loop lag (ms) before the service is considered too busy |
interval | number | How often (ms) to check event loop lag |
smoothingFactorOnRise | number | Smoothing factor when lag is increasing (0-1) |
smoothingFactorOnFall | number | Smoothing factor when lag is decreasing (0-1) |
All fields are optional. Only the fields you include will be updated.
Sending Commands
Via StrataClient
The client.command() method is the primary way to send commands:
import { StrataClient } from '@strata-js/strata';
const client = new StrataClient({
client: { name: 'AdminClient' },
backend: { type: 'redis-streams', redis: { host: 'localhost', port: 6379 } },
});
await client.start();
// client.command(command, target?, payload?)
await client.command('concurrency', 'Services:UserService', { concurrency: 25 });The method signature:
client.command(
command : ValidCommandName, // 'info' | 'concurrency' | 'shutdown' | 'toobusy'
target ?: string, // Default: 'Services' (all services)
payload ?: Record<string, unknown>
) : Promise<ServiceCommandResponse | undefined>- Returns a response only for commands that produce one (currently just
info). targetdefaults to'Services'-- which broadcasts to every service on the backend.
Directly via Redis
Since commands are just pub/sub messages, you can send them from any Redis client without Strata:
import Redis from 'ioredis';
const redis = new Redis();
const command = {
id: 'manual-cmd-001',
command: 'concurrency',
payload: { concurrency: 5 },
};
await redis.publish('Services:UserService', JSON.stringify(command));This is useful for ops tooling, scripts, or any situation where you don't want to instantiate a full StrataClient.
Command Responses
Only the info command returns a response. The response envelope is:
interface ServiceCommandResponse
{
id : string; // Matches the command's ID
payload ?: Record<string, unknown>; // Response data
}When you call client.command('info', ...), Strata automatically sets up a responseChannel and waits for the reply. The response is returned directly from the command() call:
const response = await client.command('info', 'Services:UserService:abc123');
if(response)
{
console.log(`Service ${ response.payload.serviceGroup } is running v${ response.payload.version }`);
}For commands without responses (concurrency, shutdown, toobusy), the method returns undefined.
Unknown Commands
If a service receives a command it doesn't recognize, it logs a warning and ignores it. No error response is sent. There is currently no mechanism for registering custom command handlers on the service side -- the four built-in commands are the only ones supported.
Next Steps
- Core API: Application -- service configuration and lifecycle.
- Core API: Client --
client.command()API reference. - Backends -- how commands are delivered on each backend.