Invalidate HRV cache based on latest sample timestamp

Query latest HRV sample to invalidate also yesterday if needed
This commit is contained in:
Gideon Zenz
2025-12-20 19:07:34 +01:00
committed by José Rebelo
parent 68f12ea92e
commit 3b4b7292c3
2 changed files with 70 additions and 17 deletions
@@ -448,22 +448,13 @@ public class ComputedHrvSummarySampleProvider implements TimeSampleProvider<HrvS
}
/**
* Invalidate cached summary for the current day for a specific device.
* This should be called when new HRV data is synced during the day to ensure
* the latest data is reflected in the computed summary.
* Invalidate a specific cache entry for a device and day.
*
* @param deviceAddress The MAC address of the device to invalidate cache for
* @param deviceAddress The MAC address of the device
* @param dayEndTimestamp The end timestamp of the day (23:59:59.999)
*/
public static void invalidateCurrentDay(String deviceAddress) {
final Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
final long dayEndTimestamp = cal.getTimeInMillis();
public static void invalidateCacheEntry(String deviceAddress, long dayEndTimestamp) {
final String cacheKey = deviceAddress + ":" + dayEndTimestamp;
synchronized (SUMMARY_CACHE) {
SUMMARY_CACHE.remove(cacheKey);
}
@@ -28,13 +28,21 @@ import androidx.core.content.ContextCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.ComputedHrvSummarySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.HrvValueSample;
/**
* Receiver that listens for new data events and invalidates the HRV summary cache
* for the current day. This ensures that computed HRV summaries are recalculated
* with the latest data when new HRV samples arrive during the day.
* for affected days. This ensures that computed HRV summaries are recalculated
* with the latest data when new HRV samples arrive.
*/
public class HrvCacheInvalidationReceiver {
private static final Logger LOG = LoggerFactory.getLogger(HrvCacheInvalidationReceiver.class);
@@ -67,16 +75,70 @@ public class HrvCacheInvalidationReceiver {
}
private static class HrvCacheInvalidationBroadcastReceiver extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(HrvCacheInvalidationBroadcastReceiver.class);
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_NEW_DATA.equals(intent.getAction())) {
final GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
if (device != null) {
// Invalidate HRV summary cache for current day when new data arrives
ComputedHrvSummarySampleProvider.invalidateCurrentDay(device.getAddress());
invalidateCacheForDevice(device);
}
}
}
private void invalidateCacheForDevice(GBDevice device) {
try (DBHandler db = GBApplication.acquireDB()) {
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
final TimeSampleProvider<? extends HrvValueSample> sampleProvider =
coordinator.getHrvValueSampleProvider(device, db.getDaoSession());
// Get the latest HRV sample to determine which days to invalidate
final HrvValueSample latestSample = sampleProvider.getLatestSample();
if (latestSample == null) {
// No HRV data at all, nothing to invalidate
return;
}
// Invalidate cache for days that might be affected by the latest data
final long latestTimestamp = latestSample.getTimestamp();
invalidateCacheForTimestamp(device.getAddress(), latestTimestamp);
LOG.debug("Invalidated HRV cache for device {} based on latest sample at {}",
device.getName(), new java.util.Date(latestTimestamp));
} catch (Exception e) {
LOG.error("Error invalidating HRV cache for device " + device.getName(), e);
}
}
private void invalidateCacheForTimestamp(String deviceAddress, long timestamp) {
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timestamp);
// Set to end of day for the timestamp
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
final long dayEndTimestamp = cal.getTimeInMillis();
// Invalidate the day containing the latest sample
ComputedHrvSummarySampleProvider.invalidateCacheEntry(deviceAddress, dayEndTimestamp);
// Also invalidate today if the latest sample is from a previous day
// (the weekly average and baseline calculations use data from multiple days)
final Calendar now = Calendar.getInstance();
now.set(Calendar.HOUR_OF_DAY, 23);
now.set(Calendar.MINUTE, 59);
now.set(Calendar.SECOND, 59);
now.set(Calendar.MILLISECOND, 999);
if (dayEndTimestamp < now.getTimeInMillis()) {
ComputedHrvSummarySampleProvider.invalidateCacheEntry(deviceAddress, now.getTimeInMillis());
}
}
}
}