From baaa9f723501e94b3c09c4e2d9792178b98fa2fc Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 6 Jun 2025 20:25:00 +0300 Subject: [PATCH] Update last inactivity alarm time after a successful database save; improve tests --- .../state/DefaultDeviceStateService.java | 25 ++++++--- .../state/DefaultDeviceStateServiceTest.java | 51 +++++++++---------- 2 files changed, 43 insertions(+), 33 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 13745ae6ab..23fdc6b8c0 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 @@ -340,7 +340,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService() { + @Override + public void onSuccess(Void success) { + stateData.getState().setLastInactivityAlarmTime(ts); + onDeviceActivityStatusChange(false, stateData); + } + + @Override + public void onFailure(@NonNull Throwable t) { + log.error("[{}][{}] Failed to update device last inactivity alarm time to '{}'. Device state data: {}", tenantId, deviceId, ts, stateData, t); + } + }, deviceStateCallbackExecutor); } private static boolean isActive(long ts, DeviceState state) { diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index cbf7363441..0fe29eef57 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -882,10 +882,8 @@ class DefaultDeviceStateServiceTest { } @Test - void givenInactiveDevice_whenActivityStatusChangesToActiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache() { + void givenInactiveDevice_whenActivityStatusChangesToActiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache2() { // GIVEN - doReturn(200L).when(service).getCurrentTimeMillis(); - var deviceState = DeviceState.builder() .active(false) .lastActivityTime(100L) @@ -902,20 +900,21 @@ class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); service.getPartitionedEntities(tpi).add(deviceId); - when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) - .thenAnswer(invocation -> { - AttributesSaveRequest request = invocation.getArgument(0); - AttributeKvEntry entry = request.getEntries().get(0); - return entry.getKey().equals(ACTIVITY_STATE) ? - Futures.immediateFailedFuture(new RuntimeException("failed to save")) : - Futures.immediateFuture(generateRandomVersions(1)); - }); + // WHEN-THEN - // WHEN - service.onDeviceActivity(tenantId, deviceId, 220L); + // simulating short DB outage + given(telemetrySubscriptionService.saveAttributesInternal(any())).willReturn(Futures.immediateFailedFuture(new RuntimeException("failed to save"))); + doReturn(200L).when(service).getCurrentTimeMillis(); + service.onDeviceActivity(tenantId, deviceId, 180L); + assertThat(deviceState.isActive()).isFalse(); // still inactive - // THEN - assertThat(deviceState.isActive()).isFalse(); + // 10 millis pass... and new activity message it received + + // this time DB save is successful + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + doReturn(210L).when(service).getCurrentTimeMillis(); + service.onDeviceActivity(tenantId, deviceId, 190L); + assertThat(deviceState.isActive()).isTrue(); } @Test @@ -937,21 +936,21 @@ class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); service.getPartitionedEntities(tpi).add(deviceId); - when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) - .thenAnswer(invocation -> { - AttributesSaveRequest request = invocation.getArgument(0); - AttributeKvEntry entry = request.getEntries().get(0); - return entry.getKey().equals(ACTIVITY_STATE) ? - Futures.immediateFailedFuture(new RuntimeException("failed to save")) : - Futures.immediateFuture(generateRandomVersions(1)); - }); + // WHEN-THEN (assuming periodic activity states check is done every 100 millis) - // WHEN + // simulating short DB outage + given(telemetrySubscriptionService.saveAttributesInternal(any())).willReturn(Futures.immediateFailedFuture(new RuntimeException("failed to save"))); doReturn(200L).when(service).getCurrentTimeMillis(); service.checkStates(); + assertThat(deviceState.isActive()).isTrue(); // still active - // THEN - assertThat(deviceState.isActive()).isTrue(); + // waiting 100 millis... periodic activity states check is triggered again + + // this time DB save is successful + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + doReturn(300L).when(service).getCurrentTimeMillis(); + service.checkStates(); + assertThat(deviceState.isActive()).isFalse(); } @Test