From 6f815c76a64ffe039247dd01361f618c0c8471f6 Mon Sep 17 00:00:00 2001 From: dshvaika Date: Tue, 13 Jan 2026 19:40:47 +0200 Subject: [PATCH] Handling minAllowedScheduledUpdateIntervalInSecForCF and minAllowedDeduplicationIntervalInSecForCF in running CFs + updated default values in profile and upgrade --- .../main/data/upgrade/basic/schema_update.sql | 4 +-- .../controller/TenantProfileController.java | 4 +-- .../cf/ctx/state/CalculatedFieldCtx.java | 10 ++++--- .../service/cf/ctx/state/HasEntityLimit.java | 29 +++++++++++++++++++ ...titiesAggregationCalculatedFieldState.java | 8 +++-- .../RelatedEntitiesArgumentEntry.java | 18 ++++-------- .../propagation/PropagationArgumentEntry.java | 10 ++----- .../DefaultTenantProfileConfiguration.java | 8 ++--- ui-ngx/src/app/shared/models/tenant.model.ts | 4 +-- 9 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 1700b25dbb..66243461e2 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -21,8 +21,8 @@ SET profile_data = jsonb_set( profile_data, '{configuration}', jsonb_build_object( - 'minAllowedScheduledUpdateIntervalInSecForCF', 60, - 'maxRelationLevelPerCfArgument', 10, + 'minAllowedScheduledUpdateIntervalInSecForCF', 10, + 'maxRelationLevelPerCfArgument', 2, 'maxRelatedEntitiesToReturnPerCfArgument', 100, 'minAllowedDeduplicationIntervalInSecForCF', 10, 'minAllowedAggregationIntervalInSecForCF', 60, diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index ee73b3c1a7..c0f7b9eb40 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -161,8 +161,8 @@ public class TenantProfileController extends BaseController { " \"warnThreshold\": 0,\n" + " \"maxCalculatedFieldsPerEntity\": 5,\n" + " \"maxArgumentsPerCF\": 10,\n" + - " \"minAllowedScheduledUpdateIntervalInSecForCF\": 60,\n" + - " \"maxRelationLevelPerCfArgument\": 10,\n" + + " \"minAllowedScheduledUpdateIntervalInSecForCF\": 10,\n" + + " \"maxRelationLevelPerCfArgument\": 2,\n" + " \"maxRelatedEntitiesToReturnPerCfArgument\": 100,\n" + " \"maxDataPointsPerRollingArg\": 1000,\n" + " \"maxStateSizeInKBytes\": 32,\n" + diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index e5215eca28..84a0ab008f 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -129,6 +129,8 @@ public class CalculatedFieldCtx implements Closeable { private long cfCheckReevaluationIntervalMillis; private long alarmReevaluationIntervalMillis; private long maxRelatedEntitiesPerCfArgument; + private long minScheduledUpdateIntervalMillis; + private long minDeduplicationIntervalMillis; private Argument propagationArgument; private boolean applyExpressionForResolvedArguments; @@ -312,6 +314,8 @@ public class CalculatedFieldCtx implements Closeable { this.cfCheckReevaluationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getCfReevaluationCheckInterval()); this.alarmReevaluationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getAlarmsReevaluationInterval()); this.maxRelatedEntitiesPerCfArgument = config.getMaxRelatedEntitiesToReturnPerCfArgument(); + this.minScheduledUpdateIntervalMillis = TimeUnit.SECONDS.toMillis(config.getMinAllowedScheduledUpdateIntervalInSecForCF()); + this.minDeduplicationIntervalMillis = TimeUnit.SECONDS.toMillis(config.getMinAllowedDeduplicationIntervalInSecForCF()); }); } @@ -763,9 +767,7 @@ public class CalculatedFieldCtx implements Closeable { } public boolean hasRelatedEntities() { - return CalculatedFieldType.GEOFENCING == cfType - || CalculatedFieldType.PROPAGATION == cfType - || CalculatedFieldType.RELATED_ENTITIES_AGGREGATION == cfType; + return cfHasRelationPathQuerySource; } public boolean shouldFetchRelatedEntities(CalculatedFieldState state) { @@ -781,7 +783,7 @@ public class CalculatedFieldCtx implements Closeable { if (scheduledRefreshSupported.getLastScheduledRefreshTs() == DEFAULT_LAST_UPDATE_TS) { return true; } - return scheduledRefreshSupported.getLastScheduledRefreshTs() < System.currentTimeMillis() - scheduledUpdateIntervalMillis; + return scheduledRefreshSupported.getLastScheduledRefreshTs() < System.currentTimeMillis() - Math.max(scheduledUpdateIntervalMillis, minScheduledUpdateIntervalMillis); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java new file mode 100644 index 0000000000..7d5bc47cc0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/HasEntityLimit.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.cf.ctx.state; + +public interface HasEntityLimit { + + default void checkEntityLimit(int currentEntitiesCount, CalculatedFieldCtx ctx) { + if (currentEntitiesCount >= ctx.getMaxRelatedEntitiesPerCfArgument()) { + throw new IllegalArgumentException( + "Exceeded the maximum allowed related entities per argument '" + + ctx.getMaxRelatedEntitiesPerCfArgument() + "'. Increase the limit in the tenant profile configuration." + ); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java index cb64e03d90..868e8a15ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java @@ -183,7 +183,7 @@ public class RelatedEntitiesAggregationCalculatedFieldState extends BaseCalculat } public void scheduleReevaluation() { - ScheduledFuture future = ctx.scheduleReevaluation(deduplicationIntervalMs, actorCtx); + ScheduledFuture future = ctx.scheduleReevaluation(getEnforcedDeduplicationIntervalMillis(), actorCtx); if (future != null) { reevaluationFuture = future; } @@ -209,11 +209,15 @@ public class RelatedEntitiesAggregationCalculatedFieldState extends BaseCalculat } private boolean shouldRecalculate() { - boolean intervalPassed = lastMetricsEvalTs <= System.currentTimeMillis() - deduplicationIntervalMs; + boolean intervalPassed = lastMetricsEvalTs <= System.currentTimeMillis() - getEnforcedDeduplicationIntervalMillis(); boolean argsUpdatedDuringInterval = lastArgsRefreshTs > lastMetricsEvalTs; return intervalPassed && argsUpdatedDuringInterval; } + private long getEnforcedDeduplicationIntervalMillis() { + return Math.max(deduplicationIntervalMs, ctx.getMinDeduplicationIntervalMillis()); + } + private Map> prepareInputs() { Map> inputs = new HashMap<>(); for (Map.Entry argEntry : arguments.entrySet()) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java index 7392ff1e0f..9cd40e4250 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.HasEntityLimit; import org.thingsboard.server.service.cf.ctx.state.HasLatestTs; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; @@ -34,7 +35,7 @@ import static org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldSta @Data @AllArgsConstructor -public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs { +public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs, HasEntityLimit { private final Map entityInputs; @@ -66,18 +67,18 @@ public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs @Override public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry) { - checkRelatedEntitiesNumber(ctx); + checkEntityLimit(entityInputs.size(), ctx); entityInputs.putAll(relatedEntitiesArgumentEntry.entityInputs); } else if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { if (entry.isForceResetPrevious()) { - checkRelatedEntitiesNumber(ctx); + checkEntityLimit(entityInputs.size(), ctx); entityInputs.put(singleValueArgumentEntry.getEntityId(), singleValueArgumentEntry); } else { ArgumentEntry argumentEntry = entityInputs.get(singleValueArgumentEntry.getEntityId()); if (argumentEntry != null) { argumentEntry.updateEntry(singleValueArgumentEntry, ctx); } else { - checkRelatedEntitiesNumber(ctx); + checkEntityLimit(entityInputs.size(), ctx); entityInputs.put(singleValueArgumentEntry.getEntityId(), singleValueArgumentEntry); } } @@ -87,15 +88,6 @@ public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs return true; } - private void checkRelatedEntitiesNumber(CalculatedFieldCtx ctx) { - if (entityInputs.size() >= ctx.getMaxRelatedEntitiesPerCfArgument()) { - throw new IllegalArgumentException( - "Exceeded the maximum allowed related entities per argument '" - + ctx.getMaxRelatedEntitiesPerCfArgument() + "'. Increase the limit in the tenant profile configuration." - ); - } - } - @Override public boolean isEmpty() { return entityInputs.isEmpty(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java index c3b5951da6..8fdc105168 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; +import org.thingsboard.server.service.cf.ctx.state.HasEntityLimit; import java.util.ArrayList; import java.util.Collection; @@ -30,7 +31,7 @@ import java.util.List; import java.util.Set; @Data -public class PropagationArgumentEntry implements ArgumentEntry { +public class PropagationArgumentEntry implements ArgumentEntry, HasEntityLimit { private Set entityIds; private transient List added; @@ -93,12 +94,7 @@ public class PropagationArgumentEntry implements ArgumentEntry { private boolean checkAdded(Collection updatedIds, CalculatedFieldCtx ctx) { for (EntityId id : updatedIds) { - if (entityIds.size() >= ctx.getMaxRelatedEntitiesPerCfArgument()) { - throw new IllegalArgumentException( - "Exceeded the maximum allowed related entities per argument '" - + ctx.getMaxRelatedEntitiesPerCfArgument() + "'. Increase the limit in the tenant profile configuration." - ); - } + checkEntityLimit(entityIds.size(), ctx); if (entityIds.add(id)) { if (added == null) { added = new ArrayList<>(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index dd3c7b1a7d..a04816365b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -172,12 +172,12 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxCalculatedFieldsPerEntity = 5; @Schema(example = "10") private long maxArgumentsPerCF = 10; - @Schema(example = "60") - private int minAllowedScheduledUpdateIntervalInSecForCF = 60; - @Builder.Default @Schema(example = "10") + private int minAllowedScheduledUpdateIntervalInSecForCF = 10; + @Builder.Default + @Schema(example = "2") @Positive - private int maxRelationLevelPerCfArgument = 10; + private int maxRelationLevelPerCfArgument = 2; @Builder.Default @Schema(example = "100") @Positive diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index bf68b7ea3d..262ecfc62a 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -179,11 +179,11 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxCalculatedFieldsPerEntity: 5, maxArgumentsPerCF: 10, maxDataPointsPerRollingArg: 1000, - maxRelationLevelPerCfArgument: 10, + maxRelationLevelPerCfArgument: 2, minAllowedDeduplicationIntervalInSecForCF: 10, minAllowedAggregationIntervalInSecForCF: 60, maxRelatedEntitiesToReturnPerCfArgument: 100, - minAllowedScheduledUpdateIntervalInSecForCF: 0, + minAllowedScheduledUpdateIntervalInSecForCF: 10, intermediateAggregationIntervalInSecForCF: 300, cfReevaluationCheckInterval: 60, alarmsReevaluationInterval: 60,