Skip to content

Heartbeat API

This is the API reference for the heartbeat subsystem: the heartbeat type and its lifecycle, the HeartbeatMonitor sampler, how the runtime wires it, and the config model that drives it. For the conceptual overview — the state keepalive, the sys metric, and the measures table — see the Heartbeat guide.

What the heartbeat does (identical in all four languages): every intervalSecs tick it publishes a state keepalive to ecv1/{device}/{component}/state (header name state, version 1.0, body {"status":"RUNNING","uptimeSecs":n} — optionally extended with a per-connection instances[] array, see below) through the library’s privileged reserved-publish path, and emits the enabled system measures as a metric named sys through the metric subsystem. On graceful shutdown it publishes a best-effort {"status":"STOPPED"} state, at most once.

Constant Value
State message name state
State message version 1.0
Measures metric name sys
Default interval 5 seconds
Default keepalive destination local

A component that fronts several southbound connections reports each connection’s reachability in the RUNNING state body’s optional instances[] array — without minting a UNS instance per connection (identity, data, and lifecycle stay at component scope, with no instance token). You register a connectivity provider; the heartbeat samples it on every RUNNING tick. This is the terse API surface — for the concept, the on-wire array, and worked examples, see the guide’s Per-instance connectivity section.

InstanceConnectivity — one entry per connection: { instance, connected, detail? } (the connection id, its live reachability, and an optional human detail such as the endpoint or a down reason).

Language Factory / constructor Provider you register (return type)
Java InstanceConnectivity.of(instance, connected[, detail]) InstanceConnectivityProviderList<InstanceConnectivity> instanceConnectivity()
Python InstanceConnectivity.of(instance, connected[, detail]) a Callable[[], list[InstanceConnectivity]] (or None to clear)
Rust InstanceConnectivity::of(instance, connected) / ::new(instance, connected, Option<detail>) Arc<dyn Fn() -> Vec<InstanceConnectivity> + Send + Sync>
TypeScript InstanceConnectivity.of(instance, connected[, detail]) () => InstanceConnectivity[]

Registering it — note the naming divergence (Java/TypeScript camelCase, Python/Rust snake_case):

Language Setter
Java gg.setInstanceConnectivityProvider(provider)
Python gg.set_instance_connectivity_provider(provider)
Rust gg.set_instance_connectivity_provider(Some(Arc::new(provider)))
TypeScript gg.setInstanceConnectivityProvider(provider)

Rules. The provider is sampled on RUNNING keepalives only — the shutdown STOPPED state never carries instances[]. It is best-effort: a provider that returns null / None / an empty list, or one that throws or panics, simply omits the instances[] section for that tick and never suppresses the keepalive. Registering again replaces the provider; passing null / None / undefined clears it.

com.mbreissi.edgecommons.heartbeat.Heartbeat implements ConfigurationChangeListener. Its constructor is package-private; the runtime constructs it via HeartbeatBuilder.

public class Heartbeat implements ConfigurationChangeListener {
// Register (or replace, or clear with null) the per-instance connectivity provider whose
// result shapes each RUNNING state body's instances[] array — the ONE runtime lever, wired
// from EdgeCommons.setInstanceConnectivityProvider (see "Per-instance connectivity" above).
public void setInstanceConnectivityProvider(InstanceConnectivityProvider provider);
// Re-emit the RUNNING state keepalive immediately, out of band from the periodic schedule
// (backs the republish-state broadcast re-announce). Respects heartbeat.enabled; best-effort.
public void publishStateNow();
// The component's monotonic uptime in whole seconds — the value the RUNNING keepalive carries
// as uptimeSecs; backs the command inbox's `ping` verb so ping and keepalives agree.
public long getUptimeSecs();
// Stop: cancel the periodic task, publish the best-effort STOPPED state
// (at most once; failures are swallowed), and shut down the scheduler.
public void close();
// Hot-reload hook: re-initializes the schedule on a config change.
public boolean onConfigurationChanged();
}
// package constants: STATE_MESSAGE_NAME = "state", STATE_MESSAGE_VERSION = "1.0",
// SYS_METRIC_NAME = "sys"

Beyond close() and onConfigurationChanged(), the Java Heartbeat therefore exposes three more public methods: setInstanceConnectivityProvider(...) (the runtime lever — see above), publishStateNow() (an out-of-band RUNNING re-announce that backs the republish-state broadcast), and getUptimeSecs() (the monotonic uptime the ping command verb reports).

Language Type Construct Stop Hot reload
Java Heartbeat HeartbeatBuilder.build() (runtime) close() (+ best-effort STOPPED) onConfigurationChanged() listener
Python EnhancedHeartbeat EnhancedHeartbeat(config_service) (runtime) stop() (+ best-effort STOPPED) on_configuration_change() listener
Rust Heartbeat Heartbeat::start(...)crate-private drop (RAII, + best-effort STOPPED) re-reads live config each tick
TypeScript Heartbeat Heartbeat.start(...) (runtime) stop() async (+ best-effort STOPPED) re-reads live config each tick

In every language enabled, measures, and destination changes apply on the next tick, and an intervalSecs change rebuilds the schedule. Every tick is exception-guarded, and the state and metric halves are individually best-effort — a failure in one never suppresses the other.

In production you never call these — the runtime wires the heartbeat for you. The snippets show what the runtime does (also the path the tests exercise).

HeartbeatBuilder requires both a messaging client (whose reservedPublisher() performs the state publish) and a metric emitter (for the sys metric); build() throws IllegalStateException if either is null. create(null) throws IllegalArgumentException.

import com.mbreissi.edgecommons.heartbeat.Heartbeat;
import com.mbreissi.edgecommons.heartbeat.HeartbeatBuilder;
Heartbeat heartbeat = HeartbeatBuilder.create(configManager)
.withMessagingService(messagingClient) // the state keepalive path
.withMetricService(metricEmitter) // the sys metric path
.build(); // first tick fires at delay 0
heartbeat.close(); // cancel the task + best-effort STOPPED + shut down the scheduler

HeartbeatMonitor is the sampler the heartbeat task uses each tick. It collects only the enabled measures into a nested stats object; the sys metric flattens it to one value per inner key. You can also use it directly to read process/system health. The constructor input differs by language: Java takes the HeartbeatConfiguration, Python takes the ConfigManager, and Rust/TypeScript take a Measures struct of toggles.

import com.mbreissi.edgecommons.heartbeat.HeartbeatMonitor;
import com.google.gson.JsonObject;
HeartbeatMonitor monitor = new HeartbeatMonitor(heartbeatConfiguration);
JsonObject stats = monitor.getStats(); // nested per-measure object; disabled measures omitted
monitor.updateMetrics(); // advance the OSHI process snapshot for the next
// CPU-between-ticks delta (getStats() calls it internally)

When a measure is enabled, getStats includes its nested object; disabled measures are omitted. The shape is identical across all four languages:

{
"cpu": { "cpu_usage": 12.5 },
"memory": { "memory_usage": 84 },
"disk": { "disk_total": 512.0, "disk_used": 210.4, "disk_free": 301.6 },
"threads": { "threads": 14 },
"files": { "files": 23 },
"fds": { "fds": 31 }
}

The sys metric flattens this to one metric value per inner key (cpu_usage, memory_usage, disk_total, …). Units and per-platform availability (for example Java fds is -1 on Windows, and TypeScript threads / files / fds are Linux-only) are documented in the guide’s measures table.

Each SDK has a typed model for the heartbeat config section. The accessors below are read by the runtime; you rarely touch them directly.

com.mbreissi.edgecommons.config.HeartbeatConfiguration:

public class HeartbeatConfiguration {
public static final String DEFAULT_DESTINATION = "local";
public boolean isEnabled(); // default true
public int getIntervalSecs(); // default 5, minimum 1
public boolean includeCpu();
public boolean includeMemory();
public boolean includeDisk();
public boolean includeThreads();
public boolean includeFiles();
public boolean includeFds();
public String getDestination(); // "local" | "northbound"
}

The section is validated against the heartbeat schema section of schema/edgecommons-config-schema.json. The defaults are identical in all four languages (on / 5 s / cpu+memory / local):

{
"heartbeat": {
"enabled": true,
"intervalSecs": 5,
"measures": {
"cpu": true,
"memory": true,
"disk": false,
"threads": false,
"files": false,
"fds": false
},
"destination": "local"
}
}
Field Type Default Notes
enabled boolean true Turns the whole heartbeat (keepalive + sys metric) on/off.
intervalSecs integer (min 1) 5 Tick period.
measures.cpumeasures.fds boolean cpu/memory on, rest off Per-measure toggles for the sys metric.
destination string local local | northbound — the state keepalive destination only; measures route via metricEmission.

The sys metric’s storageResolution is 1 when intervalSecs is under 60, otherwise 60 (all four languages).