Remove non-essential ES-client stats from /api/stats (#145120)

[PR #141434](https://github.com/elastic/kibana/pull/141434) exposes a
bunch of metrics related to the Elasticsearch Client in the `/api/stats`
endpoint.
While all these stats are interesting, some of them might be less
relevant than others right now.

Let's start by exposing only those stats that are more critical from a
monitoring standpoint.

<img width="440" alt="image"
src="https://user-images.githubusercontent.com/25349407/201688243-4e33cd88-5fa2-48b7-b8ca-2fd175271adc.png">
This commit is contained in:
Gerard Soldevila 2022-11-14 18:11:46 +01:00 committed by GitHub
parent e7730f0ab1
commit 46522d76da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 2 additions and 169 deletions

View file

@ -62,17 +62,9 @@ const mockedResponse: StatusResponse = {
},
},
elasticsearch_client: {
protocol: 'https',
connectedNodes: 3,
nodesWithActiveSockets: 3,
nodesWithIdleSockets: 1,
totalActiveSockets: 25,
totalIdleSockets: 2,
totalQueuedRequests: 0,
mostActiveNodeSockets: 15,
averageActiveSocketsPerNode: 8,
mostIdleNodeSockets: 2,
averageIdleSocketsPerNode: 0.5,
},
process: {
pid: 1,

View file

@ -32,7 +32,7 @@ describe('getAgentsSocketsStats()', () => {
},
});
const agent2 = getHttpAgentMock({
const agent2 = getHttpsAgentMock({
sockets: {
node1: [mockSocket, mockSocket, mockSocket],
node4: [mockSocket],
@ -47,101 +47,9 @@ describe('getAgentsSocketsStats()', () => {
const stats = getAgentsSocketsStats(new Set<Agent>([agent1, agent2]));
expect(stats).toEqual({
averageActiveSocketsPerNode: 2.6666666666666665,
averageIdleSocketsPerNode: 4.5,
connectedNodes: 4,
mostActiveNodeSockets: 6,
mostIdleNodeSockets: 8,
nodesWithActiveSockets: 3,
nodesWithIdleSockets: 2,
protocol: 'http',
totalActiveSockets: 8,
totalIdleSockets: 9,
totalQueuedRequests: 6,
});
});
it('takes into account Agent types to determine the `protocol`', () => {
const httpAgent = getHttpAgentMock({
sockets: { node1: [mockSocket] },
freeSockets: {},
requests: {},
});
const httpsAgent = getHttpsAgentMock({
sockets: { node1: [mockSocket] },
freeSockets: {},
requests: {},
});
const noAgents = new Set<Agent>();
const httpAgents = new Set<Agent>([httpAgent, httpAgent]);
const httpsAgents = new Set<Agent>([httpsAgent, httpsAgent]);
const mixedAgents = new Set<Agent>([httpAgent, httpsAgent]);
expect(getAgentsSocketsStats(noAgents).protocol).toEqual('none');
expect(getAgentsSocketsStats(httpAgents).protocol).toEqual('http');
expect(getAgentsSocketsStats(httpsAgents).protocol).toEqual('https');
expect(getAgentsSocketsStats(mixedAgents).protocol).toEqual('mixed');
});
it('does not take into account those Agents that have not had any connection to any node', () => {
const pristineAgentProps = {
sockets: {},
freeSockets: {},
requests: {},
};
const agent1 = getHttpAgentMock(pristineAgentProps);
const agent2 = getHttpAgentMock(pristineAgentProps);
const agent3 = getHttpAgentMock(pristineAgentProps);
const stats = getAgentsSocketsStats(new Set<Agent>([agent1, agent2, agent3]));
expect(stats).toEqual({
averageActiveSocketsPerNode: 0,
averageIdleSocketsPerNode: 0,
connectedNodes: 0,
mostActiveNodeSockets: 0,
mostIdleNodeSockets: 0,
nodesWithActiveSockets: 0,
nodesWithIdleSockets: 0,
protocol: 'none',
totalActiveSockets: 0,
totalIdleSockets: 0,
totalQueuedRequests: 0,
});
});
it('takes into account those Agents that have hold mappings to one or more nodes, but that do not currently have any pending requests, active connections or idle connections', () => {
const emptyAgentProps = {
sockets: {
node1: [],
},
freeSockets: {
node2: [],
},
requests: {
node3: [],
},
};
const agent1 = getHttpAgentMock(emptyAgentProps);
const agent2 = getHttpAgentMock(emptyAgentProps);
const stats = getAgentsSocketsStats(new Set<Agent>([agent1, agent2]));
expect(stats).toEqual({
averageActiveSocketsPerNode: 0,
averageIdleSocketsPerNode: 0,
connectedNodes: 3,
mostActiveNodeSockets: 0,
mostIdleNodeSockets: 0,
nodesWithActiveSockets: 0,
nodesWithIdleSockets: 0,
protocol: 'http',
totalActiveSockets: 0,
totalIdleSockets: 0,
totalQueuedRequests: 0,
});
});
});

View file

@ -7,23 +7,13 @@
*/
import { NetworkAgent } from '@kbn/core-elasticsearch-client-server-internal';
import { Agent as HttpsAgent } from 'https';
import { mean } from 'lodash';
import type {
ElasticsearchClientProtocol,
ElasticsearchClientsMetrics,
} from '@kbn/core-metrics-server';
import type { ElasticsearchClientsMetrics } from '@kbn/core-metrics-server';
export const getAgentsSocketsStats = (agents: Set<NetworkAgent>): ElasticsearchClientsMetrics => {
const nodes = new Set<string>();
let totalActiveSockets = 0;
let totalIdleSockets = 0;
let totalQueuedRequests = 0;
let http: boolean = false;
let https: boolean = false;
const nodesWithActiveSockets: Record<string, number> = {};
const nodesWithIdleSockets: Record<string, number> = {};
agents.forEach((agent) => {
const agentRequests = Object.entries(agent.requests) ?? [];
@ -31,9 +21,6 @@ export const getAgentsSocketsStats = (agents: Set<NetworkAgent>): ElasticsearchC
const agentFreeSockets = Object.entries(agent.freeSockets) ?? [];
if (agentRequests.length || agentSockets.length || agentFreeSockets.length) {
if (agent instanceof HttpsAgent) https = true;
else http = true;
agentRequests.forEach(([node, queue]) => {
nodes.add(node);
totalQueuedRequests += queue?.length ?? 0;
@ -43,39 +30,19 @@ export const getAgentsSocketsStats = (agents: Set<NetworkAgent>): ElasticsearchC
nodes.add(node);
const activeSockets = sockets?.length ?? 0;
totalActiveSockets += activeSockets;
nodesWithActiveSockets[node] = (nodesWithActiveSockets[node] ?? 0) + activeSockets;
});
agentFreeSockets.forEach(([node, freeSockets]) => {
nodes.add(node);
const idleSockets = freeSockets?.length ?? 0;
totalIdleSockets += idleSockets;
nodesWithIdleSockets[node] = (nodesWithIdleSockets[node] ?? 0) + idleSockets;
});
}
});
const activeSocketCounters = Object.values(nodesWithActiveSockets);
const idleSocketCounters = Object.values(nodesWithIdleSockets);
const protocol: ElasticsearchClientProtocol = http
? https
? 'mixed'
: 'http'
: https
? 'https'
: 'none';
return {
protocol,
connectedNodes: nodes.size,
nodesWithActiveSockets: activeSocketCounters.filter(Boolean).length,
nodesWithIdleSockets: idleSocketCounters.filter(Boolean).length,
totalActiveSockets,
totalIdleSockets,
totalQueuedRequests,
mostActiveNodeSockets: activeSocketCounters.length ? Math.max(...activeSocketCounters) : 0,
averageActiveSocketsPerNode: activeSocketCounters.length ? mean(activeSocketCounters) : 0,
mostIdleNodeSockets: idleSocketCounters.length ? Math.max(...idleSocketCounters) : 0,
averageIdleSocketsPerNode: idleSocketCounters.length ? mean(idleSocketCounters) : 0,
};
};

View file

@ -24,17 +24,9 @@ import type {
} from '@kbn/core-metrics-server';
export const sampleEsClientMetrics: ElasticsearchClientsMetrics = {
protocol: 'https',
connectedNodes: 3,
nodesWithActiveSockets: 3,
nodesWithIdleSockets: 1,
totalActiveSockets: 25,
totalIdleSockets: 2,
totalQueuedRequests: 0,
mostActiveNodeSockets: 15,
averageActiveSocketsPerNode: 8,
mostIdleNodeSockets: 2,
averageIdleSocketsPerNode: 0.5,
};
const createInternalSetupContractMock = () => {

View file

@ -52,30 +52,12 @@ export type ElasticsearchClientProtocol = 'none' | 'http' | 'https' | 'mixed';
* @public
*/
export interface ElasticsearchClientsMetrics {
/** The protocol (or protocols) that these Agents are using */
protocol: ElasticsearchClientProtocol;
/** Number of ES nodes that ES-js client is connecting to */
connectedNodes: number;
/** Number of nodes with active connections */
nodesWithActiveSockets: number;
/** Number of nodes with available connections (alive but idle).
* Note that a node can have both active and idle connections at the same time
*/
nodesWithIdleSockets: number;
/** Total number of active sockets (all nodes, all connections) */
totalActiveSockets: number;
/** Total number of available sockets (alive but idle, all nodes, all connections) */
totalIdleSockets: number;
/** Total number of queued requests (all nodes, all connections) */
totalQueuedRequests: number;
/** Number of active connections of the node with most active connections */
mostActiveNodeSockets: number;
/** Average of active sockets per node (all connections) */
averageActiveSocketsPerNode: number;
/** Number of idle connections of the node with most idle connections */
mostIdleNodeSockets: number;
/** Average of available (idle) sockets per node (all connections) */
averageIdleSocketsPerNode: number;
}
/**

View file

@ -4,14 +4,6 @@ exports[`telemetry_ops_stats should return something when there is a metric 1`]
Object {
"concurrent_connections": 1,
"elasticsearch_client": Object {
"averageActiveSocketsPerNode": 8,
"averageIdleSocketsPerNode": 0.5,
"connectedNodes": 3,
"mostActiveNodeSockets": 15,
"mostIdleNodeSockets": 2,
"nodesWithActiveSockets": 3,
"nodesWithIdleSockets": 1,
"protocol": "https",
"totalActiveSockets": 25,
"totalIdleSockets": 2,
"totalQueuedRequests": 0,