From 4f51a4d05ce6a452ff3255a0ca1eae80ba27a76e Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 7 Jun 2026 21:37:23 -0400 Subject: [PATCH] Omit current chart bucket --- .../client-statistics.service.ts | 34 +++-------------- test/timescale-redis.integration-spec.ts | 37 +++++++++++++++++-- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/ORM/client-statistics/client-statistics.service.ts b/src/ORM/client-statistics/client-statistics.service.ts index 1181dfd..f623981 100644 --- a/src/ORM/client-statistics/client-statistics.service.ts +++ b/src/ORM/client-statistics/client-statistics.service.ts @@ -66,34 +66,7 @@ export class ClientStatisticsService { WITH bounds AS ( SELECT NOW() - INTERVAL '${windowSql}' AS since, - time_bucket(INTERVAL '10 minutes', NOW()) AS realtime_start - ), - aggregate_rows AS ( - SELECT - "bucket", - SUM("shares") AS "shares", - SUM("acceptedCount") AS "acceptedCount" - FROM "accepted_share_10m", bounds - WHERE "bucket" > bounds.since - AND "bucket" < bounds.realtime_start - ${filterSql} - GROUP BY "bucket" - ), - realtime_rows AS ( - SELECT - time_bucket(INTERVAL '10 minutes', "acceptedAt") AS "bucket", - SUM("creditedDifficulty") AS "shares", - COUNT(*) AS "acceptedCount" - FROM "accepted_share_entity", bounds - WHERE "acceptedAt" > bounds.since - AND "acceptedAt" >= bounds.realtime_start - ${filterSql} - GROUP BY "bucket" - ), - combined_rows AS ( - SELECT * FROM aggregate_rows - UNION ALL - SELECT * FROM realtime_rows + time_bucket(INTERVAL '10 minutes', NOW()) AS current_bucket ) SELECT "label", @@ -106,7 +79,10 @@ export class ClientStatisticsService { ROUND((SUM("shares") * ${HASHES_PER_DIFFICULTY}) / ${CHART_BUCKET_SECONDS}) AS "data", SUM("shares") AS "shares", SUM("acceptedCount") AS "acceptedCount" - FROM combined_rows + FROM "accepted_share_10m", bounds + WHERE "bucket" > bounds.since + AND "bucket" < bounds.current_bucket + ${filterSql} GROUP BY "bucket" ORDER BY "bucket" DESC LIMIT ${limit} diff --git a/test/timescale-redis.integration-spec.ts b/test/timescale-redis.integration-spec.ts index 7d3af97..786204c 100644 --- a/test/timescale-redis.integration-spec.ts +++ b/test/timescale-redis.integration-spec.ts @@ -229,7 +229,7 @@ describe('TimescaleDB and Redis integration', () => { }])); }); - it('should serve realtime chart and hashrate data from accepted shares', async () => { + it('should serve completed chart buckets and omit the current in-progress bucket', async () => { const client = await dataSource.getRepository(ClientEntity).save({ address: 'tb1qumezefzdeqqwn5zfvgdrhxjzc5ylr39uhuxcz4', clientName: 'worker', @@ -241,7 +241,10 @@ describe('TimescaleDB and Redis integration', () => { }); const accountingService = new ShareAccountingService(dataSource.getRepository(AcceptedShareEntity)); const statisticsService = new ClientStatisticsService(dataSource); - const acceptedAt = new Date(); + const currentBucketStart = Math.floor(Date.now() / (10 * 60 * 1000)) * 10 * 60 * 1000; + const completedBucketStart = currentBucketStart - (10 * 60 * 1000); + const completedBucketEnd = currentBucketStart; + const acceptedAt = new Date(completedBucketStart + 1000); for (const [index, difficulty] of [64, 32].entries()) { await accountingService.recordAcceptedShare({ @@ -265,11 +268,36 @@ describe('TimescaleDB and Redis integration', () => { blockSubmissionResult: null, }); } + await accountingService.recordAcceptedShare({ + protocol: 'sv1', + acceptedAt: new Date(currentBucketStart + 1000), + address: client.address, + clientName: client.clientName, + sessionId: client.sessionId, + clientId: client.id, + jobId: 'current-bucket', + jobTemplateId: 'realtime-template', + blockHeight: 900001, + creditedDifficulty: 1024, + submissionDifficulty: 2048, + networkDifficulty: 100000, + nonce: 'nonce-current', + ntime: '64b3f3ec', + version: '20000000', + extraNonce2: 'c708000000000000', + isBlockCandidate: false, + blockSubmissionResult: null, + }); + await dataSource.query( + `CALL refresh_continuous_aggregate('accepted_share_10m', $1::timestamptz, $2::timestamptz)`, + [new Date(completedBucketStart), new Date(completedBucketEnd)], + ); const expectedHashRate = (96 * 4294967296) / 600; const expectedChartData = Math.round(expectedHashRate).toString(); + const expectedLiveHashRate = ((96 + 1024) * 4294967296) / 600; expect(await statisticsService.getHashRateForGroup(client.address, client.clientName)) - .toBeCloseTo(expectedHashRate); + .toBeCloseTo(expectedLiveHashRate); const addressChart = await statisticsService.getChartDataForAddress(client.address); const groupChart = await statisticsService.getChartDataForGroup(client.address, client.clientName); @@ -280,6 +308,9 @@ describe('TimescaleDB and Redis integration', () => { expect(chart).toEqual(expect.arrayContaining([ expect.objectContaining({ data: expectedChartData }), ])); + expect(chart).not.toEqual(expect.arrayContaining([ + expect.objectContaining({ label: new Date(currentBucketStart).toISOString() }), + ])); } });