11import { DurableObject } from 'cloudflare:workers' ;
2- import { eq , ne , gt , gte , lt , and , inArray , isNotNull } from 'drizzle-orm' ;
2+ import { eq , ne , gte , lt , and , inArray , isNotNull , sql } from 'drizzle-orm' ;
33import { drizzle , type DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite' ;
44import { migrate } from 'drizzle-orm/durable-sqlite/migrator' ;
55
@@ -86,6 +86,20 @@ const INGEST_META_EXTRACTORS: Array<{
8686
8787type Changes = Array < { name : ExtractableMetaKey ; value : string | null } > ;
8888
89+ export type IngestOrderCursor = { ingestedAt : number ; id : number } ;
90+
91+ export function coalescedIngestedAt ( ) {
92+ return sql < number > `coalesce(${ ingestItems . ingested_at } , 0)` ;
93+ }
94+
95+ export function afterIngestOrderCursor ( cursor : IngestOrderCursor ) {
96+ return sql `(${ coalescedIngestedAt ( ) } > ${ cursor . ingestedAt } OR (${ coalescedIngestedAt ( ) } = ${ cursor . ingestedAt } AND ${ ingestItems . id } > ${ cursor . id } ))` ;
97+ }
98+
99+ export function ingestOrderCursor ( row : { ingested_at : number | null ; id : number } ) : IngestOrderCursor {
100+ return { ingestedAt : row . ingested_at ?? 0 , id : row . id } ;
101+ }
102+
89103export class SessionIngestDO extends DurableObject < Env > {
90104 private db : DrizzleSqliteDODatabase ;
91105
@@ -295,25 +309,26 @@ export class SessionIngestDO extends DurableObject<Env> {
295309 // --- messages ---
296310 const CURSOR_BATCH = 10 ;
297311 controller . enqueue ( encoder . encode ( ',"messages":[' ) ) ;
298- let msgCursor = 0 ;
312+ let msgCursor : IngestOrderCursor = { ingestedAt : - 1 , id : 0 } ;
299313 let firstMsg = true ;
300314
301315 while ( true ) {
302316 const msgBatch = db
303317 . select ( {
304318 id : ingestItems . id ,
319+ ingested_at : ingestItems . ingested_at ,
305320 item_id : ingestItems . item_id ,
306321 item_data : ingestItems . item_data ,
307322 item_data_r2_key : ingestItems . item_data_r2_key ,
308323 } )
309324 . from ( ingestItems )
310- . where ( and ( eq ( ingestItems . item_type , 'message' ) , gt ( ingestItems . id , msgCursor ) ) )
311- . orderBy ( ingestItems . id )
325+ . where ( and ( eq ( ingestItems . item_type , 'message' ) , afterIngestOrderCursor ( msgCursor ) ) )
326+ . orderBy ( coalescedIngestedAt ( ) , ingestItems . id )
312327 . limit ( CURSOR_BATCH )
313328 . all ( ) ;
314329
315330 if ( msgBatch . length === 0 ) break ;
316- msgCursor = msgBatch [ msgBatch . length - 1 ] . id ;
331+ msgCursor = ingestOrderCursor ( msgBatch [ msgBatch . length - 1 ] ) ;
317332
318333 for ( const msgRow of msgBatch ) {
319334 if ( ! firstMsg ) controller . enqueue ( encoder . encode ( ',' ) ) ;
@@ -327,13 +342,14 @@ export class SessionIngestDO extends DurableObject<Env> {
327342 const msgId = msgRow . item_id . slice ( 'message/' . length ) ;
328343 const partRange = getPartItemIdentityRange ( msgId ) ;
329344 controller . enqueue ( encoder . encode ( ',"parts":[' ) ) ;
330- let partCursor = 0 ;
345+ let partCursor : IngestOrderCursor = { ingestedAt : - 1 , id : 0 } ;
331346 let firstPart = true ;
332347
333348 while ( true ) {
334349 const partBatch = db
335350 . select ( {
336351 id : ingestItems . id ,
352+ ingested_at : ingestItems . ingested_at ,
337353 item_data : ingestItems . item_data ,
338354 item_data_r2_key : ingestItems . item_data_r2_key ,
339355 } )
@@ -343,15 +359,15 @@ export class SessionIngestDO extends DurableObject<Env> {
343359 eq ( ingestItems . item_type , 'part' ) ,
344360 gte ( ingestItems . item_id , partRange . start ) ,
345361 lt ( ingestItems . item_id , partRange . end ) ,
346- gt ( ingestItems . id , partCursor )
362+ afterIngestOrderCursor ( partCursor )
347363 )
348364 )
349- . orderBy ( ingestItems . id )
365+ . orderBy ( coalescedIngestedAt ( ) , ingestItems . id )
350366 . limit ( CURSOR_BATCH )
351367 . all ( ) ;
352368
353369 if ( partBatch . length === 0 ) break ;
354- partCursor = partBatch [ partBatch . length - 1 ] . id ;
370+ partCursor = ingestOrderCursor ( partBatch [ partBatch . length - 1 ] ) ;
355371
356372 for ( const partRow of partBatch ) {
357373 if ( ! firstPart ) controller . enqueue ( encoder . encode ( ',' ) ) ;
@@ -404,7 +420,7 @@ export class SessionIngestDO extends DurableObject<Env> {
404420 } )
405421 . from ( ingestItems )
406422 . where ( ne ( ingestItems . item_type , 'session_diff' ) )
407- . orderBy ( ingestItems . id )
423+ . orderBy ( coalescedIngestedAt ( ) , ingestItems . id )
408424 . all ( ) ;
409425
410426 if ( rows . length === 0 ) {
0 commit comments