diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java index ad46f1af1..70723ec6d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java @@ -10,8 +10,11 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; +import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.ContextAware; import org.eclipse.hawkbit.repository.RolloutExecutor; @@ -19,6 +22,7 @@ import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.tenancy.TenantAware; +import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration; import org.springframework.integration.support.locks.LockRegistry; import org.springframework.transaction.PlatformTransactionManager; @@ -34,6 +38,7 @@ public class JpaRolloutHandler implements RolloutHandler { private final LockRegistry lockRegistry; private final PlatformTransactionManager txManager; private final ContextAware contextAware; + private final Optional meterRegistry; /** * Constructor @@ -47,13 +52,14 @@ public class JpaRolloutHandler implements RolloutHandler { public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final PlatformTransactionManager txManager, - final ContextAware contextAware) { + final ContextAware contextAware, final Optional meterRegistry) { this.tenantAware = tenantAware; this.rolloutManagement = rolloutManagement; this.rolloutExecutor = rolloutExecutor; this.lockRegistry = lockRegistry; this.txManager = txManager; this.contextAware = contextAware; + this.meterRegistry = meterRegistry; } @Override @@ -74,6 +80,8 @@ public class JpaRolloutHandler implements RolloutHandler { try { log.debug("Trigger handling {} rollouts.", rollouts.size()); + + final long startNano = System.nanoTime(); rollouts.forEach(rolloutId -> { try { handleRolloutInNewTransaction(rolloutId, handlerId); @@ -81,6 +89,10 @@ public class JpaRolloutHandler implements RolloutHandler { log.error("Failed to process rollout with id {}", rolloutId, throwable); } }); + meterRegistry + .map(mReg -> mReg.timer("hawkbit.rollout.handler", TenantMetricsConfiguration.TENANT_TAG, tenantAware.getCurrentTenant())) + .ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS)); + log.debug("Finished handling of the rollouts."); } finally { if (log.isTraceEnabled()) { @@ -94,29 +106,37 @@ public class JpaRolloutHandler implements RolloutHandler { return tenant + "-rollout"; } - // run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant - // the rollout is made for + // run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant the rollout is made for private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) { + final long startNano = System.nanoTime(); + DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> { rolloutManagement.get(rolloutId).ifPresentOrElse( rollout -> - // auditor is retrieved and set on transaction commit - // if not overridden, the system user will be the auditor - rollout.getAccessControlContext().ifPresentOrElse( - context -> // has stored context - executes it with it - contextAware.runInContext( - context, - () -> rolloutExecutor.execute(rollout)), - () -> // has no stored context - executes it in the tenant & user scope - contextAware.runAsTenantAsUser( - contextAware.getCurrentTenant(), - rollout.getCreatedBy(), () -> { - rolloutExecutor.execute(rollout); - return null; - })), + // auditor is retrieved and set on transaction commit if not overridden, the system user will be the auditor + rollout.getAccessControlContext().ifPresentOrElse( + context -> // has stored context - executes it with it + contextAware.runInContext( + context, + () -> rolloutExecutor.execute(rollout)), + () -> // has no stored context - executes it in the tenant & user scope + contextAware.runAsTenantAsUser( + contextAware.getCurrentTenant(), + rollout.getCreatedBy(), () -> { + rolloutExecutor.execute(rollout); + return null; + })), () -> log.error("Could not retrieve rollout with id {}. Will not continue with execution.", rolloutId)); return 0L; }); + + meterRegistry + .map(mReg -> mReg.timer( + "hawkbit.rollout.handler", + TenantMetricsConfiguration.TENANT_TAG, tenantAware.getCurrentTenant(), + "rollout", String.valueOf(rolloutId))) + .ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS)); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index 27c84da90..7ee7b9cdc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -18,6 +18,7 @@ import javax.sql.DataSource; import jakarta.persistence.EntityManager; import jakarta.validation.Validation; +import io.micrometer.core.instrument.MeterRegistry; import org.eclipse.hawkbit.ContextAware; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.cache.TenancyCacheManager; @@ -739,8 +740,8 @@ public class RepositoryApplicationConfiguration { @ConditionalOnMissingBean RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, - final PlatformTransactionManager txManager, final ContextAware contextAware) { - return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware); + final PlatformTransactionManager txManager, final ContextAware contextAware, final Optional meterRegistry) { + return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware, meterRegistry); } @Bean @@ -963,8 +964,8 @@ public class RepositoryApplicationConfiguration { @ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true) AutoAssignScheduler autoAssignScheduler(final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor, - final LockRegistry lockRegistry) { - return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignExecutor, lockRegistry); + final LockRegistry lockRegistry, final Optional meterRegistry) { + return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignExecutor, lockRegistry, meterRegistry); } /** @@ -1014,9 +1015,10 @@ public class RepositoryApplicationConfiguration { @ConditionalOnMissingBean @Profile("!test") @ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true) - RolloutScheduler rolloutScheduler(final SystemManagement systemManagement, - final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext, @Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize) { - return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize); + RolloutScheduler rolloutScheduler( + final SystemManagement systemManagement, final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext, + @Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize, final Optional meterRegistry) { + return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java index 66f427708..35a869773 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignScheduler.java @@ -9,12 +9,16 @@ */ package org.eclipse.hawkbit.repository.jpa.autoassign; +import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; +import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor; import org.eclipse.hawkbit.security.SystemSecurityContext; +import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration; import org.springframework.integration.support.locks.LockRegistry; import org.springframework.scheduling.annotation.Scheduled; @@ -30,6 +34,7 @@ public class AutoAssignScheduler { private final SystemSecurityContext systemSecurityContext; private final AutoAssignExecutor autoAssignExecutor; private final LockRegistry lockRegistry; + private final Optional meterRegistry; /** * Instantiates a new AutoAssignScheduler @@ -41,11 +46,12 @@ public class AutoAssignScheduler { */ public AutoAssignScheduler(final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor, - final LockRegistry lockRegistry) { + final LockRegistry lockRegistry, final Optional meterRegistry) { this.systemManagement = systemManagement; this.systemSecurityContext = systemSecurityContext; this.autoAssignExecutor = autoAssignExecutor; this.lockRegistry = lockRegistry; + this.meterRegistry = meterRegistry; } /** @@ -61,22 +67,33 @@ public class AutoAssignScheduler { @SuppressWarnings("squid:S3516") private Object executeAutoAssign() { - // workaround eclipselink that is currently not possible to - // execute a query without multitenancy if MultiTenant - // annotation is used. - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So - // iterate through all tenants and execute the rollout check for - // each tenant separately. + // workaround eclipselink that is currently not possible to execute a query without multitenancy if MultiTenant + // annotation is used - https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So iterate through all tenants and execute the rollout + // check for each tenant separately. final Lock lock = lockRegistry.obtain("autoassign"); if (!lock.tryLock()) { return null; } + final long startNano = System.nanoTime(); try { log.debug("Auto assign scheduled execution has acquired lock and started for each tenant."); - systemManagement.forEachTenant(tenant -> autoAssignExecutor.checkAllTargets()); + systemManagement.forEachTenant(tenant -> { + final long startNanoT = System.nanoTime(); + + autoAssignExecutor.checkAllTargets(); + + meterRegistry + .map(mReg -> mReg.timer( + "hawkbit.autoassign.executor", + TenantMetricsConfiguration.TENANT_TAG, tenant)) + .ifPresent(timer -> timer.record(System.nanoTime() - startNanoT, TimeUnit.NANOSECONDS)); + }); } finally { lock.unlock(); + meterRegistry + .map(mReg -> mReg.timer("hawkbit.autoassign.executor.all")) + .ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS)); log.debug("Auto assign scheduled execution has released lock and finished."); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java index ad356c244..f9f7af268 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java @@ -9,10 +9,15 @@ */ package org.eclipse.hawkbit.repository.jpa.rollout; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.security.SystemSecurityContext; +import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -29,14 +34,16 @@ public class RolloutScheduler { private final SystemManagement systemManagement; private final RolloutHandler rolloutHandler; private final SystemSecurityContext systemSecurityContext; + private final Optional meterRegistry; private final ThreadPoolTaskExecutor rolloutTaskExecutor; public RolloutScheduler( - final RolloutHandler rolloutHandler, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, - final int threadPoolSize) { + final RolloutHandler rolloutHandler, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, + final int threadPoolSize, final Optional meterRegistry) { this.systemManagement = systemManagement; this.rolloutHandler = rolloutHandler; this.systemSecurityContext = systemSecurityContext; + this.meterRegistry = meterRegistry; rolloutTaskExecutor = threadPoolTaskExecutor(threadPoolSize); } @@ -48,6 +55,8 @@ public class RolloutScheduler { @Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER) public void runningRolloutScheduler() { log.debug("rollout schedule checker has been triggered."); + final long startNano = System.nanoTime(); + // run this code in system code privileged to have the necessary // permission to query and create entities. systemSecurityContext.runAsSystem(() -> { @@ -56,8 +65,7 @@ public class RolloutScheduler { // annotation is used. // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So // iterate through all tenants and execute the rollout check for - // each tenant seperately. - + // each tenant separately. systemManagement.forEachTenant(tenant -> { if (rolloutTaskExecutor == null) { handleAll(tenant); @@ -67,15 +75,27 @@ public class RolloutScheduler { }); return null; }); + + meterRegistry + .map(mReg -> mReg.timer("hawkbit.rollout.scheduler.all")) + .ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS)); } private void handleAll(final String tenant) { log.trace("Handling rollout for tenant: {}", tenant); + final long startNano = System.nanoTime(); + try { rolloutHandler.handleAll(); } catch (Exception e) { log.error("Error processing rollout for tenant {}", tenant, e); } + + meterRegistry + .map(mReg -> mReg.timer( + "hawkbit.rollout.scheduler", + TenantMetricsConfiguration.TENANT_TAG, tenant)) + .ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS)); } private void handleAllAsync(final String tenant) { @@ -83,10 +103,9 @@ public class RolloutScheduler { handleAll(tenant); return null; }, tenant)); - } - private ThreadPoolTaskExecutor threadPoolTaskExecutor (final int threadPoolSize) { + private ThreadPoolTaskExecutor threadPoolTaskExecutor(final int threadPoolSize) { if (threadPoolSize <= 1) { return null; } @@ -101,5 +120,4 @@ public class RolloutScheduler { executor.initialize(); return executor; } - } \ No newline at end of file