Skip to content

Commit

Permalink
feat(ref-imp): only allow DB upgrade on observer node
Browse files Browse the repository at this point in the history
* fix(ref-imp): added index to operation store to support CosmosDB 4.0
* feat(ref-imp): only allow DB upgrade on Observer nodes
  • Loading branch information
thehenrytsai committed Jun 22, 2021
1 parent 81e74d1 commit 63b9301
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 15 deletions.
7 changes: 3 additions & 4 deletions lib/bitcoin/BitcoinProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,12 @@ export default class BitcoinProcessor {
return;
}

// Throw if attempting to run old code on new DB.
// Throw if attempting to downgrade.
if (actualDbVersion !== undefined && semver.lt(expectedDbVersion, actualDbVersion)) {
Logger.error(
// eslint-disable-next-line max-len
LogColor.red(`Running code dependent on DB version ${LogColor.green(expectedDbVersion)} on newer DB version ${LogColor.green(actualDbVersion)} is not supported.`)
LogColor.red(`Downgrading DB from version ${LogColor.green(actualDbVersion)} to ${LogColor.green(expectedDbVersion)} is not allowed.`)
);
throw new SidetreeError(ErrorCode.RunningOlderCodeOnNewerDatabaseUnsupported);
throw new SidetreeError(ErrorCode.DatabaseDowngradeNotAllowed);
}

// Add DB upgrade code below.
Expand Down
2 changes: 1 addition & 1 deletion lib/bitcoin/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
LockResolverTransactionIsNotPayingToScript: 'lock_resolver_transaction_is_not_paying_to_script',
LockResolverTransactionNotConfirmed: 'lock_resolver_transaction_not_confirmed',
LockResolverTransactionNotFound: 'lock_resolver_transaction_not_found',
RunningOlderCodeOnNewerDatabaseUnsupported: 'running_older_code_on_newer_database_unsupported',
DatabaseDowngradeNotAllowed: 'database_downgrade_not_allowed',
ValueTimeLockInPendingState: 'value_time_lock_in_pending_state',
VersionManagerVersionStringNotFound: 'version_manager_version_string_not_found'
};
15 changes: 10 additions & 5 deletions lib/core/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,26 @@ export default class Core {
}

private async upgradeDatabaseIfNeeded () {
const expectedDbVersion = '1.0.0';
// If this node is not the active Observer node, do not perform DB upgrade.
// Since only one active Observer is supported, this ensures only one node is performing the DB upgrade.
if (this.config.observingIntervalInSeconds === 0) {
return;
}

const expectedDbVersion = '1.0.1';
const savedServiceState = await this.serviceStateStore.get();
const actualDbVersion = savedServiceState.databaseVersion;

if (expectedDbVersion === actualDbVersion) {
return;
}

// Throw if attempting to run old code on new DB.
// Throw if attempting to downgrade.
if (actualDbVersion !== undefined && semver.lt(expectedDbVersion, actualDbVersion)) {
Logger.error(
// eslint-disable-next-line max-len
LogColor.red(`Running code dependent on DB version ${LogColor.green(expectedDbVersion)} on newer DB version ${LogColor.green(actualDbVersion)} is not supported.`)
LogColor.red(`Downgrading DB from version ${LogColor.green(actualDbVersion)} to ${LogColor.green(expectedDbVersion)} is not allowed.`)
);
throw new SidetreeError(ErrorCode.RunningOlderCodeOnNewerDatabaseUnsupported);
throw new SidetreeError(ErrorCode.DatabaseDowngradeNotAllowed);
}

// Add DB upgrade code below.
Expand Down
2 changes: 1 addition & 1 deletion lib/core/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
BlockchainReadResponseBodyNotJson: 'blockchain_read_response_body_not_json',
BlockchainReadResponseNotOk: 'blockchain_read_response_not_ok',
BlockchainWriteUnexpectedError: 'blockchain_write_unexpected_error',
RunningOlderCodeOnNewerDatabaseUnsupported: 'running_older_code_on_newer_database_unsupported',
DatabaseDowngradeNotAllowed: 'database_downgrade_not_allowed',
VersionManagerVersionStringNotFound: 'version_manager_version_string_not_found',
VersionManagerVersionMetadataIncorrectType: 'version_manager_version_metadata_incorrect_type'
};
2 changes: 2 additions & 0 deletions lib/core/MongoDbOperationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export default class MongoDbOperationStore extends MongoDbStore implements IOper
await this.collection.createIndex({ didSuffix: 1, txnNumber: 1, opIndex: 1, type: 1 }, { unique: true });
// The query in `get() method needs a corresponding composite index in some cloud-based services (CosmosDB 4.0) that supports MongoDB driver.
await this.collection.createIndex({ didSuffix: 1, txnNumber: 1, opIndex: 1 }, { unique: true });
// The query in `get() method needs a non-composite index on `didSuffix` in some cloud-based services (CosmosDB 4.0) to allow efficient queries.
await this.collection.createIndex({ didSuffix: 1 }, { unique: false });
}

public async insertOrReplace (operations: AnchoredOperationModel[]): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion tests/bitcoin/BitcoinProcessor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1885,7 +1885,7 @@ describe('BitcoinProcessor', () => {

JasmineSidetreeErrorValidator.expectSidetreeErrorToBeThrownAsync(
() => (bitcoinProcessor as any).upgradeDatabaseIfNeeded(),
ErrorCode.RunningOlderCodeOnNewerDatabaseUnsupported
ErrorCode.DatabaseDowngradeNotAllowed
);
});
});
Expand Down
18 changes: 15 additions & 3 deletions tests/core/Core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,23 @@ describe('Core', async () => {
beforeEach(() => {
});

it('should not perform upgrade if the node is not an active Observer node.', async () => {
const config = Object.assign({}, testConfig);
config.observingIntervalInSeconds = 0; // Force disabling of Observer.
const core = new Core(config, testVersionConfig, mockCas);

const serviceStateStorePutSpy = spyOn(core['serviceStateStore'], 'put');
await (core as any).upgradeDatabaseIfNeeded();

// Verify that upgrade path was NOT invoked.
expect(serviceStateStorePutSpy).not.toHaveBeenCalled();
});

it('should not perform upgrade if saved database version is the same as the expected database version.', async () => {
const core = new Core(testConfig, testVersionConfig, mockCas);

// Simulate that the saved database version is the same as the expected database version.
spyOn(core['serviceStateStore'], 'get').and.returnValue(Promise.resolve({ databaseVersion: '1.0.0' }));
spyOn(core['serviceStateStore'], 'get').and.returnValue(Promise.resolve({ databaseVersion: '1.0.1' }));

const serviceStateStorePutSpy = spyOn(core['serviceStateStore'], 'put');
await (core as any).upgradeDatabaseIfNeeded();
Expand Down Expand Up @@ -216,7 +228,7 @@ describe('Core', async () => {
expect(unresolvableTransactionStoreClearCollectionSpy).toHaveBeenCalled();
expect(transactionStoreClearCollectionSpy).toHaveBeenCalled();
expect(operationStoreCreateIndexSpy).toHaveBeenCalled();
expect(serviceStateStorePutSpy).toHaveBeenCalledWith({ databaseVersion: '1.0.0' });
expect(serviceStateStorePutSpy).toHaveBeenCalledWith({ databaseVersion: '1.0.1' });
});

it('should throw if attempting to run older code on newer DB.', async () => {
Expand All @@ -227,7 +239,7 @@ describe('Core', async () => {

JasmineSidetreeErrorValidator.expectSidetreeErrorToBeThrownAsync(
() => (core as any).upgradeDatabaseIfNeeded(),
ErrorCode.RunningOlderCodeOnNewerDatabaseUnsupported
ErrorCode.DatabaseDowngradeNotAllowed
);
});
});
Expand Down

0 comments on commit 63b9301

Please sign in to comment.