Skip to content

Commit 5abb49a

Browse files
solace scaler add avg msg rx rate (#4667)
Signed-off-by: Dennis Brinley <[email protected]>
1 parent 1a23028 commit 5abb49a

File tree

4 files changed

+253
-14
lines changed

4 files changed

+253
-14
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio
6464
- **RabbitMQ Scaler**: Add support for `unsafeSsl` in trigger metadata ([#4448](https://github.com/kedacore/keda/issues/4448))
6565
- **Prometheus Metrics**: Add new metric with KEDA build info ([#4647](https://github.com/kedacore/keda/issues/4647))
6666
- **Prometheus Scaler**: Add support for Google Managed Prometheus ([#4675](https://github.com/kedacore/keda/pull/4675))
67+
- **Solace Scaler**: Add new `messageReceiveRateTarget` metric to Solace Scaler ([#4665](https://github.com/kedacore/keda/issues/4665))
68+
6769

6870
### Fixes
6971

pkg/scalers/solace_scaler.go

+63-3
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,47 @@ import (
1818
const (
1919
solaceExtMetricType = "External"
2020
solaceScalerID = "solace"
21+
2122
// REST ENDPOINT String Patterns
22-
solaceSempEndpointURLTemplate = "%s/%s/%s/monitor/msgVpns/%s/%ss/%s"
23+
solaceSempQueryFieldURLSuffix = "?select=msgs,msgSpoolUsage,averageRxMsgRate"
24+
solaceSempEndpointURLTemplate = "%s/%s/%s/monitor/msgVpns/%s/%ss/%s" + solaceSempQueryFieldURLSuffix
25+
2326
// SEMP REST API Context
2427
solaceAPIName = "SEMP"
2528
solaceAPIVersion = "v2"
2629
solaceAPIObjectTypeQueue = "queue"
30+
2731
// Log Message Templates
2832
solaceFoundMetaFalse = "required Field %s NOT FOUND in Solace Metadata"
33+
2934
// YAML Configuration Metadata Field Names
3035
// Broker Identifiers
3136
solaceMetaSempBaseURL = "solaceSempBaseURL"
37+
3238
// Credential Identifiers
3339
solaceMetaUsername = "username"
3440
solaceMetaPassword = "password"
3541
solaceMetaUsernameFromEnv = "usernameFromEnv"
3642
solaceMetaPasswordFromEnv = "passwordFromEnv"
43+
3744
// Target Object Identifiers
3845
solaceMetaMsgVpn = "messageVpn"
3946
solaceMetaQueueName = "queueName"
47+
4048
// Metric Targets
4149
solaceMetaMsgCountTarget = "messageCountTarget"
4250
solaceMetaMsgSpoolUsageTarget = "messageSpoolUsageTarget"
51+
solaceMetaMsgRxRateTarget = "messageReceiveRateTarget"
52+
4353
// Metric Activation Targets
4454
solaceMetaActivationMsgCountTarget = "activationMessageCountTarget"
4555
solaceMetaActivationMsgSpoolUsageTarget = "activationMessageSpoolUsageTarget"
56+
solaceMetaActivationMsgRxRateTarget = "activationMessageReceiveRateTarget"
57+
4658
// Trigger type identifiers
4759
solaceTriggermsgcount = "msgcount"
4860
solaceTriggermsgspoolusage = "msgspoolusage"
61+
solaceTriggermsgrxrate = "msgrcvrate"
4962
)
5063

5164
// Struct for Observed Metric Values
@@ -54,6 +67,8 @@ type SolaceMetricValues struct {
5467
msgCount int
5568
// Observed Message Spool Usage
5669
msgSpoolUsage int
70+
// Observed Message Received Rate
71+
msgRcvRate int
5772
}
5873

5974
type SolaceScaler struct {
@@ -67,19 +82,25 @@ type SolaceMetadata struct {
6782
// Full SEMP URL to target queue (CONSTRUCTED IN CODE)
6883
endpointURL string
6984
solaceSempURL string
85+
7086
// Solace Message VPN
7187
messageVpn string
7288
queueName string
89+
7390
// Basic Auth Username
7491
username string
7592
// Basic Auth Password
7693
password string
94+
7795
// Target Message Count
7896
msgCountTarget int64
7997
msgSpoolUsageTarget int64 // Spool Use Target in Megabytes
98+
msgRxRateTarget int64 // Ingress Rate Target per consumer in msgs/second
99+
80100
// Activation Target Message Count
81101
activationMsgCountTarget int
82102
activationMsgSpoolUsageTarget int // Spool Use Target in Megabytes
103+
activationMsgRxRateTarget int // Ingress Rate Target per consumer in msgs/second
83104
// Scaler index
84105
scalerIndex int
85106
}
@@ -99,6 +120,7 @@ type solaceSEMPCollections struct {
99120
// SEMP API Response Queue Data Struct
100121
type solaceSEMPData struct {
101122
MsgSpoolUsage int `json:"msgSpoolUsage"`
123+
MsgRcvRate int `json:"averageRxMsgRate"`
102124
}
103125

104126
// SEMP API Messages Struct
@@ -162,6 +184,7 @@ func parseSolaceMetadata(config *ScalerConfig) (*SolaceMetadata, error) {
162184

163185
// GET METRIC TARGET VALUES
164186
// GET msgCountTarget
187+
meta.msgCountTarget = 0
165188
if val, ok := config.TriggerMetadata[solaceMetaMsgCountTarget]; ok && val != "" {
166189
if msgCount, err := strconv.ParseInt(val, 10, 64); err == nil {
167190
meta.msgCountTarget = msgCount
@@ -170,16 +193,26 @@ func parseSolaceMetadata(config *ScalerConfig) (*SolaceMetadata, error) {
170193
}
171194
}
172195
// GET msgSpoolUsageTarget
196+
meta.msgSpoolUsageTarget = 0
173197
if val, ok := config.TriggerMetadata[solaceMetaMsgSpoolUsageTarget]; ok && val != "" {
174198
if msgSpoolUsage, err := strconv.ParseInt(val, 10, 64); err == nil {
175199
meta.msgSpoolUsageTarget = msgSpoolUsage * 1024 * 1024
176200
} else {
177201
return nil, fmt.Errorf("can't parse [%s], not a valid integer: %w", solaceMetaMsgSpoolUsageTarget, err)
178202
}
179203
}
204+
// GET msgRcvRateTarget
205+
meta.msgRxRateTarget = 0
206+
if val, ok := config.TriggerMetadata[solaceMetaMsgRxRateTarget]; ok && val != "" {
207+
if msgRcvRate, err := strconv.ParseInt(val, 10, 64); err == nil {
208+
meta.msgRxRateTarget = msgRcvRate
209+
} else {
210+
return nil, fmt.Errorf("can't parse [%s], not a valid integer: %w", solaceMetaMsgRxRateTarget, err)
211+
}
212+
}
180213

181214
// Check that we have at least one positive target value for the scaler
182-
if meta.msgCountTarget < 1 && meta.msgSpoolUsageTarget < 1 {
215+
if meta.msgCountTarget < 1 && meta.msgSpoolUsageTarget < 1 && meta.msgRxRateTarget < 1 {
183216
return nil, fmt.Errorf("no target value found in the scaler configuration")
184217
}
185218

@@ -202,6 +235,14 @@ func parseSolaceMetadata(config *ScalerConfig) (*SolaceMetadata, error) {
202235
return nil, fmt.Errorf("can't parse [%s], not a valid integer: %w", solaceMetaActivationMsgSpoolUsageTarget, err)
203236
}
204237
}
238+
meta.activationMsgRxRateTarget = 0
239+
if val, ok := config.TriggerMetadata[solaceMetaActivationMsgRxRateTarget]; ok && val != "" {
240+
if activationMsgRxRateTarget, err := strconv.Atoi(val); err == nil {
241+
meta.activationMsgRxRateTarget = activationMsgRxRateTarget
242+
} else {
243+
return nil, fmt.Errorf("can't parse [%s], not a valid integer: %w", solaceMetaActivationMsgRxRateTarget, err)
244+
}
245+
}
205246

206247
// Format Solace SEMP Queue Endpoint (REST URL)
207248
meta.endpointURL = fmt.Sprintf(
@@ -296,6 +337,18 @@ func (s *SolaceScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec
296337
metricSpec := v2.MetricSpec{External: externalMetric, Type: solaceExtMetricType}
297338
metricSpecList = append(metricSpecList, metricSpec)
298339
}
340+
// Message Receive Rate Target Spec
341+
if s.metadata.msgRxRateTarget > 0 {
342+
metricName := kedautil.NormalizeString(fmt.Sprintf("solace-%s-%s", s.metadata.queueName, solaceTriggermsgrxrate))
343+
externalMetric := &v2.ExternalMetricSource{
344+
Metric: v2.MetricIdentifier{
345+
Name: GenerateMetricNameWithIndex(s.metadata.scalerIndex, metricName),
346+
},
347+
Target: GetMetricTarget(s.metricType, s.metadata.msgRxRateTarget),
348+
}
349+
metricSpec := v2.MetricSpec{External: externalMetric, Type: solaceExtMetricType}
350+
metricSpecList = append(metricSpecList, metricSpec)
351+
}
299352
return metricSpecList
300353
}
301354

@@ -341,6 +394,7 @@ func (s *SolaceScaler) getSolaceQueueMetricsFromSEMP(ctx context.Context) (Solac
341394
// Set Return Values
342395
metricValues.msgCount = sempResponse.Collections.Msgs.Count
343396
metricValues.msgSpoolUsage = sempResponse.Data.MsgSpoolUsage
397+
metricValues.msgRcvRate = sempResponse.Data.MsgRcvRate
344398
return metricValues, nil
345399
}
346400

@@ -363,13 +417,19 @@ func (s *SolaceScaler) GetMetricsAndActivity(ctx context.Context, metricName str
363417
metric = GenerateMetricInMili(metricName, float64(metricValues.msgCount))
364418
case strings.HasSuffix(metricName, solaceTriggermsgspoolusage):
365419
metric = GenerateMetricInMili(metricName, float64(metricValues.msgSpoolUsage))
420+
case strings.HasSuffix(metricName, solaceTriggermsgrxrate):
421+
metric = GenerateMetricInMili(metricName, float64(metricValues.msgRcvRate))
366422
default:
367423
// Should never end up here
368424
err := fmt.Errorf("unidentified metric: %s", metricName)
369425
s.logger.Error(err, "returning error to calling app")
370426
return []external_metrics.ExternalMetricValue{}, false, err
371427
}
372-
return []external_metrics.ExternalMetricValue{metric}, (metricValues.msgCount > s.metadata.activationMsgCountTarget || metricValues.msgSpoolUsage > s.metadata.activationMsgSpoolUsageTarget), nil
428+
return []external_metrics.ExternalMetricValue{metric},
429+
(metricValues.msgCount > s.metadata.activationMsgCountTarget ||
430+
metricValues.msgSpoolUsage > s.metadata.activationMsgSpoolUsageTarget ||
431+
metricValues.msgRcvRate > s.metadata.activationMsgRxRateTarget),
432+
nil
373433
}
374434

375435
// Do Nothing - Satisfies Interface

pkg/scalers/solace_scaler_test.go

+113-9
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ type testSolaceMetadata struct {
1717
}
1818

1919
var (
20-
soltestValidBaseURL = "http://localhost:8080"
21-
soltestValidUsername = "admin"
22-
soltestValidPassword = "admin"
23-
soltestValidVpn = "dennis_vpn"
24-
soltestValidQueueName = "queue3"
25-
soltestValidMsgCountTarget = "10"
26-
soltestValidMsgSpoolTarget = "20"
27-
soltestEnvUsername = "SOLTEST_USERNAME"
28-
soltestEnvPassword = "SOLTEST_PASSWORD"
20+
soltestValidBaseURL = "http://localhost:8080"
21+
soltestValidUsername = "admin"
22+
soltestValidPassword = "admin"
23+
soltestValidVpn = "dennis_vpn"
24+
soltestValidQueueName = "queue3"
25+
soltestValidMsgCountTarget = "10"
26+
soltestValidMsgSpoolTarget = "20"
27+
soltestValidMsgRxRateTarget = "15"
28+
soltestEnvUsername = "SOLTEST_USERNAME"
29+
soltestEnvPassword = "SOLTEST_PASSWORD"
2930
)
3031

3132
// AUTH RECORD FOR TEST
@@ -212,6 +213,39 @@ var testParseSolaceMetadata = []testSolaceMetadata{
212213
1,
213214
false,
214215
},
216+
// -Case - msgRxRateTarget non-numeric
217+
{
218+
"#014 - msgRxRateTarget non-numeric",
219+
map[string]string{
220+
solaceMetaSempBaseURL: soltestValidBaseURL,
221+
solaceMetaMsgVpn: soltestValidVpn,
222+
solaceMetaUsernameFromEnv: "",
223+
solaceMetaPasswordFromEnv: "",
224+
solaceMetaUsername: soltestValidUsername,
225+
solaceMetaPassword: soltestValidPassword,
226+
solaceMetaQueueName: soltestValidQueueName,
227+
solaceMetaMsgRxRateTarget: "NOT_AN_INTEGER",
228+
},
229+
1,
230+
true,
231+
},
232+
// -Case - activationMsgRxRateTarget non-numeric
233+
{
234+
"#015 - activationMsgRxRateTarget non-numeric",
235+
map[string]string{
236+
solaceMetaSempBaseURL: soltestValidBaseURL,
237+
solaceMetaMsgVpn: soltestValidVpn,
238+
solaceMetaUsernameFromEnv: "",
239+
solaceMetaPasswordFromEnv: "",
240+
solaceMetaUsername: soltestValidUsername,
241+
solaceMetaPassword: soltestValidPassword,
242+
solaceMetaQueueName: soltestValidQueueName,
243+
solaceMetaMsgRxRateTarget: "10",
244+
solaceMetaActivationMsgRxRateTarget: "NOT_AN_INTEGER",
245+
},
246+
1,
247+
true,
248+
},
215249
}
216250

217251
var testSolaceEnvCreds = []testSolaceMetadata{
@@ -399,11 +433,81 @@ var testSolaceGetMetricSpecData = []testSolaceMetadata{
399433
1,
400434
false,
401435
},
436+
// Added for 'solaceMetaMsgRxRateTarget'
437+
{
438+
"#410 - Get Metric Spec - msgRxRateTarget",
439+
map[string]string{
440+
solaceMetaSempBaseURL: soltestValidBaseURL,
441+
solaceMetaMsgVpn: soltestValidVpn,
442+
solaceMetaUsernameFromEnv: "",
443+
solaceMetaPasswordFromEnv: "",
444+
solaceMetaUsername: soltestValidUsername,
445+
solaceMetaPassword: soltestValidPassword,
446+
solaceMetaQueueName: soltestValidQueueName,
447+
solaceMetaMsgCountTarget: soltestValidMsgCountTarget,
448+
// solaceMetaMsgSpoolUsageTarget: soltestValidMsgSpoolTarget,
449+
solaceMetaMsgRxRateTarget: soltestValidMsgRxRateTarget,
450+
},
451+
1,
452+
false,
453+
},
454+
{
455+
"#411 - Get Metric Spec - ALL msgSpoolUsage, msgCountTarget, and msgRxRateTarget",
456+
map[string]string{
457+
solaceMetaSempBaseURL: soltestValidBaseURL,
458+
solaceMetaMsgVpn: soltestValidVpn,
459+
solaceMetaUsernameFromEnv: "",
460+
solaceMetaPasswordFromEnv: "",
461+
solaceMetaUsername: soltestValidUsername,
462+
solaceMetaPassword: soltestValidPassword,
463+
solaceMetaQueueName: soltestValidQueueName,
464+
solaceMetaMsgCountTarget: soltestValidMsgCountTarget,
465+
solaceMetaMsgSpoolUsageTarget: soltestValidMsgSpoolTarget,
466+
solaceMetaMsgRxRateTarget: soltestValidMsgRxRateTarget,
467+
},
468+
1,
469+
false,
470+
},
471+
{
472+
"#412 - Get Metric Spec - ALL ZERO",
473+
map[string]string{
474+
solaceMetaSempBaseURL: soltestValidBaseURL,
475+
solaceMetaMsgVpn: soltestValidVpn,
476+
solaceMetaUsernameFromEnv: "",
477+
solaceMetaPasswordFromEnv: "",
478+
solaceMetaUsername: soltestValidUsername,
479+
solaceMetaPassword: soltestValidPassword,
480+
solaceMetaQueueName: soltestValidQueueName,
481+
solaceMetaMsgCountTarget: "0",
482+
solaceMetaMsgSpoolUsageTarget: "0",
483+
solaceMetaMsgRxRateTarget: "0",
484+
},
485+
1,
486+
true,
487+
},
488+
{
489+
"#413 - Get Metric Spec - msgRxRateTarget, OTHERS ZERO",
490+
map[string]string{
491+
solaceMetaSempBaseURL: soltestValidBaseURL,
492+
solaceMetaMsgVpn: soltestValidVpn,
493+
solaceMetaUsernameFromEnv: "",
494+
solaceMetaPasswordFromEnv: "",
495+
solaceMetaUsername: soltestValidUsername,
496+
solaceMetaPassword: soltestValidPassword,
497+
solaceMetaQueueName: soltestValidQueueName,
498+
solaceMetaMsgCountTarget: "0",
499+
solaceMetaMsgSpoolUsageTarget: "0",
500+
solaceMetaMsgRxRateTarget: soltestValidMsgRxRateTarget,
501+
},
502+
1,
503+
false,
504+
},
402505
}
403506

404507
var testSolaceExpectedMetricNames = map[string]string{
405508
"s1-" + solaceScalerID + "-" + soltestValidQueueName + "-" + solaceTriggermsgcount: "",
406509
"s1-" + solaceScalerID + "-" + soltestValidQueueName + "-" + solaceTriggermsgspoolusage: "",
510+
"s1-" + solaceScalerID + "-" + soltestValidQueueName + "-" + solaceTriggermsgrxrate: "",
407511
}
408512

409513
func TestSolaceParseSolaceMetadata(t *testing.T) {

0 commit comments

Comments
 (0)