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) {
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();

View File

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

View File

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

View File

@ -53,15 +53,17 @@ class BlocksRepository {
// logger.debug(query);
await connection.query(query, params);
connection.release();
} catch (e: any) {
connection.release();
if (e.errno === 1062) { // ER_DUP_ENTRY
logger.debug(`$saveBlockInDatabase() - Block ${block.height} has already been indexed, ignoring`);
} else {
connection.release();
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 [rows]: any[] = await connection.query(`
SELECT height
FROM blocks
WHERE height <= ? AND height >= ?
ORDER BY height DESC;
`, [startHeight, endHeight]);
connection.release();
try {
const [rows]: any[] = await connection.query(`
SELECT height
FROM blocks
WHERE height <= ? AND height >= ?
ORDER BY height DESC;
`, [startHeight, endHeight]);
connection.release();
const indexedBlockHeights: number[] = [];
rows.forEach((row: any) => { indexedBlockHeights.push(row.height); });
const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse();
const missingBlocksHeights = seekedBlocks.filter(x => indexedBlockHeights.indexOf(x) === -1);
const indexedBlockHeights: number[] = [];
rows.forEach((row: any) => { indexedBlockHeights.push(row.height); });
const seekedBlocks: number[] = Array.from(Array(startHeight - endHeight + 1).keys(), n => n + endHeight).reverse();
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);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params);
connection.release();
try {
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);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params);
connection.release();
try {
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);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params);
connection.release();
try {
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);
const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(query);
connection.release();
try {
const [rows]: any[] = await connection.query(query);
connection.release();
if (rows.length <= 0) {
return -1;
if (rows.length <= 0) {
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
*/
public async $getBlocksByPool(
poolId: number,
startHeight: number | null = null
): Promise<object[]> {
public async $getBlocksByPool(poolId: number, startHeight: number | null = null): Promise<object[]> {
const params: any[] = [];
let query = `SELECT height, hash as id, tx_count, size, weight, pool_id, UNIX_TIMESTAMP(blockTimestamp) as timestamp, reward
FROM blocks
@ -227,14 +256,20 @@ class BlocksRepository {
// logger.debug(query);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, params);
connection.release();
try {
const [rows] = await connection.query(query, params);
connection.release();
for (const block of <object[]>rows) {
delete block['blockTimestamp'];
for (const block of <object[]>rows) {
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> {
const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(`
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
FROM blocks
JOIN pools ON blocks.pool_id = pools.id
WHERE height = ${height};
`);
connection.release();
try {
const [rows]: any[] = await connection.query(`
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
FROM blocks
JOIN pools ON blocks.pool_id = pools.id
WHERE height = ${height};
`);
connection.release();
if (rows.length <= 0) {
return null;
if (rows.length <= 0) {
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
`;
const [rows]: any[] = await connection.query(query);
connection.release();
try {
const [rows]: any[] = await connection.query(query);
connection.release();
for (let row of rows) {
delete row['rn'];
for (let row of rows) {
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> {
const connection = await DB.pool.getConnection();
const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`);
connection.release();
return rows[0].minHeight;
try {
const [rows]: any[] = await connection.query(`SELECT MIN(height) as minHeight FROM blocks`);
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 {
// logger.debug(query);
await connection.query(query);
connection.release();
} catch (e: any) {
connection.release();
logger.err('$saveHashrateInDatabase() error' + (e instanceof Error ? e.message : e));
throw e;
}
connection.release();
}
public async $getNetworkDailyHashrate(interval: string | null): Promise<any[]> {
@ -51,20 +51,33 @@ class HashratesRepository {
query += ` ORDER by hashrate_timestamp`;
const [rows]: any[] = await connection.query(query);
connection.release();
try {
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[]> {
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 [rows]: any[] = await connection.query(query);
connection.release();
const query = `SELECT UNIX_TIMESTAMP(hashrate_timestamp) as timestamp FROM hashrates where type = 'weekly' GROUP BY hashrate_timestamp`;
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})`;
const [rows]: any[] = await connection.query(query);
connection.release();
try {
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) {
const connection = await DB.pool.getConnection();
const query = `UPDATE state SET number = ? WHERE name = ?`;
await connection.query<any>(query, (val === null) ? [Math.round(new Date().getTime() / 1000), key] : [val, key]);
connection.release();
try {
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> {
const connection = await DB.pool.getConnection();
const query = `SELECT number FROM state WHERE name = ?`;
const [rows] = await connection.query<any>(query, [key]);
connection.release();
return rows[0]['number'];
try {
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);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query);
connection.release();
try {
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
*/
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
LEFT JOIN blocks on pools.id = blocks.pool_id AND blocks.blockTimestamp BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?)
GROUP BY pools.id`;
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, [from, to]);
connection.release();
try {
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);
const connection = await DB.pool.getConnection();
const [rows] = await connection.query(query, [poolId]);
connection.release();
try {
const [rows] = await connection.query(query, [poolId]);
connection.release();
rows[0].regexes = JSON.parse(rows[0].regexes);
rows[0].addresses = JSON.parse(rows[0].addresses);
rows[0].regexes = JSON.parse(rows[0].regexes);
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;
}
}
}