1515import org .springframework .beans .factory .annotation .Qualifier ;
1616import org .springframework .core .task .AsyncTaskExecutor ;
1717import org .springframework .stereotype .Component ;
18+ import org .springframework .transaction .support .TransactionSynchronization ;
19+ import org .springframework .transaction .support .TransactionSynchronizationManager ;
1820
1921@ Component
2022public class WorkspaceProvisioningAdapter implements ProvisioningListener {
@@ -238,36 +240,24 @@ public void onInstallationActivated(Long installationId) {
238240 // Update status first
239241 workspaceInstallationService .updateWorkspaceStatus (installationId , Workspace .WorkspaceStatus .ACTIVE );
240242
241- // Trigger initial sync asynchronously before starting NATS consumer
242- // This ensures all entities exist before NATS starts processing webhook events
243+ // Sync after the outer transaction commits so RepositoryToMonitor rows are visible.
243244 workspaceRepository
244245 .findByInstallationId (installationId )
245246 .ifPresent (workspace -> {
246- log .info (
247- "Triggering initial sync for activated installation: installationId={}, workspaceId={}" ,
248- installationId ,
249- workspace .getId ()
250- );
251-
252- // Run sync asynchronously to avoid blocking webhook processing
253247 Long workspaceId = workspace .getId ();
254- monitoringExecutor .execute (() -> {
255- try {
256- getGitHubDataSyncService ().syncAllRepositories (workspaceId );
257- log .info (
258- "Completed initial sync for activated installation: installationId={}, workspaceId={}" ,
259- installationId ,
260- workspaceId
261- );
262- } catch (Exception e ) {
263- log .error (
264- "Failed initial sync for activated installation: installationId={}, workspaceId={}" ,
265- installationId ,
266- workspaceId ,
267- e
268- );
269- }
270- });
248+
249+ if (TransactionSynchronizationManager .isSynchronizationActive ()) {
250+ TransactionSynchronizationManager .registerSynchronization (
251+ new TransactionSynchronization () {
252+ @ Override
253+ public void afterCommit () {
254+ triggerInitialSync (installationId , workspaceId );
255+ }
256+ }
257+ );
258+ } else {
259+ triggerInitialSync (installationId , workspaceId );
260+ }
271261 });
272262
273263 // Start NATS consumer to resume webhook processing
@@ -287,6 +277,32 @@ public void onRepositorySelectionChanged(Long installationId, String selection)
287277 log .debug ("Updated repository selection: installationId={}, selection={}" , installationId , repoSelection );
288278 }
289279
280+ private void triggerInitialSync (Long installationId , Long workspaceId ) {
281+ log .info (
282+ "Starting initial sync for activated installation: installationId={}, workspaceId={}" ,
283+ installationId ,
284+ workspaceId
285+ );
286+
287+ monitoringExecutor .execute (() -> {
288+ try {
289+ getGitHubDataSyncService ().syncAllRepositories (workspaceId );
290+ log .info (
291+ "Completed initial sync for activated installation: installationId={}, workspaceId={}" ,
292+ installationId ,
293+ workspaceId
294+ );
295+ } catch (Exception e ) {
296+ log .error (
297+ "Failed initial sync for activated installation: installationId={}, workspaceId={}" ,
298+ installationId ,
299+ workspaceId ,
300+ e
301+ );
302+ }
303+ });
304+ }
305+
290306 private RepositorySelection parseRepositorySelection (String selection ) {
291307 if ("all" .equalsIgnoreCase (selection )) {
292308 return RepositorySelection .ALL ;
0 commit comments