Skip to content

Commit 2aee83e

Browse files
committed
Enhance Cost Optimization Analyzer to identify unused IntegrationHub spokes and SOAP services, and improve endpoint duplication checks
1 parent 37a5043 commit 2aee83e

File tree

1 file changed

+120
-25
lines changed

1 file changed

+120
-25
lines changed

Specialized Areas/Cost Optimization/Instance Cost Analyzer/cost_analyzer.js

Lines changed: 120 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -72,41 +72,120 @@ CostOptimizationAnalyzer.prototype = {
7272
var threshold = gs.getProperty('cost.analyzer.integration.threshold', '30');
7373
var cutoffDate = gs.daysAgoStart(parseInt(threshold));
7474

75-
// Check REST Messages
76-
var restGr = new GlideRecord('sys_rest_message');
77-
restGr.query();
75+
// Check IntegrationHub Spoke usage
76+
var unusedSpokes = this.findUnusedSpokes(cutoffDate);
77+
redundantIntegrations = redundantIntegrations.concat(unusedSpokes);
7878

79-
while (restGr.next()) {
80-
var lastUsed = this.getIntegrationLastUsed(restGr.sys_id.toString(), 'rest');
81-
if (lastUsed && lastUsed < cutoffDate) {
82-
redundantIntegrations.push({
83-
name: restGr.name.toString(),
84-
type: 'REST',
85-
endpoint: restGr.endpoint.toString(),
86-
lastUsed: lastUsed,
79+
// Check SOAP Web Services
80+
var unusedSoap = this.findUnusedSoapServices(cutoffDate);
81+
redundantIntegrations = redundantIntegrations.concat(unusedSoap);
82+
83+
// Check for duplicate endpoints (still valuable)
84+
var duplicates = this.findDuplicateEndpoints();
85+
redundantIntegrations = redundantIntegrations.concat(duplicates);
86+
87+
return redundantIntegrations;
88+
},
89+
90+
findUnusedSpokes: function(cutoffDate) {
91+
var unusedSpokes = [];
92+
93+
// Get all installed spokes
94+
var spokeGr = new GlideRecord('sys_app');
95+
spokeGr.addQuery('source', 'sn_app_store');
96+
spokeGr.addQuery('name', 'CONTAINS', 'spoke');
97+
spokeGr.query();
98+
99+
while (spokeGr.next()) {
100+
var spokeId = spokeGr.sys_id.toString();
101+
var spokeName = spokeGr.name.toString();
102+
103+
// Check usage in ua_ih_usage table
104+
var usageGr = new GlideRecord('ua_ih_usage');
105+
usageGr.addQuery('spoke', spokeId);
106+
usageGr.addQuery('sys_created_on', '>=', cutoffDate);
107+
usageGr.setLimit(1);
108+
usageGr.query();
109+
110+
if (!usageGr.hasNext()) {
111+
// No recent usage found
112+
var lastUsage = this.getLastSpokeUsage(spokeId);
113+
unusedSpokes.push({
114+
name: spokeName,
115+
type: 'IntegrationHub Spoke',
116+
spokeId: spokeId,
117+
lastUsed: lastUsage,
87118
status: 'Potentially Unused'
88119
});
89120
}
90121
}
91122

92-
// Check for duplicate endpoints
93-
var duplicates = this.findDuplicateEndpoints();
94-
redundantIntegrations = redundantIntegrations.concat(duplicates);
123+
return unusedSpokes;
124+
},
125+
126+
getLastSpokeUsage: function(spokeId) {
127+
var usageGr = new GlideRecord('ua_ih_usage');
128+
usageGr.addQuery('spoke', spokeId);
129+
usageGr.orderByDesc('sys_created_on');
130+
usageGr.setLimit(1);
131+
usageGr.query();
95132

96-
return redundantIntegrations;
133+
if (usageGr.next()) {
134+
return usageGr.sys_created_on.getDisplayValue();
135+
}
136+
return 'Never used';
137+
},
138+
139+
findUnusedSoapServices: function(cutoffDate) {
140+
var unusedSoap = [];
141+
142+
var soapGr = new GlideRecord('sys_web_service');
143+
soapGr.query();
144+
145+
while (soapGr.next()) {
146+
var soapId = soapGr.sys_id.toString();
147+
var soapName = soapGr.name.toString();
148+
149+
// Check if SOAP service has been used recently
150+
var usageCount = this.getSoapUsageCount(soapId, cutoffDate);
151+
if (usageCount === 0) {
152+
unusedSoap.push({
153+
name: soapName,
154+
type: 'SOAP Web Service',
155+
endpoint: soapGr.endpoint.toString(),
156+
lastUsed: this.getLastSoapUsage(soapId),
157+
status: 'Potentially Unused'
158+
});
159+
}
160+
}
161+
162+
return unusedSoap;
163+
},
164+
165+
getSoapUsageCount: function(soapId, cutoffDate) {
166+
var usageGr = new GlideAggregate('sys_soap_log');
167+
usageGr.addQuery('web_service', soapId);
168+
usageGr.addQuery('sys_created_on', '>=', cutoffDate);
169+
usageGr.addAggregate('COUNT');
170+
usageGr.query();
171+
172+
if (usageGr.next()) {
173+
return parseInt(usageGr.getAggregate('COUNT'));
174+
}
175+
return 0;
97176
},
98177

99-
getIntegrationLastUsed: function(integrationId, type) {
100-
var logGr = new GlideRecord('syslog');
101-
logGr.addQuery('message', 'CONTAINS', integrationId);
178+
getLastSoapUsage: function(soapId) {
179+
var logGr = new GlideRecord('sys_soap_log');
180+
logGr.addQuery('web_service', soapId);
102181
logGr.orderByDesc('sys_created_on');
103182
logGr.setLimit(1);
104183
logGr.query();
105184

106185
if (logGr.next()) {
107186
return logGr.sys_created_on.getDisplayValue();
108187
}
109-
return null;
188+
return 'Never used';
110189
},
111190

112191
findDuplicateEndpoints: function() {
@@ -147,7 +226,7 @@ CostOptimizationAnalyzer.prototype = {
147226
var recordCount = this.getTableRecordCount(tableName);
148227

149228
if (recordCount > parseInt(threshold)) {
150-
var sizeInfo = this.estimateTableSize(tableName, recordCount);
229+
var sizeInfo = this.getActualTableSize(tableName, recordCount);
151230
oversizedTables.push({
152231
table: tableName,
153232
recordCount: recordCount,
@@ -162,7 +241,7 @@ CostOptimizationAnalyzer.prototype = {
162241
systemTables.forEach(function(tableName) {
163242
var recordCount = this.getTableRecordCount(tableName);
164243
if (recordCount > parseInt(threshold)) {
165-
var sizeInfo = this.estimateTableSize(tableName, recordCount);
244+
var sizeInfo = this.getActualTableSize(tableName, recordCount);
166245
oversizedTables.push({
167246
table: tableName,
168247
recordCount: recordCount,
@@ -190,9 +269,25 @@ CostOptimizationAnalyzer.prototype = {
190269
return 0;
191270
},
192271

193-
estimateTableSize: function(tableName, recordCount) {
194-
var avgRecordSize = 2; // KB per record (estimate)
195-
var sizeGB = (recordCount * avgRecordSize) / (1024 * 1024);
272+
getActualTableSize: function(tableName, recordCount) {
273+
var sizeGB = 0;
274+
275+
// Get actual table size from sys_physical_table_stats
276+
var statsGr = new GlideRecord('sys_physical_table_stats');
277+
statsGr.addQuery('table_name', tableName);
278+
statsGr.orderByDesc('sys_created_on');
279+
statsGr.setLimit(1);
280+
statsGr.query();
281+
282+
if (statsGr.next()) {
283+
// Convert bytes to GB
284+
var sizeBytes = parseInt(statsGr.size_bytes || 0);
285+
sizeGB = sizeBytes / (1024 * 1024 * 1024);
286+
} else {
287+
// Fallback to estimate if stats not available
288+
var avgRecordSize = 2; // KB per record (estimate)
289+
sizeGB = (recordCount * avgRecordSize) / (1024 * 1024);
290+
}
196291

197292
var recommendation = 'Consider archiving old records';
198293
if (tableName === 'sys_audit') {
@@ -235,4 +330,4 @@ CostOptimizationAnalyzer.prototype = {
235330
},
236331

237332
type: 'CostOptimizationAnalyzer'
238-
};
333+
}

0 commit comments

Comments
 (0)