Browse Source

added min aggregation interval to tenant profile

pull/14253/head
IrynaMatveieva 11 months ago
parent
commit
fa4e056fb2
  1. 8
      application/src/main/data/upgrade/basic/schema_update.sql
  2. 1
      application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
  3. 27
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
  4. 2
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java
  5. 4
      application/src/main/resources/thingsboard.yml
  6. 6
      application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java
  7. 1
      common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java
  8. 5
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfiguration.java
  9. 1
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/Watermark.java
  10. 2
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
  11. 17
      dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java

8
application/src/main/data/upgrade/basic/schema_update.sql

@ -46,6 +46,12 @@ SET profile_data = jsonb_set(
WHEN (profile_data -> 'configuration') ? 'minAllowedDeduplicationIntervalInSecForCF' WHEN (profile_data -> 'configuration') ? 'minAllowedDeduplicationIntervalInSecForCF'
THEN NULL THEN NULL
ELSE to_jsonb(3600) ELSE to_jsonb(3600)
END,
'minAggregationIntervalInSecForCF',
CASE
WHEN (profile_data -> 'configuration') ? 'minAggregationIntervalInSecForCF'
THEN NULL
ELSE to_jsonb(60)
END END
) )
), ),
@ -59,6 +65,8 @@ WHERE NOT (
(profile_data -> 'configuration') ? 'maxRelatedEntitiesToReturnPerCfArgument' (profile_data -> 'configuration') ? 'maxRelatedEntitiesToReturnPerCfArgument'
AND AND
(profile_data -> 'configuration') ? 'minAllowedDeduplicationIntervalInSecForCF' (profile_data -> 'configuration') ? 'minAllowedDeduplicationIntervalInSecForCF'
AND
(profile_data -> 'configuration') ? 'minAggregationIntervalInSecForCF'
); );
-- UPDATE TENANT PROFILE CONFIGURATION END -- UPDATE TENANT PROFILE CONFIGURATION END

1
application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java

@ -165,6 +165,7 @@ public class SystemInfoController extends BaseController {
systemParams.setMinAllowedScheduledUpdateIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedScheduledUpdateIntervalInSecForCF()); systemParams.setMinAllowedScheduledUpdateIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedScheduledUpdateIntervalInSecForCF());
systemParams.setMaxRelationLevelPerCfArgument(tenantProfileConfiguration.getMaxRelationLevelPerCfArgument()); systemParams.setMaxRelationLevelPerCfArgument(tenantProfileConfiguration.getMaxRelationLevelPerCfArgument());
systemParams.setMinAllowedDeduplicationIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedDeduplicationIntervalInSecForCF()); systemParams.setMinAllowedDeduplicationIntervalInSecForCF(tenantProfileConfiguration.getMinAllowedDeduplicationIntervalInSecForCF());
systemParams.setMinAggregationIntervalInSecForCF(tenantProfileConfiguration.getMinAggregationIntervalInSecForCF());
systemParams.setTrendzSettings(trendzSettingsService.findTrendzSettings(currentUser.getTenantId())); systemParams.setTrendzSettings(trendzSettingsService.findTrendzSettings(currentUser.getTenantId()));
} }
systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID)) systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID))

27
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java

@ -97,7 +97,7 @@ public class CalculatedFieldCtx implements Closeable {
private boolean useLatestTs; private boolean useLatestTs;
private boolean requiresScheduledReevaluation; private boolean requiresScheduledReevaluation;
private long lastReevaluationTs; private long aggCheckInterval;
private ActorSystemContext systemContext; private ActorSystemContext systemContext;
private TbelInvokeService tbelInvokeService; private TbelInvokeService tbelInvokeService;
@ -199,6 +199,7 @@ public class CalculatedFieldCtx implements Closeable {
if (calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration aggConfig) { if (calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration aggConfig) {
this.useLatestTs = aggConfig.isUseLatestTs(); this.useLatestTs = aggConfig.isUseLatestTs();
} }
this.aggCheckInterval = systemContext.getCfCheckInterval();
this.systemContext = systemContext; this.systemContext = systemContext;
this.tbelInvokeService = systemContext.getTbelInvokeService(); this.tbelInvokeService = systemContext.getTbelInvokeService();
this.relationService = systemContext.getRelationService(); this.relationService = systemContext.getRelationService();
@ -213,20 +214,13 @@ public class CalculatedFieldCtx implements Closeable {
public boolean isRequiresScheduledReevaluation() { public boolean isRequiresScheduledReevaluation() {
if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration entityAggregationConfig) { if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration entityAggregationConfig) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long cfCheckIntervalMillis = TimeUnit.SECONDS.toMillis(systemContext.getCfCheckInterval());
Watermark watermark = entityAggregationConfig.getWatermark(); Watermark watermark = entityAggregationConfig.getWatermark();
if (watermark != null) { if (watermark != null && watermark.getDuration() > 0) {
long checkIntervalMillis = TimeUnit.SECONDS.toMillis(watermark.getCheckInterval()); return true;
if (cfCheckIntervalMillis == checkIntervalMillis) {
return true;
}
if (now + cfCheckIntervalMillis >= lastReevaluationTs + checkIntervalMillis) {
lastReevaluationTs = now;
return true;
}
} }
long intervalEndTsMillis = entityAggregationConfig.getInterval().getCurrentIntervalEndTs(); long cfCheckIntervalMillis = TimeUnit.SECONDS.toMillis(systemContext.getCfCheckInterval());
if (now + cfCheckIntervalMillis >= intervalEndTsMillis) { long intervalEndTs = entityAggregationConfig.getInterval().getCurrentIntervalEndTs();
if (now + cfCheckIntervalMillis >= intervalEndTs) {
return true; return true;
} }
} }
@ -635,6 +629,13 @@ public class CalculatedFieldCtx implements Closeable {
&& (thisConfig.getDeduplicationIntervalInSec() != otherConfig.getDeduplicationIntervalInSec() || !thisConfig.getMetrics().equals(otherConfig.getMetrics()))) { && (thisConfig.getDeduplicationIntervalInSec() != otherConfig.getDeduplicationIntervalInSec() || !thisConfig.getMetrics().equals(otherConfig.getMetrics()))) {
return true; return true;
} }
if (calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration thisConfig
&& other.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration otherConfig) {
boolean metricsChanged = thisConfig.getMetrics().equals(otherConfig.getMetrics());
boolean intervalChanged = thisConfig.getInterval().equals(otherConfig.getInterval());
boolean watermarkChanged = thisConfig.getWatermark().equals(otherConfig.getWatermark());
return metricsChanged || intervalChanged || watermarkChanged;
}
return false; return false;
} }

2
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java

@ -104,7 +104,7 @@ public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldSt
intervalDuration = configuration.getInterval().getIntervalDurationMillis(); intervalDuration = configuration.getInterval().getIntervalDurationMillis();
Watermark watermark = configuration.getWatermark(); Watermark watermark = configuration.getWatermark();
watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration());
checkInterval = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getCheckInterval()); checkInterval = ctx.getAggCheckInterval();
interval = configuration.getInterval(); interval = configuration.getInterval();
metrics = configuration.getMetrics(); metrics = configuration.getMetrics();
} }

4
application/src/main/resources/thingsboard.yml

@ -529,8 +529,8 @@ actors:
configuration: "${ACTORS_CALCULATED_FIELD_DEBUG_MODE_RATE_LIMITS_PER_TENANT_CONFIGURATION:50000:3600}" configuration: "${ACTORS_CALCULATED_FIELD_DEBUG_MODE_RATE_LIMITS_PER_TENANT_CONFIGURATION:50000:3600}"
# Time in seconds to receive calculation result. # Time in seconds to receive calculation result.
calculation_timeout: "${ACTORS_CALCULATION_TIMEOUT_SEC:5}" calculation_timeout: "${ACTORS_CALCULATION_TIMEOUT_SEC:5}"
# Interval in seconds to re-evaluate calculated fields that have a time schedule. 2 minutes by default. # Interval in seconds to re-evaluate calculated fields that have a time schedule. 1 minute by default.
check_interval: "${ACTORS_CALCULATED_FIELDS_CHECK_INTERVAL_SEC:30}" check_interval: "${ACTORS_CALCULATED_FIELDS_CHECK_INTERVAL_SEC:60}"
debug: debug:
settings: settings:

6
application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java

@ -109,7 +109,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest
postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3));
long interval = customInterval.getIntervalDurationMillis(); long interval = customInterval.getIntervalDurationMillis();
Watermark watermark = new Watermark(60, 10); Watermark watermark = new Watermark(60);
CalculatedField totalConsumptionCF = createTotalConsumptionCF(device.getId(), customInterval, watermark); CalculatedField totalConsumptionCF = createTotalConsumptionCF(device.getId(), customInterval, watermark);
await().alias("create CF and perform aggregation after interval end") await().alias("create CF and perform aggregation after interval end")
@ -140,7 +140,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest
postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3));
long interval = customInterval.getIntervalDurationMillis(); long interval = customInterval.getIntervalDurationMillis();
Watermark watermark = new Watermark(60, 10); Watermark watermark = new Watermark(60);
CalculatedField totalConsumptionCF = createTotalConsumptionCF(device.getId(), customInterval, watermark); CalculatedField totalConsumptionCF = createTotalConsumptionCF(device.getId(), customInterval, watermark);
await().alias("create CF and perform aggregation after interval end") await().alias("create CF and perform aggregation after interval end")
@ -155,7 +155,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest
postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":300}}", tsInInterval_1)); postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":300}}", tsInInterval_1));
await().alias("create CF and perform aggregation after interval end") await().alias("create CF and perform aggregation after interval end")
.atMost(2 * watermark.getCheckInterval(), TimeUnit.SECONDS) .atMost(2 * 10, TimeUnit.SECONDS)
.pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
ObjectNode result = getLatestTelemetry(device.getId(), "consumptionPerMin"); ObjectNode result = getLatestTelemetry(device.getId(), "consumptionPerMin");

1
common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java

@ -41,5 +41,6 @@ public class SystemParams {
int minAllowedScheduledUpdateIntervalInSecForCF; int minAllowedScheduledUpdateIntervalInSecForCF;
int maxRelationLevelPerCfArgument; int maxRelationLevelPerCfArgument;
long minAllowedDeduplicationIntervalInSecForCF; long minAllowedDeduplicationIntervalInSecForCF;
long minAggregationIntervalInSecForCF;
TrendzSettings trendzSettings; TrendzSettings trendzSettings;
} }

5
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/EntityAggregationCalculatedFieldConfiguration.java

@ -20,6 +20,7 @@ import jakarta.validation.constraints.NotEmpty;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric;
@ -49,8 +50,8 @@ public class EntityAggregationCalculatedFieldConfiguration implements ArgumentsB
if (arguments.containsKey("ctx")) { if (arguments.containsKey("ctx")) {
throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used."); throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used.");
} }
if (arguments.values().stream().anyMatch(Argument::hasTsRollingArgument)) { if (arguments.values().stream().anyMatch(argument -> !ArgumentType.TS_LATEST.equals(argument.getRefEntityKey().getType()))) {
throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support TS_ROLLING arguments."); throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' support only TS_LATEST arguments.");
} }
} }

1
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/single/interval/Watermark.java

@ -25,6 +25,5 @@ import lombok.NoArgsConstructor;
public class Watermark { public class Watermark {
private long duration; private long duration;
private long checkInterval;
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java

@ -188,6 +188,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private long maxSingleValueArgumentSizeInKBytes = 2; private long maxSingleValueArgumentSizeInKBytes = 2;
@Schema(example = "3600") @Schema(example = "3600")
private long minAllowedDeduplicationIntervalInSecForCF = 3600; private long minAllowedDeduplicationIntervalInSecForCF = 3600;
@Schema(example = "60")
private long minAggregationIntervalInSecForCF = 60;
@Override @Override
public long getProfileThreshold(ApiUsageRecordKey key) { public long getProfileThreshold(ApiUsageRecordKey key) {

17
dao/src/main/java/org/thingsboard/server/dao/service/validator/CalculatedFieldDataValidator.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalcula
import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration;
import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.aggregation.RelatedEntitiesAggregationCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.cf.CalculatedFieldDao; import org.thingsboard.server.dao.cf.CalculatedFieldDao;
@ -30,6 +31,7 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component @Component
@ -48,6 +50,7 @@ public class CalculatedFieldDataValidator extends DataValidator<CalculatedField>
validateSchedulingConfiguration(tenantId, calculatedField); validateSchedulingConfiguration(tenantId, calculatedField);
validateRelationQuerySourceArguments(tenantId, calculatedField); validateRelationQuerySourceArguments(tenantId, calculatedField);
validateAggregationConfiguration(tenantId, calculatedField); validateAggregationConfiguration(tenantId, calculatedField);
validateEntityAggregationConfiguration(tenantId, calculatedField);
} }
@Override @Override
@ -123,6 +126,20 @@ public class CalculatedFieldDataValidator extends DataValidator<CalculatedField>
} }
} }
private void validateEntityAggregationConfiguration(TenantId tenantId, CalculatedField calculatedField) {
if (!(calculatedField.getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration aggConfiguration)) {
return;
}
long minAggregationIntervalInSec = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMinAggregationIntervalInSecForCF);
if (minAggregationIntervalInSec <= 0) {
return;
}
if (aggConfiguration.getInterval().getIntervalDurationMillis() > TimeUnit.SECONDS.toMillis(minAggregationIntervalInSec)) {
throw new IllegalArgumentException("Aggregation interval duration is less than configured " +
"minimum allowed aggregation interval in tenant profile: " + minAggregationIntervalInSec + " sec.");
}
}
private static void wrapAsDataValidation(Runnable validation) { private static void wrapAsDataValidation(Runnable validation) {
try { try {
validation.run(); validation.run();

Loading…
Cancel
Save