Skip to content

Commit

Permalink
fix(ref-imp): integrate custom logger with mongodb (#1157)
Browse files Browse the repository at this point in the history
* fix(ref-imp): integrate custom logger with mongodb

This change integrates custom logger with mongodb
  • Loading branch information
xinaxu authored Oct 6, 2021
1 parent b89de53 commit a357828
Show file tree
Hide file tree
Showing 19 changed files with 259 additions and 267 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ nunitresults.xml
.nyc_output/
coverage/

style-conversion
# Intellij
.idea/

style-conversion
4 changes: 2 additions & 2 deletions lib/bitcoin/BitcoinProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default class BitcoinProcessor {

this.serviceStateStore = new MongoDbServiceStateStore(config.mongoDbConnectionString, config.databaseName);
this.blockMetadataStore = new MongoDbBlockMetadataStore(config.mongoDbConnectionString, config.databaseName);
this.transactionStore = new MongoDbTransactionStore();
this.transactionStore = new MongoDbTransactionStore(config.mongoDbConnectionString, config.databaseName);

this.spendingMonitor = new SpendingMonitor(config.bitcoinFeeSpendingCutoffPeriodInBlocks,
BitcoinClient.convertBtcToSatoshis(config.bitcoinFeeSpendingCutoff),
Expand Down Expand Up @@ -165,7 +165,7 @@ export default class BitcoinProcessor {
await this.versionManager.initialize(versionModels, this.config, this.blockMetadataStore);
await this.serviceStateStore.initialize();
await this.blockMetadataStore.initialize();
await this.transactionStore.initialize(this.config.mongoDbConnectionString, this.config.databaseName);
await this.transactionStore.initialize();
await this.bitcoinClient.initialize();
await this.mongoDbLockTransactionStore.initialize();

Expand Down
58 changes: 13 additions & 45 deletions lib/bitcoin/lock/MongoDbLockTransactionStore.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Collection, Db, Long, MongoClient } from 'mongodb';
import Logger from '../../common/Logger';
import { Long } from 'mongodb';
import MongoDbStore from '../../common/MongoDbStore';
import SavedLockModel from './../models/SavedLockedModel';

/**
* Encapsulates functionality to store the bitcoin lock information to Db.
*/
export default class MongoDbLockTransactionStore {
private db: Db | undefined;
private lockCollection: Collection<any> | undefined;

export default class MongoDbLockTransactionStore extends MongoDbStore {
/** The collection name */
public static readonly lockCollectionName = 'locks';

Expand All @@ -18,17 +15,9 @@ export default class MongoDbLockTransactionStore {
* @param databaseName The database name where the collection should be saved.
*/
public constructor (
private serverUrl: string,
private databaseName: string) {
}

/**
* Initializes this object by creating the required collection.
*/
public async initialize () {
const client = await MongoClient.connect(this.serverUrl, { useNewUrlParser: true }); // `useNewUrlParser` addresses nodejs's URL parser deprecation warning.
this.db = client.db(this.databaseName);
this.lockCollection = await MongoDbLockTransactionStore.creatLockCollectionIfNotExist(this.db);
serverUrl: string,
databaseName: string) {
super(serverUrl, MongoDbLockTransactionStore.lockCollectionName, databaseName);
}

/**
Expand All @@ -47,22 +36,14 @@ export default class MongoDbLockTransactionStore {
type: bitcoinLock.type
};

await this.lockCollection!.insertOne(lockInMongoDb);
}

/**
* Clears the store.
*/
public async clearCollection () {
await this.lockCollection!.drop();
this.lockCollection = await MongoDbLockTransactionStore.creatLockCollectionIfNotExist(this.db!);
await this.collection!.insertOne(lockInMongoDb);
}

/**
* Gets the latest lock (highest create timestamp) saved in the db; or undefined if nothing saved.
*/
public async getLastLock (): Promise<SavedLockModel | undefined> {
const lastLocks = await this.lockCollection!
const lastLocks = await this.collection!
.find()
.limit(1)
.sort({ createTimestamp: -1 })
Expand All @@ -75,23 +56,10 @@ export default class MongoDbLockTransactionStore {
return lastLocks[0] as SavedLockModel;
}

private static async creatLockCollectionIfNotExist (db: Db): Promise<Collection<SavedLockModel>> {
const collections = await db.collections();
const collectionNames = collections.map(collection => collection.collectionName);

// If 'locks' collection exists, use it; else create it.
let lockCollection;
if (collectionNames.includes(MongoDbLockTransactionStore.lockCollectionName)) {
Logger.info('Locks collection already exists.');
lockCollection = db.collection(MongoDbLockTransactionStore.lockCollectionName);
} else {
Logger.info('Locks collection does not exists, creating...');
lockCollection = await db.createCollection(MongoDbLockTransactionStore.lockCollectionName);

await lockCollection.createIndex({ createTimestamp: -1 });
Logger.info('Locks collection created.');
}

return lockCollection;
/**
* @inheritDoc
*/
public async createIndex (): Promise<void> {
await this.collection.createIndex({ createTimestamp: -1 });
}
}
4 changes: 4 additions & 0 deletions lib/common/ConsoleLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import ILogger from './interfaces/ILogger';
* Console Logger.
*/
export default class ConsoleLogger implements ILogger {
debug (data: any): void {
console.debug(data);
}

info (data: any): void {
console.info(data);
}
Expand Down
7 changes: 7 additions & 0 deletions lib/common/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export default class Logger {
}
}

/**
* Logs debug.
*/
public static debug (data: any): void {
Logger.singleton.debug(data);
}

/**
* Logs info.
*/
Expand Down
49 changes: 47 additions & 2 deletions lib/common/MongoDbStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Collection, Db, MongoClient } from 'mongodb';
import { Collection, Db, LoggerState, MongoClient } from 'mongodb';
import Logger from '../common/Logger';

/**
Expand All @@ -13,6 +13,44 @@ export default class MongoDbStore {
/** MongoDB collection */
protected collection!: Collection<any>;

/**
* Set the logger for mongodb command monitoring.
* @param client the mongodb client
*/
public static enableCommandResultLogging (client: MongoClient) {
client.on('commandSucceeded', (event: any) => {
Logger.info(event);
});
client.on('commandFailed', (event: any) => {
Logger.warn(event);
});
}

/**
* The custom logger for general logging purpose in mongodb client
* @param _message The message is already included in the state so there is no need to log the message twice.
* @param state The complete logging event state
*/
public static customLogger (_message: string | undefined, state: LoggerState | undefined): void {
if (state === undefined) {
return;
}

switch (state.type) {
case 'warn':
Logger.warn(state);
break;
case 'error':
Logger.error(state);
break;
case 'debug':
Logger.debug(state);
break;
default:
Logger.info(state);
}
};

/**
* Constructs a `MongoDbStore`;
*/
Expand All @@ -22,7 +60,14 @@ export default class MongoDbStore {
* Initialize the MongoDB transaction store.
*/
public async initialize (): Promise<void> {
const client = await MongoClient.connect(this.serverUrl, { useNewUrlParser: true }); // `useNewUrlParser` addresses nodejs's URL parser deprecation warning.
// `useNewUrlParser` addresses nodejs's URL parser deprecation warning.
const client = await MongoClient.connect(this.serverUrl, {
useNewUrlParser: true,
logger: MongoDbStore.customLogger,
monitorCommands: true,
loggerLevel: 'debug'
});
MongoDbStore.enableCommandResultLogging(client);
this.db = client.db(this.databaseName);
await this.createCollectionIfNotExist(this.db);
}
Expand Down
79 changes: 25 additions & 54 deletions lib/common/MongoDbTransactionStore.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
import { Collection, Cursor, Db, Long, MongoClient } from 'mongodb';
import { Cursor, Long } from 'mongodb';
import ITransactionStore from '../core/interfaces/ITransactionStore';
import Logger from '../common/Logger';
import MongoDbStore from './MongoDbStore';
import TransactionModel from './models/TransactionModel';

/**
* Implementation of ITransactionStore that stores the transaction data in a MongoDB database.
*/
export default class MongoDbTransactionStore implements ITransactionStore {
/** Default database name used if not specified in constructor. */
public static readonly defaultDatabaseName: string = 'sidetree';
export default class MongoDbTransactionStore extends MongoDbStore implements ITransactionStore {
/** Collection name for transactions. */
public static readonly transactionCollectionName: string = 'transactions';

private db: Db | undefined;
private transactionCollection: Collection<any> | undefined;

/**
* Initialize the MongoDB transaction store.
* Creates a new instance of this object.
* @param serverUrl The target server url.
* @param databaseName The database name where the collection should be saved.
*/
public async initialize (serverUrl: string, databaseName: string): Promise<void> {
const client = await MongoClient.connect(serverUrl, { useNewUrlParser: true }); // `useNewUrlParser` addresses nodejs's URL parser deprecation warning.
this.db = client.db(databaseName);
this.transactionCollection = await MongoDbTransactionStore.createTransactionCollectionIfNotExist(this.db);
public constructor (
serverUrl: string,
databaseName: string) {
super(serverUrl, MongoDbTransactionStore.transactionCollectionName, databaseName);
}

/**
* Returns the number of transactions in the store.
* Mainly used by tests.
*/
public async getTransactionsCount (): Promise<number> {
const transactionCount = await this.transactionCollection!.count();
const transactionCount = await this.collection!.count();
return transactionCount;
}

public async getTransaction (transactionNumber: number): Promise<TransactionModel | undefined> {
const transactions = await this.transactionCollection!.find({ transactionNumber: Long.fromNumber(transactionNumber) }).toArray();
const transactions = await this.collection!.find({ transactionNumber: Long.fromNumber(transactionNumber) }).toArray();
if (transactions.length === 0) {
return undefined;
}
Expand All @@ -52,9 +50,9 @@ export default class MongoDbTransactionStore implements ITransactionStore {

// If given `undefined`, return transactions from the start.
if (transactionNumber === undefined) {
dbCursor = this.transactionCollection!.find();
dbCursor = this.collection!.find();
} else {
dbCursor = this.transactionCollection!.find({ transactionNumber: { $gt: Long.fromNumber(transactionNumber) } });
dbCursor = this.collection!.find({ transactionNumber: { $gt: Long.fromNumber(transactionNumber) } });
}

// If a limit is defined then set it.
Expand All @@ -75,16 +73,6 @@ export default class MongoDbTransactionStore implements ITransactionStore {
return transactions;
}

/**
* Clears the transaction store.
*/
public async clearCollection () {
// NOTE: We avoid implementing this by deleting and recreating the collection in rapid succession,
// because doing so against some cloud MongoDB services such as CosmosDB,
// especially in rapid repetition that can occur in tests, will lead to `MongoError: ns not found` connectivity error.
await this.transactionCollection!.deleteMany({ }); // Empty filter removes all entries in collection.
}

async addTransaction (transaction: TransactionModel): Promise<void> {
try {
const transactionInMongoDb = {
Expand All @@ -97,7 +85,7 @@ export default class MongoDbTransactionStore implements ITransactionStore {
normalizedTransactionFee: transaction.normalizedTransactionFee,
writer: transaction.writer
};
await this.transactionCollection!.insertOne(transactionInMongoDb);
await this.collection!.insertOne(transactionInMongoDb);
} catch (error) {
// Swallow duplicate insert errors (error code 11000) as no-op; rethrow others
if (error.code !== 11000) {
Expand All @@ -107,7 +95,7 @@ export default class MongoDbTransactionStore implements ITransactionStore {
}

async getLastTransaction (): Promise<TransactionModel | undefined> {
const lastTransactions = await this.transactionCollection!.find().limit(1).sort({ transactionNumber: -1 }).toArray();
const lastTransactions = await this.collection!.find().limit(1).sort({ transactionNumber: -1 }).toArray();
if (lastTransactions.length === 0) {
return undefined;
}
Expand All @@ -118,7 +106,7 @@ export default class MongoDbTransactionStore implements ITransactionStore {

async getExponentiallySpacedTransactions (): Promise<TransactionModel[]> {
const exponentiallySpacedTransactions: TransactionModel[] = [];
const allTransactions = await this.transactionCollection!.find().sort({ transactionNumber: 1 }).toArray();
const allTransactions = await this.collection!.find().sort({ transactionNumber: 1 }).toArray();

let index = allTransactions.length - 1;
let distance = 1;
Expand All @@ -137,23 +125,23 @@ export default class MongoDbTransactionStore implements ITransactionStore {
return;
}

await this.transactionCollection!.deleteMany({ transactionNumber: { $gt: Long.fromNumber(transactionNumber) } });
await this.collection!.deleteMany({ transactionNumber: { $gt: Long.fromNumber(transactionNumber) } });
}

/**
* Remove transactions by transaction time hash
* @param transactionTimeHash the transaction time hash which the transactions should be removed for
*/
public async removeTransactionByTransactionTimeHash (transactionTimeHash: string) {
await this.transactionCollection!.deleteMany({ transactionTimeHash: { $eq: transactionTimeHash } });
await this.collection!.deleteMany({ transactionTimeHash: { $eq: transactionTimeHash } });
}

/**
* Gets the list of processed transactions.
* Mainly used for test purposes.
*/
public async getTransactions (): Promise<TransactionModel[]> {
const transactions = await this.transactionCollection!.find().sort({ transactionNumber: 1 }).toArray();
const transactions = await this.collection!.find().sort({ transactionNumber: 1 }).toArray();
return transactions;
}

Expand All @@ -166,9 +154,9 @@ export default class MongoDbTransactionStore implements ITransactionStore {
let cursor: Cursor<any>;
if (inclusiveBeginTransactionTime === exclusiveEndTransactionTime) {
// if begin === end, query for 1 transaction time
cursor = this.transactionCollection!.find({ transactionTime: { $eq: Long.fromNumber(inclusiveBeginTransactionTime) } });
cursor = this.collection!.find({ transactionTime: { $eq: Long.fromNumber(inclusiveBeginTransactionTime) } });
} else {
cursor = this.transactionCollection!.find({
cursor = this.collection!.find({
$and: [
{ transactionTime: { $gte: Long.fromNumber(inclusiveBeginTransactionTime) } },
{ transactionTime: { $lt: Long.fromNumber(exclusiveEndTransactionTime) } }
Expand All @@ -181,26 +169,9 @@ export default class MongoDbTransactionStore implements ITransactionStore {
}

/**
* Creates the `transaction` collection with indexes if it does not exists.
* @returns The existing collection if exists, else the newly created collection.
* @inheritDoc
*/
private static async createTransactionCollectionIfNotExist (db: Db): Promise<Collection<TransactionModel>> {
const collections = await db.collections();
const collectionNames = collections.map(collection => collection.collectionName);

// If 'transactions' collection exists, use it; else create it.
let transactionCollection;
if (collectionNames.includes(MongoDbTransactionStore.transactionCollectionName)) {
Logger.info('Transaction collection already exists.');
transactionCollection = db.collection(MongoDbTransactionStore.transactionCollectionName);
} else {
Logger.info('Transaction collection does not exists, creating...');
transactionCollection = await db.createCollection(MongoDbTransactionStore.transactionCollectionName);
// Note the unique index, so duplicate inserts are rejected.
await transactionCollection.createIndex({ transactionNumber: 1 }, { unique: true });
Logger.info('Transaction collection created.');
}

return transactionCollection;
public async createIndex (): Promise<void> {
await this.collection.createIndex({ transactionNumber: 1 }, { unique: true });
}
}
Loading

0 comments on commit a357828

Please sign in to comment.