From fd50a147a23cde26bc6936434bb23c30454cce67 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 15 Apr 2026 22:32:05 +0200 Subject: [PATCH] fix: make tenant provisioning truly async via self-proxy @Async on provisionAsync() was bypassed because all call sites were internal (this.provisionAsync), skipping the Spring proxy. Inject self via @Lazy to route through the proxy so provisioning runs in a background thread and the API returns immediately. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cameleer/saas/vendor/VendorTenantService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantService.java b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantService.java index 409bc37..5140343 100644 --- a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantService.java +++ b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantService.java @@ -22,6 +22,7 @@ import net.siegeln.cameleer.saas.tenant.TenantStatus; import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -49,6 +50,7 @@ public class VendorTenantService { private final ProvisioningProperties provisioningProps; private final TenantDataCleanupService dataCleanupService; private final TenantDatabaseService tenantDatabaseService; + private final VendorTenantService self; public VendorTenantService(TenantService tenantService, TenantRepository tenantRepository, @@ -60,7 +62,8 @@ public class VendorTenantService { AuditService auditService, ProvisioningProperties provisioningProps, TenantDataCleanupService dataCleanupService, - TenantDatabaseService tenantDatabaseService) { + TenantDatabaseService tenantDatabaseService, + @Lazy VendorTenantService self) { this.tenantService = tenantService; this.tenantRepository = tenantRepository; this.licenseService = licenseService; @@ -72,6 +75,7 @@ public class VendorTenantService { this.provisioningProps = provisioningProps; this.dataCleanupService = dataCleanupService; this.tenantDatabaseService = tenantDatabaseService; + this.self = self; } @Transactional @@ -114,7 +118,7 @@ public class VendorTenantService { // 4. Provision server asynchronously (Docker containers, health check, config push) if (tenantProvisioner.isAvailable()) { - provisionAsync(tenant.getId(), tenant.getSlug(), tenant.getTier().name(), license.getToken(), actorId); + self.provisionAsync(tenant.getId(), tenant.getSlug(), tenant.getTier().name(), license.getToken(), actorId); } return tenant; @@ -251,7 +255,7 @@ public class VendorTenantService { tenantProvisioner.remove(tenant.getSlug()); var license = licenseService.getActiveLicense(tenantId).orElse(null); String token = license != null ? license.getToken() : ""; - provisionAsync(tenantId, tenant.getSlug(), tenant.getTier().name(), token, null); + self.provisionAsync(tenantId, tenant.getSlug(), tenant.getTier().name(), token, null); return; } throw e; @@ -268,7 +272,7 @@ public class VendorTenantService { // Re-provision with freshly pulled images var license = licenseService.getActiveLicense(tenantId).orElse(null); String token = license != null ? license.getToken() : ""; - provisionAsync(tenantId, tenant.getSlug(), tenant.getTier().name(), token, null); + self.provisionAsync(tenantId, tenant.getSlug(), tenant.getTier().name(), token, null); } @Transactional