improved ability to fetch common arguments for profile cfs
This commit is contained in:
parent
3038510150
commit
4fed2cd416
@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.cf.configuration.Argument;
|
|||||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.AssetProfileId;
|
import org.thingsboard.server.common.data.id.AssetProfileId;
|
||||||
import org.thingsboard.server.common.data.id.CalculatedFieldId;
|
import org.thingsboard.server.common.data.id.CalculatedFieldId;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
|
||||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||||
@ -89,7 +88,7 @@ import java.util.Set;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.thingsboard.server.common.data.DataConstants.SCOPE;
|
import static org.thingsboard.server.common.data.DataConstants.SCOPE;
|
||||||
@ -117,8 +116,6 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
private final ConcurrentMap<CalculatedFieldEntityCtxId, CalculatedFieldEntityCtx> states = new ConcurrentHashMap<>();
|
private final ConcurrentMap<CalculatedFieldEntityCtxId, CalculatedFieldEntityCtx> states = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final ConcurrentMap<EntityId, Set<EntityId>> profileEntities = new ConcurrentHashMap<>();
|
private final ConcurrentMap<EntityId, Set<EntityId>> profileEntities = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<TenantId, List<KvEntry>> tenantStorage = new ConcurrentHashMap<>();
|
|
||||||
private final ConcurrentMap<CustomerId, List<KvEntry>> customerStorage = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static final int MAX_LAST_RECORDS_VALUE = 1024;
|
private static final int MAX_LAST_RECORDS_VALUE = 1024;
|
||||||
|
|
||||||
@ -196,7 +193,11 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
}
|
}
|
||||||
case ASSET_PROFILE, DEVICE_PROFILE -> {
|
case ASSET_PROFILE, DEVICE_PROFILE -> {
|
||||||
log.info("Initializing state for all entities in profile: tenantId=[{}], profileId=[{}]", tenantId, entityId);
|
log.info("Initializing state for all entities in profile: tenantId=[{}], profileId=[{}]", tenantId, entityId);
|
||||||
getOrFetchFromDBProfileEntities(tenantId, entityId).forEach(assetId -> initializeStateForEntity(calculatedFieldCtx, assetId, callback));
|
fetchCommonArguments(calculatedFieldCtx, callback, commonArguments -> {
|
||||||
|
getOrFetchFromDBProfileEntities(tenantId, entityId).forEach(assetId -> {
|
||||||
|
initializeStateForEntity(calculatedFieldCtx, assetId, commonArguments, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
default ->
|
default ->
|
||||||
throw new IllegalArgumentException("Entity type '" + calculatedFieldId.getEntityType() + "' does not support calculated fields.");
|
throw new IllegalArgumentException("Entity type '" + calculatedFieldId.getEntityType() + "' does not support calculated fields.");
|
||||||
@ -221,12 +222,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
CalculatedField calculatedField = getOrFetchFromDb(tenantId, calculatedFieldId);
|
CalculatedField calculatedField = getOrFetchFromDb(tenantId, calculatedFieldId);
|
||||||
CalculatedFieldCtx calculatedFieldCtx = calculatedFieldsCtx.computeIfAbsent(calculatedFieldId, id -> new CalculatedFieldCtx(calculatedField, tbelInvokeService));
|
CalculatedFieldCtx calculatedFieldCtx = calculatedFieldsCtx.computeIfAbsent(calculatedFieldId, id -> new CalculatedFieldCtx(calculatedField, tbelInvokeService));
|
||||||
Map<String, ArgumentEntry> argumentValues = updatedTelemetry.entrySet().stream()
|
Map<String, ArgumentEntry> argumentValues = updatedTelemetry.entrySet().stream()
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, entry -> {
|
.collect(Collectors.toMap(Map.Entry::getKey, entry -> ArgumentEntry.createSingleValueArgument(entry.getValue())));
|
||||||
if (EntityType.TENANT.equals(entityId.getEntityType()) || EntityType.CUSTOMER.equals(entityId.getEntityType())) {
|
|
||||||
updateStorage(tenantId, entityId, Optional.of(entry.getValue()));
|
|
||||||
}
|
|
||||||
return ArgumentEntry.createSingleValueArgument(entry.getValue());
|
|
||||||
}));
|
|
||||||
|
|
||||||
EntityId cfEntityId = calculatedField.getEntityId();
|
EntityId cfEntityId = calculatedField.getEntityId();
|
||||||
switch (cfEntityId.getEntityType()) {
|
switch (cfEntityId.getEntityType()) {
|
||||||
@ -371,33 +367,71 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
.forEach(cfCtx -> initializeStateForEntity(cfCtx, entityId, callback));
|
.forEach(cfCtx -> initializeStateForEntity(cfCtx, entityId, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeStateForEntity(CalculatedFieldCtx calculatedFieldCtx, EntityId entityId, TbCallback callback) {
|
private void fetchCommonArguments(CalculatedFieldCtx calculatedFieldCtx, TbCallback callback, Consumer<Map<String, ArgumentEntry>> onComplete) {
|
||||||
Map<String, Argument> arguments = calculatedFieldCtx.getArguments();
|
|
||||||
Map<String, ArgumentEntry> argumentValues = new HashMap<>();
|
Map<String, ArgumentEntry> argumentValues = new HashMap<>();
|
||||||
AtomicInteger remaining = new AtomicInteger(arguments.size());
|
List<ListenableFuture<ArgumentEntry>> futures = new ArrayList<>();
|
||||||
arguments.forEach((key, argument) -> Futures.addCallback(fetchArgumentValue(calculatedFieldCtx, entityId, argument), new FutureCallback<>() {
|
|
||||||
|
calculatedFieldCtx.getArguments().forEach((key, argument) -> {
|
||||||
|
if (!EntityType.DEVICE_PROFILE.equals(argument.getEntityId().getEntityType()) &&
|
||||||
|
!EntityType.ASSET_PROFILE.equals(argument.getEntityId().getEntityType())) {
|
||||||
|
futures.add(Futures.transform(fetchKvEntry(calculatedFieldCtx.getTenantId(), argument.getEntityId(), argument),
|
||||||
|
result -> {
|
||||||
|
argumentValues.put(key, result);
|
||||||
|
return result;
|
||||||
|
}, calculatedFieldCallbackExecutor));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ArgumentEntry result) {
|
public void onSuccess(List<ArgumentEntry> results) {
|
||||||
argumentValues.put(key, result);
|
onComplete.accept(argumentValues);
|
||||||
if (remaining.decrementAndGet() == 0) {
|
|
||||||
updateOrInitializeState(calculatedFieldCtx, entityId, argumentValues);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable t) {
|
public void onFailure(Throwable t) {
|
||||||
log.warn("Failed to initialize state for entity: [{}]", entityId, t);
|
log.error("Failed to fetch common arguments", t);
|
||||||
callback.onFailure(t);
|
callback.onFailure(t);
|
||||||
}
|
}
|
||||||
}, calculatedFieldCallbackExecutor));
|
}, calculatedFieldCallbackExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeStateForEntity(CalculatedFieldCtx calculatedFieldCtx, EntityId entityId, TbCallback callback) {
|
||||||
|
initializeStateForEntity(calculatedFieldCtx, entityId, new HashMap<>(), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeStateForEntity(CalculatedFieldCtx calculatedFieldCtx, EntityId entityId, Map<String, ArgumentEntry> commonArguments, TbCallback callback) {
|
||||||
|
Map<String, ArgumentEntry> argumentValues = new HashMap<>(commonArguments);
|
||||||
|
List<ListenableFuture<ArgumentEntry>> futures = new ArrayList<>();
|
||||||
|
|
||||||
|
calculatedFieldCtx.getArguments().forEach((key, argument) -> {
|
||||||
|
if (!commonArguments.containsKey(key)) {
|
||||||
|
futures.add(Futures.transform(fetchArgumentValue(calculatedFieldCtx, entityId, argument),
|
||||||
|
result -> {
|
||||||
|
argumentValues.put(key, result);
|
||||||
|
return result;
|
||||||
|
}, calculatedFieldCallbackExecutor));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<ArgumentEntry> results) {
|
||||||
|
updateOrInitializeState(calculatedFieldCtx, entityId, argumentValues);
|
||||||
|
callback.onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
log.error("Failed to initialize state for entity: [{}]", entityId, t);
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
}, calculatedFieldCallbackExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenableFuture<ArgumentEntry> fetchArgumentValue(CalculatedFieldCtx calculatedFieldCtx, EntityId targetEntityId, Argument argument) {
|
private ListenableFuture<ArgumentEntry> fetchArgumentValue(CalculatedFieldCtx calculatedFieldCtx, EntityId targetEntityId, Argument argument) {
|
||||||
TenantId tenantId = calculatedFieldCtx.getTenantId();
|
TenantId tenantId = calculatedFieldCtx.getTenantId();
|
||||||
EntityId argumentEntityId = argument.getEntityId();
|
EntityId argumentEntityId = argument.getEntityId();
|
||||||
if (EntityType.TENANT.equals(argumentEntityId.getEntityType()) || EntityType.CUSTOMER.equals(argumentEntityId.getEntityType())) {
|
|
||||||
return fetchFromStorage(tenantId, argumentEntityId, argument);
|
|
||||||
}
|
|
||||||
EntityId entityId = EntityType.DEVICE_PROFILE.equals(argumentEntityId.getEntityType()) || EntityType.ASSET_PROFILE.equals(argumentEntityId.getEntityType())
|
EntityId entityId = EntityType.DEVICE_PROFILE.equals(argumentEntityId.getEntityType()) || EntityType.ASSET_PROFILE.equals(argumentEntityId.getEntityType())
|
||||||
? targetEntityId
|
? targetEntityId
|
||||||
: argumentEntityId;
|
: argumentEntityId;
|
||||||
@ -407,21 +441,6 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
return fetchKvEntry(tenantId, entityId, argument);
|
return fetchKvEntry(tenantId, entityId, argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenableFuture<ArgumentEntry> fetchFromStorage(TenantId tenantId, EntityId entityId, Argument argument) {
|
|
||||||
List<KvEntry> kvEntries;
|
|
||||||
if (EntityType.TENANT.equals(entityId.getEntityType())) {
|
|
||||||
kvEntries = tenantStorage.computeIfAbsent(tenantId, id -> new ArrayList<>());
|
|
||||||
} else {
|
|
||||||
kvEntries = customerStorage.computeIfAbsent((CustomerId) entityId, id -> new ArrayList<>());
|
|
||||||
}
|
|
||||||
for (KvEntry kvEntry : kvEntries) {
|
|
||||||
if (kvEntry.getKey().equals(argument.getKey())) {
|
|
||||||
return Futures.immediateFuture(ArgumentEntry.createSingleValueArgument(kvEntry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fetchKvEntry(tenantId, entityId, argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListenableFuture<ArgumentEntry> fetchLastRecords(TenantId tenantId, EntityId entityId, Argument argument) {
|
private ListenableFuture<ArgumentEntry> fetchLastRecords(TenantId tenantId, EntityId entityId, Argument argument) {
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
long timeWindow = argument.getTimeWindow() == 0 ? System.currentTimeMillis() : argument.getTimeWindow();
|
long timeWindow = argument.getTimeWindow() == 0 ? System.currentTimeMillis() : argument.getTimeWindow();
|
||||||
@ -450,26 +469,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
|||||||
calculatedFieldExecutor);
|
calculatedFieldExecutor);
|
||||||
default -> throw new IllegalArgumentException("Invalid argument type '" + argument.getType() + "'.");
|
default -> throw new IllegalArgumentException("Invalid argument type '" + argument.getType() + "'.");
|
||||||
};
|
};
|
||||||
return Futures.transform(kvEntryFuture, kvEntry -> {
|
return Futures.transform(kvEntryFuture, kvEntry -> ArgumentEntry.createSingleValueArgument(kvEntry.orElse(null)), calculatedFieldExecutor);
|
||||||
if (EntityType.TENANT.equals(entityId.getEntityType()) || EntityType.CUSTOMER.equals(entityId.getEntityType())) {
|
|
||||||
updateStorage(tenantId, entityId, kvEntry);
|
|
||||||
}
|
|
||||||
return ArgumentEntry.createSingleValueArgument(kvEntry.orElse(null));
|
|
||||||
}, calculatedFieldExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateStorage(TenantId tenantId, EntityId entityId, Optional<? extends KvEntry> kvEntry) {
|
|
||||||
kvEntry.ifPresent(entry -> {
|
|
||||||
List<KvEntry> kvEntries = switch (entityId.getEntityType()) {
|
|
||||||
case TENANT -> tenantStorage.computeIfAbsent(tenantId, id -> new ArrayList<>());
|
|
||||||
case CUSTOMER -> customerStorage.computeIfAbsent((CustomerId) entityId, id -> new ArrayList<>());
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
if (kvEntries != null) {
|
|
||||||
kvEntries.removeIf(existingEntry -> existingEntry.getKey().equals(entry.getKey()));
|
|
||||||
kvEntries.add(entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private KvEntry createDefaultKvEntry(Argument argument) {
|
private KvEntry createDefaultKvEntry(Argument argument) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user