@@ -528,6 +528,29 @@ export async function applyStorageUsageDeltasInTx(
528528 return destinationUpdatedUsage
529529}
530530
531+ /** Locks the canonical workspace before existing-file mutations that later change its storage ledger. */
532+ export async function lockWorkspaceStorageForMutationInTx (
533+ tx : DbOrTx ,
534+ workspaceId : string
535+ ) : Promise < LockedWorkspaceStorage > {
536+ const [ workspacePayer ] = await tx
537+ . select ( {
538+ billedAccountUserId : workspace . billedAccountUserId ,
539+ organizationId : workspace . organizationId ,
540+ storageUsedBytes : workspace . storageUsedBytes ,
541+ } )
542+ . from ( workspace )
543+ . where ( eq ( workspace . id , workspaceId ) )
544+ . for ( 'no key update' )
545+ . limit ( 1 )
546+
547+ if ( ! workspacePayer ) {
548+ throw new Error ( `Workspace ${ workspaceId } not found for storage accounting` )
549+ }
550+
551+ return { id : workspaceId , ...workspacePayer }
552+ }
553+
531554/**
532555 * Mutates the durable workspace total and its current routed payer as one
533556 * transaction. The workspace row is the serialization point shared with payer
@@ -545,25 +568,9 @@ async function mutateWorkspaceStorageUsage(
545568 maximumUsage : number | undefined ,
546569 context : StorageBillingContext
547570) : Promise < WorkspaceStorageMutationResult > {
548- const [ workspacePayer ] = await tx
549- . select ( {
550- billedAccountUserId : workspace . billedAccountUserId ,
551- organizationId : workspace . organizationId ,
552- storageUsedBytes : workspace . storageUsedBytes ,
553- } )
554- . from ( workspace )
555- . where ( eq ( workspace . id , workspaceId ) )
556- . for ( 'no key update' )
557- . limit ( 1 )
558-
559- if ( ! workspacePayer ) {
560- throw new Error ( `Workspace ${ workspaceId } not found for storage accounting` )
561- }
571+ const workspacePayer = await lockWorkspaceStorageForMutationInTx ( tx , workspaceId )
562572
563- const billingEntity = assertWorkspaceStorageContext (
564- { id : workspaceId , ...workspacePayer } ,
565- context
566- )
573+ const billingEntity = assertWorkspaceStorageContext ( workspacePayer , context )
567574 const currentPayerUsage = await lockStorageUsageForMutation ( tx , billingEntity )
568575
569576 if ( mutation === 'decrement' && workspacePayer . storageUsedBytes < bytes ) {
0 commit comments