Skip to content

Commit eba7e3f

Browse files
wlahtisykesm
authored andcommitted
Instrument metrics into chaincode handler
Adds metrics for transactions received, completed, and transaction duration. FAB-12797 #done Change-Id: I4bd48ed57e37e2be8ac95319a996889d61fa09e2 Signed-off-by: Will Lahti <wtlahti@us.ibm.com> Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
1 parent 61656ce commit eba7e3f

File tree

8 files changed

+199
-5
lines changed

8 files changed

+199
-5
lines changed

core/chaincode/chaincode_support.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/golang/protobuf/proto"
14+
"github.com/hyperledger/fabric/common/metrics"
1415
"github.com/hyperledger/fabric/common/util"
1516
"github.com/hyperledger/fabric/core/chaincode/platforms"
1617
"github.com/hyperledger/fabric/core/common/ccprovider"
@@ -54,6 +55,7 @@ type ChaincodeSupport struct {
5455
SystemCCProvider sysccprovider.SystemChaincodeProvider
5556
Lifecycle Lifecycle
5657
appConfig ApplicationConfigRetriever
58+
Metrics *Metrics
5759
}
5860

5961
// NewChaincodeSupport creates a new ChaincodeSupport instance.
@@ -70,6 +72,7 @@ func NewChaincodeSupport(
7072
SystemCCProvider sysccprovider.SystemChaincodeProvider,
7173
platformRegistry *platforms.Registry,
7274
appConfig ApplicationConfigRetriever,
75+
metricsProvider metrics.Provider,
7376
) *ChaincodeSupport {
7477
cs := &ChaincodeSupport{
7578
UserRunsCC: userRunsCC,
@@ -80,6 +83,7 @@ func NewChaincodeSupport(
8083
SystemCCProvider: SystemCCProvider,
8184
Lifecycle: lifecycle,
8285
appConfig: appConfig,
86+
Metrics: NewMetrics(metricsProvider),
8387
}
8488

8589
// Keep TestQueries working
@@ -177,6 +181,7 @@ func (cs *ChaincodeSupport) HandleChaincodeStream(stream ccintf.ChaincodeStream)
177181
UUIDGenerator: UUIDGeneratorFunc(util.GenerateUUID),
178182
LedgerGetter: peer.Default,
179183
AppConfig: cs.appConfig,
184+
Metrics: cs.Metrics,
180185
}
181186

182187
return handler.ProcessStream(stream)

core/chaincode/chaincode_support_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/golang/protobuf/proto"
2424
"github.com/hyperledger/fabric/common/crypto/tlsgen"
2525
commonledger "github.com/hyperledger/fabric/common/ledger"
26+
"github.com/hyperledger/fabric/common/metrics/disabled"
2627
mc "github.com/hyperledger/fabric/common/mocks/config"
2728
mocklgr "github.com/hyperledger/fabric/common/mocks/ledger"
2829
mockpeer "github.com/hyperledger/fabric/common/mocks/peer"
@@ -192,6 +193,7 @@ func initMockPeer(chainIDs ...string) (*ChaincodeSupport, error) {
192193
sccp,
193194
pr,
194195
peer.DefaultSupport,
196+
&disabled.Provider{},
195197
)
196198
ipRegistry.ChaincodeSupport = chaincodeSupport
197199

core/chaincode/exectransaction_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/hyperledger/fabric/common/channelconfig"
3131
"github.com/hyperledger/fabric/common/crypto/tlsgen"
3232
"github.com/hyperledger/fabric/common/flogging"
33+
"github.com/hyperledger/fabric/common/metrics/disabled"
3334
mc "github.com/hyperledger/fabric/common/mocks/config"
3435
mockpolicies "github.com/hyperledger/fabric/common/mocks/policies"
3536
"github.com/hyperledger/fabric/common/policies"
@@ -140,6 +141,7 @@ func initPeer(chainIDs ...string) (net.Listener, *ChaincodeSupport, func(), erro
140141
sccp,
141142
pr,
142143
peer.DefaultSupport,
144+
&disabled.Provider{},
143145
)
144146
ipRegistry.ChaincodeSupport = chaincodeSupport
145147
pb.RegisterChaincodeSupportServer(grpcServer, chaincodeSupport)

core/chaincode/handler.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package chaincode
99
import (
1010
"fmt"
1111
"io"
12+
"strconv"
1213
"strings"
1314
"sync"
1415
"time"
@@ -162,6 +163,8 @@ type Handler struct {
162163
chatStream ccintf.ChaincodeStream
163164
// errChan is used to communicate errors from the async send to the receive loop
164165
errChan chan error
166+
// Metrics holds chaincode metrics
167+
Metrics *Metrics
165168
}
166169

167170
// handleMessage is called by ProcessStream to dispatch messages.
@@ -244,6 +247,7 @@ func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFun
244247
return
245248
}
246249

250+
startTime := time.Now()
247251
var txContext *TransactionContext
248252
var err error
249253
if msg.Type == pb.ChaincodeMessage_INVOKE_CHAINCODE {
@@ -252,6 +256,13 @@ func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFun
252256
txContext, err = h.isValidTxSim(msg.ChannelId, msg.Txid, "no ledger context")
253257
}
254258

259+
chaincodeName := h.chaincodeID.Name + ":" + h.chaincodeID.Version
260+
h.Metrics.ShimRequestsReceived.With(
261+
"type", msg.Type.String(),
262+
"channel", msg.ChannelId,
263+
"chaincode", chaincodeName,
264+
).Add(1)
265+
255266
var resp *pb.ChaincodeMessage
256267
if err == nil {
257268
resp, err = delegate(msg, txContext)
@@ -266,6 +277,19 @@ func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFun
266277
chaincodeLogger.Debugf("[%s] Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type, resp.Type)
267278
h.ActiveTransactions.Remove(msg.ChannelId, msg.Txid)
268279
h.serialSendAsync(resp)
280+
duration := time.Since(startTime)
281+
h.Metrics.ShimRequestDuration.With(
282+
"type", msg.Type.String(),
283+
"channel", msg.ChannelId,
284+
"chaincode", chaincodeName,
285+
"success", strconv.FormatBool(resp.Type != pb.ChaincodeMessage_ERROR),
286+
).Observe(float64(duration) / float64(time.Second))
287+
h.Metrics.ShimRequestsCompleted.With(
288+
"type", msg.Type.String(),
289+
"channel", msg.ChannelId,
290+
"chaincode", chaincodeName,
291+
"success", strconv.FormatBool(resp.Type != pb.ChaincodeMessage_ERROR),
292+
).Add(1)
269293
}
270294

271295
func shorttxid(txid string) string {

core/chaincode/handler_test.go

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/golang/protobuf/proto"
14+
"github.com/hyperledger/fabric/common/metrics/metricsfakes"
1415
"github.com/hyperledger/fabric/common/mocks/config"
1516
"github.com/hyperledger/fabric/common/util"
1617
"github.com/hyperledger/fabric/core/aclmgmt/resources"
@@ -42,6 +43,9 @@ var _ = Describe("Handler", func() {
4243
fakeLedgerGetter *mock.LedgerGetter
4344
fakeHandlerRegistry *fake.Registry
4445
fakeApplicationConfigRetriever *fake.ApplicationConfigRetriever
46+
fakeShimRequestsReceived *metricsfakes.Counter
47+
fakeShimRequestsCompleted *metricsfakes.Counter
48+
fakeShimRequestDuration *metricsfakes.Histogram
4549

4650
responseNotifier chan *pb.ChaincodeMessage
4751
txContext *chaincode.TransactionContext
@@ -84,6 +88,19 @@ var _ = Describe("Handler", func() {
8488
}
8589
fakeApplicationConfigRetriever.GetApplicationConfigReturns(applicationCapability, true)
8690

91+
fakeShimRequestsReceived = &metricsfakes.Counter{}
92+
fakeShimRequestsReceived.WithReturns(fakeShimRequestsReceived)
93+
fakeShimRequestsCompleted = &metricsfakes.Counter{}
94+
fakeShimRequestsCompleted.WithReturns(fakeShimRequestsCompleted)
95+
fakeShimRequestDuration = &metricsfakes.Histogram{}
96+
fakeShimRequestDuration.WithReturns(fakeShimRequestDuration)
97+
98+
chaincodeMetrics := &chaincode.Metrics{
99+
ShimRequestsReceived: fakeShimRequestsReceived,
100+
ShimRequestsCompleted: fakeShimRequestsCompleted,
101+
ShimRequestDuration: fakeShimRequestDuration,
102+
}
103+
87104
handler = &chaincode.Handler{
88105
ACLProvider: fakeACLProvider,
89106
ActiveTransactions: fakeTransactionRegistry,
@@ -100,9 +117,10 @@ var _ = Describe("Handler", func() {
100117
return "generated-query-id"
101118
}),
102119
AppConfig: fakeApplicationConfigRetriever,
120+
Metrics: chaincodeMetrics,
103121
}
104122
chaincode.SetHandlerChatStream(handler, fakeChatStream)
105-
chaincode.SetHandlerChaincodeID(handler, &pb.ChaincodeID{Name: "test-handler-name"})
123+
chaincode.SetHandlerChaincodeID(handler, &pb.ChaincodeID{Name: "test-handler-name", Version: "1.0"})
106124
chaincode.SetHandlerCCInstance(handler, &sysccprovider.ChaincodeInstance{ChaincodeName: "cc-instance-name"})
107125
})
108126

@@ -178,7 +196,99 @@ var _ = Describe("Handler", func() {
178196
Expect(transactionID).To(Equal("tx-id"))
179197
})
180198

181-
Context("wwhen the transaction ID has already been regustered", func() {
199+
It("records shim requests received before requests completed", func() {
200+
fakeShimRequestsReceived.AddStub = func(delta float64) {
201+
defer GinkgoRecover()
202+
Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(0))
203+
}
204+
205+
handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle)
206+
Eventually(fakeChatStream.SendCallCount).Should(Equal(1))
207+
msg := fakeChatStream.SendArgsForCall(0)
208+
Expect(msg).To(Equal(expectedResponse))
209+
210+
Expect(fakeShimRequestsReceived.WithCallCount()).To(Equal(1))
211+
labelValues := fakeShimRequestsReceived.WithArgsForCall(0)
212+
Expect(labelValues).To(Equal([]string{
213+
"type", "GET_STATE",
214+
"channel", "channel-id",
215+
"chaincode", "test-handler-name:1.0",
216+
}))
217+
Expect(fakeShimRequestsReceived.AddCallCount()).To(Equal(1))
218+
Expect(fakeShimRequestsReceived.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
219+
})
220+
221+
It("records transactions completed after transactions received", func() {
222+
fakeShimRequestsCompleted.AddStub = func(delta float64) {
223+
defer GinkgoRecover()
224+
Expect(fakeShimRequestsReceived.AddCallCount()).To(Equal(1))
225+
}
226+
227+
handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle)
228+
Eventually(fakeChatStream.SendCallCount).Should(Equal(1))
229+
230+
Expect(fakeShimRequestsCompleted.WithCallCount()).To(Equal(1))
231+
labelValues := fakeShimRequestsCompleted.WithArgsForCall(0)
232+
Expect(labelValues).To(Equal([]string{
233+
"type", "GET_STATE",
234+
"channel", "channel-id",
235+
"chaincode", "test-handler-name:1.0",
236+
"success", "true",
237+
}))
238+
Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(1))
239+
Expect(fakeShimRequestsCompleted.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
240+
})
241+
242+
It("records transactions duration", func() {
243+
handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle)
244+
Eventually(fakeChatStream.SendCallCount).Should(Equal(1))
245+
246+
Expect(fakeShimRequestDuration.WithCallCount()).To(Equal(1))
247+
labelValues := fakeShimRequestDuration.WithArgsForCall(0)
248+
Expect(labelValues).To(Equal([]string{
249+
"type", "GET_STATE",
250+
"channel", "channel-id",
251+
"chaincode", "test-handler-name:1.0",
252+
"success", "true",
253+
}))
254+
Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).NotTo(BeZero())
255+
Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0))
256+
})
257+
258+
Context("when the transaction returns an error", func() {
259+
BeforeEach(func() {
260+
fakeMessageHandler.HandleReturns(nil, errors.New("I am a total failure"))
261+
})
262+
263+
It("records metrics with success=false", func() {
264+
handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle)
265+
Eventually(fakeChatStream.SendCallCount).Should(Equal(1))
266+
267+
Expect(fakeShimRequestsCompleted.WithCallCount()).To(Equal(1))
268+
labelValues := fakeShimRequestsCompleted.WithArgsForCall(0)
269+
Expect(labelValues).To(Equal([]string{
270+
"type", "GET_STATE",
271+
"channel", "channel-id",
272+
"chaincode", "test-handler-name:1.0",
273+
"success", "false",
274+
}))
275+
Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(1))
276+
Expect(fakeShimRequestsCompleted.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
277+
278+
Expect(fakeShimRequestDuration.WithCallCount()).To(Equal(1))
279+
labelValues = fakeShimRequestDuration.WithArgsForCall(0)
280+
Expect(labelValues).To(Equal([]string{
281+
"type", "GET_STATE",
282+
"channel", "channel-id",
283+
"chaincode", "test-handler-name:1.0",
284+
"success", "false",
285+
}))
286+
Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).NotTo(BeZero())
287+
Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0))
288+
})
289+
})
290+
291+
Context("when the transaction ID has already been registered", func() {
182292
BeforeEach(func() {
183293
fakeTransactionRegistry.AddReturns(false)
184294
})

core/chaincode/metrics.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package chaincode
8+
9+
import "github.com/hyperledger/fabric/common/metrics"
10+
11+
var (
12+
shimRequestsReceived = metrics.CounterOpts{
13+
Namespace: "chaincode",
14+
Name: "shim_requests_received",
15+
Help: "The number of chaincode shim requests received.",
16+
LabelNames: []string{"type", "channel", "chaincode"},
17+
StatsdFormat: "%{#fqname}.%{type}.%{channel}.%{chaincode}",
18+
}
19+
shimRequestsCompleted = metrics.CounterOpts{
20+
Namespace: "chaincode",
21+
Name: "shim_requests_completed",
22+
Help: "The number of chaincode shim requests completed.",
23+
LabelNames: []string{"type", "channel", "chaincode", "success"},
24+
StatsdFormat: "%{#fqname}.%{type}.%{channel}.%{chaincode}.%{success}",
25+
}
26+
shimRequestDuration = metrics.HistogramOpts{
27+
Namespace: "chaincode",
28+
Name: "shim_request_duration",
29+
Help: "The time to complete chaincode shim requests.",
30+
LabelNames: []string{"type", "channel", "chaincode", "success"},
31+
StatsdFormat: "%{#fqname}.%{type}.%{channel}.%{chaincode}.%{success}",
32+
}
33+
)
34+
35+
type Metrics struct {
36+
ShimRequestsReceived metrics.Counter
37+
ShimRequestsCompleted metrics.Counter
38+
ShimRequestDuration metrics.Histogram
39+
}
40+
41+
func NewMetrics(p metrics.Provider) *Metrics {
42+
return &Metrics{
43+
ShimRequestsReceived: p.NewCounter(shimRequestsReceived),
44+
ShimRequestsCompleted: p.NewCounter(shimRequestsCompleted),
45+
ShimRequestDuration: p.NewHistogram(shimRequestDuration),
46+
}
47+
}

core/scc/cscc/configure_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hyperledger/fabric/common/crypto/tlsgen"
2020
"github.com/hyperledger/fabric/common/genesis"
2121
"github.com/hyperledger/fabric/common/localmsp"
22+
"github.com/hyperledger/fabric/common/metrics/disabled"
2223
"github.com/hyperledger/fabric/common/mocks/scc"
2324
"github.com/hyperledger/fabric/common/policies"
2425
"github.com/hyperledger/fabric/common/tools/configtxgen/configtxgentest"
@@ -234,6 +235,7 @@ func TestConfigerInvokeJoinChainCorrectParams(t *testing.T) {
234235
mp,
235236
platforms.NewRegistry(&golang.Platform{}),
236237
peer.DefaultSupport,
238+
&disabled.Provider{},
237239
)
238240

239241
// Init the policy checker

peer/node/start.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func serve(args []string) error {
251251
pb.RegisterDeliverServer(peerServer.Server(), abServer)
252252

253253
// Initialize chaincode service
254-
chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr)
254+
chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr, metricsProvider)
255255

256256
logger.Debugf("Running peer")
257257

@@ -606,7 +606,7 @@ func computeChaincodeEndpoint(peerHostname string) (ccEndpoint string, err error
606606
//NOTE - when we implement JOIN we will no longer pass the chainID as param
607607
//The chaincode support will come up without registering system chaincodes
608608
//which will be registered only during join phase.
609-
func registerChaincodeSupport(grpcServer *comm.GRPCServer, ccEndpoint string, ca tlsgen.CA, packageProvider *persistence.PackageProvider, aclProvider aclmgmt.ACLProvider, pr *platforms.Registry, lifecycleSCC *lifecycle.SCC) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider) {
609+
func registerChaincodeSupport(grpcServer *comm.GRPCServer, ccEndpoint string, ca tlsgen.CA, packageProvider *persistence.PackageProvider, aclProvider aclmgmt.ACLProvider, pr *platforms.Registry, lifecycleSCC *lifecycle.SCC, metricsProvider metrics.Provider) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider) {
610610
//get user mode
611611
userRunsCC := chaincode.IsDevMode()
612612
tlsEnabled := viper.GetBool("peer.tls.enabled")
@@ -636,6 +636,7 @@ func registerChaincodeSupport(grpcServer *comm.GRPCServer, ccEndpoint string, ca
636636
sccp,
637637
pr,
638638
peer.DefaultSupport,
639+
metricsProvider,
639640
)
640641
ipRegistry.ChaincodeSupport = chaincodeSupport
641642
ccp := chaincode.NewProvider(chaincodeSupport)
@@ -662,7 +663,7 @@ func registerChaincodeSupport(grpcServer *comm.GRPCServer, ccEndpoint string, ca
662663
// 1) setup local chaincode install path
663664
// 2) create chaincode specific tls CA
664665
// 3) start the chaincode specific gRPC listening service
665-
func startChaincodeServer(peerHost string, aclProvider aclmgmt.ACLProvider, pr *platforms.Registry) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider, *persistence.PackageProvider) {
666+
func startChaincodeServer(peerHost string, aclProvider aclmgmt.ACLProvider, pr *platforms.Registry, metricsProvider metrics.Provider) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider, *persistence.PackageProvider) {
666667
// Setup chaincode path
667668
chaincodeInstallPath := ccprovider.GetChaincodeInstallPathFromViper()
668669
ccprovider.SetChaincodesPath(chaincodeInstallPath)
@@ -703,6 +704,7 @@ func startChaincodeServer(peerHost string, aclProvider aclmgmt.ACLProvider, pr *
703704
aclProvider,
704705
pr,
705706
lifecycleSCC,
707+
metricsProvider,
706708
)
707709
go ccSrv.Start()
708710
return chaincodeSupport, ccp, sccp, packageProvider

0 commit comments

Comments
 (0)