Fai Luk 19 часов назад
Родитель
Сommit
1fb5200e16
3 измененных файлов: 35 добавлений и 5 удалений
  1. +26
    -3
      src/main/java/com/ffii/fpsms/modules/monitoring/service/ClientPresenceService.kt
  2. +7
    -2
      src/main/java/com/ffii/fpsms/modules/monitoring/service/MonitoringRetentionService.kt
  3. +2
    -0
      src/main/resources/application.yml

+ 26
- 3
src/main/java/com/ffii/fpsms/modules/monitoring/service/ClientPresenceService.kt Просмотреть файл

@@ -24,6 +24,10 @@ open class ClientPresenceService(
@Value("\${fpsms.client-presence.history-max-range-days:30}") @Value("\${fpsms.client-presence.history-max-range-days:30}")
private var historyMaxRangeDays: Long = 30 private var historyMaxRangeDays: Long = 30


/** Omit from live monitor and purge rows with no heartbeat for longer than this. */
@Value("\${fpsms.client-presence.live-presence-retention-days:7}")
private var livePresenceRetentionDays: Long = 7

data class HeartbeatRequest( data class HeartbeatRequest(
val deviceId: String, val deviceId: String,
val displayName: String? = null, val displayName: String? = null,
@@ -201,13 +205,16 @@ open class ClientPresenceService(
mapOf("cutoff" to cutoff), mapOf("cutoff" to cutoff),
) )
for (row in rows) { for (row in rows) {
maybeRecordEvent(row, "offline", deriveStatus(row, now), now)
val lastHb = parseDateTime(row["lastHeartbeat"])
val statusBeforeOffline = if (lastHb != null) deriveStatus(row, lastHb) else null
maybeRecordEvent(row, "offline", statusBeforeOffline, now)
} }
} }


@Transactional(readOnly = true) @Transactional(readOnly = true)
open fun listActive(clientTypeFilter: String?): List<Map<String, Any>> { open fun listActive(clientTypeFilter: String?): List<Map<String, Any>> {
val type = clientTypeFilter?.trim()?.lowercase()?.takeIf { it.isNotBlank() && it != "all" } val type = clientTypeFilter?.trim()?.lowercase()?.takeIf { it.isNotBlank() && it != "all" }
val minHeartbeat = LocalDateTime.now().minusDays(livePresenceRetentionDays.coerceAtLeast(1))
val sql = buildString { val sql = buildString {
append( append(
""" """
@@ -226,7 +233,7 @@ open class ClientPresenceService(
last_heartbeat AS lastHeartbeat, last_heartbeat AS lastHeartbeat,
last_activity AS lastActivity last_activity AS lastActivity
FROM client_presence FROM client_presence
WHERE 1=1
WHERE last_heartbeat >= :minHeartbeat
""".trimIndent(), """.trimIndent(),
) )
if (type != null) { if (type != null) {
@@ -234,12 +241,28 @@ open class ClientPresenceService(
} }
append(" ORDER BY last_heartbeat DESC") append(" ORDER BY last_heartbeat DESC")
} }
val args = if (type != null) mapOf("clientType" to type) else emptyMap<String, Any>()
val args = mutableMapOf<String, Any>("minHeartbeat" to minHeartbeat)
if (type != null) {
args["clientType"] = type
}
val rows = jdbcDao.queryForList(sql, args) val rows = jdbcDao.queryForList(sql, args)
val now = LocalDateTime.now() val now = LocalDateTime.now()
return rows.map { row -> enrichWithStatus(row, now) } return rows.map { row -> enrichWithStatus(row, now) }
} }


@Transactional
open fun purgeStalePresence(): Int {
val days = livePresenceRetentionDays.coerceAtLeast(1)
val cutoff = LocalDateTime.now().minusDays(days)
return jdbcDao.executeUpdate(
"""
DELETE FROM client_presence
WHERE last_heartbeat < :cutoff
""".trimIndent(),
mapOf("cutoff" to cutoff),
)
}

@Transactional(readOnly = true) @Transactional(readOnly = true)
open fun listHistory( open fun listHistory(
from: LocalDateTime, from: LocalDateTime,


+ 7
- 2
src/main/java/com/ffii/fpsms/modules/monitoring/service/MonitoringRetentionService.kt Просмотреть файл

@@ -10,6 +10,7 @@ import java.time.LocalDateTime
@Service @Service
open class MonitoringRetentionService( open class MonitoringRetentionService(
private val jdbcDao: JdbcDao, private val jdbcDao: JdbcDao,
private val clientPresenceService: ClientPresenceService,
) { ) {
private val log = LoggerFactory.getLogger(MonitoringRetentionService::class.java) private val log = LoggerFactory.getLogger(MonitoringRetentionService::class.java)


@@ -36,19 +37,23 @@ open class MonitoringRetentionService(
mapOf("cutoff" to cutoff), mapOf("cutoff" to cutoff),
) )


if (deviceDeleted > 0 || printerDeleted > 0) {
val stalePresenceDeleted = clientPresenceService.purgeStalePresence()

if (deviceDeleted > 0 || printerDeleted > 0 || stalePresenceDeleted > 0) {
log.info( log.info(
"Monitoring retention: deleted {} device event(s) and {} printer event(s) older than {} day(s) (before {})",
"Monitoring retention: deleted {} device event(s), {} printer event(s) older than {} day(s) (before {}), and {} stale presence row(s)",
deviceDeleted, deviceDeleted,
printerDeleted, printerDeleted,
days, days,
cutoff, cutoff,
stalePresenceDeleted,
) )
} }


return mapOf( return mapOf(
"deviceEventsDeleted" to deviceDeleted, "deviceEventsDeleted" to deviceDeleted,
"printerEventsDeleted" to printerDeleted, "printerEventsDeleted" to printerDeleted,
"stalePresenceDeleted" to stalePresenceDeleted,
"retentionDays" to days.toInt(), "retentionDays" to days.toInt(),
) )
} }


+ 2
- 0
src/main/resources/application.yml Просмотреть файл

@@ -81,6 +81,8 @@ fpsms:
idle-threshold-sec: 300 idle-threshold-sec: 300
history-sample-sec: 60 history-sample-sec: 60
history-max-range-days: 30 history-max-range-days: 30
# Live monitor omits devices with no heartbeat longer than this; stale rows are purged daily.
live-presence-retention-days: 7
offline-scan-interval-ms: 60000 offline-scan-interval-ms: 60000
printer-monitor: printer-monitor:
scan-interval-ms: 120000 scan-interval-ms: 120000


Загрузка…
Отмена
Сохранить