@@ -177,6 +177,15 @@ export async function nextImportStartPosition(tableId: string): Promise<number>
177177 return maxPos + 1
178178}
179179
180+ /**
181+ * Append anchor `order_key` for an import — `max(order_key)`, or null when empty. Read once,
182+ * unlocked, before streaming (the import worker is the table's sole writer); each batch threads
183+ * the previous batch's last key forward so no per-batch max scan is needed.
184+ */
185+ export async function nextImportStartOrderKey ( tableId : string ) : Promise < string | null > {
186+ return maxOrderKey ( db , tableId )
187+ }
188+
180189const TIMEOUT_CAP_MS = 10 * 60_000
181190
182191/**
@@ -1030,6 +1039,15 @@ async function nextRowPosition(trx: DbTransaction, tableId: string): Promise<num
10301039 return maxPos + 1
10311040}
10321041
1042+ /** Largest `order_key` for a table, or `null` when empty — the append anchor for new keys. */
1043+ async function maxOrderKey ( executor : DbOrTx , tableId : string ) : Promise < string | null > {
1044+ const [ { maxKey } ] = await executor
1045+ . select ( { maxKey : sql < string | null > `max(${ userTableRows . orderKey } )` } )
1046+ . from ( userTableRows )
1047+ . where ( eq ( userTableRows . tableId , tableId ) )
1048+ return maxKey ?? null
1049+ }
1050+
10331051/** Shifts every row at or after `position` up by one (`position + 1`). */
10341052async function shiftRowsUpFrom ( trx : DbTransaction , tableId : string , position : number ) {
10351053 await trx
@@ -1208,11 +1226,7 @@ async function resolveInsertOrderKey(
12081226 return r ?. orderKey ?? null
12091227 }
12101228 if ( requestedPosition === undefined ) {
1211- const [ { maxKey } ] = await trx
1212- . select ( { maxKey : sql < string | null > `max(${ userTableRows . orderKey } )` } )
1213- . from ( userTableRows )
1214- . where ( eq ( userTableRows . tableId , tableId ) )
1215- return keyBetween ( maxKey ?? null , null )
1229+ return keyBetween ( await maxOrderKey ( trx , tableId ) , null )
12161230 }
12171231 const lo = await orderKeyAtSlot ( requestedPosition - 1 )
12181232 const hi = await orderKeyAtSlot ( requestedPosition )
@@ -1301,11 +1315,7 @@ async function resolveBatchInsertOrderKeys(
13011315 positions ?: number [ ]
13021316) : Promise < string [ ] > {
13031317 if ( ! positions || positions . length === 0 ) {
1304- const [ { maxKey } ] = await trx
1305- . select ( { maxKey : sql < string | null > `max(${ userTableRows . orderKey } )` } )
1306- . from ( userTableRows )
1307- . where ( eq ( userTableRows . tableId , tableId ) )
1308- return nKeysBetween ( maxKey ?? null , null , count )
1318+ return nKeysBetween ( await maxOrderKey ( trx , tableId ) , null , count )
13091319 }
13101320 const keys : string [ ] = [ ]
13111321 for ( const pos of positions ) {
@@ -1707,6 +1717,8 @@ export interface BulkImportBatch {
17071717 rows : RowData [ ]
17081718 /** Position of the first row in this batch; rows get contiguous positions from here. */
17091719 startPosition : number
1720+ /** Previous batch's last `order_key` (the append anchor); null for the first batch / empty table. */
1721+ afterOrderKey ?: string | null
17101722}
17111723
17121724/**
@@ -1727,7 +1739,7 @@ export async function bulkInsertImportBatch(
17271739 data : BulkImportBatch ,
17281740 table : TableDefinition ,
17291741 requestId : string
1730- ) : Promise < number > {
1742+ ) : Promise < { inserted : number ; lastOrderKey : string | null } > {
17311743 for ( let i = 0 ; i < data . rows . length ; i ++ ) {
17321744 const sizeValidation = validateRowSize ( data . rows [ i ] )
17331745 if ( ! sizeValidation . valid ) {
@@ -1755,12 +1767,9 @@ export async function bulkInsertImportBatch(
17551767 }
17561768
17571769 const now = new Date ( )
1758- // Import worker is the table's sole writer; append keys after the current max.
1759- const [ { maxKey } ] = await db
1760- . select ( { maxKey : sql < string | null > `max(${ userTableRows . orderKey } )` } )
1761- . from ( userTableRows )
1762- . where ( eq ( userTableRows . tableId , data . tableId ) )
1763- const orderKeys = nKeysBetween ( maxKey ?? null , null , data . rows . length )
1770+ // Import worker is the table's sole writer; append keys after the anchor the caller threads
1771+ // from the previous batch's last key — no per-batch max(order_key) scan over a growing table.
1772+ const orderKeys = nKeysBetween ( data . afterOrderKey ?? null , null , data . rows . length )
17641773 const rowsToInsert = data . rows . map ( ( rowData , i ) => ( {
17651774 id : `row_${ generateId ( ) . replace ( / - / g, '' ) } ` ,
17661775 tableId : data . tableId ,
@@ -1775,7 +1784,10 @@ export async function bulkInsertImportBatch(
17751784
17761785 await db . insert ( userTableRows ) . values ( rowsToInsert )
17771786 logger . info ( `[${ requestId } ] Bulk-imported ${ rowsToInsert . length } rows into table ${ data . tableId } ` )
1778- return rowsToInsert . length
1787+ return {
1788+ inserted : rowsToInsert . length ,
1789+ lastOrderKey : orderKeys [ orderKeys . length - 1 ] ?? data . afterOrderKey ?? null ,
1790+ }
17791791}
17801792
17811793/** Deletes every row of a table (set-based; the statement-level trigger zeroes `row_count`). */
0 commit comments