From 5f66a954023eddf805dd0022d53ee2763d2dafb1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 7 Jan 2024 18:02:11 +0000 Subject: [PATCH 1/2] Smooth out irregular mempool block updates --- .../block-overview-graph/block-scene.ts | 2 +- .../mempool-block-overview.component.ts | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index cb589527d..adcf736fc 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -11,7 +11,7 @@ export default class BlockScene { getColor: ((tx: TxView) => Color) = defaultColorFunction; orientation: string; flip: boolean; - animationDuration: number = 1000; + animationDuration: number = 900; configAnimationOffset: number | null; animationOffset: number; highlightingEnabled: boolean; diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index f09b8f5ea..84c9a3416 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -3,8 +3,8 @@ import { Component, ComponentRef, ViewChild, HostListener, Input, Output, EventE import { StateService } from '../../services/state.service'; import { MempoolBlockDelta, TransactionStripped } from '../../interfaces/websocket.interface'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; -import { Subscription, BehaviorSubject, merge, of } from 'rxjs'; -import { switchMap, filter } from 'rxjs/operators'; +import { Subscription, BehaviorSubject, merge, of, timer } from 'rxjs'; +import { switchMap, filter, concatMap, map } from 'rxjs/operators'; import { WebsocketService } from '../../services/websocket.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { Router } from '@angular/router'; @@ -33,7 +33,8 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang poolDirection: string = 'left'; blockSub: Subscription; - deltaSub: Subscription; + rateLimit = 1000; + private lastEventTime = Date.now() - this.rateLimit; firstLoad: boolean = true; @@ -55,11 +56,32 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang ngAfterViewInit(): void { this.blockSub = merge( - of(true), - this.stateService.connectionState$.pipe(filter((state) => state === 2)) - ) - .pipe(switchMap(() => this.stateService.mempoolBlockTransactions$)) - .subscribe((transactionsStripped) => { + this.stateService.mempoolBlockTransactions$, + this.stateService.mempoolBlockDelta$, + ).pipe( + concatMap(event => { + const now = Date.now(); + const timeSinceLastEvent = now - this.lastEventTime; + this.lastEventTime = Math.max(now, this.lastEventTime + this.rateLimit); + + // If time since last event is less than X seconds, delay this event + if (timeSinceLastEvent < this.rateLimit) { + return timer(this.rateLimit - timeSinceLastEvent).pipe( + // Emit the event after the timer + map(() => event) + ); + } else { + // If enough time has passed, emit the event immediately + return of(event); + } + }) + ).subscribe((update) => { + if (update['added']) { + // delta + this.updateBlock(update as MempoolBlockDelta); + } else { + const transactionsStripped = update as TransactionStripped[]; + // new transactions if (this.firstLoad) { this.replaceBlock(transactionsStripped); } else { @@ -94,9 +116,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang added }); } - }); - this.deltaSub = this.stateService.mempoolBlockDelta$.subscribe((delta) => { - this.updateBlock(delta); + } }); } @@ -113,7 +133,6 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang ngOnDestroy(): void { this.blockSub.unsubscribe(); - this.deltaSub.unsubscribe(); this.timeLtrSubscription.unsubscribe(); this.websocketService.stopTrackMempoolBlock(); } From fc8eca4c26c38b2e57ce86c62f854f44e072e71d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 8 Jan 2024 14:42:00 +0000 Subject: [PATCH 2/2] Handle stale smoothed mempool block updates --- .../mempool-block-overview.component.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 84c9a3416..8dad6a9c1 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -35,6 +35,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang blockSub: Subscription; rateLimit = 1000; private lastEventTime = Date.now() - this.rateLimit; + private subId = 0; firstLoad: boolean = true; @@ -59,23 +60,30 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang this.stateService.mempoolBlockTransactions$, this.stateService.mempoolBlockDelta$, ).pipe( - concatMap(event => { + concatMap(update => { const now = Date.now(); const timeSinceLastEvent = now - this.lastEventTime; this.lastEventTime = Math.max(now, this.lastEventTime + this.rateLimit); + const subId = this.subId; + // If time since last event is less than X seconds, delay this event if (timeSinceLastEvent < this.rateLimit) { return timer(this.rateLimit - timeSinceLastEvent).pipe( // Emit the event after the timer - map(() => event) + map(() => ({ update, subId })) ); } else { // If enough time has passed, emit the event immediately - return of(event); + return of({ update, subId }); } }) - ).subscribe((update) => { + ).subscribe(({ update, subId }) => { + // discard stale updates after a block transition + if (subId !== this.subId) { + return; + } + // process update if (update['added']) { // delta this.updateBlock(update as MempoolBlockDelta); @@ -122,6 +130,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang ngOnChanges(changes): void { if (changes.index) { + this.subId++; this.firstLoad = true; if (this.blockGraph) { this.blockGraph.clear(changes.index.currentValue > changes.index.previousValue ? this.chainDirection : this.poolDirection);