Skip to content

Commit 0d58d13

Browse files
committed
Strip unneeded parts from MQ client
Add tracing for mqmetric functions Add a mutex around callback controller (#148) Verify qdepth for CHSTATUS operations
1 parent a7f5d93 commit 0d58d13

File tree

14 files changed

+404
-39
lines changed

14 files changed

+404
-39
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Changelog
22
Newest updates are at the top of this file.
33

4+
## Sep 10 2020 - v5.1.2
5+
* mqmetric - Add loglevel=TRACE and trace-points for all key functions in the package
6+
* mqmetric - Add channel status bytes and buffer counts
7+
* mqmetric - Check queue depth appropriate for all CHSTATUS operations
8+
* ibmmq - Fix for potential race condition (#148)
9+
410
## Aug 07 2020 - v5.1.1
511
* ibmmq - Fix STS structure (#146)
612
* Add flag for Windows build that seems no longer to be automatically set by cgo

Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,17 @@ ENV RDURL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messag
5555
VRMF=9.2.0.0
5656

5757
# Install the MQ client from the Redistributable package. This also contains the
58-
# header files we need to compile against.
58+
# header files we need to compile against. Setup the subset of the package
59+
# we are going to keep - the genmqpkg.sh script removes unneeded parts
60+
ENV genmqpkg_incnls=1 \
61+
genmqpkg_incsdk=1 \
62+
genmqpkg_inctls=1
63+
5964
RUN cd /opt/mqm \
6065
&& curl -LO "$RDURL/$VRMF-$RDTAR" \
6166
&& tar -zxf ./*.tar.gz \
62-
&& rm -f ./*.tar.gz
67+
&& rm -f ./*.tar.gz \
68+
&& bin/genmqpkg.sh -b /opt/mqm
6369

6470
# Insert the script that will do the build
6571
COPY buildInDocker.sh $GOPATH

ibmmq/mqicb.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import "C"
3535
import (
3636
"fmt"
3737
"strings"
38+
"sync"
3839
"unsafe"
3940
)
4041

@@ -54,6 +55,9 @@ type cbInfo struct {
5455
// This map is indexed by a combination of the hConn and hObj values
5556
var cbMap = make(map[string]*cbInfo)
5657

58+
// Add a mutex to control access to it as there may be several threads going for different qmgrs
59+
var mutex sync.Mutex
60+
5761
/*
5862
MQCALLBACK_Go is a wrapper callback function that will invoke the user-supplied callback
5963
after converting the C structures into the corresponding Go format.
@@ -90,7 +94,9 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ
9094
}
9195

9296
key := makeKey(hConn, mqcbc.Hobj)
97+
mapLock()
9398
info, ok := cbMap[key]
99+
mapUnlock()
94100

95101
// The MQ Client libraries seem to sometimes call us with an EVENT
96102
// even if it's not been registered. And therefore the cbMap does not
@@ -105,6 +111,7 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ
105111
if !ok {
106112
if gocbc.CallType == MQCBCT_EVENT_CALL && mqcbc.Hobj == C.MQHO_NONE {
107113
key = makePartialKey(hConn)
114+
mapLock()
108115
for k, i := range cbMap {
109116
if strings.HasPrefix(k, key) {
110117
ok = true
@@ -114,6 +121,7 @@ func MQCALLBACK_Go(hConn C.MQHCONN, mqmd *C.MQMD, mqgmo *C.MQGMO, mqBuffer C.PMQ
114121
break
115122
}
116123
}
124+
mapUnlock()
117125
}
118126
} else {
119127
cbHObj = info.hObj
@@ -185,14 +193,18 @@ func (object *MQObject) CB(goOperation int32, gocbd *MQCBD, gomd *MQMD, gogmo *M
185193
// Add or remove the control information in the map used by the callback routines
186194
switch mqOperation {
187195
case C.MQOP_DEREGISTER:
196+
mapLock()
188197
delete(cbMap, key)
198+
mapUnlock()
189199
case C.MQOP_REGISTER:
190200
// Stash the hObj and real function to be called
191201
info := &cbInfo{hObj: object,
192202
callbackFunction: gocbd.CallbackFunction,
193203
connectionArea: nil,
194204
callbackArea: gocbd.CallbackArea}
205+
mapLock()
195206
cbMap[key] = info
207+
mapUnlock()
196208
default: // Other values leave the map alone
197209
}
198210

@@ -230,14 +242,18 @@ func (object *MQQueueManager) CB(goOperation int32, gocbd *MQCBD) error {
230242
// Add or remove the control information in the map used by the callback routines
231243
switch mqOperation {
232244
case C.MQOP_DEREGISTER:
245+
mapLock()
233246
delete(cbMap, key)
247+
mapUnlock()
234248
case C.MQOP_REGISTER:
235249
// Stash an hObj and real function to be called
236250
info := &cbInfo{hObj: &MQObject{qMgr: object, Name: ""},
237251
callbackFunction: gocbd.CallbackFunction,
238252
connectionArea: nil,
239253
callbackArea: gocbd.CallbackArea}
254+
mapLock()
240255
cbMap[key] = info
256+
mapUnlock()
241257
default: // Other values leave the map alone
242258
}
243259

@@ -259,11 +275,13 @@ func (x *MQQueueManager) Ctl(goOperation int32, goctlo *MQCTLO) error {
259275
// Need to make sure control information is available before the callback
260276
// is enabled. So this gets setup even if the MQCTL fails.
261277
key := makePartialKey(x.hConn)
278+
mapLock()
262279
for k, info := range cbMap {
263280
if strings.HasPrefix(k, key) {
264281
info.connectionArea = goctlo.ConnectionArea
265282
}
266283
}
284+
mapUnlock()
267285

268286
C.MQCTL(x.hConn, mqOperation, (C.PMQVOID)(unsafe.Pointer(&mqctlo)), &mqcc, &mqrc)
269287

@@ -295,14 +313,25 @@ func makePartialKey(hConn C.MQHCONN) string {
295313
func cbRemoveConnection(hConn C.MQHCONN) {
296314
// Remove all of the hObj values for this hconn
297315
key := makePartialKey(hConn)
316+
mapLock()
298317
for k, _ := range cbMap {
299318
if strings.HasPrefix(k, key) {
300319
delete(cbMap, k)
301320
}
302321
}
322+
mapUnlock()
303323
}
304324

305325
func cbRemoveHandle(hConn C.MQHCONN, hObj C.MQHOBJ) {
306326
key := makeKey(hConn, hObj)
327+
mapLock()
307328
delete(cbMap, key)
329+
mapUnlock()
330+
}
331+
332+
func mapLock() {
333+
mutex.Lock()
334+
}
335+
func mapUnlock() {
336+
mutex.Unlock()
308337
}

ibmmq/mqistr.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// +build go1.13
2-
31
package ibmmq
42

53
/*

mqmetric/channel.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ storage mechanisms including Prometheus and InfluxDB.
66
package mqmetric
77

88
/*
9-
Copyright (c) IBM Corporation 2016, 2019
9+
Copyright (c) IBM Corporation 2016, 2020
1010
1111
Licensed under the Apache License, Version 2.0 (the "License");
1212
you may not use this file except in compliance with the License.
@@ -43,6 +43,10 @@ const (
4343
ATTR_CHL_RQMNAME = "rqmname"
4444

4545
ATTR_CHL_MESSAGES = "messages"
46+
ATTR_CHL_BYTES_SENT = "bytes_sent"
47+
ATTR_CHL_BYTES_RCVD = "bytes_rcvd"
48+
ATTR_CHL_BUFFERS_SENT = "buffers_sent"
49+
ATTR_CHL_BUFFERS_RCVD = "buffers_rcvd"
4650
ATTR_CHL_BATCHES = "batches"
4751
ATTR_CHL_STATUS = "status"
4852
ATTR_CHL_STATUS_SQUASH = ATTR_CHL_STATUS + "_squash"
@@ -81,7 +85,9 @@ text. The elements can be expanded later; just trying to give a starting point
8185
for now.
8286
*/
8387
func ChannelInitAttributes() {
88+
traceEntry("ChannelInitAttributes")
8489
if chlAttrsInit {
90+
traceExit("ChannelInitAttributes", 1)
8591
return
8692
}
8793
ChannelStatus.Attributes = make(map[string]*StatusAttribute)
@@ -101,6 +107,18 @@ func ChannelInitAttributes() {
101107
attr = ATTR_CHL_MESSAGES
102108
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Messages (API Calls for SVRCONN)", ibmmq.MQIACH_MSGS)
103109
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
110+
attr = ATTR_CHL_BYTES_SENT
111+
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Bytes sent", ibmmq.MQIACH_BYTES_SENT)
112+
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
113+
attr = ATTR_CHL_BYTES_RCVD
114+
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Bytes rcvd", ibmmq.MQIACH_BYTES_RCVD)
115+
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
116+
attr = ATTR_CHL_BUFFERS_SENT
117+
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Buffers sent", ibmmq.MQIACH_BUFFERS_SENT)
118+
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
119+
attr = ATTR_CHL_BUFFERS_RCVD
120+
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Buffers rcvd", ibmmq.MQIACH_BUFFERS_RCVD)
121+
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
104122
attr = ATTR_CHL_BATCHES
105123
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "Completed Batches", ibmmq.MQIACH_BATCHES)
106124
ChannelStatus.Attributes[attr].delta = true // We have to manage the differences as MQ reports cumulative values
@@ -156,18 +174,26 @@ func ChannelInitAttributes() {
156174
attr = ATTR_CHL_MAX_INSTC
157175
ChannelStatus.Attributes[attr] = newStatusAttribute(attr, "MaxInstC", -1)
158176

177+
traceExit("ChannelInitAttributes", 0)
159178
}
160179

161180
// If we need to list the channels that match a pattern. Not needed for
162181
// the status queries as they (unlike the pub/sub resource stats) accept
163182
// patterns in the PCF command
164183
func InquireChannels(patterns string) ([]string, error) {
184+
traceEntry("InquireChannels")
165185
ChannelInitAttributes()
166-
return inquireObjects(patterns, ibmmq.MQOT_CHANNEL)
186+
rc, err := inquireObjects(patterns, ibmmq.MQOT_CHANNEL)
187+
188+
traceExitErr("InquireChannels", 0, err)
189+
return rc, err
167190
}
168191

169192
func CollectChannelStatus(patterns string) error {
170193
var err error
194+
195+
traceEntry("CollectChannelStatus")
196+
171197
channelsSeen = make(map[string]bool) // Record which channels have been seen in this period
172198
ChannelInitAttributes()
173199

@@ -178,6 +204,7 @@ func CollectChannelStatus(patterns string) error {
178204

179205
channelPatterns := strings.Split(patterns, ",")
180206
if len(channelPatterns) == 0 {
207+
traceExit("CollectChannelStatus", 1)
181208
return nil
182209
}
183210

@@ -245,6 +272,7 @@ func CollectChannelStatus(patterns string) error {
245272
}
246273
}
247274
}
275+
traceExitErr("CollectChannelStatus", 0, err)
248276
return err
249277

250278
}
@@ -253,6 +281,9 @@ func CollectChannelStatus(patterns string) error {
253281
// Collect the responses and build up the statistics
254282
func collectChannelStatus(pattern string, instanceType int32) error {
255283
var err error
284+
285+
traceEntryF("collectChannelStatus", "Pattern: %s", pattern)
286+
256287
statusClearReplyQ()
257288

258289
putmqmd, pmo, cfh, buf := statusSetCommandHeaders()
@@ -283,6 +314,7 @@ func collectChannelStatus(pattern string, instanceType int32) error {
283314
// And now put the command to the queue
284315
err = cmdQObj.Put(putmqmd, pmo, buf)
285316
if err != nil {
317+
traceExitErr("collectChannelStatus", 1, err)
286318
return err
287319
}
288320

@@ -298,13 +330,16 @@ func collectChannelStatus(pattern string, instanceType int32) error {
298330
}
299331
}
300332

333+
traceExitErr("collectChannelStatus", 0, err)
301334
return err
302335
}
303336

304337
// Given a PCF response message, parse it to extract the desired statistics
305338
func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
306339
var elem *ibmmq.PCFParameter
307340

341+
traceEntry("parseChlData")
342+
308343
chlName := ""
309344
connName := ""
310345
jobName := ""
@@ -321,6 +356,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
321356
offset := 0
322357
datalen := len(buf)
323358
if cfh == nil || cfh.ParameterCount == 0 {
359+
traceExit("parseChlData", 1)
324360
return ""
325361
}
326362

@@ -379,6 +415,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
379415
for k, _ := range channelsSeen {
380416
re := regexp.MustCompile(subKey)
381417
if re.MatchString(k) {
418+
traceExit("parseChlData", 2)
382419
return ""
383420
}
384421
}
@@ -421,6 +458,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
421458
ChannelStatus.Attributes[ATTR_CHL_MAX_INST].Values[key] = newStatusValueInt64(maxInst)
422459
}
423460

461+
traceExitF("parseChlData", 0, "Key: %s", key)
424462
return key
425463
}
426464

@@ -430,6 +468,7 @@ func parseChlData(instanceType int32, cfh *ibmmq.MQCFH, buf []byte) string {
430468
func ChannelNormalise(attr *StatusAttribute, v int64) float64 {
431469
var f float64
432470

471+
traceEntry("ChannelNormalise")
433472
if attr.squash {
434473
switch attr.pcfAttr {
435474

@@ -467,6 +506,9 @@ func ChannelNormalise(attr *StatusAttribute, v int64) float64 {
467506
f = 0
468507
}
469508
}
509+
510+
traceExit("ChannelNormalise", 0)
511+
470512
return f
471513
}
472514

@@ -476,9 +518,12 @@ func ChannelNormalise(attr *StatusAttribute, v int64) float64 {
476518
func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*ObjInfo) error {
477519
var err error
478520

521+
traceEntry("inquireChannelAttributes")
522+
479523
statusClearReplyQ()
480524

481525
if objectPatternsList == "" {
526+
traceExitErr("inquireChannelAttributes", 1, err)
482527
return err
483528
}
484529

@@ -528,6 +573,7 @@ func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*Obj
528573
// And now put the command to the queue
529574
err = cmdQObj.Put(putmqmd, pmo, buf)
530575
if err != nil {
576+
traceExitErr("inquireChannelAttributes", 2, err)
531577
return err
532578
}
533579

@@ -538,6 +584,8 @@ func inquireChannelAttributes(objectPatternsList string, infoMap map[string]*Obj
538584
}
539585
}
540586
}
587+
traceExit("inquireChannelAttributes", 0)
588+
541589
return nil
542590
}
543591

@@ -546,13 +594,16 @@ func parseChannelAttrData(cfh *ibmmq.MQCFH, buf []byte, infoMap map[string]*ObjI
546594
var ci *ObjInfo
547595
var ok bool
548596

597+
traceEntry("parseChannelAttrData")
598+
549599
chlName := ""
550600

551601
parmAvail := true
552602
bytesRead := 0
553603
offset := 0
554604
datalen := len(buf)
555605
if cfh.ParameterCount == 0 {
606+
traceExit("parseChannelAttrData", 1)
556607
return
557608
}
558609
// Parse it once to extract the fields that are needed for the map key
@@ -631,6 +682,7 @@ func parseChannelAttrData(cfh *ibmmq.MQCFH, buf []byte, infoMap map[string]*ObjI
631682
}
632683
}
633684

685+
traceExit("parseChannelAttrData", 0)
634686
return
635687
}
636688

0 commit comments

Comments
 (0)