From aa4dcbecf0e95ec2a02d82a79449117c5d31183e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 8 May 2026 09:06:53 +0200 Subject: [PATCH] DeviceStateService: swallow TenantNotFoundException in async device-added callback Wrap the body of `Futures.addCallback(fetchDeviceState(device), ...).onSuccess` in try/catch. When the tenant has been removed between `fetchDeviceState` completing and `partitionService.resolve` running (test tearDown, shutdown, rebalance), `getRoutingInfo` throws `TenantNotFoundException` from a `ForkJoinPool` worker thread. Guava's `Futures.CallbackListener.run` does not wrap `onSuccess`, so the exception escapes to the executor and is attributed by Surefire to whichever test was last on that thread, producing spurious flaky failures (e.g. PskLwm2mIntegrationTest testWithPskConnectLwm2mBadPskKeyByLength_BAD_REQUEST). Treat `TenantNotFoundException` as a no-op (log + onSuccess), and route any other unexpected throwable through `callback.onFailure(...)` consistent with the outer `onQueueMsg` handler. --- .../state/DefaultDeviceStateService.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 40f453de42..5a2b106339 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -358,16 +358,26 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService() { @Override public void onSuccess(DeviceStateData state) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId()); - Set deviceIds = partitionedEntities.get(tpi); - boolean isMyPartition = deviceIds != null; - if (isMyPartition) { - deviceIds.add(state.getDeviceId()); - initializeActivityState(deviceId, state); + try { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId()); + Set deviceIds = partitionedEntities.get(tpi); + boolean isMyPartition = deviceIds != null; + if (isMyPartition) { + deviceIds.add(state.getDeviceId()); + initializeActivityState(deviceId, state); + callback.onSuccess(); + } else { + log.debug("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}", tenantId, deviceId, tpi.getFullTopicName()); + callback.onFailure(new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!")); + } + } catch (TenantNotFoundException e) { + // Tenant was removed (e.g. during shutdown / test tearDown) between fetchDeviceState completing + // and partition resolution. Device-state registration is moot in that case. + log.debug("[{}][{}] Tenant not found while registering device to the state service; skipping.", tenantId, deviceId); callback.onSuccess(); - } else { - log.debug("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}", tenantId, deviceId, tpi.getFullTopicName()); - callback.onFailure(new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!")); + } catch (Throwable t) { + log.warn("[{}][{}] Failed to register device to the state service", tenantId, deviceId, t); + callback.onFailure(t); } }