Add rollout and autoasigments metric (#2344)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-04-08 15:37:57 +03:00
committed by GitHub
parent 65c103c435
commit ee26dff6f9
4 changed files with 96 additions and 39 deletions

View File

@@ -10,8 +10,11 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ContextAware; import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.RolloutExecutor; 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.RolloutManagement;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration;
import org.springframework.integration.support.locks.LockRegistry; import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@@ -34,6 +38,7 @@ public class JpaRolloutHandler implements RolloutHandler {
private final LockRegistry lockRegistry; private final LockRegistry lockRegistry;
private final PlatformTransactionManager txManager; private final PlatformTransactionManager txManager;
private final ContextAware contextAware; private final ContextAware contextAware;
private final Optional<MeterRegistry> meterRegistry;
/** /**
* Constructor * Constructor
@@ -47,13 +52,14 @@ public class JpaRolloutHandler implements RolloutHandler {
public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
final PlatformTransactionManager txManager, final PlatformTransactionManager txManager,
final ContextAware contextAware) { final ContextAware contextAware, final Optional<MeterRegistry> meterRegistry) {
this.tenantAware = tenantAware; this.tenantAware = tenantAware;
this.rolloutManagement = rolloutManagement; this.rolloutManagement = rolloutManagement;
this.rolloutExecutor = rolloutExecutor; this.rolloutExecutor = rolloutExecutor;
this.lockRegistry = lockRegistry; this.lockRegistry = lockRegistry;
this.txManager = txManager; this.txManager = txManager;
this.contextAware = contextAware; this.contextAware = contextAware;
this.meterRegistry = meterRegistry;
} }
@Override @Override
@@ -74,6 +80,8 @@ public class JpaRolloutHandler implements RolloutHandler {
try { try {
log.debug("Trigger handling {} rollouts.", rollouts.size()); log.debug("Trigger handling {} rollouts.", rollouts.size());
final long startNano = System.nanoTime();
rollouts.forEach(rolloutId -> { rollouts.forEach(rolloutId -> {
try { try {
handleRolloutInNewTransaction(rolloutId, handlerId); handleRolloutInNewTransaction(rolloutId, handlerId);
@@ -81,6 +89,10 @@ public class JpaRolloutHandler implements RolloutHandler {
log.error("Failed to process rollout with id {}", rolloutId, throwable); 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."); log.debug("Finished handling of the rollouts.");
} finally { } finally {
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
@@ -94,29 +106,37 @@ public class JpaRolloutHandler implements RolloutHandler {
return tenant + "-rollout"; return tenant + "-rollout";
} }
// run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant // run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant the rollout is made for
// the rollout is made for
private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) { private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) {
final long startNano = System.nanoTime();
DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> { DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> {
rolloutManagement.get(rolloutId).ifPresentOrElse( rolloutManagement.get(rolloutId).ifPresentOrElse(
rollout -> rollout ->
// auditor is retrieved and set on transaction commit // auditor is retrieved and set on transaction commit if not overridden, the system user will be the auditor
// if not overridden, the system user will be the auditor rollout.getAccessControlContext().ifPresentOrElse(
rollout.getAccessControlContext().ifPresentOrElse( context -> // has stored context - executes it with it
context -> // has stored context - executes it with it contextAware.runInContext(
contextAware.runInContext( context,
context, () -> rolloutExecutor.execute(rollout)),
() -> rolloutExecutor.execute(rollout)), () -> // has no stored context - executes it in the tenant & user scope
() -> // has no stored context - executes it in the tenant & user scope contextAware.runAsTenantAsUser(
contextAware.runAsTenantAsUser( contextAware.getCurrentTenant(),
contextAware.getCurrentTenant(), rollout.getCreatedBy(), () -> {
rollout.getCreatedBy(), () -> { rolloutExecutor.execute(rollout);
rolloutExecutor.execute(rollout); return null;
return null; })),
})),
() -> log.error("Could not retrieve rollout with id {}. Will not continue with execution.", () -> log.error("Could not retrieve rollout with id {}. Will not continue with execution.",
rolloutId)); rolloutId));
return 0L; 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));
} }
} }

View File

@@ -18,6 +18,7 @@ import javax.sql.DataSource;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.validation.Validation; import jakarta.validation.Validation;
import io.micrometer.core.instrument.MeterRegistry;
import org.eclipse.hawkbit.ContextAware; import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.cache.TenancyCacheManager; import org.eclipse.hawkbit.cache.TenancyCacheManager;
@@ -739,8 +740,8 @@ public class RepositoryApplicationConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
final PlatformTransactionManager txManager, final ContextAware contextAware) { final PlatformTransactionManager txManager, final ContextAware contextAware, final Optional<MeterRegistry> meterRegistry) {
return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware); return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware, meterRegistry);
} }
@Bean @Bean
@@ -963,8 +964,8 @@ public class RepositoryApplicationConfiguration {
@ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true)
AutoAssignScheduler autoAssignScheduler(final SystemManagement systemManagement, AutoAssignScheduler autoAssignScheduler(final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor, final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor,
final LockRegistry lockRegistry) { final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignExecutor, lockRegistry); return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignExecutor, lockRegistry, meterRegistry);
} }
/** /**
@@ -1014,9 +1015,10 @@ public class RepositoryApplicationConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
@Profile("!test") @Profile("!test")
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
RolloutScheduler rolloutScheduler(final SystemManagement systemManagement, RolloutScheduler rolloutScheduler(
final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext, @Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize) { final SystemManagement systemManagement, final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext,
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize); @Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize, final Optional<MeterRegistry> meterRegistry) {
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
} }
/** /**

View File

@@ -9,12 +9,16 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.autoassign; package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor; import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration;
import org.springframework.integration.support.locks.LockRegistry; import org.springframework.integration.support.locks.LockRegistry;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
@@ -30,6 +34,7 @@ public class AutoAssignScheduler {
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
private final AutoAssignExecutor autoAssignExecutor; private final AutoAssignExecutor autoAssignExecutor;
private final LockRegistry lockRegistry; private final LockRegistry lockRegistry;
private final Optional<MeterRegistry> meterRegistry;
/** /**
* Instantiates a new AutoAssignScheduler * Instantiates a new AutoAssignScheduler
@@ -41,11 +46,12 @@ public class AutoAssignScheduler {
*/ */
public AutoAssignScheduler(final SystemManagement systemManagement, public AutoAssignScheduler(final SystemManagement systemManagement,
final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor, final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor,
final LockRegistry lockRegistry) { final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.autoAssignExecutor = autoAssignExecutor; this.autoAssignExecutor = autoAssignExecutor;
this.lockRegistry = lockRegistry; this.lockRegistry = lockRegistry;
this.meterRegistry = meterRegistry;
} }
/** /**
@@ -61,22 +67,33 @@ public class AutoAssignScheduler {
@SuppressWarnings("squid:S3516") @SuppressWarnings("squid:S3516")
private Object executeAutoAssign() { private Object executeAutoAssign() {
// workaround eclipselink that is currently not possible to // workaround eclipselink that is currently not possible to execute a query without multitenancy if MultiTenant
// 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
// annotation is used. // check for each tenant separately.
// 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"); final Lock lock = lockRegistry.obtain("autoassign");
if (!lock.tryLock()) { if (!lock.tryLock()) {
return null; return null;
} }
final long startNano = System.nanoTime();
try { try {
log.debug("Auto assign scheduled execution has acquired lock and started for each tenant."); 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 { } finally {
lock.unlock(); 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."); log.debug("Auto assign scheduled execution has released lock and finished.");
} }

View File

@@ -9,10 +9,15 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.rollout; 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 lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutHandler;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantMetricsConfiguration;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -29,14 +34,16 @@ public class RolloutScheduler {
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
private final RolloutHandler rolloutHandler; private final RolloutHandler rolloutHandler;
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
private final Optional<MeterRegistry> meterRegistry;
private final ThreadPoolTaskExecutor rolloutTaskExecutor; private final ThreadPoolTaskExecutor rolloutTaskExecutor;
public RolloutScheduler( public RolloutScheduler(
final RolloutHandler rolloutHandler, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final RolloutHandler rolloutHandler, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext,
final int threadPoolSize) { final int threadPoolSize, final Optional<MeterRegistry> meterRegistry) {
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.rolloutHandler = rolloutHandler; this.rolloutHandler = rolloutHandler;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.meterRegistry = meterRegistry;
rolloutTaskExecutor = threadPoolTaskExecutor(threadPoolSize); rolloutTaskExecutor = threadPoolTaskExecutor(threadPoolSize);
} }
@@ -48,6 +55,8 @@ public class RolloutScheduler {
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER) @Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void runningRolloutScheduler() { public void runningRolloutScheduler() {
log.debug("rollout schedule checker has been triggered."); log.debug("rollout schedule checker has been triggered.");
final long startNano = System.nanoTime();
// run this code in system code privileged to have the necessary // run this code in system code privileged to have the necessary
// permission to query and create entities. // permission to query and create entities.
systemSecurityContext.runAsSystem(() -> { systemSecurityContext.runAsSystem(() -> {
@@ -56,8 +65,7 @@ public class RolloutScheduler {
// annotation is used. // annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for // iterate through all tenants and execute the rollout check for
// each tenant seperately. // each tenant separately.
systemManagement.forEachTenant(tenant -> { systemManagement.forEachTenant(tenant -> {
if (rolloutTaskExecutor == null) { if (rolloutTaskExecutor == null) {
handleAll(tenant); handleAll(tenant);
@@ -67,15 +75,27 @@ public class RolloutScheduler {
}); });
return null; 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) { private void handleAll(final String tenant) {
log.trace("Handling rollout for tenant: {}", tenant); log.trace("Handling rollout for tenant: {}", tenant);
final long startNano = System.nanoTime();
try { try {
rolloutHandler.handleAll(); rolloutHandler.handleAll();
} catch (Exception e) { } catch (Exception e) {
log.error("Error processing rollout for tenant {}", tenant, 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) { private void handleAllAsync(final String tenant) {
@@ -83,10 +103,9 @@ public class RolloutScheduler {
handleAll(tenant); handleAll(tenant);
return null; return null;
}, tenant)); }, tenant));
} }
private ThreadPoolTaskExecutor threadPoolTaskExecutor (final int threadPoolSize) { private ThreadPoolTaskExecutor threadPoolTaskExecutor(final int threadPoolSize) {
if (threadPoolSize <= 1) { if (threadPoolSize <= 1) {
return null; return null;
} }
@@ -101,5 +120,4 @@ public class RolloutScheduler {
executor.initialize(); executor.initialize();
return executor; return executor;
} }
} }