11import { DurableObject } from 'cloudflare:workers' ;
2- import { eq , ne , gt , gte , lt , and , inArray , isNotNull } from 'drizzle-orm' ;
2+ import { eq , ne , gt , gte , lt , and , or , inArray , isNull , isNotNull } from 'drizzle-orm' ;
33import { drizzle , type DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite' ;
44import { migrate } from 'drizzle-orm/durable-sqlite/migrator' ;
55
@@ -86,6 +86,26 @@ const INGEST_META_EXTRACTORS: Array<{
8686
8787type Changes = Array < { name : ExtractableMetaKey ; value : string | null } > ;
8888
89+ export type IngestOrderCursor = { ingestedAt : number | null ; id : number } ;
90+
91+ export function afterIngestOrderCursor ( cursor : IngestOrderCursor ) {
92+ if ( cursor . ingestedAt === null ) {
93+ return or (
94+ and ( isNull ( ingestItems . ingested_at ) , gt ( ingestItems . id , cursor . id ) ) ,
95+ isNotNull ( ingestItems . ingested_at )
96+ ) ;
97+ }
98+
99+ return or (
100+ gt ( ingestItems . ingested_at , cursor . ingestedAt ) ,
101+ and ( eq ( ingestItems . ingested_at , cursor . ingestedAt ) , gt ( ingestItems . id , cursor . id ) )
102+ ) ;
103+ }
104+
105+ export function ingestOrderCursor ( row : { ingested_at : number | null ; id : number } ) : IngestOrderCursor {
106+ return { ingestedAt : row . ingested_at , id : row . id } ;
107+ }
108+
89109export class SessionIngestDO extends DurableObject < Env > {
90110 private db : DrizzleSqliteDODatabase ;
91111
@@ -295,25 +315,31 @@ export class SessionIngestDO extends DurableObject<Env> {
295315 // --- messages ---
296316 const CURSOR_BATCH = 10 ;
297317 controller . enqueue ( encoder . encode ( ',"messages":[' ) ) ;
298- let msgCursor = 0 ;
318+ let msgCursor : IngestOrderCursor | undefined ;
299319 let firstMsg = true ;
300320
301321 while ( true ) {
302322 const msgBatch = db
303323 . select ( {
304324 id : ingestItems . id ,
325+ ingested_at : ingestItems . ingested_at ,
305326 item_id : ingestItems . item_id ,
306327 item_data : ingestItems . item_data ,
307328 item_data_r2_key : ingestItems . item_data_r2_key ,
308329 } )
309330 . from ( ingestItems )
310- . where ( and ( eq ( ingestItems . item_type , 'message' ) , gt ( ingestItems . id , msgCursor ) ) )
311- . orderBy ( ingestItems . id )
331+ . where (
332+ and (
333+ eq ( ingestItems . item_type , 'message' ) ,
334+ msgCursor ? afterIngestOrderCursor ( msgCursor ) : undefined
335+ )
336+ )
337+ . orderBy ( ingestItems . ingested_at , ingestItems . id )
312338 . limit ( CURSOR_BATCH )
313339 . all ( ) ;
314340
315341 if ( msgBatch . length === 0 ) break ;
316- msgCursor = msgBatch [ msgBatch . length - 1 ] . id ;
342+ msgCursor = ingestOrderCursor ( msgBatch [ msgBatch . length - 1 ] ) ;
317343
318344 for ( const msgRow of msgBatch ) {
319345 if ( ! firstMsg ) controller . enqueue ( encoder . encode ( ',' ) ) ;
@@ -327,13 +353,14 @@ export class SessionIngestDO extends DurableObject<Env> {
327353 const msgId = msgRow . item_id . slice ( 'message/' . length ) ;
328354 const partRange = getPartItemIdentityRange ( msgId ) ;
329355 controller . enqueue ( encoder . encode ( ',"parts":[' ) ) ;
330- let partCursor = 0 ;
356+ let partCursor : IngestOrderCursor | undefined ;
331357 let firstPart = true ;
332358
333359 while ( true ) {
334360 const partBatch = db
335361 . select ( {
336362 id : ingestItems . id ,
363+ ingested_at : ingestItems . ingested_at ,
337364 item_data : ingestItems . item_data ,
338365 item_data_r2_key : ingestItems . item_data_r2_key ,
339366 } )
@@ -343,15 +370,15 @@ export class SessionIngestDO extends DurableObject<Env> {
343370 eq ( ingestItems . item_type , 'part' ) ,
344371 gte ( ingestItems . item_id , partRange . start ) ,
345372 lt ( ingestItems . item_id , partRange . end ) ,
346- gt ( ingestItems . id , partCursor )
373+ partCursor ? afterIngestOrderCursor ( partCursor ) : undefined
347374 )
348375 )
349- . orderBy ( ingestItems . id )
376+ . orderBy ( ingestItems . ingested_at , ingestItems . id )
350377 . limit ( CURSOR_BATCH )
351378 . all ( ) ;
352379
353380 if ( partBatch . length === 0 ) break ;
354- partCursor = partBatch [ partBatch . length - 1 ] . id ;
381+ partCursor = ingestOrderCursor ( partBatch [ partBatch . length - 1 ] ) ;
355382
356383 for ( const partRow of partBatch ) {
357384 if ( ! firstPart ) controller . enqueue ( encoder . encode ( ',' ) ) ;
@@ -404,7 +431,7 @@ export class SessionIngestDO extends DurableObject<Env> {
404431 } )
405432 . from ( ingestItems )
406433 . where ( ne ( ingestItems . item_type , 'session_diff' ) )
407- . orderBy ( ingestItems . id )
434+ . orderBy ( ingestItems . ingested_at , ingestItems . id )
408435 . all ( ) ;
409436
410437 if ( rows . length === 0 ) {
0 commit comments