@@ -130,9 +130,14 @@ func (h *Handler) Handle(ctx context.Context, event *ExecuteSyncPlanEvent) error
130130 return fmt .Errorf ("sync plan %s has no app ID" , plan .ID )
131131 }
132132
133+ // postCommit holds a publish call that must run after the transaction commits.
134+ // By capturing it here and executing after RunWithNoValue returns, we ensure
135+ // the DB state is visible before the event fires, while still propagating errors.
136+ var postCommit func () error
137+
133138 // Execute within a transaction with an advisory lock to prevent parallel execution
134139 // of multiple plans for the same invoice (e.g., draft and issuing plans).
135- return transaction .RunWithNoValue (ctx , h .adapter , func (ctx context.Context ) error {
140+ if err := transaction .RunWithNoValue (ctx , h .adapter , func (ctx context.Context ) error {
136141 // Acquire advisory lock scoped to this invoice
137142 if err := h .lockFunc (ctx , event .Namespace , event .InvoiceID ); err != nil {
138143 if errors .Is (err , ErrSyncPlanLocked ) {
@@ -204,20 +209,14 @@ func (h *Handler) Handle(ctx context.Context, event *ExecuteSyncPlanEvent) error
204209 }
205210
206211 if ! result .Done {
207- transaction . OnCommit ( ctx , func (ctx context. Context ) {
208- if err := h .publisher .Publish (ctx , ExecuteSyncPlanEvent {
212+ postCommit = func () error {
213+ return h .publisher .Publish (ctx , ExecuteSyncPlanEvent {
209214 PlanID : event .PlanID ,
210215 InvoiceID : event .InvoiceID ,
211216 Namespace : event .Namespace ,
212217 CustomerID : event .CustomerID ,
213- }); err != nil {
214- h .logger .ErrorContext (ctx , "failed to publish next sync plan event" ,
215- "plan_id" , event .PlanID ,
216- "invoice_id" , event .InvoiceID ,
217- "error" , err ,
218- )
219- }
220- })
218+ })
219+ }
221220 return nil
222221 }
223222
@@ -232,12 +231,21 @@ func (h *Handler) Handle(ctx context.Context, event *ExecuteSyncPlanEvent) error
232231 return fmt .Errorf ("refreshing sync plan: %w" , err )
233232 }
234233
235- return h .handlePlanCompletion (ctx , event , plan )
236- })
234+ return h .handlePlanCompletion (ctx , event , plan , & postCommit )
235+ }); err != nil {
236+ return err
237+ }
238+
239+ if postCommit != nil {
240+ return postCommit ()
241+ }
242+
243+ return nil
237244}
238245
239246// handlePlanCompletion writes results back to the invoice and triggers advancement.
240- func (h * Handler ) handlePlanCompletion (ctx context.Context , event * ExecuteSyncPlanEvent , plan * SyncPlan ) error {
247+ // The postCommit parameter captures any publish that must happen after the transaction commits.
248+ func (h * Handler ) handlePlanCompletion (ctx context.Context , event * ExecuteSyncPlanEvent , plan * SyncPlan , postCommit * func () error ) error {
241249 invoiceID := billing.InvoiceID {
242250 Namespace : event .Namespace ,
243251 ID : event .InvoiceID ,
@@ -299,18 +307,13 @@ func (h *Handler) handlePlanCompletion(ctx context.Context, event *ExecuteSyncPl
299307 }
300308
301309 case SyncPlanPhaseDelete :
302- // For delete, we just need to advance the invoice
303- transaction . OnCommit ( ctx , func (ctx context. Context ) {
304- if err := h .publisher .Publish (ctx , billing.AdvanceStandardInvoiceEvent {
310+ // For delete, we just need to advance the invoice after the transaction commits.
311+ * postCommit = func () error {
312+ return h .publisher .Publish (ctx , billing.AdvanceStandardInvoiceEvent {
305313 Invoice : invoiceID ,
306314 CustomerID : event .CustomerID ,
307- }); err != nil {
308- h .logger .ErrorContext (ctx , "failed to publish advance event after delete sync" ,
309- "invoice_id" , invoiceID .ID ,
310- "error" , err ,
311- )
312- }
313- })
315+ })
316+ }
314317 return nil
315318
316319 default :
0 commit comments