diff --git a/package-lock.json b/package-lock.json index d048806..e846e8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "discord.js": "^14.11.0", "merkle-lib": "^2.0.10", "node-telegram-bot-api": "^0.61.0", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rpc-bitcoin": "^2.0.0", "rxjs": "^7.2.0", @@ -3730,6 +3731,14 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -8636,6 +8645,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8781,6 +8795,89 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -8916,6 +9013,41 @@ "node": ">=4" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11299,6 +11431,14 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -14231,6 +14371,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -17951,6 +18096,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -18060,6 +18210,68 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "requires": {} + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -18164,6 +18376,29 @@ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -19801,6 +20036,11 @@ "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "requires": {} }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 0c528c7..f75ea5c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "discord.js": "^14.11.0", "merkle-lib": "^2.0.10", "node-telegram-bot-api": "^0.61.0", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rpc-bitcoin": "^2.0.0", "rxjs": "^7.2.0", diff --git a/src/ORM/address-settings/address-settings.entity.ts b/src/ORM/address-settings/address-settings.entity.ts index bf1fcf3..00fcd4e 100644 --- a/src/ORM/address-settings/address-settings.entity.ts +++ b/src/ORM/address-settings/address-settings.entity.ts @@ -11,7 +11,7 @@ export class AddressSettingsEntity extends TrackedEntity { @Column({ default: 0 }) shares: number; - @Column({ type: 'real', default: 0 }) + @Column({ type: 'decimal', default: 0 }) bestDifficulty: number; @Column({ nullable: true }) diff --git a/src/ORM/client-statistics/client-statistics.entity.ts b/src/ORM/client-statistics/client-statistics.entity.ts index 111a60e..c7599e3 100644 --- a/src/ORM/client-statistics/client-statistics.entity.ts +++ b/src/ORM/client-statistics/client-statistics.entity.ts @@ -2,7 +2,7 @@ import { Column, Entity, Index, PrimaryColumn } from 'typeorm'; import { TrackedEntity } from '../utils/TrackedEntity.entity'; -@Entity({ withoutRowid: true }) +@Entity() //Index for the heartbeat update @Index(["address", "clientName", "sessionId", "time"]) export class ClientStatisticsEntity extends TrackedEntity { @@ -21,13 +21,13 @@ export class ClientStatisticsEntity extends TrackedEntity { sessionId: string; @Index() - @Column({ type: 'integer' }) + @Column({ type: 'bigint' }) time: number; - @Column({ type: 'real' }) + @Column({ type: 'decimal' }) shares: number; - @Column({ default: 0, type: 'integer' }) + @Column({ default: 0, type: 'bigint' }) acceptedCount: number; diff --git a/src/ORM/client-statistics/client-statistics.service.ts b/src/ORM/client-statistics/client-statistics.service.ts index 028ce48..5a50cb9 100644 --- a/src/ORM/client-statistics/client-statistics.service.ts +++ b/src/ORM/client-statistics/client-statistics.service.ts @@ -67,7 +67,7 @@ export class ClientStatisticsService { return result.map(res => { - res.label = new Date(res.label).toISOString(); + res.label = new Date(parseInt(res.label)).toISOString(); return res; }).slice(0, result.length - 1) @@ -84,7 +84,7 @@ export class ClientStatisticsService { // FROM // client_statistics_entity AS entry // WHERE - // entry.address = ? AND entry.time > ${oneHour} + // entry.address = $1 AND entry.time > ${oneHour} // `; // const result = await this.clientStatisticsRepository.query(query, [address]); @@ -101,12 +101,12 @@ export class ClientStatisticsService { const query = ` SELECT - time label, + time AS label, (SUM(shares) * 4294967296) / 600 AS data FROM client_statistics_entity AS entry WHERE - entry.address = ? AND entry.time > ${yesterday.getTime()} + entry.address = $1 AND entry.time > $2 GROUP BY time ORDER BY @@ -115,10 +115,10 @@ export class ClientStatisticsService { `; - const result = await this.clientStatisticsRepository.query(query, [address]); + const result = await this.clientStatisticsRepository.query(query, [address, yesterday.getTime()]); return result.map(res => { - res.label = new Date(res.label).toISOString(); + res.label = new Date(parseInt(res.label)).toISOString(); return res; }).slice(0, result.length - 1); @@ -136,7 +136,7 @@ export class ClientStatisticsService { FROM client_statistics_entity AS entry WHERE - entry.address = ? AND entry.clientName = ? AND entry.time > ${oneHour.getTime()} + entry.address = $1 AND entry.clientName = $2 AND entry.time > ${oneHour.getTime()} `; const result = await this.clientStatisticsRepository.query(query, [address, clientName]); @@ -153,12 +153,12 @@ export class ClientStatisticsService { const query = ` SELECT - time label, + time AS label, (SUM(shares) * 4294967296) / 600 AS data FROM client_statistics_entity AS entry WHERE - entry.address = ? AND entry.clientName = ? AND entry.time > ${yesterday.getTime()} + entry.address = $1 AND entry."clientName" = $2 AND entry.time > ${yesterday.getTime()} GROUP BY time ORDER BY @@ -169,7 +169,7 @@ export class ClientStatisticsService { const result = await this.clientStatisticsRepository.query(query, [address, clientName]); return result.map(res => { - res.label = new Date(res.label).toISOString(); + res.label = new Date(parseInt(res.label)).toISOString(); return res; }).slice(0, result.length - 1); @@ -181,13 +181,13 @@ export class ClientStatisticsService { const query = ` SELECT - createdAt, - updatedAt, + "createdAt", + "updatedAt", shares FROM client_statistics_entity AS entry WHERE - entry.address = ? AND entry.clientName = ? AND entry.sessionId = ? + entry.address = $1 AND entry."clientName" = $2 AND entry."sessionId" = $3 ORDER BY time DESC LIMIT 2; `; @@ -227,7 +227,7 @@ export class ClientStatisticsService { FROM client_statistics_entity AS entry WHERE - entry.address = ? AND entry.clientName = ? AND entry.sessionId = ? AND entry.time > ${yesterday.getTime()} + entry.address = $1 AND entry."clientName" = $2 AND entry."sessionId" = $3 AND entry.time > ${yesterday.getTime()} GROUP BY time ORDER BY @@ -238,7 +238,7 @@ export class ClientStatisticsService { const result = await this.clientStatisticsRepository.query(query, [address, clientName, sessionId]); return result.map(res => { - res.label = new Date(res.label).toISOString(); + res.label = new Date(parseInt(res.label)).toISOString(); return res; }).slice(0, result.length - 1); diff --git a/src/ORM/client/client.entity.ts b/src/ORM/client/client.entity.ts index 5787362..ba70650 100644 --- a/src/ORM/client/client.entity.ts +++ b/src/ORM/client/client.entity.ts @@ -1,14 +1,9 @@ import { Column, Entity, Index, PrimaryColumn } from 'typeorm'; -import { DateTimeTransformer } from '../utils/DateTimeTransformer'; import { TrackedEntity } from '../utils/TrackedEntity.entity'; -//https://www.sqlite.org/withoutrowid.html -//The WITHOUT ROWID optimization is likely to be helpful for tables that have non-integer -// or composite (multi-column) PRIMARY KEYs and that do not store large strings or BLOBs. -//WITHOUT ROWID tables work best when individual rows are not too large. -@Entity({ withoutRowid: true }) +@Entity() @Index(['address', 'clientName', 'sessionId'], { unique: true }) export class ClientEntity extends TrackedEntity { @@ -28,13 +23,13 @@ export class ClientEntity extends TrackedEntity { - @Column({ type: 'datetime', transformer: new DateTimeTransformer() }) + @Column({ type: 'timestamp' }) startTime: Date; - @Column({ type: 'real', default: 0 }) + @Column({ type: 'decimal', default: 0 }) bestDifficulty: number - @Column({ default: 0 }) + @Column({ default: 0, type: 'decimal' }) hashRate: number; } diff --git a/src/ORM/client/client.service.ts b/src/ORM/client/client.service.ts index 8798879..809e90d 100644 --- a/src/ORM/client/client.service.ts +++ b/src/ORM/client/client.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { Interval } from '@nestjs/schedule'; import { InjectRepository } from '@nestjs/typeorm'; -import { BehaviorSubject, firstValueFrom } from 'rxjs'; -import { ObjectLiteral, Repository } from 'typeorm'; +import { Repository } from 'typeorm'; import { ClientEntity } from './client.entity'; @@ -12,8 +10,6 @@ import { ClientEntity } from './client.entity'; export class ClientService { - public insertQueue: { result: BehaviorSubject, partialClient: Partial }[] = []; - constructor( @InjectRepository(ClientEntity) @@ -22,26 +18,13 @@ export class ClientService { } - @Interval(1000 * 5) - public async insertClients() { - const queueCopy = [...this.insertQueue]; - this.insertQueue = []; - - const results = await this.clientRepository.insert(queueCopy.map(c => c.partialClient)); - - queueCopy.forEach((c, index) => { - c.result.next(results.generatedMaps[index]); - }); - } - public async killDeadClients() { - var fiveMinutes = new Date(new Date().getTime() - (5 * 60 * 1000)).toISOString(); return await this.clientRepository .createQueryBuilder() .update(ClientEntity) - .set({ deletedAt: () => "DATETIME('now')" }) - .where("deletedAt IS NULL AND updatedAt < DATETIME(:fiveMinutes)", { fiveMinutes }) + .set({ deletedAt: () => "NOW()" }) + .where("deletedAt IS NULL AND updatedAt < NOW() + interval '5 minutes' ") .execute(); } @@ -55,19 +38,11 @@ export class ClientService { public async insert(partialClient: Partial): Promise { - - const result = new BehaviorSubject(null); - - this.insertQueue.push({ result, partialClient }); - - - // const insertResult = await this.clientRepository.insert(partialClient); - - const generatedMap = await firstValueFrom(result); + const insertResult = await this.clientRepository.insert(partialClient); const client = { ...partialClient, - ...generatedMap + ...insertResult.generatedMaps[0] }; return client as ClientEntity; diff --git a/src/ORM/utils/TrackedEntity.entity.ts b/src/ORM/utils/TrackedEntity.entity.ts index e314b8c..07a8adc 100644 --- a/src/ORM/utils/TrackedEntity.entity.ts +++ b/src/ORM/utils/TrackedEntity.entity.ts @@ -1,14 +1,12 @@ import { CreateDateColumn, DeleteDateColumn, UpdateDateColumn } from 'typeorm'; -import { DateTimeTransformer } from './DateTimeTransformer'; - export abstract class TrackedEntity { - @DeleteDateColumn({ nullable: true, type: 'datetime', transformer: new DateTimeTransformer() }) + @DeleteDateColumn({ nullable: true, type: 'timestamp' }) public deletedAt?: Date; - @CreateDateColumn({ type: 'datetime', transformer: new DateTimeTransformer() }) + @CreateDateColumn({ type: 'timestamp' }) public createdAt?: Date - @UpdateDateColumn({ type: 'datetime', transformer: new DateTimeTransformer() }) + @UpdateDateColumn({ type: 'timestamp' }) public updatedAt?: Date } \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index bb10740..4b3ec9b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,7 +1,7 @@ import { HttpModule } from '@nestjs/axios'; import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; +import { ConfigModule, ConfigService } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; @@ -9,11 +9,17 @@ import { AppController } from './app.controller'; import { AddressController } from './controllers/address/address.controller'; import { ClientController } from './controllers/client/client.controller'; import { BitcoinAddressValidator } from './models/validators/bitcoin-address.validator'; +import { AddressSettingsEntity } from './ORM/address-settings/address-settings.entity'; import { AddressSettingsModule } from './ORM/address-settings/address-settings.module'; +import { BlocksEntity } from './ORM/blocks/blocks.entity'; import { BlocksModule } from './ORM/blocks/blocks.module'; +import { ClientStatisticsEntity } from './ORM/client-statistics/client-statistics.entity'; import { ClientStatisticsModule } from './ORM/client-statistics/client-statistics.module'; +import { ClientEntity } from './ORM/client/client.entity'; import { ClientModule } from './ORM/client/client.module'; +import { RpcBlockEntity } from './ORM/rpc-block/rpc-block.entity'; import { RpcBlocksModule } from './ORM/rpc-block/rpc-block.module'; +import { TelegramSubscriptionsEntity } from './ORM/telegram-subscriptions/telegram-subscriptions.entity'; import { TelegramSubscriptionsModule } from './ORM/telegram-subscriptions/telegram-subscriptions.module'; import { AppService } from './services/app.service'; import { BitcoinRpcService } from './services/bitcoin-rpc.service'; @@ -39,16 +45,30 @@ const ORMModules = [ @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forRoot({ - type: 'sqlite', - database: './DB/public-pool.sqlite', - synchronize: true, - autoLoadEntities: true, - cache: true, - logging: false, - enableWAL: true, - busyTimeout: 30 * 1000, + TypeOrmModule.forRootAsync({ + useFactory: (configService: ConfigService) => { + return { + type: 'postgres', + host: configService.get('DB_HOST'), + port: parseInt(configService.get('DB_PORT')), + username: configService.get('DB_USERNAME'), + password: configService.get('DB_PASSWORD'), + database: configService.get('DB_DATABASE'), + entities: [ + ClientEntity, + AddressSettingsEntity, + BlocksEntity, + ClientStatisticsEntity, + RpcBlockEntity, + TelegramSubscriptionsEntity + ], + synchronize: true, + logging: false, + } + }, + imports: [ConfigModule], + inject: [ConfigService] }), CacheModule.register(), ScheduleModule.forRoot(), diff --git a/src/controllers/client/client.controller.ts b/src/controllers/client/client.controller.ts index 7a3d2c2..530266b 100644 --- a/src/controllers/client/client.controller.ts +++ b/src/controllers/client/client.controller.ts @@ -30,7 +30,7 @@ export class ClientController { return { sessionId: worker.sessionId, name: worker.clientName, - bestDifficulty: worker.bestDifficulty.toFixed(2), + bestDifficulty: parseFloat(worker.bestDifficulty as any).toFixed(2), hashRate: worker.hashRate, startTime: worker.startTime, lastSeen: worker.updatedAt diff --git a/src/services/app.service.ts b/src/services/app.service.ts index 6897424..c02cedf 100644 --- a/src/services/app.service.ts +++ b/src/services/app.service.ts @@ -17,16 +17,7 @@ export class AppService implements OnModuleInit { } async onModuleInit() { - // if (process.env.NODE_APP_INSTANCE == '0') { - // await this.dataSource.query(`VACUUM;`); - // } - //https://phiresky.github.io/blog/2020/sqlite-performance-tuning/ - //500 MB DB cache - await this.dataSource.query(`PRAGMA cache_size = -500000;`); - //Normal is still completely corruption safe in WAL mode, and means only WAL checkpoints have to wait for FSYNC. - await this.dataSource.query(`PRAGMA synchronous = off;`); - //6Gb - await this.dataSource.query(`PRAGMA mmap_size = 6000000000;`); + } @Interval(1000 * 60 * 60)