Set connection pool timezone to UTC - Close mysql connections upon error

This commit is contained in:
nymkappa 2022-03-06 16:44:09 +01:00
parent 1ced44d970
commit 89411f23d8
No known key found for this signature in database
GPG Key ID: E155910B16E8BD04
6 changed files with 196 additions and 93 deletions

View File

@ -134,7 +134,8 @@ class DatabaseMigration {
} }
if (databaseSchemaVersion < 9) { if (databaseSchemaVersion < 9) {
await this.$executeQuery(connection, 'ALTER TABLE `state` CHANGE `name` `name` varchar(100)') await this.$executeQuery(connection, '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`)');
} }
connection.release(); connection.release();

View File

@ -97,9 +97,8 @@ class Mining {
const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps(); const indexedTimestamp = await HashratesRepository.$getWeeklyHashrateTimestamps();
const hashrates: any[] = []; const hashrates: any[] = [];
const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f const genesisTimestamp = 1231006505; // bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
const lastWeekMidnight = this.getDateMidnight(new Date()); const lastMidnight = this.getDateMidnight(new Date());
lastWeekMidnight.setDate(lastWeekMidnight.getDate() - 7); let toTimestamp = Math.round((lastMidnight.getTime() - 604800) / 1000);
let toTimestamp = Math.round(lastWeekMidnight.getTime() / 1000);
const totalWeekIndexed = (await BlocksRepository.$blockCount(null, null)) / 1008; const totalWeekIndexed = (await BlocksRepository.$blockCount(null, null)) / 1008;
let indexedThisRun = 0; let indexedThisRun = 0;
@ -167,7 +166,6 @@ class Mining {
this.weeklyHashrateIndexingStarted = false; this.weeklyHashrateIndexingStarted = false;
throw e; throw e;
} }
} }
/** /**

View File

@ -11,6 +11,7 @@ export class DB {
password: config.DATABASE.PASSWORD, password: config.DATABASE.PASSWORD,
connectionLimit: 10, connectionLimit: 10,
supportBigNumbers: true, supportBigNumbers: true,
timezone: '+00:00',
}); });
} }

View File

@ -53,15 +53,17 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
await connection.query(query, params); await connection.query(query, params);
connection.release();
} catch (e: any) { } catch (e: any) {
connection.release();
if (e.errno === 1062) { // ER_DUP_ENTRY if (e.errno === 1062) { // ER_DUP_ENTRY
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('$saveBlockInDatabase() error' + (e instanceof Error ? e.message : e)); logger.err('$saveBlockInDatabase() error' + (e instanceof Error ? e.message : e));
throw e;
} }
} }
connection.release();
} }
/** /**
@ -73,20 +75,26 @@ class BlocksRepository {
} }
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(` try {
SELECT height const [rows]: any[] = await connection.query(`
FROM blocks SELECT height
WHERE height <= ? AND height >= ? FROM blocks
ORDER BY height DESC; WHERE height <= ? AND height >= ?
`, [startHeight, endHeight]); ORDER BY height DESC;
connection.release(); `, [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); });
const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse(); const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse();
const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1); const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1);
return missingBlocksHeights; return missingBlocksHeights;
} catch (e) {
connection.release();
logger.err('$getMissingBlocksBetweenHeights() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -111,10 +119,16 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params); try {
connection.release(); const [rows] = await connection.query(query, params);
connection.release();
return <EmptyBlocks[]>rows; return <EmptyBlocks[]>rows;
} catch (e) {
connection.release();
logger.err('$getEmptyBlocks() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -143,10 +157,16 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params); try {
connection.release(); const [rows] = await connection.query(query, params);
connection.release();
return <number>rows[0].blockCount; return <number>rows[0].blockCount;
} catch (e) {
connection.release();
logger.err('$blockCount() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -177,10 +197,16 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params); try {
connection.release(); const [rows] = await connection.query(query, params);
connection.release();
return <number>rows[0]; return <number>rows[0];
} catch (e) {
connection.release();
logger.err('$blockCountBetweenTimestamp() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -194,23 +220,26 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(query); try {
connection.release(); const [rows]: any[] = await connection.query(query);
connection.release();
if (rows.length <= 0) { if (rows.length <= 0) {
return -1; return -1;
}
return <number>rows[0].blockTimestamp;
} catch (e) {
connection.release();
logger.err('$oldestBlockTimestamp() error' + (e instanceof Error ? e.message : e));
throw e;
} }
return <number>rows[0].blockTimestamp;
} }
/** /**
* Get blocks mined by a specific mining pool * Get blocks mined by a specific mining pool
*/ */
public async $getBlocksByPool( public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise<object[]> {
poolId: number,
startHeight: number | null = null
): Promise<object[]> {
const params: any[] = []; const params: any[] = [];
let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward
FROM blocks FROM blocks
@ -227,14 +256,20 @@ class BlocksRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params); try {
connection.release(); const [rows] = await connection.query(query, params);
connection.release();
for (const block of <object[]>rows) { for (const block of <object[]>rows) {
delete block['blockTimestamp']; delete block['blockTimestamp'];
}
return <object[]>rows;
} catch (e) {
connection.release();
logger.err('$getBlocksByPool() error' + (e instanceof Error ? e.message : e));
throw e;
} }
return <object[]>rows;
} }
/** /**
@ -242,19 +277,25 @@ class BlocksRepository {
*/ */
public async $getBlockByHeight(height: number): Promise<object | null> { public async $getBlockByHeight(height: number): Promise<object | null> {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(` try {
SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes const [rows]: any[] = await connection.query(`
FROM blocks SELECT *, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.addresses as pool_addresses, pools.regexes as pool_regexes
JOIN pools ON blocks.pool_id = pools.id FROM blocks
WHERE height = ${height}; JOIN pools ON blocks.pool_id = pools.id
`); WHERE height = ${height};
connection.release(); `);
connection.release();
if (rows.length <= 0) { if (rows.length <= 0) {
return null; return null;
}
return rows[0];
} catch (e) {
connection.release();
logger.err('$getBlockByHeight() error' + (e instanceof Error ? e.message : e));
throw e;
} }
return rows[0];
} }
/** /**
@ -297,21 +338,34 @@ class BlocksRepository {
ORDER BY t.height ORDER BY t.height
`; `;
const [rows]: any[] = await connection.query(query); try {
connection.release(); const [rows]: any[] = await connection.query(query);
connection.release();
for (let row of rows) { for (let row of rows) {
delete row['rn']; delete row['rn'];
}
return rows;
} catch (e) {
connection.release();
logger.err('$getBlocksDifficulty() error' + (e instanceof Error ? e.message : e));
throw e;
} }
return rows;
} }
public async $getOldestIndexedBlockHeight(): Promise<number> { public async $getOldestIndexedBlockHeight(): Promise<number> {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`); try {
connection.release(); const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`);
return rows[0].minHeight; connection.release();
return rows[0].minHeight;
} catch (e) {
connection.release();
logger.err('$getOldestIndexedBlockHeight() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
} }

View File

@ -24,12 +24,12 @@ class HashratesRepository {
try { try {
// logger.debug(query); // logger.debug(query);
await connection.query(query); await connection.query(query);
connection.release();
} catch (e: any) { } catch (e: any) {
connection.release();
logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e)); logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e));
throw e; throw e;
} }
connection.release();
} }
public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> { public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
@ -51,20 +51,33 @@ class HashratesRepository {
query += ` ORDER by hashrate_timestamp`; query += ` ORDER by hashrate_timestamp`;
const [rows]: any[] = await connection.query(query); try {
connection.release(); const [rows]: any[] = await connection.query(query);
connection.release();
return rows; return rows;
} catch (e) {
connection.release();
logger.err('$getNetworkDailyHashrate() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
public async $getWeeklyHashrateTimestamps(): Promise<number[]> { public async $getWeeklyHashrateTimestamps(): Promise<number[]> {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
let query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`; const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`;
const [rows]: any[] = await connection.query(query);
connection.release();
return rows.map(row => row.timestamp); try {
const [rows]: any[] = await connection.query(query);
connection.release();
return rows.map(row => row.timestamp);
} catch (e) {
connection.release();
logger.err('$getWeeklyHashrateTimestamps() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -91,26 +104,44 @@ class HashratesRepository {
query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`; query += ` ORDER by hashrate_timestamp, FIELD(pool_id, ${topPoolsId})`;
const [rows]: any[] = await connection.query(query); try {
connection.release(); const [rows]: any[] = await connection.query(query);
connection.release();
return rows; return rows;
} catch (e) {
connection.release();
logger.err('$getPoolsWeeklyHashrate() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
public async $setLatestRunTimestamp(key: string, val: any = null) { public async $setLatestRunTimestamp(key: string, val: any = null) {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const query = `UPDATE state SET number = ? WHERE name = ?`; const query = `UPDATE state SET number = ? WHERE name = ?`;
await connection.query<any>(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]); try {
connection.release(); await connection.query<any>(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]);
connection.release();
} catch (e) {
connection.release();
}
} }
public async $getLatestRunTimestamp(key: string): Promise<number> { public async $getLatestRunTimestamp(key: string): Promise<number> {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const query = `SELECT number FROM state WHERE name = ?`; const query = `SELECT number FROM state WHERE name = ?`;
const [rows] = await connection.query<any>(query, [key]);
connection.release(); try {
return rows[0]['number']; const [rows] = await connection.query<any>(query, [key]);
connection.release();
return rows[0]['number'];
} catch (e) {
connection.release();
logger.err('$setLatestRunTimestamp() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
} }

View File

@ -43,26 +43,38 @@ class PoolsRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query); try {
connection.release(); const [rows] = await connection.query(query);
connection.release();
return <PoolInfo[]>rows; return <PoolInfo[]>rows;
} catch (e) {
connection.release();
logger.err('$getPoolsInfo() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
* Get basic pool info and block count between two timestamp * Get basic pool info and block count between two timestamp
*/ */
public async $getPoolsInfoBetween(from: number, to: number): Promise<PoolInfo[]> { public async $getPoolsInfoBetween(from: number, to: number): Promise<PoolInfo[]> {
let query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName const query = `SELECT COUNT(height) as blockCount, pools.id as poolId, pools.name as poolName
FROM pools FROM pools
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.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, [from, to]); try {
connection.release(); const [rows] = await connection.query(query, [from, to]);
connection.release();
return <PoolInfo[]>rows; return <PoolInfo[]>rows;
} catch (e) {
connection.release();
logger.err('$getPoolsInfoBetween() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
/** /**
@ -76,13 +88,19 @@ class PoolsRepository {
// logger.debug(query); // logger.debug(query);
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, [poolId]); try {
connection.release(); const [rows] = await connection.query(query, [poolId]);
connection.release();
rows[0].regexes = JSON.parse(rows[0].regexes); rows[0].regexes = JSON.parse(rows[0].regexes);
rows[0].addresses = JSON.parse(rows[0].addresses); rows[0].addresses = JSON.parse(rows[0].addresses);
return rows[0]; return rows[0];
} catch (e) {
connection.release();
logger.err('$getPool() error' + (e instanceof Error ? e.message : e));
throw e;
}
} }
} }