1- import { useCallback , useEffect , useMemo , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import {
33 type archivePeriods ,
44 chartConfig ,
@@ -98,24 +98,27 @@ export const useInsightChart = (
9898
9999 const [ data , setData ] = useState < Array < SingleDataArchive > > ( [ ] ) ;
100100
101+ const prevOffsetRef = useRef ( offset ) ;
102+ const activeRequestIdRef = useRef ( 0 ) ;
103+ const scheduledTimeoutIdRef = useRef < number | null > ( null ) ;
104+
105+ const stepMultiplierMap : Record < ( typeof archivePeriods ) [ number ] , number > = {
106+ 10 : 1 ,
107+ 30 : 1 ,
108+ 60 : 1 ,
109+ 180 : 1 ,
110+ 720 : 10 ,
111+ 1440 : 30 ,
112+ 10080 : 60 ,
113+ 20160 : 180 ,
114+ 43200 : 720 ,
115+ } ;
116+
101117 const step =
102- {
103- 10 : 1 ,
104- 30 : 1 ,
105- 60 : 1 ,
106- 180 : 1 ,
107- 720 : 10 ,
108- 1440 : 30 ,
109- 10080 : 60 ,
110- 20160 : 180 ,
111- 43200 : 720 ,
112- } [ period ] * chartConfig . archiveUpdateIntervalMilSec ;
113-
114- const endAt = useMemo ( ( ) => {
115- return new Date ( Date . now ( ) - offset * step ) ;
116- } , [ offset , step ] ) ;
118+ ( stepMultiplierMap [ period ] ?? 1 ) * chartConfig . archiveUpdateIntervalMilSec ;
117119
118120 const getData = useCallback ( async ( ) : Promise < SingleDataArchive [ ] > => {
121+ const endAt = new Date ( Date . now ( ) - offset * step ) ;
119122 const adjustedEndAt = new Date (
120123 endAt . getTime ( ) - chartConfig . archiveUpdateIntervalMilSec ,
121124 ) ;
@@ -141,7 +144,7 @@ export const useInsightChart = (
141144 } ) ( ) ;
142145
143146 return await ( await sqlitePromise ) . load ( sql ) ;
144- } , [ endAt , hardwareType , period , dataStats , gpuName , dataType ] ) ;
147+ } , [ hardwareType , period , dataStats , gpuName , dataType , offset , step ] ) ;
145148
146149 const formatValue = useCallback (
147150 ( value : number | null ) => {
@@ -164,24 +167,75 @@ export const useInsightChart = (
164167 ) ;
165168
166169 useEffect ( ( ) => {
167- const updateData = ( ) => {
168- getData ( ) . then ( ( data ) =>
169- setData ( data . map ( ( V ) => ( { ...V , value : formatValue ( V . value ) } ) ) ) ,
170- ) ;
170+ const isOffsetChanged = prevOffsetRef . current !== offset ;
171+ prevOffsetRef . current = offset ;
172+
173+ // Offset is updated every 100ms while pressing arrows in Insights.
174+ // Debounce DB reads to avoid spamming when scrubbing.
175+ const debounceMs = isOffsetChanged ? 250 : 0 ;
176+
177+ const requestId = activeRequestIdRef . current + 1 ;
178+ activeRequestIdRef . current = requestId ;
179+
180+ const run = async ( ) => {
181+ try {
182+ const rows = await getData ( ) ;
183+ if ( activeRequestIdRef . current !== requestId ) {
184+ return ;
185+ }
186+ setData ( rows . map ( ( v ) => ( { ...v , value : formatValue ( v . value ) } ) ) ) ;
187+ } catch ( e ) {
188+ console . error ( e ) ;
189+ }
171190 } ;
172191
173- updateData ( ) ;
192+ if ( scheduledTimeoutIdRef . current != null ) {
193+ clearTimeout ( scheduledTimeoutIdRef . current ) ;
194+ }
195+
196+ scheduledTimeoutIdRef . current = window . setTimeout ( run , debounceMs ) ;
197+
198+ return ( ) => {
199+ if ( scheduledTimeoutIdRef . current != null ) {
200+ clearTimeout ( scheduledTimeoutIdRef . current ) ;
201+ scheduledTimeoutIdRef . current = null ;
202+ }
203+ } ;
204+ } , [ getData , formatValue , offset ] ) ;
205+
206+ useEffect ( ( ) => {
207+ // Only auto-refresh the "current" window.
208+ if ( offset !== 0 ) {
209+ return ;
210+ }
211+
212+ const intervalId = window . setInterval ( ( ) => {
213+ const requestId = activeRequestIdRef . current + 1 ;
214+ activeRequestIdRef . current = requestId ;
215+
216+ void getData ( )
217+ . then ( ( rows ) => {
218+ if ( activeRequestIdRef . current !== requestId ) {
219+ return ;
220+ }
221+ setData ( rows . map ( ( v ) => ( { ...v , value : formatValue ( v . value ) } ) ) ) ;
222+ } )
223+ . catch ( ( e ) => {
224+ console . error ( e ) ;
225+ } ) ;
226+ } , chartConfig . archiveUpdateIntervalMilSec ) ;
174227
175- const intervalId = setInterval (
176- updateData ,
177- chartConfig . archiveUpdateIntervalMilSec ,
178- ) ;
179228 return ( ) => clearInterval ( intervalId ) ;
180- } , [ getData , formatValue ] ) ;
229+ } , [ getData , formatValue , offset ] ) ;
230+
231+ const endAtForBucket = useMemo (
232+ ( ) => new Date ( Date . now ( ) - offset * step ) ,
233+ [ offset , step ] ,
234+ ) ;
181235
182236 const startTime = useMemo (
183- ( ) => new Date ( endAt . getTime ( ) - period * 60 * 1000 ) ,
184- [ endAt , period ] ,
237+ ( ) => new Date ( endAtForBucket . getTime ( ) - period * 60 * 1000 ) ,
238+ [ endAtForBucket , period ] ,
185239 ) ;
186240
187241 const startBucket =
@@ -190,7 +244,8 @@ export const useInsightChart = (
190244 ) * step ;
191245 const endBucket =
192246 Math . ceil (
193- ( endAt . getTime ( ) - chartConfig . archiveUpdateIntervalMilSec ) / step ,
247+ ( endAtForBucket . getTime ( ) - chartConfig . archiveUpdateIntervalMilSec ) /
248+ step ,
194249 ) * step ;
195250
196251 const bucketedData = useMemo ( ( ) => {
@@ -246,7 +301,10 @@ export const useInsightChart = (
246301 for ( let t = startBucket ; t <= endBucket ; t += step ) {
247302 const bucketData = bucketedData [ t ] ;
248303 if ( ! bucketData || bucketData . length <= 0 ) {
249- if ( t <= endAt . getTime ( ) - chartConfig . archiveUpdateIntervalMilSec ) {
304+ if (
305+ t <=
306+ endAtForBucket . getTime ( ) - chartConfig . archiveUpdateIntervalMilSec
307+ ) {
250308 filledChartData . push ( null ) ;
251309 filledLabels . push ( dateFormatter . format ( new Date ( t ) ) ) ;
252310 }
@@ -264,7 +322,7 @@ export const useInsightChart = (
264322 aggregateFn ,
265323 bucketedData ,
266324 endBucket ,
267- endAt ,
325+ endAtForBucket ,
268326 startBucket ,
269327 step ,
270328 dateFormatter ,
0 commit comments