Skip to content

Commit

Permalink
FABN-1069: TimeoutError on commit event timeout
Browse files Browse the repository at this point in the history
Allows calling code to distinguish between transaction invocation
failure and a timeout waiting for a commit event following a
successful transaction invocation. Timeout errors can be identified
either by checking the error name or type:

- error.name === 'TimeoutError'
- error instanceof TimeoutError

Change-Id: I9a58bde13fbe2e2271680d9f5441b1b1de1712a8
Signed-off-by: Mark S. Lewis <[email protected]>
  • Loading branch information
bestbeforetoday committed Dec 18, 2018
1 parent ca9b9d1 commit aa1236a
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 2 deletions.
1 change: 1 addition & 0 deletions fabric-network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ module.exports.HSMWalletMixin = require('./lib/impl/wallet/hsmwalletmixin');
module.exports.FileSystemWallet = require('./lib/impl/wallet/filesystemwallet');
module.exports.CouchDBWallet = require('./lib/impl/wallet/couchdbwallet');
module.exports.DefaultEventHandlerStrategies = require('fabric-network/lib/impl/event/defaulteventhandlerstrategies');
module.exports.TimeoutError = require('fabric-network/lib/errors/timeouterror');
2 changes: 2 additions & 0 deletions fabric-network/lib/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ class Contract {
* @param {string} name Transaction function name.
* @param {...string} [args] Transaction function arguments.
* @returns {Buffer} Payload response from the transaction function.
* @throws {module:fabric-network.TimeoutError} If the transaction was successfully submitted to the orderer but
* timed out before a commit event was received from peers.
*/
async submitTransaction(name, ...args) {
return this.createTransaction(name).submit(...args);
Expand Down
33 changes: 33 additions & 0 deletions fabric-network/lib/errors/fabricerror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2018 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/

'use strict';

/**
* Base type for Fabric-specific errors.
* @interface
* @memberof module:fabric-network
* @property {Error} [cause] Underlying error that caused this error.
* @property {string} [transactionId] ID of the associated transaction.
*/
class FabricError extends Error {
/*
* Constructor.
* @param {(string|object)} [info] Either an error message (string) or additional properties to assign to this
* inctance (object).
*/
constructor(info) {
if (!info || typeof info === 'string') {
super(info);
} else {
super(info.message);
Object.assign(this, info);
}
this.name = this.constructor.name;
}
}

module.exports = FabricError;
22 changes: 22 additions & 0 deletions fabric-network/lib/errors/timeouterror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright 2018 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/

'use strict';

const FabricError = require('./fabricerror');

/**
* Error indicating a timeout.
* @extends module:fabric-network.FabricError
* @memberof module:fabric-network
*/
class TimeoutError extends FabricError {
constructor(info) {
super(info);
}
}

module.exports = TimeoutError;
8 changes: 7 additions & 1 deletion fabric-network/lib/impl/event/transactioneventhandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

'use strict';

const TimeoutError = require('fabric-network/lib/errors/timeouterror');

const logger = require('fabric-network/lib/logger').getLogger('TransactionEventHandler');
const util = require('util');

Expand Down Expand Up @@ -104,7 +106,11 @@ class TransactionEventHandler {
.map((eventHub) => eventHub.getName())
.join(', ');
const message = 'Event strategy not satisfied within the timeout period. No response received from event hubs: ' + unrespondedEventHubs;
this._strategyFail(new Error(message));
const error = new TimeoutError({
message,
transactionId: this.transactionId
});
this._strategyFail(error);
}

_onEvent(eventHub, txId, code) {
Expand Down
2 changes: 2 additions & 0 deletions fabric-network/lib/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class Transaction {
* @async
* @param {...String} [args] Transaction function arguments.
* @returns {Buffer} Payload response from the transaction function.
* @throws {module:fabric-network.TimeoutError} If the transaction was successfully submitted to the orderer but
* timed out before a commit event was received from peers.
*/
async submit(...args) {
verifyArguments(args);
Expand Down
51 changes: 51 additions & 0 deletions fabric-network/test/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright 2018 IBM All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/

'use strict';

const chai = require('chai');
const expect = chai.expect;

const FabricError = require('fabric-network/lib/errors/fabricerror');
const TimeoutError = require('fabric-network/lib/errors/timeouterror');

describe('Common error behaviour', () => {
[FabricError, TimeoutError].forEach((ErrorType) => describe(ErrorType.name, () => {
it('name property matches class name', () => {
const error = new ErrorType();
expect(error.name).to.equal(ErrorType.name);
});

it('created with error message string', () => {
const message = 'message';
const error = new ErrorType(message);
expect(error.message).to.equal(message);
});

it('created with error message property', () => {
const info = {message: 'message'};
const error = new ErrorType(info);
expect(error.message).to.equal(info.message);
});

it('created with cause error', () => {
const cause = new Error('cause');
const error = new ErrorType({cause});
expect(error.cause).to.equal(cause);
});

it('associated with transaction ID', () => {
const transactionId = 'txId';
const error = new ErrorType({transactionId});
expect(error.transactionId).to.equal(transactionId);
});

it('name property does not override error name', () => {
const error = new ErrorType({name: 'wrong'});
expect(error.name).to.equal(ErrorType.name);
});
}));
});
17 changes: 16 additions & 1 deletion fabric-network/test/impl/event/transactioneventhandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const sinon = require('sinon');
const ChannelEventHub = require('fabric-client').ChannelEventHub;

const TransactionEventHandler = require('fabric-network/lib/impl/event/transactioneventhandler');
const TimeoutError = require('fabric-network/lib/errors/timeouterror');

describe('TransactionEventHandler', () => {
let stubEventHub;
Expand Down Expand Up @@ -183,7 +184,7 @@ describe('TransactionEventHandler', () => {
await handler.startListening();
const promise = handler.waitForEvents();
clock.runAll();
return expect(promise).to.be.rejectedWith('Event strategy not satisfied within the timeout period');
return expect(promise).to.be.rejectedWith(TimeoutError);
});

it('does not timeout if timeout set to zero', async () => {
Expand Down Expand Up @@ -215,5 +216,19 @@ describe('TransactionEventHandler', () => {
clock.runAll();
return expect(handler.waitForEvents()).to.be.fulfilled;
});

it('timeout failure error has transaction ID property', async () => {
const options = {commitTimeout: 418};
handler = new TransactionEventHandler(transactionId, stubStrategy, options);
await handler.startListening();
const promise = handler.waitForEvents();
clock.runAll();
try {
await promise;
chai.assert.fail('Expected an error');
} catch (error) {
expect(error.transactionId).to.equal(transactionId);
}
});
});
});
7 changes: 7 additions & 0 deletions fabric-network/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export class Transaction {
public submit(...args: string[]): Promise<Buffer>;
}

export interface FabricError {
cause?: Error;
transactionId?: string;
}

export class TimeoutError implements FabricError {}

//-------------------------------------------
// Wallet Management
//-------------------------------------------
Expand Down

0 comments on commit aa1236a

Please sign in to comment.