11import { NavigationPage } from '@/components/navigation/NavigationPage' ;
2- import { clearData , syncErrorTracker } from '@/library/powersync/ConnectionManager' ;
2+ import { clearData , db , syncErrorTracker } from '@/library/powersync/ConnectionManager' ;
33import {
44 Box ,
55 Button ,
@@ -15,7 +15,6 @@ import {
1515 styled
1616} from '@mui/material' ;
1717import { DataGrid , GridColDef } from '@mui/x-data-grid' ;
18- import { useQuery } from '@powersync/react' ;
1918import React from 'react' ;
2019
2120const BUCKETS_QUERY = `
2423 (SELECT
2524 bucket,
2625 row_type,
27- sum(length(data)) as data_size,
26+ sum(case when op = 3 and superseded = 0 then length(data) else 0 end ) as data_size,
2827 sum(length(row_type) + length(row_id) + length(bucket) + length(key) + 40) as metadata_size,
29- count( ) as row_count
28+ sum(case when op = 3 and superseded = 0 then 1 else 0 end ) as row_count
3029 FROM ps_oplog GROUP BY bucket, row_type),
3130
3231 oplog_stats AS
@@ -51,23 +50,65 @@ FROM local_bucket_data local
5150LEFT JOIN oplog_stats stats ON stats.name = local.id` ;
5251
5352const TABLES_QUERY = `
54- SELECT row_type as name, count() as count, sum(length(data)) as size FROM ps_oplog GROUP BY row_type
53+ SELECT row_type as name, count() as count, sum(length(data)) as size FROM ps_oplog WHERE superseded = 0 and op = 3 GROUP BY row_type
5554` ;
5655
57- export default function SyncDiagnosticsPage ( ) {
58- const { data : bucketRows , isLoading : bucketRowsLoading } = useQuery ( BUCKETS_QUERY , undefined , {
59- rawTableNames : true ,
60- tables : [ 'ps_oplog' , 'ps_data_local__local_bucket_data' ] ,
61- throttleMs : 500
62- } ) ;
63- const { data : tableRows , isLoading : tableRowsLoading } = useQuery ( TABLES_QUERY , undefined , {
64- rawTableNames : true ,
65- tables : [ 'ps_oplog' , 'ps_data_local__local_bucket_data' ] ,
66- throttleMs : 500
67- } ) ;
56+ const BUCKETS_QUERY_FAST = `
57+ SELECT
58+ local.id as name ,
59+ '[]' as tables ,
60+ 0 as data_size,
61+ 0 as metadata_size,
62+ 0 as row_count,
63+ local.download_size ,
64+ local.total_operations ,
65+ local.downloading
66+ FROM local_bucket_data local` ;
6867
68+ export default function SyncDiagnosticsPage ( ) {
69+ const [ bucketRows , setBucketRows ] = React . useState < null | any [ ] > ( null ) ;
70+ const [ tableRows , setTableRows ] = React . useState < null | any [ ] > ( null ) ;
6971 const [ syncError , setSyncError ] = React . useState < Error | null > ( syncErrorTracker . lastSyncError ) ;
7072
73+ const bucketRowsLoading = bucketRows == null ;
74+ const tableRowsLoading = tableRows == null ;
75+
76+ const refreshStats = async ( ) => {
77+ // Similar to db.currentState.hasSynced, but synchronized to the onChange events
78+ const hasSynced = await db . getOptional ( 'SELECT 1 FROM ps_buckets WHERE last_applied_op > 0 LIMIT 1' ) ;
79+ if ( hasSynced != null ) {
80+ // These are potentially expensive queries - do not run during initial sync
81+ const bucketRows = await db . getAll ( BUCKETS_QUERY ) ;
82+ const tableRows = await db . getAll ( TABLES_QUERY ) ;
83+ setBucketRows ( bucketRows ) ;
84+ setTableRows ( tableRows ) ;
85+ } else {
86+ // Fast query to show progress during initial sync
87+ const bucketRows = await db . getAll ( BUCKETS_QUERY_FAST ) ;
88+ setBucketRows ( bucketRows ) ;
89+ setTableRows ( null ) ;
90+ }
91+ } ;
92+
93+ React . useEffect ( ( ) => {
94+ const controller = new AbortController ( ) ;
95+
96+ db . onChangeWithCallback (
97+ {
98+ async onChange ( event ) {
99+ await refreshStats ( ) ;
100+ }
101+ } ,
102+ { rawTableNames : true , tables : [ 'ps_oplog' , 'ps_buckets' , 'ps_data_local__local_bucket_data' ] , throttleMs : 500 }
103+ ) ;
104+
105+ refreshStats ( ) ;
106+
107+ return ( ) => {
108+ controller . abort ( ) ;
109+ } ;
110+ } , [ ] ) ;
111+
71112 React . useEffect ( ( ) => {
72113 const l = syncErrorTracker . registerListener ( {
73114 lastErrorUpdated ( error ) {
@@ -111,7 +152,7 @@ export default function SyncDiagnosticsPage() {
111152 }
112153 ] ;
113154
114- const rows = bucketRows . map ( ( r ) => {
155+ const rows = ( bucketRows ?? [ ] ) . map ( ( r ) => {
115156 return {
116157 id : r . name ,
117158 name : r . name ,
@@ -146,7 +187,7 @@ export default function SyncDiagnosticsPage() {
146187 }
147188 ] ;
148189
149- const tablesRows = tableRows . map ( ( r ) => {
190+ const tablesRows = ( tableRows ?? [ ] ) . map ( ( r ) => {
150191 return {
151192 id : r . name ,
152193 ...r
@@ -181,50 +222,40 @@ export default function SyncDiagnosticsPage() {
181222 ) ;
182223
183224 const tablesTable = (
184- < S . QueryResultContainer >
185- < Typography variant = "h4" gutterBottom >
186- Tables
187- </ Typography >
188- < DataGrid
189- autoHeight = { true }
190- rows = { tablesRows }
191- columns = { tablesColumns }
192- initialState = { {
193- pagination : {
194- paginationModel : {
195- pageSize : 10
196- }
225+ < DataGrid
226+ autoHeight = { true }
227+ rows = { tablesRows }
228+ columns = { tablesColumns }
229+ initialState = { {
230+ pagination : {
231+ paginationModel : {
232+ pageSize : 10
197233 }
198- } }
199- pageSizeOptions = { [ 10 , 50 , 100 ] }
200- disableRowSelectionOnClick
201- />
202- </ S . QueryResultContainer >
234+ }
235+ } }
236+ pageSizeOptions = { [ 10 , 50 , 100 ] }
237+ disableRowSelectionOnClick
238+ / >
203239 ) ;
204240
205241 const bucketsTable = (
206- < S . QueryResultContainer >
207- < Typography variant = "h4" gutterBottom >
208- Buckets
209- </ Typography >
210- < DataGrid
211- autoHeight = { true }
212- rows = { rows }
213- columns = { columns }
214- initialState = { {
215- pagination : {
216- paginationModel : {
217- pageSize : 50
218- }
219- } ,
220- sorting : {
221- sortModel : [ { field : 'total_operations' , sort : 'desc' } ]
242+ < DataGrid
243+ autoHeight = { true }
244+ rows = { rows }
245+ columns = { columns }
246+ initialState = { {
247+ pagination : {
248+ paginationModel : {
249+ pageSize : 50
222250 }
223- } }
224- pageSizeOptions = { [ 10 , 50 , 100 ] }
225- disableRowSelectionOnClick
226- />
227- </ S . QueryResultContainer >
251+ } ,
252+ sorting : {
253+ sortModel : [ { field : 'total_operations' , sort : 'desc' } ]
254+ }
255+ } }
256+ pageSizeOptions = { [ 10 , 50 , 100 ] }
257+ disableRowSelectionOnClick
258+ />
228259 ) ;
229260
230261 return (
@@ -239,8 +270,18 @@ export default function SyncDiagnosticsPage() {
239270 } } >
240271 Clear & Redownload
241272 </ Button >
242- { tableRowsLoading ? < CircularProgress /> : tablesTable }
243- { bucketRowsLoading ? < CircularProgress /> : bucketsTable }
273+ < S . QueryResultContainer >
274+ < Typography variant = "h4" gutterBottom >
275+ Tables
276+ </ Typography >
277+ { tableRowsLoading ? < CircularProgress /> : tablesTable }
278+ </ S . QueryResultContainer >
279+ < S . QueryResultContainer >
280+ < Typography variant = "h4" gutterBottom >
281+ Buckets
282+ </ Typography >
283+ { bucketRowsLoading ? < CircularProgress /> : bucketsTable }
284+ </ S . QueryResultContainer >
244285 </ S . MainContainer >
245286 </ NavigationPage >
246287 ) ;
0 commit comments