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;
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> 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> 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));
}
}

View File

@@ -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> 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> 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> meterRegistry) {
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
}
/**

View File

@@ -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> 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> 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.");
}

View File

@@ -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> 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> 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;
}
}