@ -49,6 +49,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.queue.settings.TbQueueCalculatedFieldSettings ;
import org.thingsboard.server.queue.settings.TbQueueCalculatedFieldSettings ;
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService ;
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService ;
import org.thingsboard.server.service.cf.CalculatedFieldStateService ;
import org.thingsboard.server.service.cf.CalculatedFieldStateService ;
import org.thingsboard.server.service.cf.OwnerService ;
import org.thingsboard.server.service.cf.cache.TenantEntityProfileCache ;
import org.thingsboard.server.service.cf.cache.TenantEntityProfileCache ;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId ;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId ;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx ;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx ;
@ -58,8 +59,10 @@ import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.List ;
import java.util.List ;
import java.util.Map ;
import java.util.Map ;
import java.util.Set ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.CopyOnWriteArrayList ;
import java.util.concurrent.CopyOnWriteArrayList ;
import java.util.concurrent.ScheduledFuture ;
import java.util.concurrent.ScheduledFuture ;
@ -77,6 +80,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
private final Map < CalculatedFieldId , CalculatedFieldCtx > calculatedFields = new HashMap < > ( ) ;
private final Map < CalculatedFieldId , CalculatedFieldCtx > calculatedFields = new HashMap < > ( ) ;
private final Map < EntityId , List < CalculatedFieldCtx > > entityIdCalculatedFields = new HashMap < > ( ) ;
private final Map < EntityId , List < CalculatedFieldCtx > > entityIdCalculatedFields = new HashMap < > ( ) ;
private final Map < EntityId , List < CalculatedFieldLink > > entityIdCalculatedFieldLinks = new HashMap < > ( ) ;
private final Map < EntityId , List < CalculatedFieldLink > > entityIdCalculatedFieldLinks = new HashMap < > ( ) ;
private final Map < EntityId , Set < EntityId > > ownerEntities = new HashMap < > ( ) ;
private final Map < CalculatedFieldId , ScheduledFuture < ? > > cfDynamicArgumentsRefreshTasks = new ConcurrentHashMap < > ( ) ;
private final Map < CalculatedFieldId , ScheduledFuture < ? > > cfDynamicArgumentsRefreshTasks = new ConcurrentHashMap < > ( ) ;
private ScheduledFuture < ? > cfsReevaluationTask ;
private ScheduledFuture < ? > cfsReevaluationTask ;
@ -88,6 +92,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
private final TbAssetProfileCache assetProfileCache ;
private final TbAssetProfileCache assetProfileCache ;
private final TbDeviceProfileCache deviceProfileCache ;
private final TbDeviceProfileCache deviceProfileCache ;
private final TenantEntityProfileCache entityProfileCache ;
private final TenantEntityProfileCache entityProfileCache ;
private final OwnerService ownerService ;
private final TbQueueCalculatedFieldSettings cfSettings ;
private final TbQueueCalculatedFieldSettings cfSettings ;
protected final TenantId tenantId ;
protected final TenantId tenantId ;
@ -103,6 +108,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
this . assetProfileCache = systemContext . getAssetProfileCache ( ) ;
this . assetProfileCache = systemContext . getAssetProfileCache ( ) ;
this . deviceProfileCache = systemContext . getDeviceProfileCache ( ) ;
this . deviceProfileCache = systemContext . getDeviceProfileCache ( ) ;
this . entityProfileCache = new TenantEntityProfileCache ( ) ;
this . entityProfileCache = new TenantEntityProfileCache ( ) ;
this . ownerService = systemContext . getOwnerService ( ) ;
this . cfSettings = systemContext . getCalculatedFieldSettings ( ) ;
this . cfSettings = systemContext . getCalculatedFieldSettings ( ) ;
this . tenantId = tenantId ;
this . tenantId = tenantId ;
}
}
@ -128,7 +134,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
public void onCacheInitMsg ( CalculatedFieldCacheInitMsg msg ) {
public void onCacheInitMsg ( CalculatedFieldCacheInitMsg msg ) {
log . debug ( "[{}] Processing CF actor init message." , msg . getTenantId ( ) . getId ( ) ) ;
log . debug ( "[{}] Processing CF actor init message." , msg . getTenantId ( ) . getId ( ) ) ;
initEntityProf il eCache ( ) ;
initEntities Cache ( ) ;
initCalculatedFields ( ) ;
initCalculatedFields ( ) ;
scheduleCfsReevaluation ( ) ;
scheduleCfsReevaluation ( ) ;
msg . getCallback ( ) . onSuccess ( ) ;
msg . getCallback ( ) . onSuccess ( ) ;
@ -251,6 +257,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
if ( profileId ! = null ) {
if ( profileId ! = null ) {
entityProfileCache . add ( profileId , entityId ) ;
entityProfileCache . add ( profileId , entityId ) ;
}
}
updateEntityOwner ( entityId ) ;
if ( ! isMyPartition ( entityId , callback ) ) {
if ( ! isMyPartition ( entityId , callback ) ) {
return ;
return ;
}
}
@ -283,11 +290,16 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
} else {
} else {
callback . onSuccess ( ) ;
callback . onSuccess ( ) ;
}
}
} else if ( msg . isOwnerChanged ( ) ) {
onEntityOwnerChanged ( msg , callback ) ;
} else {
callback . onSuccess ( ) ;
}
}
}
}
private void onEntityDeleted ( ComponentLifecycleMsg msg , TbCallback callback ) {
private void onEntityDeleted ( ComponentLifecycleMsg msg , TbCallback callback ) {
entityProfileCache . removeEntityId ( msg . getEntityId ( ) ) ;
entityProfileCache . removeEntityId ( msg . getEntityId ( ) ) ;
ownerEntities . values ( ) . forEach ( entities - > entities . remove ( msg . getEntityId ( ) ) ) ;
if ( isMyPartition ( msg . getEntityId ( ) , callback ) ) {
if ( isMyPartition ( msg . getEntityId ( ) , callback ) ) {
log . debug ( "Pushing entity lifecycle msg to specific actor [{}]" , msg . getEntityId ( ) ) ;
log . debug ( "Pushing entity lifecycle msg to specific actor [{}]" , msg . getEntityId ( ) ) ;
getOrCreateActor ( msg . getEntityId ( ) ) . tell ( new CalculatedFieldEntityDeleteMsg ( tenantId , msg . getEntityId ( ) , callback ) ) ;
getOrCreateActor ( msg . getEntityId ( ) ) . tell ( new CalculatedFieldEntityDeleteMsg ( tenantId , msg . getEntityId ( ) , callback ) ) ;
@ -415,8 +427,8 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
public void onTelemetryMsg ( CalculatedFieldTelemetryMsg msg ) {
public void onTelemetryMsg ( CalculatedFieldTelemetryMsg msg ) {
EntityId entityId = msg . getEntityId ( ) ;
EntityId entityId = msg . getEntityId ( ) ;
log . debug ( "Received telemetry msg from entity [{}]" , entityId ) ;
log . debug ( "Received telemetry msg from entity [{}]" , entityId ) ;
// 2 = 1 for CF processing + 1 for links processing
// 3 = 1 for CF processing + 1 for links processing + 1 for owner entity processing
MultipleTbCallback callback = new MultipleTbCallback ( 2 , msg . getCallback ( ) ) ;
MultipleTbCallback callback = new MultipleTbCallback ( 3 , msg . getCallback ( ) ) ;
// process all cfs related to entity, or it's profile;
// process all cfs related to entity, or it's profile;
var entityIdFields = getCalculatedFieldsByEntityId ( entityId ) ;
var entityIdFields = getCalculatedFieldsByEntityId ( entityId ) ;
var profileIdFields = getCalculatedFieldsByEntityId ( getProfileId ( tenantId , entityId ) ) ;
var profileIdFields = getCalculatedFieldsByEntityId ( getProfileId ( tenantId , entityId ) ) ;
@ -434,6 +446,17 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
} else {
} else {
callback . onSuccess ( ) ;
callback . onSuccess ( ) ;
}
}
// process all cfs related to owner entity
if ( entityId . getEntityType ( ) . isOneOf ( EntityType . TENANT , EntityType . CUSTOMER ) ) {
List < CalculatedFieldEntityCtxId > ownerCFs = filterOwnerEntitiesCFs ( msg ) ;
if ( ! ownerCFs . isEmpty ( ) ) {
cfExecService . pushMsgToLinks ( msg , ownerCFs , callback ) ;
} else {
callback . onSuccess ( ) ;
}
} else {
callback . onSuccess ( ) ;
}
}
}
public void onLinkedTelemetryMsg ( CalculatedFieldLinkedTelemetryMsg msg ) {
public void onLinkedTelemetryMsg ( CalculatedFieldLinkedTelemetryMsg msg ) {
@ -456,6 +479,31 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
}
}
}
}
private void onEntityOwnerChanged ( ComponentLifecycleMsg msg , TbCallback msgCallback ) {
EntityId entityId = msg . getEntityId ( ) ;
log . debug ( "Received changed owner msg from entity [{}]" , entityId ) ;
updateEntityOwner ( entityId ) ;
List < CalculatedFieldCtx > cfs = new ArrayList < > ( ) ;
cfs . addAll ( getCalculatedFieldsByEntityId ( entityId ) ) ;
cfs . addAll ( getCalculatedFieldsByEntityId ( getProfileId ( tenantId , entityId ) ) ) ;
if ( cfs . isEmpty ( ) ) {
msgCallback . onSuccess ( ) ;
return ;
}
MultipleTbCallback callback = new MultipleTbCallback ( cfs . size ( ) , msgCallback ) ;
cfs . forEach ( cf - > {
if ( isMyPartition ( entityId , callback ) ) {
if ( cf . hasCurrentOwnerSourceArguments ( ) ) {
CalculatedFieldArgumentResetMsg argResetMsg = new CalculatedFieldArgumentResetMsg ( tenantId , cf , callback ) ;
log . debug ( "Pushing CF argument reset msg to specific actor [{}]" , entityId ) ;
getOrCreateActor ( entityId ) . tell ( argResetMsg ) ;
} else {
callback . onSuccess ( ) ;
}
}
} ) ;
}
private List < CalculatedFieldEntityCtxId > filterCalculatedFieldLinks ( CalculatedFieldTelemetryMsg msg ) {
private List < CalculatedFieldEntityCtxId > filterCalculatedFieldLinks ( CalculatedFieldTelemetryMsg msg ) {
EntityId entityId = msg . getEntityId ( ) ;
EntityId entityId = msg . getEntityId ( ) ;
var proto = msg . getProto ( ) ;
var proto = msg . getProto ( ) ;
@ -469,6 +517,27 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
return result ;
return result ;
}
}
private List < CalculatedFieldEntityCtxId > filterOwnerEntitiesCFs ( CalculatedFieldTelemetryMsg msg ) {
Set < EntityId > entities = getOwnerEntities ( msg . getEntityId ( ) ) ;
var proto = msg . getProto ( ) ;
List < CalculatedFieldEntityCtxId > result = new ArrayList < > ( ) ;
for ( var entityId : entities ) {
var ownerEntityCFs = getCalculatedFieldsByEntityId ( entityId ) ;
for ( var ctx : ownerEntityCFs ) {
if ( ctx . dynamicSourceMatches ( proto ) ) {
result . add ( new CalculatedFieldEntityCtxId ( tenantId , ctx . getCfId ( ) , entityId ) ) ;
}
}
var ownerEntityProfileCFs = getCalculatedFieldsByEntityId ( getProfileId ( tenantId , entityId ) ) ;
for ( var ctx : ownerEntityProfileCFs ) {
if ( ctx . dynamicSourceMatches ( proto ) ) {
result . add ( new CalculatedFieldEntityCtxId ( tenantId , ctx . getCfId ( ) , entityId ) ) ;
}
}
}
return result ;
}
private List < CalculatedFieldCtx > getCalculatedFieldsByEntityId ( EntityId entityId ) {
private List < CalculatedFieldCtx > getCalculatedFieldsByEntityId ( EntityId entityId ) {
if ( entityId = = null ) {
if ( entityId = = null ) {
return Collections . emptyList ( ) ;
return Collections . emptyList ( ) ;
@ -491,6 +560,17 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
return result ;
return result ;
}
}
private Set < EntityId > getOwnerEntities ( EntityId entityId ) {
if ( entityId = = null ) {
return Collections . emptySet ( ) ;
}
var result = ownerEntities . get ( entityId ) ;
if ( result = = null ) {
result = Collections . emptySet ( ) ;
}
return result ;
}
private void scheduleDynamicArgumentsRefreshTaskForCfIfNeeded ( CalculatedFieldCtx cfCtx ) {
private void scheduleDynamicArgumentsRefreshTaskForCfIfNeeded ( CalculatedFieldCtx cfCtx ) {
CalculatedField cf = cfCtx . getCalculatedField ( ) ;
CalculatedField cf = cfCtx . getCalculatedField ( ) ;
if ( ! ( cf . getConfiguration ( ) instanceof ScheduledUpdateSupportedCalculatedFieldConfiguration scheduledCfConfig ) ) {
if ( ! ( cf . getConfiguration ( ) instanceof ScheduledUpdateSupportedCalculatedFieldConfiguration scheduledCfConfig ) ) {
@ -623,12 +703,13 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
entityIdCalculatedFieldLinks . computeIfAbsent ( link . getEntityId ( ) , id - > new CopyOnWriteArrayList < > ( ) ) . add ( link ) ;
entityIdCalculatedFieldLinks . computeIfAbsent ( link . getEntityId ( ) , id - > new CopyOnWriteArrayList < > ( ) ) . add ( link ) ;
}
}
private void initEntityProf il eCache ( ) {
private void initEntities Cache ( ) {
PageDataIterable < ProfileEntityIdInfo > deviceIdInfos = new PageDataIterable < > ( pageLink - > deviceService . findProfileEntityIdInfosByTenantId ( tenantId , pageLink ) , cfSettings . getInitTenantFetchPackSize ( ) ) ;
PageDataIterable < ProfileEntityIdInfo > deviceIdInfos = new PageDataIterable < > ( pageLink - > deviceService . findProfileEntityIdInfosByTenantId ( tenantId , pageLink ) , cfSettings . getInitTenantFetchPackSize ( ) ) ;
for ( ProfileEntityIdInfo idInfo : deviceIdInfos ) {
for ( ProfileEntityIdInfo idInfo : deviceIdInfos ) {
log . trace ( "Processing device record: {}" , idInfo ) ;
log . trace ( "Processing device record: {}" , idInfo ) ;
try {
try {
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
ownerEntities . computeIfAbsent ( idInfo . getOwnerId ( ) , ownerId - > new HashSet < > ( ) ) . add ( idInfo . getEntityId ( ) ) ;
} catch ( Exception e ) {
} catch ( Exception e ) {
log . error ( "Failed to process device record: {}" , idInfo , e ) ;
log . error ( "Failed to process device record: {}" , idInfo , e ) ;
}
}
@ -638,12 +719,19 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
log . trace ( "Processing asset record: {}" , idInfo ) ;
log . trace ( "Processing asset record: {}" , idInfo ) ;
try {
try {
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
ownerEntities . computeIfAbsent ( idInfo . getOwnerId ( ) , ownerId - > new HashSet < > ( ) ) . add ( idInfo . getEntityId ( ) ) ;
} catch ( Exception e ) {
} catch ( Exception e ) {
log . error ( "Failed to process asset record: {}" , idInfo , e ) ;
log . error ( "Failed to process asset record: {}" , idInfo , e ) ;
}
}
}
}
}
}
private void updateEntityOwner ( EntityId entityId ) {
ownerEntities . values ( ) . forEach ( entities - > entities . remove ( entityId ) ) ;
EntityId owner = ownerService . getOwner ( tenantId , entityId ) ;
ownerEntities . computeIfAbsent ( owner , ownerId - > new HashSet < > ( ) ) . add ( entityId ) ;
}
private void applyToTargetCfEntityActors ( CalculatedFieldCtx ctx ,
private void applyToTargetCfEntityActors ( CalculatedFieldCtx ctx ,
TbCallback callback ,
TbCallback callback ,
BiConsumer < EntityId , TbCallback > action ) {
BiConsumer < EntityId , TbCallback > action ) {