@ -15,7 +15,9 @@
* /
* /
package org.thingsboard.server.actors.calculatedField ;
package org.thingsboard.server.actors.calculatedField ;
import lombok.Getter ;
import lombok.extern.slf4j.Slf4j ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.beans.factory.annotation.Value ;
import org.thingsboard.server.actors.ActorSystemContext ;
import org.thingsboard.server.actors.ActorSystemContext ;
import org.thingsboard.server.actors.TbActorCtx ;
import org.thingsboard.server.actors.TbActorCtx ;
import org.thingsboard.server.actors.TbActorRef ;
import org.thingsboard.server.actors.TbActorRef ;
@ -24,6 +26,7 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor ;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor ;
import org.thingsboard.server.common.data.DataConstants ;
import org.thingsboard.server.common.data.DataConstants ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.ProfileEntityIdInfo ;
import org.thingsboard.server.common.data.cf.CalculatedField ;
import org.thingsboard.server.common.data.cf.CalculatedField ;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink ;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink ;
import org.thingsboard.server.common.data.id.AssetId ;
import org.thingsboard.server.common.data.id.AssetId ;
@ -31,17 +34,22 @@ import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.DeviceId ;
import org.thingsboard.server.common.data.id.DeviceId ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.page.PageDataIterable ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldCacheInitMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldInitMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldInitMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldInitProfileEntityMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldLinkInitMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldLinkInitMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg ;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg ;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg ;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg ;
import org.thingsboard.server.common.msg.queue.ServiceType ;
import org.thingsboard.server.common.msg.queue.ServiceType ;
import org.thingsboard.server.common.msg.queue.TbCallback ;
import org.thingsboard.server.common.msg.queue.TbCallback ;
import org.thingsboard.server.dao.asset.AssetService ;
import org.thingsboard.server.dao.cf.CalculatedFieldService ;
import org.thingsboard.server.dao.cf.CalculatedFieldService ;
import org.thingsboard.server.dao.device.DeviceService ;
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.cache.CalculatedField EntityProfileCache ;
import org.thingsboard.server.service.cf.cache.Tenant EntityProfileCache ;
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 ;
import org.thingsboard.server.service.profile.TbAssetProfileCache ;
import org.thingsboard.server.service.profile.TbAssetProfileCache ;
@ -68,22 +76,30 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
private final CalculatedFieldProcessingService cfExecService ;
private final CalculatedFieldProcessingService cfExecService ;
private final CalculatedFieldStateService cfStateService ;
private final CalculatedFieldStateService cfStateService ;
private final CalculatedFieldEntityProfileCache cfEntityCache ;
private final CalculatedFieldService cfDaoService ;
private final CalculatedFieldService cfDaoService ;
private final DeviceService deviceService ;
private final AssetService assetService ;
private final TbAssetProfileCache assetProfileCache ;
private final TbAssetProfileCache assetProfileCache ;
private final TbDeviceProfileCache deviceProfileCache ;
private final TbDeviceProfileCache deviceProfileCache ;
private final TenantEntityProfileCache entityProfileCache ;
protected final TenantId tenantId ;
protected final TenantId tenantId ;
protected TbActorCtx ctx ;
protected TbActorCtx ctx ;
@Value ( "${queue.calculated_fields.init_tenant_fetch_pack_size:1000}" )
@Getter
private int initFetchPackSize ;
CalculatedFieldManagerMessageProcessor ( ActorSystemContext systemContext , TenantId tenantId ) {
CalculatedFieldManagerMessageProcessor ( ActorSystemContext systemContext , TenantId tenantId ) {
super ( systemContext ) ;
super ( systemContext ) ;
this . cfEntityCache = systemContext . getCalculatedFieldEntityProfileCache ( ) ;
this . cfExecService = systemContext . getCalculatedFieldProcessingService ( ) ;
this . cfExecService = systemContext . getCalculatedFieldProcessingService ( ) ;
this . cfStateService = systemContext . getCalculatedFieldStateService ( ) ;
this . cfStateService = systemContext . getCalculatedFieldStateService ( ) ;
this . cfDaoService = systemContext . getCalculatedFieldService ( ) ;
this . cfDaoService = systemContext . getCalculatedFieldService ( ) ;
this . deviceService = systemContext . getDeviceService ( ) ;
this . assetService = systemContext . getAssetService ( ) ;
this . assetProfileCache = systemContext . getAssetProfileCache ( ) ;
this . assetProfileCache = systemContext . getAssetProfileCache ( ) ;
this . deviceProfileCache = systemContext . getDeviceProfileCache ( ) ;
this . deviceProfileCache = systemContext . getDeviceProfileCache ( ) ;
this . entityProfileCache = new TenantEntityProfileCache ( ) ;
this . tenantId = tenantId ;
this . tenantId = tenantId ;
}
}
@ -100,6 +116,19 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
ctx . stop ( ctx . getSelf ( ) ) ;
ctx . stop ( ctx . getSelf ( ) ) ;
}
}
public void onCacheInitMsg ( CalculatedFieldCacheInitMsg msg ) {
log . debug ( "[{}] Processing CF actor init message." , msg . getTenantId ( ) . getId ( ) ) ;
initEntityProfileCache ( ) ;
initCalculatedFields ( ) ;
msg . getCallback ( ) . onSuccess ( ) ;
}
public void onProfileEntityMsg ( CalculatedFieldInitProfileEntityMsg msg ) {
log . debug ( "[{}] Processing profile entity message." , msg . getTenantId ( ) . getId ( ) ) ;
entityProfileCache . add ( msg . getProfileEntityId ( ) , msg . getEntityId ( ) ) ;
msg . getCallback ( ) . onSuccess ( ) ;
}
public void onFieldInitMsg ( CalculatedFieldInitMsg msg ) throws CalculatedFieldException {
public void onFieldInitMsg ( CalculatedFieldInitMsg msg ) throws CalculatedFieldException {
log . debug ( "[{}] Processing CF init message." , msg . getCf ( ) . getId ( ) ) ;
log . debug ( "[{}] Processing CF init message." , msg . getCf ( ) . getId ( ) ) ;
var cf = msg . getCf ( ) ;
var cf = msg . getCf ( ) ;
@ -180,16 +209,35 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
}
}
break ;
break ;
}
}
case DEVICE_PROFILE :
case ASSET_PROFILE : {
switch ( event ) {
case DELETED :
onProfileDeleted ( msg . getData ( ) , msg . getCallback ( ) ) ;
break ;
default :
msg . getCallback ( ) . onSuccess ( ) ;
break ;
}
break ;
}
default : {
default : {
msg . getCallback ( ) . onSuccess ( ) ;
msg . getCallback ( ) . onSuccess ( ) ;
}
}
}
}
}
}
private void onProfileDeleted ( ComponentLifecycleMsg msg , TbCallback callback ) {
entityProfileCache . removeProfileId ( msg . getEntityId ( ) ) ;
callback . onSuccess ( ) ;
}
private void onEntityCreated ( ComponentLifecycleMsg msg , TbCallback callback ) {
private void onEntityCreated ( ComponentLifecycleMsg msg , TbCallback callback ) {
EntityId entityId = msg . getEntityId ( ) ;
EntityId entityId = msg . getEntityId ( ) ;
EntityId profileId = getProfileId ( tenantId , entityId ) ;
EntityId profileId = getProfileId ( tenantId , entityId ) ;
cfEntityCache . add ( tenantId , profileId , entityId ) ;
if ( profileId ! = null ) {
entityProfileCache . add ( profileId , entityId ) ;
}
if ( ! isMyPartition ( entityId , callback ) ) {
if ( ! isMyPartition ( entityId , callback ) ) {
return ;
return ;
}
}
@ -207,7 +255,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
private void onEntityUpdated ( ComponentLifecycleMsg msg , TbCallback callback ) {
private void onEntityUpdated ( ComponentLifecycleMsg msg , TbCallback callback ) {
if ( msg . getOldProfileId ( ) ! = null & & ! msg . getOldProfileId ( ) . equals ( msg . getProfileId ( ) ) ) {
if ( msg . getOldProfileId ( ) ! = null & & ! msg . getOldProfileId ( ) . equals ( msg . getProfileId ( ) ) ) {
cfEntity Cache. update ( tenantId , msg . getOldProfileId ( ) , msg . getProfileId ( ) , msg . getEntityId ( ) ) ;
entityProfile Cache. update ( msg . getOldProfileId ( ) , msg . getProfileId ( ) , msg . getEntityId ( ) ) ;
if ( ! isMyPartition ( msg . getEntityId ( ) , callback ) ) {
if ( ! isMyPartition ( msg . getEntityId ( ) , callback ) ) {
return ;
return ;
}
}
@ -226,7 +274,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
}
}
private void onEntityDeleted ( ComponentLifecycleMsg msg , TbCallback callback ) {
private void onEntityDeleted ( ComponentLifecycleMsg msg , TbCallback callback ) {
cfEntityCache . evict ( tenantId , msg . getEntityId ( ) ) ;
entityProfileCache . removeEntityId ( 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 ) ) ;
@ -322,11 +370,15 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
EntityId entityId = cfCtx . getEntityId ( ) ;
EntityId entityId = cfCtx . getEntityId ( ) ;
EntityType entityType = cfCtx . getEntityId ( ) . getEntityType ( ) ;
EntityType entityType = cfCtx . getEntityId ( ) . getEntityType ( ) ;
if ( isProfileEntity ( entityType ) ) {
if ( isProfileEntity ( entityType ) ) {
var entityIds = cfEntity Cache. getMy EntityIdsByProfileId ( tenantId , entityId ) ;
var entityIds = entityProfile Cache. getEntityIdsByProfileId ( entityId ) ;
if ( ! entityIds . isEmpty ( ) ) {
if ( ! entityIds . isEmpty ( ) ) {
//TODO: no need to do this if we cache all created actors and know which one belong to us;
//TODO: no need to do this if we cache all created actors and know which one belong to us;
var multiCallback = new MultipleTbCallback ( entityIds . size ( ) , callback ) ;
var multiCallback = new MultipleTbCallback ( entityIds . size ( ) , callback ) ;
entityIds . forEach ( id - > deleteCfForEntity ( id , cfId , multiCallback ) ) ;
entityIds . forEach ( id - > {
if ( isMyPartition ( id , multiCallback ) ) {
deleteCfForEntity ( id , cfId , multiCallback ) ;
}
} ) ;
} else {
} else {
callback . onSuccess ( ) ;
callback . onSuccess ( ) ;
}
}
@ -366,10 +418,11 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
EntityId sourceEntityId = msg . getEntityId ( ) ;
EntityId sourceEntityId = msg . getEntityId ( ) ;
log . debug ( "Received linked telemetry msg from entity [{}]" , sourceEntityId ) ;
log . debug ( "Received linked telemetry msg from entity [{}]" , sourceEntityId ) ;
var proto = msg . getProto ( ) ;
var proto = msg . getProto ( ) ;
var callback = msg . getCallback ( ) ;
var linksList = proto . getLinksList ( ) ;
var linksList = proto . getLinksList ( ) ;
if ( linksList . isEmpty ( ) ) {
if ( linksList . isEmpty ( ) ) {
log . debug ( "[{}] No CF links to process new telemetry." , msg . getTenantId ( ) ) ;
log . debug ( "[{}] No CF links to process new telemetry." , msg . getTenantId ( ) ) ;
msg . getC allback( ) . onSuccess ( ) ;
c allback. onSuccess ( ) ;
}
}
for ( var linkProto : linksList ) {
for ( var linkProto : linksList ) {
var link = fromProto ( linkProto ) ;
var link = fromProto ( linkProto ) ;
@ -378,21 +431,23 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
var cf = calculatedFields . get ( link . cfId ( ) ) ;
var cf = calculatedFields . get ( link . cfId ( ) ) ;
if ( EntityType . DEVICE_PROFILE . equals ( targetEntityType ) | | EntityType . ASSET_PROFILE . equals ( targetEntityType ) ) {
if ( EntityType . DEVICE_PROFILE . equals ( targetEntityType ) | | EntityType . ASSET_PROFILE . equals ( targetEntityType ) ) {
// iterate over all entities that belong to profile and push the message for corresponding CF
// iterate over all entities that belong to profile and push the message for corresponding CF
var entityIds = cfEntity Cache. getMy EntityIdsByProfileId ( tenantId , targetEntityId ) ;
var entityIds = entityProfile Cache. getEntityIdsByProfileId ( targetEntityId ) ;
if ( ! entityIds . isEmpty ( ) ) {
if ( ! entityIds . isEmpty ( ) ) {
MultipleTbCallback c allback = new MultipleTbCallback ( entityIds . size ( ) , msg . getC allback( ) ) ;
MultipleTbCallback multipleC allback = new MultipleTbCallback ( entityIds . size ( ) , c allback) ;
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg ( tenantId , sourceEntityId , proto . getMsg ( ) , cf , c allback) ;
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg ( tenantId , sourceEntityId , proto . getMsg ( ) , cf , multipleC allback) ;
entityIds . forEach ( entityId - > {
entityIds . forEach ( entityId - > {
log . debug ( "Pushing linked telemetry msg to specific actor [{}]" , entityId ) ;
if ( isMyPartition ( entityId , multipleCallback ) ) {
getOrCreateActor ( entityId ) . tell ( newMsg ) ;
log . debug ( "Pushing linked telemetry msg to specific actor [{}]" , entityId ) ;
getOrCreateActor ( entityId ) . tell ( newMsg ) ;
}
} ) ;
} ) ;
} else {
} else {
msg . getC allback( ) . onSuccess ( ) ;
c allback. onSuccess ( ) ;
}
}
} else {
} else {
if ( isMyPartition ( targetEntityId , msg . getC allback( ) ) ) {
if ( isMyPartition ( targetEntityId , c allback) ) {
log . debug ( "Pushing linked telemetry msg to specific actor [{}]" , targetEntityId ) ;
log . debug ( "Pushing linked telemetry msg to specific actor [{}]" , targetEntityId ) ;
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg ( tenantId , sourceEntityId , proto . getMsg ( ) , cf , msg . getC allback( ) ) ;
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg ( tenantId , sourceEntityId , proto . getMsg ( ) , cf , c allback) ;
getOrCreateActor ( targetEntityId ) . tell ( newMsg ) ;
getOrCreateActor ( targetEntityId ) . tell ( newMsg ) ;
}
}
}
}
@ -438,10 +493,14 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
EntityId entityId = cfCtx . getEntityId ( ) ;
EntityId entityId = cfCtx . getEntityId ( ) ;
EntityType entityType = cfCtx . getEntityId ( ) . getEntityType ( ) ;
EntityType entityType = cfCtx . getEntityId ( ) . getEntityType ( ) ;
if ( isProfileEntity ( entityType ) ) {
if ( isProfileEntity ( entityType ) ) {
var entityIds = cfEntity Cache. getMy EntityIdsByProfileId ( tenantId , entityId ) ;
var entityIds = entityProfile Cache. getEntityIdsByProfileId ( entityId ) ;
if ( ! entityIds . isEmpty ( ) ) {
if ( ! entityIds . isEmpty ( ) ) {
var multiCallback = new MultipleTbCallback ( entityIds . size ( ) , callback ) ;
var multiCallback = new MultipleTbCallback ( entityIds . size ( ) , callback ) ;
entityIds . forEach ( id - > initCfForEntity ( id , cfCtx , forceStateReinit , multiCallback ) ) ;
entityIds . forEach ( id - > {
if ( isMyPartition ( id , multiCallback ) ) {
initCfForEntity ( id , cfCtx , forceStateReinit , multiCallback ) ;
}
} ) ;
} else {
} else {
callback . onSuccess ( ) ;
callback . onSuccess ( ) ;
}
}
@ -479,7 +538,8 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
return switch ( entityId . getEntityType ( ) ) {
return switch ( entityId . getEntityType ( ) ) {
case ASSET - > assetProfileCache . get ( tenantId , ( AssetId ) entityId ) . getId ( ) ;
case ASSET - > assetProfileCache . get ( tenantId , ( AssetId ) entityId ) . getId ( ) ;
case DEVICE - > deviceProfileCache . get ( tenantId , ( DeviceId ) entityId ) . getId ( ) ;
case DEVICE - > deviceProfileCache . get ( tenantId , ( DeviceId ) entityId ) . getId ( ) ;
default - > null ;
default - >
throw new IllegalArgumentException ( "'" + entityId . getEntityType ( ) + "' is not profile entity." + entityId ) ;
} ;
} ;
}
}
@ -504,4 +564,45 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
public void onPartitionChange ( CalculatedFieldPartitionChangeMsg msg ) {
public void onPartitionChange ( CalculatedFieldPartitionChangeMsg msg ) {
ctx . broadcastToChildren ( msg , true ) ;
ctx . broadcastToChildren ( msg , true ) ;
}
}
public void initCalculatedFields ( ) {
PageDataIterable < CalculatedField > cfs = new PageDataIterable < > ( pageLink - > cfDaoService . findCalculatedFieldsByTenantId ( tenantId , pageLink ) , initFetchPackSize ) ;
cfs . forEach ( cf - > {
log . trace ( "Processing calculated field record: {}" , cf ) ;
try {
onFieldInitMsg ( new CalculatedFieldInitMsg ( cf . getTenantId ( ) , cf ) ) ;
} catch ( CalculatedFieldException e ) {
log . error ( "Failed to process calculated field record: {}" , cf , e ) ;
}
} ) ;
calculatedFields . values ( ) . forEach ( cf - > {
entityIdCalculatedFields . computeIfAbsent ( cf . getEntityId ( ) , id - > new CopyOnWriteArrayList < > ( ) ) . add ( cf ) ;
} ) ;
PageDataIterable < CalculatedFieldLink > cfls = new PageDataIterable < > ( pageLink - > cfDaoService . findAllCalculatedFieldLinksByTenantId ( tenantId , pageLink ) , initFetchPackSize ) ;
cfls . forEach ( link - > {
onLinkInitMsg ( new CalculatedFieldLinkInitMsg ( link . getTenantId ( ) , link ) ) ;
} ) ;
}
private void initEntityProfileCache ( ) {
PageDataIterable < ProfileEntityIdInfo > deviceIdInfos = new PageDataIterable < > ( pageLink - > deviceService . findProfileEntityIdInfosByTenantId ( tenantId , pageLink ) , initFetchPackSize ) ;
for ( ProfileEntityIdInfo idInfo : deviceIdInfos ) {
log . trace ( "Processing device record: {}" , idInfo ) ;
try {
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
} catch ( Exception e ) {
log . error ( "Failed to process device record: {}" , idInfo , e ) ;
}
}
PageDataIterable < ProfileEntityIdInfo > assetIdInfos = new PageDataIterable < > ( pageLink - > assetService . findProfileEntityIdInfosByTenantId ( tenantId , pageLink ) , initFetchPackSize ) ;
for ( ProfileEntityIdInfo idInfo : assetIdInfos ) {
log . trace ( "Processing asset record: {}" , idInfo ) ;
try {
entityProfileCache . add ( idInfo . getProfileId ( ) , idInfo . getEntityId ( ) ) ;
} catch ( Exception e ) {
log . error ( "Failed to process asset record: {}" , idInfo , e ) ;
}
}
}
}
}