Refactor database pool use

This commit is contained in:
nymkappa 2022-04-12 15:15:57 +09:00
parent ec4405b07a
commit 6fb0571b06
No known key found for this signature in database
GPG key ID: E155910B16E8BD04
10 changed files with 164 additions and 358 deletions

View file

@ -1,4 +1,3 @@
import { PoolConnection } from 'mysql2/promise';
import config from '../config'; import config from '../config';
import { DB } from '../database'; import { DB } from '../database';
import logger from '../logger'; import logger from '../logger';
@ -77,116 +76,112 @@ class DatabaseMigration {
await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion); await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion);
const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK); const isBitcoin = ['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK);
const connection = await DB.getConnection();
try { try {
await this.$executeQuery(connection, this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs')); await this.$executeQuery(this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs'));
await this.$executeQuery(connection, this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics')); await this.$executeQuery(this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics'));
if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) { if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) {
await this.$executeQuery(connection, `CREATE INDEX added ON statistics (added);`); await this.$executeQuery(`CREATE INDEX added ON statistics (added);`);
} }
if (databaseSchemaVersion < 3) { if (databaseSchemaVersion < 3) {
await this.$executeQuery(connection, this.getCreatePoolsTableQuery(), await this.$checkIfTableExists('pools')); await this.$executeQuery(this.getCreatePoolsTableQuery(), await this.$checkIfTableExists('pools'));
} }
if (databaseSchemaVersion < 4) { if (databaseSchemaVersion < 4) {
await this.$executeQuery(connection, 'DROP table IF EXISTS blocks;'); await this.$executeQuery('DROP table IF EXISTS blocks;');
await this.$executeQuery(connection, this.getCreateBlocksTableQuery(), await this.$checkIfTableExists('blocks')); await this.$executeQuery(this.getCreateBlocksTableQuery(), await this.$checkIfTableExists('blocks'));
} }
if (databaseSchemaVersion < 5 && isBitcoin === true) { if (databaseSchemaVersion < 5 && isBitcoin === true) {
logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`); logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE blocks;'); // Need to re-index await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `reward` double unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks ADD `reward` double unsigned NOT NULL DEFAULT "0"');
} }
if (databaseSchemaVersion < 6 && isBitcoin === true) { if (databaseSchemaVersion < 6 && isBitcoin === true) {
logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`); logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE blocks;'); // Need to re-index await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
// Cleanup original blocks fields type // Cleanup original blocks fields type
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `height` integer unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `height` integer unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `tx_count` smallint unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `tx_count` smallint unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `size` integer unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `size` integer unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `weight` integer unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `weight` integer unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `difficulty` double NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` double NOT NULL DEFAULT "0"');
// We also fix the pools.id type so we need to drop/re-create the foreign key // We also fix the pools.id type so we need to drop/re-create the foreign key
await this.$executeQuery(connection, 'ALTER TABLE blocks DROP FOREIGN KEY IF EXISTS `blocks_ibfk_1`'); await this.$executeQuery('ALTER TABLE blocks DROP FOREIGN KEY IF EXISTS `blocks_ibfk_1`');
await this.$executeQuery(connection, 'ALTER TABLE pools MODIFY `id` smallint unsigned AUTO_INCREMENT'); await this.$executeQuery('ALTER TABLE pools MODIFY `id` smallint unsigned AUTO_INCREMENT');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `pool_id` smallint unsigned NULL'); await this.$executeQuery('ALTER TABLE blocks MODIFY `pool_id` smallint unsigned NULL');
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD FOREIGN KEY (`pool_id`) REFERENCES `pools` (`id`)'); await this.$executeQuery('ALTER TABLE blocks ADD FOREIGN KEY (`pool_id`) REFERENCES `pools` (`id`)');
// Add new block indexing fields // Add new block indexing fields
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `version` integer unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks ADD `version` integer unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `bits` integer unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks ADD `bits` integer unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `nonce` bigint unsigned NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks ADD `nonce` bigint unsigned NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""'); await this.$executeQuery('ALTER TABLE blocks ADD `merkle_root` varchar(65) NOT NULL DEFAULT ""');
await this.$executeQuery(connection, 'ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL'); await this.$executeQuery('ALTER TABLE blocks ADD `previous_block_hash` varchar(65) NULL');
} }
if (databaseSchemaVersion < 7 && isBitcoin === true) { if (databaseSchemaVersion < 7 && isBitcoin === true) {
await this.$executeQuery(connection, 'DROP table IF EXISTS hashrates;'); await this.$executeQuery('DROP table IF EXISTS hashrates;');
await this.$executeQuery(connection, this.getCreateDailyStatsTableQuery(), await this.$checkIfTableExists('hashrates')); await this.$executeQuery(this.getCreateDailyStatsTableQuery(), await this.$checkIfTableExists('hashrates'));
} }
if (databaseSchemaVersion < 8 && isBitcoin === true) { if (databaseSchemaVersion < 8 && isBitcoin === true) {
logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`); logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` DROP INDEX `PRIMARY`'); await this.$executeQuery('ALTER TABLE `hashrates` DROP INDEX `PRIMARY`');
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST'); await this.$executeQuery('ALTER TABLE `hashrates` ADD `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST');
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD `share` float NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE `hashrates` ADD `share` float NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD `type` enum("daily", "weekly") DEFAULT "daily"'); await this.$executeQuery('ALTER TABLE `hashrates` ADD `type` enum("daily", "weekly") DEFAULT "daily"');
} }
if (databaseSchemaVersion < 9 && isBitcoin === true) { if (databaseSchemaVersion < 9 && isBitcoin === true) {
logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`); logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)'); await this.$executeQuery('ALTER TABLE `state` CHANGE `name` `name` varchar(100)');
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)'); await this.$executeQuery('ALTER TABLE `hashrates` ADD UNIQUE `hashrate_timestamp_pool_id` (`hashrate_timestamp`, `pool_id`)');
} }
if (databaseSchemaVersion < 10 && isBitcoin === true) { if (databaseSchemaVersion < 10 && isBitcoin === true) {
await this.$executeQuery(connection, 'ALTER TABLE `blocks` ADD INDEX `blockTimestamp` (`blockTimestamp`)'); await this.$executeQuery('ALTER TABLE `blocks` ADD INDEX `blockTimestamp` (`blockTimestamp`)');
} }
if (databaseSchemaVersion < 11 && isBitcoin === true) { if (databaseSchemaVersion < 11 && isBitcoin === true) {
logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`); logger.warn(`'blocks' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE blocks;'); // Need to re-index await this.$executeQuery('TRUNCATE blocks;'); // Need to re-index
await this.$executeQuery(connection, `ALTER TABLE blocks await this.$executeQuery(`ALTER TABLE blocks
ADD avg_fee INT UNSIGNED NULL, ADD avg_fee INT UNSIGNED NULL,
ADD avg_fee_rate INT UNSIGNED NULL ADD avg_fee_rate INT UNSIGNED NULL
`); `);
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `reward` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `reward` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `median_fee` INT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` INT UNSIGNED NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `fees` INT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` INT UNSIGNED NOT NULL DEFAULT "0"');
} }
if (databaseSchemaVersion < 12 && isBitcoin === true) { if (databaseSchemaVersion < 12 && isBitcoin === true) {
// No need to re-index because the new data type can contain larger values // No need to re-index because the new data type can contain larger values
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `fees` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `fees` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
} }
if (databaseSchemaVersion < 13 && isBitcoin === true) { if (databaseSchemaVersion < 13 && isBitcoin === true) {
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `difficulty` DOUBLE UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `difficulty` DOUBLE UNSIGNED NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `median_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `median_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `avg_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
await this.$executeQuery(connection, 'ALTER TABLE blocks MODIFY `avg_fee_rate` BIGINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE blocks MODIFY `avg_fee_rate` BIGINT UNSIGNED NOT NULL DEFAULT "0"');
} }
if (databaseSchemaVersion < 14 && isBitcoin === true) { if (databaseSchemaVersion < 14 && isBitcoin === true) {
logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`); logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` DROP FOREIGN KEY `hashrates_ibfk_1`'); await this.$executeQuery('ALTER TABLE `hashrates` DROP FOREIGN KEY `hashrates_ibfk_1`');
await this.$executeQuery(connection, 'ALTER TABLE `hashrates` MODIFY `pool_id` SMALLINT UNSIGNED NOT NULL DEFAULT "0"'); await this.$executeQuery('ALTER TABLE `hashrates` MODIFY `pool_id` SMALLINT UNSIGNED NOT NULL DEFAULT "0"');
} }
if (databaseSchemaVersion < 16 && isBitcoin === true) { if (databaseSchemaVersion < 16 && isBitcoin === true) {
logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`); logger.warn(`'hashrates' table has been truncated. Re-indexing from scratch.`);
await this.$executeQuery(connection, 'TRUNCATE hashrates;'); // Need to re-index because we changed timestamps await this.$executeQuery('TRUNCATE hashrates;'); // Need to re-index because we changed timestamps
} }
if (databaseSchemaVersion < 17 && isBitcoin === true) { if (databaseSchemaVersion < 17 && isBitcoin === true) {
await this.$executeQuery(connection, 'ALTER TABLE `pools` ADD `slug` CHAR(50) NULL'); await this.$executeQuery('ALTER TABLE `pools` ADD `slug` CHAR(50) NULL');
} }
connection.release();
} catch (e) { } catch (e) {
connection.release();
throw e; throw e;
} }
} }
@ -203,13 +198,11 @@ class DatabaseMigration {
return; return;
} }
const connection = await DB.getConnection();
try { try {
// We don't use "CREATE INDEX IF NOT EXISTS" because it is not supported on old mariadb version 5.X // We don't use "CREATE INDEX IF NOT EXISTS" because it is not supported on old mariadb version 5.X
const query = `SELECT COUNT(1) hasIndex FROM INFORMATION_SCHEMA.STATISTICS const query = `SELECT COUNT(1) hasIndex FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_schema=DATABASE() AND table_name='statistics' AND index_name='added';`; WHERE table_schema=DATABASE() AND table_name='statistics' AND index_name='added';`;
const [rows] = await this.$executeQuery(connection, query, true); const [rows] = await this.$executeQuery(query, true);
if (rows[0].hasIndex === 0) { if (rows[0].hasIndex === 0) {
logger.debug('MIGRATIONS: `statistics.added` is not indexed'); logger.debug('MIGRATIONS: `statistics.added` is not indexed');
this.statisticsAddedIndexed = false; this.statisticsAddedIndexed = false;
@ -223,28 +216,24 @@ class DatabaseMigration {
logger.err('MIGRATIONS: Unable to check if `statistics.added` INDEX exist or not.'); logger.err('MIGRATIONS: Unable to check if `statistics.added` INDEX exist or not.');
this.statisticsAddedIndexed = true; this.statisticsAddedIndexed = true;
} }
connection.release();
} }
/** /**
* Small query execution wrapper to log all executed queries * Small query execution wrapper to log all executed queries
*/ */
private async $executeQuery(connection: PoolConnection, query: string, silent: boolean = false): Promise<any> { private async $executeQuery(query: string, silent: boolean = false): Promise<any> {
if (!silent) { if (!silent) {
logger.debug('MIGRATIONS: Execute query:\n' + query); logger.debug('MIGRATIONS: Execute query:\n' + query);
} }
return connection.query<any>({ sql: query, timeout: this.queryTimeout }); return DB.query({ sql: query, timeout: this.queryTimeout });
} }
/** /**
* Check if 'table' exists in the database * Check if 'table' exists in the database
*/ */
private async $checkIfTableExists(table: string): Promise<boolean> { private async $checkIfTableExists(table: string): Promise<boolean> {
const connection = await DB.getConnection();
const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`; const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release();
return rows[0]['COUNT(*)'] === 1; return rows[0]['COUNT(*)'] === 1;
} }
@ -252,10 +241,8 @@ class DatabaseMigration {
* Get current database version * Get current database version
*/ */
private async $getSchemaVersionFromDatabase(): Promise<number> { private async $getSchemaVersionFromDatabase(): Promise<number> {
const connection = await DB.getConnection();
const query = `SELECT number FROM state WHERE name = 'schema_version';`; const query = `SELECT number FROM state WHERE name = 'schema_version';`;
const [rows] = await this.$executeQuery(connection, query, true); const [rows] = await this.$executeQuery(query, true);
connection.release();
return rows[0]['number']; return rows[0]['number'];
} }
@ -263,8 +250,6 @@ class DatabaseMigration {
* Create the `state` table * Create the `state` table
*/ */
private async $createMigrationStateTable(): Promise<void> { private async $createMigrationStateTable(): Promise<void> {
const connection = await DB.getConnection();
try { try {
const query = `CREATE TABLE IF NOT EXISTS state ( const query = `CREATE TABLE IF NOT EXISTS state (
name varchar(25) NOT NULL, name varchar(25) NOT NULL,
@ -272,15 +257,12 @@ class DatabaseMigration {
string varchar(100) NULL, string varchar(100) NULL,
CONSTRAINT name_unique UNIQUE (name) CONSTRAINT name_unique UNIQUE (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
await this.$executeQuery(connection, query); await this.$executeQuery(query);
// Set initial values // Set initial values
await this.$executeQuery(connection, `INSERT INTO state VALUES('schema_version', 0, NULL);`); await this.$executeQuery(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
await this.$executeQuery(connection, `INSERT INTO state VALUES('last_elements_block', 0, NULL);`); await this.$executeQuery(`INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
connection.release();
} catch (e) { } catch (e) {
connection.release();
throw e; throw e;
} }
} }
@ -295,18 +277,14 @@ class DatabaseMigration {
} }
transactionQueries.push(this.getUpdateToLatestSchemaVersionQuery()); transactionQueries.push(this.getUpdateToLatestSchemaVersionQuery());
const connection = await DB.getConnection();
try { try {
await this.$executeQuery(connection, 'START TRANSACTION;'); await this.$executeQuery('START TRANSACTION;');
for (const query of transactionQueries) { for (const query of transactionQueries) {
await this.$executeQuery(connection, query); await this.$executeQuery(query);
} }
await this.$executeQuery(connection, 'COMMIT;'); await this.$executeQuery('COMMIT;');
connection.release();
} catch (e) { } catch (e) {
await this.$executeQuery(connection, 'ROLLBACK;'); await this.$executeQuery('ROLLBACK;');
connection.release();
throw e; throw e;
} }
} }
@ -346,14 +324,12 @@ class DatabaseMigration {
* Print current database version * Print current database version
*/ */
private async $printDatabaseVersion() { private async $printDatabaseVersion() {
const connection = await DB.getConnection();
try { try {
const [rows] = await this.$executeQuery(connection, 'SELECT VERSION() as version;', true); const [rows] = await this.$executeQuery('SELECT VERSION() as version;', true);
logger.debug(`MIGRATIONS: Database engine version '${rows[0].version}'`); logger.debug(`MIGRATIONS: Database engine version '${rows[0].version}'`);
} catch (e) { } catch (e) {
logger.debug(`MIGRATIONS: Could not fetch database engine version. ` + e); logger.debug(`MIGRATIONS: Could not fetch database engine version. ` + e);
} }
connection.release();
} }
// Couple of wrappers to clean the main logic // Couple of wrappers to clean the main logic
@ -490,24 +466,22 @@ class DatabaseMigration {
public async $truncateIndexedData(tables: string[]) { public async $truncateIndexedData(tables: string[]) {
const allowedTables = ['blocks', 'hashrates']; const allowedTables = ['blocks', 'hashrates'];
const connection = await DB.getConnection();
try { try {
for (const table of tables) { for (const table of tables) {
if (!allowedTables.includes(table)) { if (!allowedTables.includes(table)) {
logger.debug(`Table ${table} cannot to be re-indexed (not allowed)`); logger.debug(`Table ${table} cannot to be re-indexed (not allowed)`);
continue; continue;
}; }
await this.$executeQuery(connection, `TRUNCATE ${table}`, true); await this.$executeQuery(`TRUNCATE ${table}`, true);
if (table === 'hashrates') { if (table === 'hashrates') {
await this.$executeQuery(connection, 'UPDATE state set number = 0 where name = "last_hashrates_indexing"', true); await this.$executeQuery('UPDATE state set number = 0 where name = "last_hashrates_indexing"', true);
} }
logger.notice(`Table ${table} has been truncated`); logger.notice(`Table ${table} has been truncated`);
} }
} catch (e) { } catch (e) {
logger.warn(`Unable to erase indexed data`); logger.warn(`Unable to erase indexed data`);
} }
connection.release();
} }
} }

View file

@ -33,10 +33,8 @@ class ElementsParser {
} }
public async $getPegDataByMonth(): Promise<any> { public async $getPegDataByMonth(): Promise<any> {
const connection = await DB.getConnection();
const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`; const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`;
const [rows] = await connection.query<any>(query); const [rows] = await DB.query(query);
connection.release();
return rows; return rows;
} }
@ -79,7 +77,6 @@ class ElementsParser {
protected async $savePegToDatabase(height: number, blockTime: number, amount: number, txid: string, protected async $savePegToDatabase(height: number, blockTime: number, amount: number, txid: string,
txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, final_tx: number): Promise<void> { txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, final_tx: number): Promise<void> {
const connection = await DB.getConnection();
const query = `INSERT INTO elements_pegs( const query = `INSERT INTO elements_pegs(
block, datetime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx block, datetime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`; ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
@ -87,24 +84,19 @@ class ElementsParser {
const params: (string | number)[] = [ const params: (string | number)[] = [
height, blockTime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx height, blockTime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx
]; ];
await connection.query(query, params); await DB.query(query, params);
connection.release();
logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`); logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`);
} }
protected async $getLatestBlockHeightFromDatabase(): Promise<number> { protected async $getLatestBlockHeightFromDatabase(): Promise<number> {
const connection = await DB.getConnection();
const query = `SELECT number FROM state WHERE name = 'last_elements_block'`; const query = `SELECT number FROM state WHERE name = 'last_elements_block'`;
const [rows] = await connection.query<any>(query); const [rows] = await DB.query(query);
connection.release();
return rows[0]['number']; return rows[0]['number'];
} }
protected async $saveLatestBlockToDatabase(blockHeight: number) { protected async $saveLatestBlockToDatabase(blockHeight: number) {
const connection = await DB.getConnection();
const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`; const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`;
await connection.query<any>(query, [blockHeight]); await DB.query(query, [blockHeight]);
connection.release();
} }
} }

View file

@ -59,13 +59,11 @@ class PoolsParser {
logger.debug(`Found ${poolNames.length} unique mining pools`); logger.debug(`Found ${poolNames.length} unique mining pools`);
// Get existing pools from the db // Get existing pools from the db
const connection = await DB.getConnection();
let existingPools; let existingPools;
try { try {
[existingPools] = await connection.query<any>({ sql: 'SELECT * FROM pools;', timeout: 120000 }); [existingPools] = await DB.query({ sql: 'SELECT * FROM pools;', timeout: 120000 });
} catch (e) { } catch (e) {
logger.err('Cannot get existing pools from the database, skipping pools.json import'); logger.err('Cannot get existing pools from the database, skipping pools.json import');
connection.release();
return; return;
} }
@ -145,17 +143,15 @@ class PoolsParser {
try { try {
if (finalPoolDataAdd.length > 0) { if (finalPoolDataAdd.length > 0) {
await connection.query<any>({ sql: queryAdd, timeout: 120000 }); await DB.query({ sql: queryAdd, timeout: 120000 });
} }
for (const query of updateQueries) { for (const query of updateQueries) {
await connection.query<any>({ sql: query, timeout: 120000 }); await DB.query({ sql: query, timeout: 120000 });
} }
await this.insertUnknownPool(); await this.insertUnknownPool();
connection.release();
logger.info('Mining pools.json import completed'); logger.info('Mining pools.json import completed');
} catch (e) { } catch (e) {
connection.release(); logger.err(`Cannot import pools in the database`);
logger.err(`Unable to import pools in the database`);
throw e; throw e;
} }
} }
@ -164,16 +160,15 @@ class PoolsParser {
* Manually add the 'unknown pool' * Manually add the 'unknown pool'
*/ */
private async insertUnknownPool() { private async insertUnknownPool() {
const connection = await DB.getConnection();
try { try {
const [rows]: any[] = await connection.query({ sql: 'SELECT name from pools where name="Unknown"', timeout: 120000 }); const [rows]: any[] = await DB.query({ sql: 'SELECT name from pools where name="Unknown"', timeout: 120000 });
if (rows.length === 0) { if (rows.length === 0) {
await connection.query({ await DB.query({
sql: `INSERT INTO pools(name, link, regexes, addresses, slug) sql: `INSERT INTO pools(name, link, regexes, addresses, slug)
VALUES("Unknown", "https://learnmeabitcoin.com/technical/coinbase-transaction", "[]", "[]", "unknown"); VALUES("Unknown", "https://learnmeabitcoin.com/technical/coinbase-transaction", "[]", "[]", "unknown");
`}); `});
} else { } else {
await connection.query(`UPDATE pools await DB.query(`UPDATE pools
SET name='Unknown', link='https://learnmeabitcoin.com/technical/coinbase-transaction', SET name='Unknown', link='https://learnmeabitcoin.com/technical/coinbase-transaction',
regexes='[]', addresses='[]', regexes='[]', addresses='[]',
slug='unknown' slug='unknown'
@ -183,8 +178,6 @@ class PoolsParser {
} catch (e) { } catch (e) {
logger.err('Unable to insert "Unknown" mining pool'); logger.err('Unable to insert "Unknown" mining pool');
} }
connection.release();
} }
} }

View file

@ -155,7 +155,6 @@ class Statistics {
} }
private async $createZeroedStatistic(): Promise<number | undefined> { private async $createZeroedStatistic(): Promise<number | undefined> {
const connection = await DB.getConnection();
try { try {
const query = `INSERT INTO statistics( const query = `INSERT INTO statistics(
added, added,
@ -206,17 +205,14 @@ class Statistics {
) )
VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`;
const [result]: any = await connection.query(query); const [result]: any = await DB.query(query);
connection.release();
return result.insertId; return result.insertId;
} catch (e) { } catch (e) {
connection.release();
logger.err('$create() error' + (e instanceof Error ? e.message : e)); logger.err('$create() error' + (e instanceof Error ? e.message : e));
} }
} }
private async $create(statistics: Statistic): Promise<number | undefined> { private async $create(statistics: Statistic): Promise<number | undefined> {
const connection = await DB.getConnection();
try { try {
const query = `INSERT INTO statistics( const query = `INSERT INTO statistics(
added, added,
@ -314,11 +310,9 @@ class Statistics {
statistics.vsize_1800, statistics.vsize_1800,
statistics.vsize_2000, statistics.vsize_2000,
]; ];
const [result]: any = await connection.query(query, params); const [result]: any = await DB.query(query, params);
connection.release();
return result.insertId; return result.insertId;
} catch (e) { } catch (e) {
connection.release();
logger.err('$create() error' + (e instanceof Error ? e.message : e)); logger.err('$create() error' + (e instanceof Error ? e.message : e));
} }
} }
@ -421,10 +415,8 @@ class Statistics {
private async $get(id: number): Promise<OptimizedStatistic | undefined> { private async $get(id: number): Promise<OptimizedStatistic | undefined> {
try { try {
const connection = await DB.getConnection();
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE id = ?`; const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE id = ?`;
const [rows] = await connection.query<any>(query, [id]); const [rows] = await DB.query(query, [id]);
connection.release();
if (rows[0]) { if (rows[0]) {
return this.mapStatisticToOptimizedStatistic([rows[0]])[0]; return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
} }
@ -435,11 +427,9 @@ class Statistics {
public async $list2H(): Promise<OptimizedStatistic[]> { public async $list2H(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`; const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list2H() error' + (e instanceof Error ? e.message : e)); logger.err('$list2H() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -448,11 +438,9 @@ class Statistics {
public async $list24H(): Promise<OptimizedStatistic[]> { public async $list24H(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`; const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list24h() error' + (e instanceof Error ? e.message : e)); logger.err('$list24h() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -461,11 +449,9 @@ class Statistics {
public async $list1W(): Promise<OptimizedStatistic[]> { public async $list1W(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = this.getQueryForDaysAvg(300, '1 WEEK'); // 5m interval const query = this.getQueryForDaysAvg(300, '1 WEEK'); // 5m interval
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list1W() error' + (e instanceof Error ? e.message : e)); logger.err('$list1W() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -474,11 +460,9 @@ class Statistics {
public async $list1M(): Promise<OptimizedStatistic[]> { public async $list1M(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = this.getQueryForDaysAvg(1800, '1 MONTH'); // 30m interval const query = this.getQueryForDaysAvg(1800, '1 MONTH'); // 30m interval
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list1M() error' + (e instanceof Error ? e.message : e)); logger.err('$list1M() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -487,11 +471,9 @@ class Statistics {
public async $list3M(): Promise<OptimizedStatistic[]> { public async $list3M(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = this.getQueryForDaysAvg(7200, '3 MONTH'); // 2h interval const query = this.getQueryForDaysAvg(7200, '3 MONTH'); // 2h interval
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list3M() error' + (e instanceof Error ? e.message : e)); logger.err('$list3M() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -500,11 +482,9 @@ class Statistics {
public async $list6M(): Promise<OptimizedStatistic[]> { public async $list6M(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection(); const query = this.getQueryForDaysAvg(10800, '6 MONTH'); // 3h interval
const query = this.getQueryForDaysAvg(10800, '6 MONTH'); // 3h interval const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
connection.release();
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list6M() error' + (e instanceof Error ? e.message : e)); logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -513,11 +493,9 @@ class Statistics {
public async $list1Y(): Promise<OptimizedStatistic[]> { public async $list1Y(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection();
const query = this.getQueryForDays(28800, '1 YEAR'); // 8h interval const query = this.getQueryForDays(28800, '1 YEAR'); // 8h interval
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
connection.release(); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list1Y() error' + (e instanceof Error ? e.message : e)); logger.err('$list1Y() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -526,11 +504,9 @@ class Statistics {
public async $list2Y(): Promise<OptimizedStatistic[]> { public async $list2Y(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection(); const query = this.getQueryForDays(28800, '2 YEAR'); // 8h interval
const query = this.getQueryForDays(28800, "2 YEAR"); // 8h interval const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
connection.release();
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list2Y() error' + (e instanceof Error ? e.message : e)); logger.err('$list2Y() error' + (e instanceof Error ? e.message : e));
return []; return [];
@ -539,11 +515,9 @@ class Statistics {
public async $list3Y(): Promise<OptimizedStatistic[]> { public async $list3Y(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.getConnection(); const query = this.getQueryForDays(43200, '3 YEAR'); // 12h interval
const query = this.getQueryForDays(43200, "3 YEAR"); // 12h interval const [rows] = await DB.query({ sql: query, timeout: this.queryTimeout });
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); return this.mapStatisticToOptimizedStatistic(rows as Statistic[]);
connection.release();
return this.mapStatisticToOptimizedStatistic(rows);
} catch (e) { } catch (e) {
logger.err('$list3Y() error' + (e instanceof Error ? e.message : e)); logger.err('$list3Y() error' + (e instanceof Error ? e.message : e));
return []; return [];

View file

@ -1,11 +1,14 @@
import config from './config'; import config from './config';
import { createPool, PoolConnection } from 'mysql2/promise'; import { createPool, Pool, PoolConnection } from 'mysql2/promise';
import logger from './logger'; import logger from './logger';
import { PoolOptions } from 'mysql2/typings/mysql'; import { PoolOptions } from 'mysql2/typings/mysql';
export class DB { export class DB {
static poolConfig = ():PoolOptions => { static connectionsReady: number[] = [];
let poolConfig:PoolOptions = { static pool: Pool | null = null;
static poolConfig = (): PoolOptions => {
const poolConfig: PoolOptions = {
port: config.DATABASE.PORT, port: config.DATABASE.PORT,
database: config.DATABASE.DATABASE, database: config.DATABASE.DATABASE,
user: config.DATABASE.USERNAME, user: config.DATABASE.USERNAME,
@ -13,9 +16,9 @@ export class DB {
connectionLimit: 10, connectionLimit: 10,
supportBigNumbers: true, supportBigNumbers: true,
timezone: '+00:00', timezone: '+00:00',
} };
if (config.DATABASE.SOCKET !== "") { if (config.DATABASE.SOCKET !== '') {
poolConfig.socketPath = config.DATABASE.SOCKET; poolConfig.socketPath = config.DATABASE.SOCKET;
} else { } else {
poolConfig.host = config.DATABASE.HOST; poolConfig.host = config.DATABASE.HOST;
@ -23,27 +26,27 @@ export class DB {
return poolConfig; return poolConfig;
} }
static pool = createPool(DB.poolConfig());
static connectionsReady: number[] = []; static async getPool(): Promise<Pool> {
if (DB.pool === null) {
static async getConnection() { DB.pool = createPool(DB.poolConfig());
const connection: PoolConnection = await DB.pool.getConnection(); DB.pool.on('connection', function (newConnection: PoolConnection) {
const connectionId = connection['connection'].connectionId; newConnection.query(`SET time_zone='+00:00'`);
if (!DB.connectionsReady.includes(connectionId)) { });
await connection.query(`SET time_zone='+00:00';`);
this.connectionsReady.push(connectionId);
} }
return connection; return DB.pool;
}
static async query(query, params?) {
const pool = await DB.getPool();
return pool.query(query, params);
} }
} }
export async function checkDbConnection() { export async function checkDbConnection() {
try { try {
const connection = await DB.getConnection(); await DB.query('SELECT ?', [1]);
logger.info('Database connection established.'); logger.info('Database connection established.');
connection.release();
} catch (e) { } catch (e) {
logger.err('Could not connect to database: ' + (e instanceof Error ? e.message : e)); logger.err('Could not connect to database: ' + (e instanceof Error ? e.message : e));
process.exit(1); process.exit(1);

View file

@ -188,7 +188,7 @@ class Server {
await BlocksRepository.$deleteBlocks(10); await BlocksRepository.$deleteBlocks(10);
await HashratesRepository.$deleteLastEntries(); await HashratesRepository.$deleteLastEntries();
} }
blocks.$generateBlockDatabase(); await blocks.$generateBlockDatabase();
await mining.$generateNetworkHashrateHistory(); await mining.$generateNetworkHashrateHistory();
await mining.$generatePoolHashrateHistory(); await mining.$generatePoolHashrateHistory();
} catch (e) { } catch (e) {

View file

@ -10,11 +10,7 @@ class BlocksRepository {
* Save indexed block data in the database * Save indexed block data in the database
*/ */
public async $saveBlockInDatabase(block: BlockExtended) { public async $saveBlockInDatabase(block: BlockExtended) {
let connection;
try { try {
connection = await DB.getConnection();
const query = `INSERT INTO blocks( const query = `INSERT INTO blocks(
height, hash, blockTimestamp, size, height, hash, blockTimestamp, size,
weight, tx_count, coinbase_raw, difficulty, weight, tx_count, coinbase_raw, difficulty,
@ -52,14 +48,11 @@ class BlocksRepository {
block.extras.avgFeeRate, block.extras.avgFeeRate,
]; ];
await connection.query(query, params); await DB.query(query, params);
connection.release();
} catch (e: any) { } catch (e: any) {
connection.release();
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`); logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`);
} else { } else {
connection.release();
logger.err('Cannot save indexed block into db. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot save indexed block into db. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -74,16 +67,13 @@ class BlocksRepository {
return []; return [];
} }
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(`
const [rows]: any[] = await connection.query(`
SELECT height SELECT height
FROM blocks FROM blocks
WHERE height <= ? AND height >= ? WHERE height <= ? AND height >= ?
ORDER BY height DESC; ORDER BY height DESC;
`, [startHeight, endHeight]); `, [startHeight, endHeight]);
connection.release();
const indexedBlockHeights: number[] = []; const indexedBlockHeights: number[] = [];
rows.forEach((row: any) => { indexedBlockHeights.push(row.height); }); rows.forEach((row: any) => { indexedBlockHeights.push(row.height); });
@ -92,7 +82,6 @@ class BlocksRepository {
return missingBlocksHeights; return missingBlocksHeights;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot retrieve blocks list to index. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot retrieve blocks list to index. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -121,15 +110,10 @@ class BlocksRepository {
query += ` GROUP by pools.id`; query += ` GROUP by pools.id`;
let connection;
try { try {
connection = await DB.getConnection(); const [rows] = await DB.query(query, params);
const [rows] = await connection.query(query, params);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot count empty blocks. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot count empty blocks. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -159,15 +143,10 @@ class BlocksRepository {
query += ` blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; query += ` blockTimestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`;
} }
let connection;
try { try {
connection = await DB.getConnection(); const [rows] = await DB.query(query, params);
const [rows] = await connection.query(query, params);
connection.release();
return <number>rows[0].blockCount; return <number>rows[0].blockCount;
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot count blocks for this pool (using offset). Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot count blocks for this pool (using offset). Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -199,15 +178,10 @@ class BlocksRepository {
} }
query += ` blockTimestamp BETWEEN FROM_UNIXTIME('${from}') AND FROM_UNIXTIME('${to}')`; query += ` blockTimestamp BETWEEN FROM_UNIXTIME('${from}') AND FROM_UNIXTIME('${to}')`;
let connection;
try { try {
connection = await DB.getConnection(); const [rows] = await DB.query(query, params);
const [rows] = await connection.query(query, params);
connection.release();
return <number>rows[0]; return <number>rows[0];
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot count blocks for this pool (using timestamps). Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot count blocks for this pool (using timestamps). Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -222,11 +196,8 @@ class BlocksRepository {
ORDER BY height ORDER BY height
LIMIT 1;`; LIMIT 1;`;
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(query);
const [rows]: any[] = await connection.query(query);
connection.release();
if (rows.length <= 0) { if (rows.length <= 0) {
return -1; return -1;
@ -234,7 +205,6 @@ class BlocksRepository {
return <number>rows[0].blockTimestamp; return <number>rows[0].blockTimestamp;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot get oldest indexed block timestamp. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot get oldest indexed block timestamp. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -243,7 +213,7 @@ class BlocksRepository {
/** /**
* Get blocks mined by a specific mining pool * Get blocks mined by a specific mining pool
*/ */
public async $getBlocksByPool(slug: string, startHeight: number | undefined = undefined): Promise<object[]> { public async $getBlocksByPool(slug: string, startHeight?: number): Promise<object[]> {
const pool = await PoolsRepository.$getPool(slug); const pool = await PoolsRepository.$getPool(slug);
if (!pool) { if (!pool) {
throw new Error(`This mining pool does not exist`); throw new Error(`This mining pool does not exist`);
@ -264,20 +234,16 @@ class BlocksRepository {
query += ` ORDER BY height DESC query += ` ORDER BY height DESC
LIMIT 10`; LIMIT 10`;
let connection;
try { try {
connection = await DB.getConnection(); const [rows] = await DB.query(query, params);
const [rows] = await connection.query(query, params);
connection.release();
const blocks: BlockExtended[] = []; const blocks: BlockExtended[] = [];
for (let block of <object[]>rows) { for (const block of <object[]>rows) {
blocks.push(prepareBlock(block)); blocks.push(prepareBlock(block));
} }
return blocks; return blocks;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot get blocks for this pool. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot get blocks for this pool. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -287,10 +253,8 @@ class BlocksRepository {
* Get one block by height * Get one block by height
*/ */
public async $getBlockByHeight(height: number): Promise<object | null> { public async $getBlockByHeight(height: number): Promise<object | null> {
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(`
const [rows]: any[] = await connection.query(`
SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp,
pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
pools.addresses as pool_addresses, pools.regexes as pool_regexes, pools.addresses as pool_addresses, pools.regexes as pool_regexes,
@ -299,7 +263,6 @@ class BlocksRepository {
JOIN pools ON blocks.pool_id = pools.id JOIN pools ON blocks.pool_id = pools.id
WHERE height = ${height}; WHERE height = ${height};
`); `);
connection.release();
if (rows.length <= 0) { if (rows.length <= 0) {
return null; return null;
@ -307,7 +270,6 @@ class BlocksRepository {
return rows[0]; return rows[0];
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot get indexed block ${height}. Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -351,20 +313,15 @@ class BlocksRepository {
ORDER BY t.height ORDER BY t.height
`; `;
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(query);
const [rows]: any[] = await connection.query(query);
connection.release();
for (const row of rows) { for (const row of rows) {
delete row['rn']; delete row['rn'];
} }
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot generate difficulty history. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot generate difficulty history. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -374,10 +331,7 @@ class BlocksRepository {
* Get general block stats * Get general block stats
*/ */
public async $getBlockStats(blockCount: number): Promise<any> { public async $getBlockStats(blockCount: number): Promise<any> {
let connection;
try { try {
connection = await DB.getConnection();
// We need to use a subquery // We need to use a subquery
const query = ` const query = `
SELECT MIN(height) as startBlock, MAX(height) as endBlock, SUM(reward) as totalReward, SUM(fees) as totalFee, SUM(tx_count) as totalTx SELECT MIN(height) as startBlock, MAX(height) as endBlock, SUM(reward) as totalReward, SUM(fees) as totalFee, SUM(tx_count) as totalTx
@ -386,12 +340,10 @@ class BlocksRepository {
ORDER by height DESC ORDER by height DESC
LIMIT ?) as sub`; LIMIT ?) as sub`;
const [rows]: any = await connection.query(query, [blockCount]); const [rows]: any = await DB.query(query, [blockCount]);
connection.release();
return rows[0]; return rows[0];
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot generate reward stats. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot generate reward stats. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -401,12 +353,8 @@ class BlocksRepository {
* Check if the last 10 blocks chain is valid * Check if the last 10 blocks chain is valid
*/ */
public async $validateRecentBlocks(): Promise<boolean> { public async $validateRecentBlocks(): Promise<boolean> {
let connection;
try { try {
connection = await DB.getConnection(); const [lastBlocks]: any[] = await DB.query(`SELECT height, hash, previous_block_hash FROM blocks ORDER BY height DESC LIMIT 10`);
const [lastBlocks] = await connection.query(`SELECT height, hash, previous_block_hash FROM blocks ORDER BY height DESC LIMIT 10`);
connection.release();
for (let i = 0; i < lastBlocks.length - 1; ++i) { for (let i = 0; i < lastBlocks.length - 1; ++i) {
if (lastBlocks[i].previous_block_hash !== lastBlocks[i + 1].hash) { if (lastBlocks[i].previous_block_hash !== lastBlocks[i + 1].hash) {
@ -417,8 +365,6 @@ class BlocksRepository {
return true; return true;
} catch (e) { } catch (e) {
connection.release();
return true; // Don't do anything if there is a db error return true; // Don't do anything if there is a db error
} }
} }
@ -428,26 +374,19 @@ class BlocksRepository {
*/ */
public async $deleteBlocks(count: number) { public async $deleteBlocks(count: number) {
logger.info(`Delete ${count} most recent indexed blocks from the database`); logger.info(`Delete ${count} most recent indexed blocks from the database`);
let connection;
try { try {
connection = await DB.getConnection(); await DB.query(`DELETE FROM blocks ORDER BY height DESC LIMIT ${count};`);
await connection.query(`DELETE FROM blocks ORDER BY height DESC LIMIT ${count};`);
} catch (e) { } catch (e) {
logger.err('Cannot delete recent indexed blocks. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot delete recent indexed blocks. Reason: ' + (e instanceof Error ? e.message : e));
} }
connection.release();
} }
/** /**
* Get the historical averaged block fees * Get the historical averaged block fees
*/ */
public async $getHistoricalBlockFees(div: number, interval: string | null): Promise<any> { public async $getHistoricalBlockFees(div: number, interval: string | null): Promise<any> {
let connection;
try { try {
connection = await DB.getConnection();
let query = `SELECT CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, let query = `SELECT CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
CAST(AVG(fees) as INT) as avg_fees CAST(AVG(fees) as INT) as avg_fees
FROM blocks`; FROM blocks`;
@ -458,12 +397,9 @@ class BlocksRepository {
query += ` GROUP BY UNIX_TIMESTAMP(blockTimestamp) DIV ${div}`; query += ` GROUP BY UNIX_TIMESTAMP(blockTimestamp) DIV ${div}`;
const [rows]: any = await connection.query(query); const [rows]: any = await DB.query(query);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot generate block fees history. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot generate block fees history. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -473,10 +409,7 @@ class BlocksRepository {
* Get the historical averaged block rewards * Get the historical averaged block rewards
*/ */
public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> { public async $getHistoricalBlockRewards(div: number, interval: string | null): Promise<any> {
let connection;
try { try {
connection = await DB.getConnection();
let query = `SELECT CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, let query = `SELECT CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
CAST(AVG(reward) as INT) as avg_rewards CAST(AVG(reward) as INT) as avg_rewards
FROM blocks`; FROM blocks`;
@ -487,12 +420,9 @@ class BlocksRepository {
query += ` GROUP BY UNIX_TIMESTAMP(blockTimestamp) DIV ${div}`; query += ` GROUP BY UNIX_TIMESTAMP(blockTimestamp) DIV ${div}`;
const [rows]: any = await connection.query(query); const [rows]: any = await DB.query(query);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot generate block rewards history. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot generate block rewards history. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }

View file

@ -20,13 +20,9 @@ class HashratesRepository {
} }
query = query.slice(0, -1); query = query.slice(0, -1);
let connection;
try { try {
connection = await DB.getConnection(); await DB.query(query);
await connection.query(query);
connection.release();
} catch (e: any) { } catch (e: any) {
connection.release();
logger.err('Cannot save indexed hashrate into db. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot save indexed hashrate into db. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -35,8 +31,6 @@ class HashratesRepository {
public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> { public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
interval = Common.getSqlInterval(interval); interval = Common.getSqlInterval(interval);
const connection = await DB.getConnection();
let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate
FROM hashrates`; FROM hashrates`;
@ -50,32 +44,24 @@ class HashratesRepository {
query += ` ORDER by hashrate_timestamp`; query += ` ORDER by hashrate_timestamp`;
try { try {
const [rows]: any[] = await connection.query(query); const [rows]: any[] = await DB.query(query);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot fetch network hashrate history. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot fetch network hashrate history. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
} }
public async $getWeeklyHashrateTimestamps(): Promise<number[]> { public async $getWeeklyHashrateTimestamps(): Promise<number[]> {
const connection = await DB.getConnection();
const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp
FROM hashrates FROM hashrates
WHERE type = 'weekly' WHERE type = 'weekly'
GROUP BY hashrate_timestamp`; GROUP BY hashrate_timestamp`;
try { try {
const [rows]: any[] = await connection.query(query); const [rows]: any[] = await DB.query(query);
connection.release();
return rows.map(row => row.timestamp); return rows.map(row => row.timestamp);
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot retreive indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot retreive indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -87,7 +73,6 @@ class HashratesRepository {
public async $getPoolsWeeklyHashrate(interval: string | null): Promise<any[]> { public async $getPoolsWeeklyHashrate(interval: string | null): Promise<any[]> {
interval = Common.getSqlInterval(interval); interval = Common.getSqlInterval(interval);
const connection = await DB.getConnection();
const topPoolsId = (await PoolsRepository.$getPoolsInfo('1w')).map((pool) => pool.poolId); const topPoolsId = (await PoolsRepository.$getPoolsInfo('1w')).map((pool) => pool.poolId);
let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate, share, pools.name as poolName let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp, avg_hashrate as avgHashrate, share, pools.name as poolName
@ -106,12 +91,9 @@ class HashratesRepository {
query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`; query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`;
try { try {
const [rows]: any[] = await connection.query(query); const [rows]: any[] = await DB.query(query);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot fetch weekly pools hashrate history. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot fetch weekly pools hashrate history. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -128,8 +110,8 @@ class HashratesRepository {
// Find hashrate boundaries // Find hashrate boundaries
let query = `SELECT MIN(hashrate_timestamp) as firstTimestamp, MAX(hashrate_timestamp) as lastTimestamp let query = `SELECT MIN(hashrate_timestamp) as firstTimestamp, MAX(hashrate_timestamp) as lastTimestamp
FROM hashrates FROM hashrates
JOIN pools on pools.id = pool_id JOIN pools on pools.id = pool_id
WHERE hashrates.type = 'weekly' AND pool_id = ? AND avg_hashrate != 0 WHERE hashrates.type = 'weekly' AND pool_id = ? AND avg_hashrate != 0
ORDER by hashrate_timestamp LIMIT 1`; ORDER by hashrate_timestamp LIMIT 1`;
@ -138,14 +120,10 @@ class HashratesRepository {
lastTimestamp: '9999-01-01' lastTimestamp: '9999-01-01'
}; };
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(query, [pool.id]);
const [rows]: any[] = await connection.query(query, [pool.id]);
boundaries = rows[0]; boundaries = rows[0];
connection.release();
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot fetch hashrate start/end timestamps for this pool. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot fetch hashrate start/end timestamps for this pool. Reason: ' + (e instanceof Error ? e.message : e));
} }
@ -158,12 +136,9 @@ class HashratesRepository {
ORDER by hashrate_timestamp`; ORDER by hashrate_timestamp`;
try { try {
const [rows]: any[] = await connection.query(query, [boundaries.firstTimestamp, boundaries.lastTimestamp, pool.id]); const [rows]: any[] = await DB.query(query, [boundaries.firstTimestamp, boundaries.lastTimestamp, pool.id]);
connection.release();
return rows; return rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot fetch pool hashrate history for this pool. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot fetch pool hashrate history for this pool. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -173,14 +148,11 @@ class HashratesRepository {
* Set latest run timestamp * Set latest run timestamp
*/ */
public async $setLatestRunTimestamp(key: string, val: any = null) { public async $setLatestRunTimestamp(key: string, val: any = null) {
const connection = await DB.getConnection();
const query = `UPDATE state SET number = ? WHERE name = ?`; const query = `UPDATE state SET number = ? WHERE name = ?`;
try { try {
await connection.query<any>(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); await DB.query(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]);
connection.release();
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot set last indexing timestamp for ${key}. Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot set last indexing timestamp for ${key}. Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -190,19 +162,16 @@ class HashratesRepository {
* Get latest run timestamp * Get latest run timestamp
*/ */
public async $getLatestRunTimestamp(key: string): Promise<number> { public async $getLatestRunTimestamp(key: string): Promise<number> {
const connection = await DB.getConnection();
const query = `SELECT number FROM state WHERE name = ?`; const query = `SELECT number FROM state WHERE name = ?`;
try { try {
const [rows] = await connection.query<any>(query, [key]); const [rows]: any[] = await DB.query(query, [key]);
connection.release();
if (rows.length === 0) { if (rows.length === 0) {
return 0; return 0;
} }
return rows[0]['number']; return rows[0]['number'];
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot retreive last indexing timestamp for ${key}. Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot retreive last indexing timestamp for ${key}. Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -214,12 +183,10 @@ class HashratesRepository {
public async $deleteLastEntries() { public async $deleteLastEntries() {
logger.info(`Delete latest hashrates data points from the database`); logger.info(`Delete latest hashrates data points from the database`);
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(`SELECT MAX(hashrate_timestamp) as timestamp FROM hashrates GROUP BY type`);
const [rows] = await connection.query(`SELECT MAX(hashrate_timestamp) as timestamp FROM hashrates GROUP BY type`);
for (const row of rows) { for (const row of rows) {
await connection.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]); await DB.query(`DELETE FROM hashrates WHERE hashrate_timestamp = ?`, [row.timestamp]);
} }
// Re-run the hashrate indexing to fill up missing data // Re-run the hashrate indexing to fill up missing data
await this.$setLatestRunTimestamp('last_hashrates_indexing', 0); await this.$setLatestRunTimestamp('last_hashrates_indexing', 0);
@ -227,8 +194,6 @@ class HashratesRepository {
} catch (e) { } catch (e) {
logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot delete latest hashrates data points. Reason: ' + (e instanceof Error ? e.message : e));
} }
connection.release();
} }
} }

View file

@ -9,9 +9,7 @@ class PoolsRepository {
* Get all pools tagging info * Get all pools tagging info
*/ */
public async $getPools(): Promise<PoolTag[]> { public async $getPools(): Promise<PoolTag[]> {
const connection = await DB.getConnection(); const [rows] = await DB.query('SELECT id, name, addresses, regexes, slug FROM pools;');
const [rows] = await connection.query('SELECT id, name, addresses, regexes, slug FROM pools;');
connection.release();
return <PoolTag[]>rows; return <PoolTag[]>rows;
} }
@ -19,9 +17,7 @@ class PoolsRepository {
* Get unknown pool tagging info * Get unknown pool tagging info
*/ */
public async $getUnknownPool(): Promise<PoolTag> { public async $getUnknownPool(): Promise<PoolTag> {
const connection = await DB.getConnection(); const [rows] = await DB.query('SELECT id, name, slug FROM pools where name = "Unknown"');
const [rows] = await connection.query('SELECT id, name, slug FROM pools where name = "Unknown"');
connection.release();
return <PoolTag>rows[0]; return <PoolTag>rows[0];
} }
@ -42,14 +38,10 @@ class PoolsRepository {
query += ` GROUP BY pool_id query += ` GROUP BY pool_id
ORDER BY COUNT(height) DESC`; ORDER BY COUNT(height) DESC`;
const connection = await DB.getConnection();
try { try {
const [rows] = await connection.query(query); const [rows] = await DB.query(query);
connection.release();
return <PoolInfo[]>rows; return <PoolInfo[]>rows;
} catch (e) { } catch (e) {
connection.release();
logger.err(`Cannot generate pools stats. Reason: ` + (e instanceof Error ? e.message : e)); logger.err(`Cannot generate pools stats. Reason: ` + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -64,14 +56,10 @@ class PoolsRepository {
LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?) LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?)
GROUP BY pools.id`; GROUP BY pools.id`;
const connection = await DB.getConnection();
try { try {
const [rows] = await connection.query(query, [from, to]); const [rows] = await DB.query(query, [from, to]);
connection.release();
return <PoolInfo[]>rows; return <PoolInfo[]>rows;
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot generate pools blocks count. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot generate pools blocks count. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
@ -86,12 +74,8 @@ class PoolsRepository {
FROM pools FROM pools
WHERE pools.slug = ?`; WHERE pools.slug = ?`;
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query(query, [slug]);
const [rows] = await connection.query(query, [slug]);
connection.release();
if (rows.length < 1) { if (rows.length < 1) {
logger.debug(`This slug does not match any known pool`); logger.debug(`This slug does not match any known pool`);
@ -107,7 +91,6 @@ class PoolsRepository {
return rows[0]; return rows[0];
} catch (e) { } catch (e) {
connection.release();
logger.err('Cannot get pool from db. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot get pool from db. Reason: ' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }

View file

@ -1,8 +1,8 @@
const https = require('https'); const https = require('https');
import poolsParser from "../api/pools-parser"; import poolsParser from '../api/pools-parser';
import config from "../config"; import config from '../config';
import { DB } from "../database"; import { DB } from '../database';
import logger from "../logger"; import logger from '../logger';
/** /**
* Maintain the most recent version of pools.json * Maintain the most recent version of pools.json
@ -64,15 +64,11 @@ class PoolsUpdater {
* Fetch our latest pools.json sha from the db * Fetch our latest pools.json sha from the db
*/ */
private async updateDBSha(githubSha: string) { private async updateDBSha(githubSha: string) {
let connection;
try { try {
connection = await DB.getConnection(); await DB.query('DELETE FROM state where name="pools_json_sha"');
await connection.query('DELETE FROM state where name="pools_json_sha"'); await DB.query(`INSERT INTO state VALUES('pools_json_sha', NULL, '${githubSha}')`);
await connection.query(`INSERT INTO state VALUES('pools_json_sha', NULL, '${githubSha}')`);
connection.release();
} catch (e) { } catch (e) {
logger.err('Cannot save github pools.json sha into the db. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot save github pools.json sha into the db. Reason: ' + (e instanceof Error ? e.message : e));
connection.release();
return undefined; return undefined;
} }
} }
@ -81,15 +77,11 @@ class PoolsUpdater {
* Fetch our latest pools.json sha from the db * Fetch our latest pools.json sha from the db
*/ */
private async getShaFromDb(): Promise<string | undefined> { private async getShaFromDb(): Promise<string | undefined> {
let connection;
try { try {
connection = await DB.getConnection(); const [rows]: any[] = await DB.query('SELECT string FROM state WHERE name="pools_json_sha"');
const [rows] = await connection.query('SELECT string FROM state WHERE name="pools_json_sha"');
connection.release();
return (rows.length > 0 ? rows[0].string : undefined); return (rows.length > 0 ? rows[0].string : undefined);
} catch (e) { } catch (e) {
logger.err('Cannot fetch pools.json sha from db. Reason: ' + (e instanceof Error ? e.message : e)); logger.err('Cannot fetch pools.json sha from db. Reason: ' + (e instanceof Error ? e.message : e));
connection.release();
return undefined; return undefined;
} }
} }
@ -140,7 +132,7 @@ class PoolsUpdater {
request.on('error', (error) => { request.on('error', (error) => {
logger.err('Github API query failed. Reason: ' + error); logger.err('Github API query failed. Reason: ' + error);
reject(error); reject(error);
}) });
}); });
} }
} }