Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit 3ed1794

Browse files
authored
[Master] Experimental Pluggable Query Handler (#4537)
This is an experimental feature for composer and as such is not documented. Signed-off-by: Dave Kelsey <d_kelsey@uk.ibm.com>
1 parent c20b2b7 commit 3ed1794

File tree

4 files changed

+352
-314
lines changed

4 files changed

+352
-314
lines changed

packages/composer-connector-hlfv1/lib/hlfconnection.js

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const fs = require('fs-extra');
1919
const HLFSecurityContext = require('./hlfsecuritycontext');
2020
const HLFUtil = require('./hlfutil');
2121
const HLFTxEventHandler = require('./hlftxeventhandler');
22-
const HLFQueryHandler = require('./hlfqueryhandler');
2322
const Logger = require('composer-common').Logger;
2423
const path = require('path');
2524
const temp = require('temp').track();
@@ -44,6 +43,8 @@ const installDependencies = {
4443

4544
const chaincodePathSection = 'businessnetwork';
4645

46+
let HLFQueryHandler;
47+
4748
/**
4849
* Class representing a connection to a business network running on Hyperledger
4950
* Fabric, using the hfc module.
@@ -67,10 +68,18 @@ class HLFConnection extends Connection {
6768
/**
6869
* create a new Query Handler.
6970
* @param {HLFConnection} connection The connection to be used by the query handler.
70-
* @return {HLFQueryManager} A new query manager.
71+
* @param {string} queryHandlerImpl The query handler to require
72+
* @return {HLFQueryHandler} A new query handler.
7173
*/
72-
static createQueryHandler(connection) {
73-
return new HLFQueryHandler(connection);
74+
static createQueryHandler(connection, queryHandlerImpl) {
75+
const method = 'createQueryHandler';
76+
if (typeof queryHandlerImpl === 'string') {
77+
LOG.info(method, `attemping to load query handler module ${queryHandlerImpl}`);
78+
HLFQueryHandler = require(queryHandlerImpl);
79+
return new HLFQueryHandler(connection);
80+
} else {
81+
return new queryHandlerImpl(connection);
82+
}
7483
}
7584

7685
/**
@@ -128,10 +137,18 @@ class HLFConnection extends Connection {
128137
this.caClient = caClient;
129138
this.initialized = false;
130139
this.commitTimeout = connectOptions['x-commitTimeout'] ? connectOptions['x-commitTimeout'] * 1000 : 300 * 1000;
140+
LOG.debug(method, `commit timeout set to ${this.commitTimeout}`);
141+
131142
this.requiredEventHubs = isNaN(connectOptions['x-requiredEventHubs'] * 1) ? 1 : connectOptions['x-requiredEventHubs'] * 1;
132143
LOG.debug(method, `required event hubs set to ${this.requiredEventHubs}`);
133-
this.queryHandler = HLFConnection.createQueryHandler(this);
134-
LOG.debug(method, `commit timeout set to ${this.commitTimeout}`);
144+
145+
let queryHandlerImpl = './hlfqueryhandler';
146+
if (process.env.COMPOSER_QUERY_HANDLER && process.env.COMPOSER_QUERY_HANDLER.length !== 0) {
147+
queryHandlerImpl = process.env.COMPOSER_QUERY_HANDLER;
148+
} else if (connectOptions.queryHandler && connectOptions.queryHandler.length !== 0) {
149+
queryHandlerImpl = connectOptions.queryHandler;
150+
}
151+
this.queryHandler = HLFConnection.createQueryHandler(this, queryHandlerImpl);
135152

136153
// We create promisified versions of these APIs.
137154
this.fs = thenifyAll(fs);
@@ -1337,6 +1354,88 @@ class HLFConnection extends Connection {
13371354
return txId;
13381355
}
13391356

1357+
/**
1358+
* Send a query
1359+
* @param {Peer} peer The peer to query
1360+
* @param {TransactionID} txId the transaction id to use
1361+
* @param {string} functionName the function name of the query
1362+
* @param {array} args the arguments to ass
1363+
* @returns {Buffer} asynchronous response to query
1364+
*/
1365+
async querySinglePeer(peer, txId, functionName, args) {
1366+
const method = 'querySinglePeer';
1367+
LOG.entry(method, peer.getName(), txId, functionName, args);
1368+
const request = {
1369+
targets: [peer],
1370+
chaincodeId: this.businessNetworkIdentifier,
1371+
txId: txId,
1372+
fcn: functionName,
1373+
args: args
1374+
};
1375+
1376+
const t0 = Date.now();
1377+
let payloads = await this.queryByChaincode(request);
1378+
LOG.perf(method, `Total duration for queryByChaincode to ${functionName}: `, txId, t0);
1379+
LOG.debug(method, `Received ${payloads.length} payloads(s) from querying the composer runtime chaincode`);
1380+
if (!payloads.length) {
1381+
LOG.error(method, 'No payloads were returned from the query request:' + functionName);
1382+
throw new Error('No payloads were returned from the query request:' + functionName);
1383+
}
1384+
const payload = payloads[0];
1385+
1386+
//
1387+
// need to also handle the grpc error codes as before, but now need to handle the change in the
1388+
// node-sdk with a horrible match a string error, but would need a fix to node-sdk to resolve.
1389+
// A Fix is in 1.3
1390+
if (payload instanceof Error && (
1391+
(payload.code && (payload.code === 14 || payload.code === 1 || payload.code === 4)) ||
1392+
(payload.message.match(/Failed to connect before the deadline/))
1393+
)) {
1394+
throw payload;
1395+
}
1396+
1397+
LOG.exit(method, payload);
1398+
return payload;
1399+
1400+
}
1401+
1402+
/**
1403+
* Perform a chaincode query and parse the responses.
1404+
* @param {object} request the proposal for a query
1405+
* @return {array} the responses
1406+
*/
1407+
async queryByChaincode(request) {
1408+
const method = 'queryByChaincode';
1409+
LOG.entry(method, request);
1410+
try {
1411+
const results = await this.channel.sendTransactionProposal(request);
1412+
const responses = results[0];
1413+
if (responses && Array.isArray(responses)) {
1414+
let results = [];
1415+
for (let i = 0; i < responses.length; i++) {
1416+
let response = responses[i];
1417+
if (response instanceof Error) {
1418+
results.push(response);
1419+
}
1420+
else if (response.response && response.response.payload) {
1421+
results.push(response.response.payload);
1422+
}
1423+
else {
1424+
results.push(new Error(response));
1425+
}
1426+
}
1427+
LOG.exit(method);
1428+
return results;
1429+
}
1430+
const err = new Error('Payload results are missing from the chaincode query');
1431+
LOG.error(method, err);
1432+
throw err;
1433+
} catch(err) {
1434+
LOG.error(method, err);
1435+
throw err;
1436+
}
1437+
}
1438+
13401439
}
13411440

13421441
module.exports = HLFConnection;

packages/composer-connector-hlfv1/lib/hlfqueryhandler.js

Lines changed: 2 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class HLFQueryHandler {
7373
if (this.queryPeerIndex !== -1) {
7474
let peer = this.allQueryPeers[this.queryPeerIndex];
7575
try {
76-
payload = await this.querySinglePeer(peer, txId, functionName, args);
76+
payload = await this.connection.querySinglePeer(peer, txId, functionName, args);
7777
success = true;
7878
} catch (error) {
7979
allErrors.push(error);
@@ -93,7 +93,7 @@ class HLFQueryHandler {
9393
}
9494
let peer = this.allQueryPeers[i];
9595
try {
96-
payload = await this.querySinglePeer(peer, txId, functionName, args);
96+
payload = await this.connection.querySinglePeer(peer, txId, functionName, args);
9797
this.queryPeerIndex = i;
9898
success = true;
9999
break;
@@ -119,88 +119,6 @@ class HLFQueryHandler {
119119
return payload;
120120

121121
}
122-
123-
/**
124-
* Send a query
125-
* @param {Peer} peer The peer to query
126-
* @param {TransactionID} txId the transaction id to use
127-
* @param {string} functionName the function name of the query
128-
* @param {array} args the arguments to ass
129-
* @returns {Buffer} asynchronous response to query
130-
*/
131-
async querySinglePeer(peer, txId, functionName, args) {
132-
const method = 'querySinglePeer';
133-
LOG.entry(method, peer.getName(), txId, functionName, args);
134-
const request = {
135-
targets: [peer],
136-
chaincodeId: this.connection.businessNetworkIdentifier,
137-
txId: txId,
138-
fcn: functionName,
139-
args: args
140-
};
141-
142-
const t0 = Date.now();
143-
let payloads = await this.queryByChaincode(request);
144-
LOG.perf(method, `Total duration for queryByChaincode to ${functionName}: `, txId, t0);
145-
LOG.debug(method, `Received ${payloads.length} payloads(s) from querying the composer runtime chaincode`);
146-
if (!payloads.length) {
147-
LOG.error(method, 'No payloads were returned from the query request:' + functionName);
148-
throw new Error('No payloads were returned from the query request:' + functionName);
149-
}
150-
const payload = payloads[0];
151-
152-
//
153-
// need to also handle the grpc error codes as before, but now need to handle the change in the
154-
// node-sdk with a horrible match a string error, but would need a fix to node-sdk to resolve.
155-
// A Fix is in 1.3
156-
if (payload instanceof Error && (
157-
(payload.code && (payload.code === 14 || payload.code === 1 || payload.code === 4)) ||
158-
(payload.message.match(/Failed to connect before the deadline/))
159-
)) {
160-
throw payload;
161-
}
162-
163-
LOG.exit(method, payload);
164-
return payload;
165-
166-
}
167-
168-
/**
169-
* Perform a chaincode query and parse the responses.
170-
* @param {object} request the proposal for a query
171-
* @return {array} the responses
172-
*/
173-
async queryByChaincode(request) {
174-
const method = 'queryByChaincode';
175-
LOG.entry(method, request);
176-
try {
177-
const results = await this.connection.channel.sendTransactionProposal(request);
178-
const responses = results[0];
179-
if (responses && Array.isArray(responses)) {
180-
let results = [];
181-
for (let i = 0; i < responses.length; i++) {
182-
let response = responses[i];
183-
if (response instanceof Error) {
184-
results.push(response);
185-
}
186-
else if (response.response && response.response.payload) {
187-
results.push(response.response.payload);
188-
}
189-
else {
190-
results.push(new Error(response));
191-
}
192-
}
193-
LOG.exit(method);
194-
return results;
195-
}
196-
const err = new Error('Payload results are missing from the chaincode query');
197-
LOG.error(method, err);
198-
throw err;
199-
} catch(err) {
200-
LOG.error(method, err);
201-
throw err;
202-
}
203-
}
204122
}
205123

206124
module.exports = HLFQueryHandler;

0 commit comments

Comments
 (0)