@@ -151,35 +151,52 @@ export async function up() {
151151 console . log ( ` Backfill Start: ${ startDate . toISOString ( ) . split ( 'T' ) [ 0 ] } ` ) ;
152152 console . log ( ` Backfill End: ${ endDate . toISOString ( ) . split ( 'T' ) [ 0 ] } (inclusive)` ) ;
153153 console . log ( ` Backfill Days: ${ backfillDays } days` ) ;
154+ console . log ( ` Chunking: Hourly (24 chunks per day)` ) ;
155+ console . log ( ` Total Chunks: ${ backfillDays * 24 } hourly inserts` ) ;
154156 console . log ( ` Total Events: ${ totalEvents . toLocaleString ( ) } in events table` ) ;
155157 console . log ( '========================================' ) ;
156158 console . log ( '' ) ;
157159
158- // Generate day-by-day INSERT statements with proper GROUP BY
160+ // Generate INSERT statements chunked by hour to reduce memory usage
161+ // Process each day in 24 hourly chunks to avoid ClickHouse memory limits
159162 const backfillSqls : string [ ] = [ ] ;
160163
161164 let currentDate = new Date ( startDate ) ; // Start from startDate (go forward)
162165
163166 while ( currentDate <= endDate ) {
164167 const dateStr = currentDate . toISOString ( ) . split ( 'T' ) [ 0 ] ;
165168
166- const sql = `INSERT INTO ${ targetTable }
167- SELECT
168- project_id,
169- name,
170- toDate(created_at) as date,
171- uniqState(profile_id) as unique_profiles_state,
172- uniqState(session_id) as unique_sessions_state,
173- countState() as event_count
174- FROM events
175- WHERE toDate(created_at) = '${ dateStr } '
176- GROUP BY project_id, name, date` ;
169+ // Process each day in 24 hourly chunks
170+ for ( let hour = 0 ; hour < 24 ; hour ++ ) {
171+ const hourStart = `${ dateStr } ${ String ( hour ) . padStart ( 2 , '0' ) } :00:00` ;
172+ const hourEnd = `${ dateStr } ${ String ( hour ) . padStart ( 2 , '0' ) } :59:59` ;
173+
174+ const sql = `INSERT INTO ${ targetTable }
175+ SELECT
176+ project_id,
177+ name,
178+ toDate(created_at) as date,
179+ uniqState(profile_id) as unique_profiles_state,
180+ uniqState(session_id) as unique_sessions_state,
181+ countState() as event_count
182+ FROM events
183+ WHERE created_at >= toDateTime('${ hourStart } ')
184+ AND created_at <= toDateTime('${ hourEnd } ')
185+ GROUP BY project_id, name, date` ;
186+
187+ backfillSqls . push ( sql ) ;
188+ }
177189
178- backfillSqls . push ( sql ) ;
179190 currentDate . setDate ( currentDate . getDate ( ) + 1 ) ;
180191 }
181192
182193 sqls . push ( ...backfillSqls ) ;
194+
195+ // Add OPTIMIZE TABLE to merge hourly chunks into daily aggregates
196+ const optimizeSql = `OPTIMIZE TABLE ${ targetTable } FINAL` ;
197+ sqls . push ( optimizeSql ) ;
198+ console . log ( 'Added OPTIMIZE TABLE command to merge hourly chunks' ) ;
199+ console . log ( '' ) ;
183200 } else {
184201 console . log ( 'No data found in the specified date range, skipping backfill' ) ;
185202 }
@@ -204,10 +221,13 @@ export async function up() {
204221 const mvOpsTotal = sqls . filter ( sql => sql . includes ( 'CREATE' ) || sql . includes ( 'MATERIALIZED VIEW' ) ) . length ;
205222 const insertOpsTotal = sqls . filter ( sql => sql . includes ( 'INSERT' ) ) . length ;
206223
224+ const optimizeOpsTotal = sqls . filter ( sql => sql . includes ( 'OPTIMIZE' ) ) . length ;
225+
207226 console . log ( `Generated ${ sqls . length } SQL statements:` ) ;
208227 console . log ( ` - ${ deleteOpsTotal } DELETE operations (day-by-day)` ) ;
209228 console . log ( ` - ${ mvOpsTotal } MV creation operations` ) ;
210- console . log ( ` - ${ insertOpsTotal } INSERT operations (day-by-day)` ) ;
229+ console . log ( ` - ${ insertOpsTotal } INSERT operations (hourly chunks)` ) ;
230+ console . log ( ` - ${ optimizeOpsTotal } OPTIMIZE operations (merge chunks)` ) ;
211231 console . log ( `SQL written to: ${ sqlFilePath } ` ) ;
212232 console . log ( '' ) ;
213233
0 commit comments