Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/opamp-client-node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# @elastic/opamp-client-node Changelog

## v0.4.0

- BREAKING CHANGE: The `heartbeatIntervalSeconds` option to `createOpAMPClient`
used to *clamp* the given value to `[100ms, 1d]`. Starting in this version,
a value less than 100ms will **be ignored**, and the default value will be
used. The reason for this is to ignore a possible accidental error case where
*zero* or a negative number is provided, resulting in a too-fast 100ms
interval. (The 100ms lower bound really only exists for faster testing. It
is not a reasonable value for production.)

- Add `opampClient.setHeartbeatIntervalSeconds(num)` and
`.resetHeartbeatIntervalSeconds()` methods for dynamically changing the
heartbeat interval used by the OpAMP client. Values less than 100ms are
ignored (with a log.warn) and values greater than 1d are clamped to 1d.
[#1128](https://github.com/elastic/elastic-otel-node/issues/1128)

## v0.3.0

- Add TLS and mTLS support. [#1044](https://github.com/elastic/elastic-otel-node/issues/1044)
Expand Down
36 changes: 32 additions & 4 deletions packages/opamp-client-node/lib/opamp-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ function normalizeHeartbeatIntervalSeconds(input) {
} else if (typeof input !== 'number' || isNaN(input)) {
throw new Error(`invalid "heartbeatIntervalSeconds" value: ${input}`);
} else if (input < MIN_HEARTBEAT_INTERVAL_SECONDS) {
return MIN_HEARTBEAT_INTERVAL_SECONDS;
throw new Error(
`"heartbeatIntervalSeconds" value is too short (<100ms): ${input}`
);
} else if (input > MAX_HEARTBEAT_INTERVAL_SECONDS) {
return MAX_HEARTBEAT_INTERVAL_SECONDS;
} else {
Expand Down Expand Up @@ -174,8 +176,8 @@ function normalizeHeartbeatIntervalSeconds(input) {
* remote config. Receiving remote config requires setting the
* `AcceptsRemoteConfig` capability in `capabilities`.
* @property {Number} [heartbeatIntervalSeconds] The approximate time between
* heartbeat messages sent by the client. Default 30.
* Clamped to [100ms, 1d].
* heartbeat messages sent by the client. Default 30. Values less than
* 100ms will be ignored. Values greater than 1d will be clamped to 1d.
* @property {number} [headersTimeout] The timeout (in milliseconds) to wait
* for the response headers on a request to the OpAMP server. Default 10s.
* @property {number} [bodyTimeout] The timeout (in milliseconds) to wait for
Expand Down Expand Up @@ -222,11 +224,12 @@ class OpAMPClient {
} catch (err) {
this._log.warn(
{err, heartbeatIntervalSeconds: opts.heartbeatIntervalSeconds},
'invalid heartbeatIntervalSeconds'
'invalid heartbeatIntervalSeconds, using default value'
);
this._heartbeatIntervalMs =
DEFAULT_HEARTBEAT_INTERVAL_SECONDS * 1000;
}
this._initialHeartbeatIntervalMs = this._heartbeatIntervalMs;
this._onMessage = opts.onMessage;

if (opts.diagEnabled) {
Expand Down Expand Up @@ -303,6 +306,31 @@ class OpAMPClient {
}
}

/**
* Dynamically set the heartbeat interval of the client to a new value.
*
* @param {Number} n - A number of seconds.
*/
setHeartbeatIntervalSeconds(n) {
try {
this._heartbeatIntervalMs =
normalizeHeartbeatIntervalSeconds(n) * 1000;
} catch (err) {
this._log.warn(
{err, heartbeatIntervalSeconds: n},
'invalid heartbeatIntervalSeconds, ignoring'
);
}
}

/**
* Reset the heartbeat interval used by the client back to its initial
* value set at creation time.
*/
resetHeartbeatIntervalSeconds() {
this._heartbeatIntervalMs = this._initialHeartbeatIntervalMs;
}

/**
* Dev Note: This client manages the `instanceUid`, so I'm not sure if this
* API method is useful. The instanceUid *can* be changed by the OpAMP
Expand Down
2 changes: 1 addition & 1 deletion packages/opamp-client-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@elastic/opamp-client-node",
"version": "0.3.0",
"version": "0.4.0",
"type": "commonjs",
"description": "an OpAMP client for Node.js",
"publishConfig": {
Expand Down
53 changes: 53 additions & 0 deletions packages/opamp-client-node/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,59 @@ test('OpAMPClient', (suite) => {
t.end();
});

suite.test('client.{set,reset}HeartbeatIntervalSeconds', async (t) => {
const server = new MockOpAMPServer({
logLevel: 'warn', // use 'debug' for some debugging of the server
hostname: '127.0.0.1',
port: 0,
testMode: true,
});
await server.start();

const client = createOpAMPClient({
log,
endpoint: server.endpoint,
heartbeatIntervalSeconds: 0.5,
diagEnabled: true,
});
client.setAgentDescription({identifyingAttributes: {foo: 'bar'}});
client.start();

// Cheating a bit by testing a private attribute.
t.equal(client._heartbeatIntervalMs, 500, 'initial value');

client.setHeartbeatIntervalSeconds(1);
t.equal(client._heartbeatIntervalMs, 1000, 'valid value works');

client.setHeartbeatIntervalSeconds(0.01);
t.equal(client._heartbeatIntervalMs, 1000, 'too low value ignored');

client.setHeartbeatIntervalSeconds(0.1);
t.equal(client._heartbeatIntervalMs, 100, 'min value works');

client.setHeartbeatIntervalSeconds('bogus');
t.equal(client._heartbeatIntervalMs, 100, 'invalid type is ignored');

const DAY_IN_S = 86400;
client.setHeartbeatIntervalSeconds(DAY_IN_S * 2);
t.equal(
client._heartbeatIntervalMs,
DAY_IN_S * 1000,
'too large value is clamped to 1d'
);

client.resetHeartbeatIntervalSeconds();
t.equal(
client._heartbeatIntervalMs,
500, // the initial value
'resetHeartbeatIntervalSeconds works'
);

await client.shutdown();
await server.close();
t.end();
});

suite.test('remote config', async (t) => {
// Setup MockOpAMPServer to provide `config` as remote config.
const config = {foo: 42};
Expand Down
20 changes: 16 additions & 4 deletions packages/opamp-client-node/types/opamp-client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export type OpAMPClientOptions = {
onMessage?: OnMessageCallback;
/**
* The approximate time between
* heartbeat messages sent by the client. Default 30.
* Clamped to [100ms, 1d].
* heartbeat messages sent by the client. Default 30. Values less than
* 100ms will be ignored. Values greater than 1d will be clamped to 1d.
*/
heartbeatIntervalSeconds?: number;
/**
Expand Down Expand Up @@ -138,8 +138,8 @@ export function createOpAMPClient(opts: OpAMPClientOptions): OpAMPClient;
* remote config. Receiving remote config requires setting the
* `AcceptsRemoteConfig` capability in `capabilities`.
* @property {Number} [heartbeatIntervalSeconds] The approximate time between
* heartbeat messages sent by the client. Default 30.
* Clamped to [100ms, 1d].
* heartbeat messages sent by the client. Default 30. Values less than
* 100ms will be ignored. Values greater than 1d will be clamped to 1d.
* @property {number} [headersTimeout] The timeout (in milliseconds) to wait
* for the response headers on a request to the OpAMP server. Default 10s.
* @property {number} [bodyTimeout] The timeout (in milliseconds) to wait for
Expand Down Expand Up @@ -175,6 +175,7 @@ declare class OpAMPClient {
_instanceUidStr: string;
_capabilities: bigint;
_heartbeatIntervalMs: number;
_initialHeartbeatIntervalMs: number;
_onMessage: OnMessageCallback;
_diagChs: {
"opamp-client.send.success": import("diagnostics_channel").Channel<unknown, unknown>;
Expand Down Expand Up @@ -209,6 +210,17 @@ declare class OpAMPClient {
nonIdentifyingAttributes?: object;
}): void;
_agentDescriptionSer: any;
/**
* Dynamically set the heartbeat interval of the client to a new value.
*
* @param {Number} n - A number of seconds.
*/
setHeartbeatIntervalSeconds(n: number): void;
/**
* Reset the heartbeat interval used by the client back to its initial
* value set at creation time.
*/
resetHeartbeatIntervalSeconds(): void;
/**
* Dev Note: This client manages the `instanceUid`, so I'm not sure if this
* API method is useful. The instanceUid *can* be changed by the OpAMP
Expand Down
Loading