Skip to content

Commit

Permalink
feat(ref-imp): added error code metadata for batch writer loop failur…
Browse files Browse the repository at this point in the history
…e event
  • Loading branch information
thehenrytsai committed Apr 7, 2021
1 parent 2b5354e commit 9ee807f
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 18 deletions.
9 changes: 8 additions & 1 deletion docs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,13 @@ HTTP/1.1 200 OK
### `sidetree_batch_writer_loop_failure`
Occurs every time the batch writer fails a processing loop.

Event data:
```json
{
"code": "Error code of the failure. Dependent on blockchain service implementation."
}
```

Event data: none

### `sidetree_batch_writer_loop_success`
Expand All @@ -705,7 +712,7 @@ Occurs every time the batch writer completes a processing loop.
Event data:
```json
{
"batchSize": "The size of the batch written.",
"batchSize": "The size of the batch written."
}
```

Expand Down
17 changes: 14 additions & 3 deletions lib/core/BatchScheduler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as timeSpan from 'time-span';
import ErrorCode from './ErrorCode';
import EventCode from './EventCode';
import EventEmitter from '../common/EventEmitter';
import IBlockchain from './interfaces/IBlockchain';
import IVersionManager from './interfaces/IVersionManager';
import Logger from '../common/Logger';
import SidetreeError from '../common/SidetreeError';

/**
* Class that performs periodic writing of batches of Sidetree operations to CAS and blockchain.
Expand Down Expand Up @@ -55,9 +57,18 @@ export default class BatchScheduler {

EventEmitter.emit(EventCode.SidetreeBatchWriterLoopSuccess, { batchSize });
} catch (error) {
EventEmitter.emit(EventCode.SidetreeBatchWriterLoopFailure);
Logger.error('Unexpected and unhandled error during batch writing, investigate and fix:');
Logger.error(error);
// Default the error to unexpected error.
const loopFailureEventData = { code: ErrorCode.BatchSchedulerWriteUnexpectedError };

// Only overwrite the error code if this is a concrete known error.
if (error instanceof SidetreeError && error.code !== ErrorCode.BlockchainWriteUnexpectedError) {
loopFailureEventData.code = error.code;
} else {
Logger.error('Unexpected and unhandled error during batch writing, investigate and fix:');
Logger.error(error);
}

EventEmitter.emit(EventCode.SidetreeBatchWriterLoopFailure, loopFailureEventData);
} finally {
Logger.info(`End batch writing. Duration: ${endTimer.rounded()} ms.`);

Expand Down
20 changes: 18 additions & 2 deletions lib/core/Blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,26 @@ export default class Blockchain implements IBlockchain {
};
const response = await this.fetch(this.transactionsUri, requestParameters);

// Throw error with meaningful code if possible.
if (response.status !== HttpStatus.OK) {
const body = response.body.read().toString();
Logger.error(`Blockchain write error response status: ${response.status}`);
Logger.error(`Blockchain write error body: ${response.body.read()}`);
throw new SidetreeError(CoreErrorCode.BlockchainWriteResponseNotOk);
Logger.error(`Blockchain write error body: ${body}`);

let parsedBody;
try {
parsedBody = JSON.parse(body);
} catch {
// Continue even if JSON parsing fails. We are just trying to parse the body as an object if possible.
}

// Throw Sidetree error with specific code if given.
if (parsedBody !== undefined && parsedBody.code !== undefined) {
throw new SidetreeError(parsedBody.code, 'Remote blockchain service returned a known write error.');
}

// Else throw generic sidetree error.
throw new SidetreeError(CoreErrorCode.BlockchainWriteUnexpectedError, 'Remote blockchain service returned an unexpected write error.');
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/core/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
* Error codes used by Sidetree core service.
*/
export default {
BatchSchedulerWriteUnexpectedError: 'batch_scheduler_write_unexpected_error',
BlockchainGetFeeResponseNotOk: 'blockchain_get_fee_response_not_ok',
BlockchainGetLatestTimeResponseNotOk: 'blockchain_get_latest_time_response_not_ok',
BlockchainGetLockResponseNotOk: 'blockchain_get_lock_response_not_ok',
BlockchainGetWriterLockResponseNotOk: 'blockchain_get_writer_lock_response_not_ok',
BlockchainReadInvalidArguments: 'blockchain_read_invalid_arguments',
BlockchainReadResponseBodyNotJson: 'blockchain_read_response_body_not_json',
BlockchainReadResponseNotOk: 'blockchain_read_response_not_ok',
BlockchainWriteResponseNotOk: 'blockchain_write_response_not_ok',
BlockchainWriteUnexpectedError: 'blockchain_write_unexpected_error',
VersionManagerVersionStringNotFound: 'version_manager_version_string_not_found',
VersionManagerVersionMetadataIncorrectType: 'version_manager_version_metadata_incorrect_type'
};
18 changes: 18 additions & 0 deletions tests/core/BatchScheduler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as retry from 'async-retry';
import BatchScheduler from '../../lib/core/BatchScheduler';
import EventCode from '../../lib/core/EventCode';
import EventEmitter from '../../lib/common/EventEmitter';
import MockBatchWriter from '../mocks/MockBatchWriter';
import MockBlockchain from '../mocks/MockBlockchain';
import MockVersionManager from '../mocks/MockVersionManager';
import SidetreeError from '../../lib/common/SidetreeError';

describe('BatchScheduler', async () => {
it('should periodically invoke batch writer.', async () => {
Expand Down Expand Up @@ -34,4 +37,19 @@ describe('BatchScheduler', async () => {

expect(batchWriter.invocationCount).toBeGreaterThanOrEqual(2);
});

it('should emit failure event with specific code if known SidetreeError is thrown.', async () => {
const blockchain = new MockBlockchain();

const dummyErrorCode = 'any error code';
const versionManager = new MockVersionManager();
spyOn(versionManager, 'getBatchWriter').and.callFake(() => { throw new SidetreeError(dummyErrorCode); });

const eventEmitterEmitSpy = spyOn(EventEmitter, 'emit');
const batchScheduler = new BatchScheduler(versionManager, blockchain, 1);

await batchScheduler.writeOperationBatch();

expect(eventEmitterEmitSpy).toHaveBeenCalledWith(EventCode.SidetreeBatchWriterLoopFailure, { code: dummyErrorCode });
});
});
30 changes: 19 additions & 11 deletions tests/core/Blockchain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ describe('Blockchain', async () => {
expect(fetchSpy).toHaveBeenCalled();
});

it('should throw if writing anchor string returned an error.', async () => {
it('should throw SidetreeError with correct error code if blockchain service returns an unexpected error.', async () => {
const blockchainClient = new Blockchain('Unused URI');
const mockFetchResponse = {
status: 500,
Expand All @@ -198,19 +198,27 @@ describe('Blockchain', async () => {
};
spyOn(blockchainClient as any, 'fetch').and.returnValue(Promise.resolve(mockFetchResponse));

try {
await blockchainClient.write('Unused anchor string.', 100);
} catch (error) {
// Throwing error is the expected case.
await JasmineSidetreeErrorValidator.expectSidetreeErrorToBeThrownAsync(
() => blockchainClient.write('Unused anchor string.', 100),
CoreErrorCode.BlockchainWriteUnexpectedError
);
});

if (error.code !== CoreErrorCode.BlockchainWriteResponseNotOk) {
fail();
it('should throw SidetreeError with correct error code if blockchain service returns a known Sidetree error.', async () => {
const blockchainClient = new Blockchain('Unused URI');
const dummyErrorCode = 'dummy error code';
const mockFetchResponse = {
status: 500,
body: {
read: () => { return JSON.stringify({ code: dummyErrorCode }); }
}
};
spyOn(blockchainClient as any, 'fetch').and.returnValue(Promise.resolve(mockFetchResponse));

return;
}

fail();
await JasmineSidetreeErrorValidator.expectSidetreeErrorToBeThrownAsync(
() => blockchainClient.write('Unused anchor string.', 100),
dummyErrorCode
);
});
});

Expand Down

0 comments on commit 9ee807f

Please sign in to comment.