Interface MetricRecordRepository

All Superinterfaces:
org.springframework.data.repository.CrudRepository<MetricRecordEntity,Long>, org.springframework.data.jpa.repository.JpaRepository<MetricRecordEntity,Long>, org.springframework.data.repository.ListCrudRepository<MetricRecordEntity,Long>, org.springframework.data.repository.ListPagingAndSortingRepository<MetricRecordEntity,Long>, org.springframework.data.repository.PagingAndSortingRepository<MetricRecordEntity,Long>, org.springframework.data.repository.query.QueryByExampleExecutor<MetricRecordEntity>, org.springframework.data.repository.Repository<MetricRecordEntity,Long>

@Repository public interface MetricRecordRepository extends org.springframework.data.jpa.repository.JpaRepository<MetricRecordEntity,Long>
Spring Data JPA repository for MetricRecordEntity.

Provides query methods for time-range retrieval, anomaly detection history (top-100 records per service used as the statistical baseline), and aggregate computations used by the SLA calculation and retention services.

Author:
Nazar Montytskyi
  • Method Details

    • findTop100ByServiceIdOrderByRecordedAtDesc

      List<MetricRecordEntity> findTop100ByServiceIdOrderByRecordedAtDesc(Long serviceId)
      Returns the N most recent metric records for a service, ordered newest first. Used by AnomalyDetector to build the sliding window for Z-score calculation.
      Parameters:
      serviceId - the service identifier
      Returns:
      up to 100 most recent records
    • findByServiceIdAndRecordedAtBetweenOrderByRecordedAtAsc

      List<MetricRecordEntity> findByServiceIdAndRecordedAtBetweenOrderByRecordedAtAsc(Long serviceId, LocalDateTime from, LocalDateTime to)
      Returns all metric records for a service within a time range. Used by the REST aggregate endpoint (/api/metrics/{serviceId}/aggregate).
      Parameters:
      serviceId - the service identifier
      from - start of the time window (inclusive)
      to - end of the time window (inclusive)
      Returns:
      records ordered oldest first
    • findTopByServiceIdOrderByRecordedAtDesc

      Optional<MetricRecordEntity> findTopByServiceIdOrderByRecordedAtDesc(Long serviceId)
      Returns the most recent metric record for a service regardless of source. Used by the service detail page to display the current state.
      Parameters:
      serviceId - the service identifier
      Returns:
      the latest record, if any
    • aggregateByServiceAndPeriod

      @Query("SELECT\n AVG(m.responseTimeMs),\n MIN(m.responseTimeMs),\n MAX(m.responseTimeMs),\n COUNT(m.id),\n SUM(CASE WHEN m.status = 'UP' THEN 1 ELSE 0 END)\nFROM MetricRecordEntity m\nWHERE m.service.id = :serviceId\n AND m.recordedAt BETWEEN :from AND :to\n") List<Object[]> aggregateByServiceAndPeriod(@Param("serviceId") Long serviceId, @Param("from") LocalDateTime from, @Param("to") LocalDateTime to)
      Calculates aggregate metrics for a service over a time window.

      Returns a single row with:

      • average, minimum, and maximum response_time_ms
      • total number of records in the window
      • number of records where status is 'UP'

      The caller computes uptime % as (upCount / total) * 100.

      Parameters:
      serviceId - the service identifier
      from - start of the time window
      to - end of the time window
      Returns:
      single-element list containing an Object[] array: [avgMs, minMs, maxMs, total, upCount]; empty list if no records exist in the window
    • countByEndpointAndPeriod

      @Query("SELECT m.endpoint, COUNT(m.id), AVG(m.responseTimeMs)\nFROM MetricRecordEntity m\nWHERE m.service.id = :serviceId\n AND m.endpoint IS NOT NULL\n AND m.recordedAt BETWEEN :from AND :to\nGROUP BY m.endpoint\nORDER BY COUNT(m.id) DESC\n") List<Object[]> countByEndpointAndPeriod(@Param("serviceId") Long serviceId, @Param("from") LocalDateTime from, @Param("to") LocalDateTime to)
      Counts records per endpoint for a service over a time window. Used by the endpoint breakdown table on the service detail page (FR-3).
      Parameters:
      serviceId - the service identifier
      from - start of the time window
      to - end of the time window
      Returns:
      list of [endpoint, count, avgResponseTimeMs] arrays
    • avgResponseTimeSince

      @Query("SELECT AVG(m.responseTimeMs) FROM MetricRecordEntity m WHERE m.service.id = :serviceId AND m.recordedAt >= :since") Double avgResponseTimeSince(@Param("serviceId") Long serviceId, @Param("since") LocalDateTime since)
    • countByServiceIdAndStatusSince

      @Query("SELECT COUNT(m) FROM MetricRecordEntity m WHERE m.service.id = :serviceId AND m.recordedAt >= :since AND m.status = :status") long countByServiceIdAndStatusSince(@Param("serviceId") Long serviceId, @Param("since") LocalDateTime since, @Param("status") HealthStatus status)
    • countByServiceIdSince

      @Query("SELECT COUNT(m) FROM MetricRecordEntity m WHERE m.service.id = :serviceId AND m.recordedAt >= :since") long countByServiceIdSince(@Param("serviceId") Long serviceId, @Param("since") LocalDateTime since)
    • findPercentiles

      @Query(value="SELECT\n percentile_cont(0.50) WITHIN GROUP (ORDER BY response_time_ms) AS p50,\n percentile_cont(0.95) WITHIN GROUP (ORDER BY response_time_ms) AS p95,\n percentile_cont(0.99) WITHIN GROUP (ORDER BY response_time_ms) AS p99\nFROM metric_records\nWHERE service_id = :serviceId\n AND recorded_at >= :since\n", nativeQuery=true) List<Object[]> findPercentiles(@Param("serviceId") Long serviceId, @Param("since") LocalDateTime since)
      Calculates the P50, P95, and P99 response-time percentiles for a service over a sliding time window using PostgreSQL's percentile_cont function.

      Uses a native query because JPQL does not support ordered-set aggregate functions (percentile_cont … WITHIN GROUP (ORDER BY …)).

      When no records match the filter (empty window), PostgreSQL returns a single row with NULL values for all three percentile columns.

      Parameters:
      serviceId - the service identifier
      since - start of the time window (inclusive)
      Returns:
      single-element list containing an Object[] row [p50, p95, p99]; the values are null when no records exist in the window. Returns List.of(new Object[]{null,null,null}) on empty input (PostgreSQL ordered-set aggregates always return one row).
    • countErrors

      @Query(value="SELECT COUNT(*) FROM metric_records\nWHERE service_id = :serviceId\n AND recorded_at >= :since\n AND error_flag = true\n", nativeQuery=true) long countErrors(@Param("serviceId") Long serviceId, @Param("since") LocalDateTime since)
      Counts the number of metric records for a service in the given time window where error_flag is TRUE.

      Uses the denormalized error_flag column to avoid expensive IS NOT NULL predicates on error_message.

      Parameters:
      serviceId - the service identifier
      since - start of the time window (inclusive)
      Returns:
      count of error records in the window
    • findTop40ByServiceIdAndSourceOrderByRecordedAtDesc

      List<MetricRecordEntity> findTop40ByServiceIdAndSourceOrderByRecordedAtDesc(Long serviceId, MetricRecordEntity.MetricSource source)
    • deleteByRecordedAtBefore

      @Modifying @Query("DELETE FROM MetricRecordEntity m WHERE m.recordedAt < :threshold") int deleteByRecordedAtBefore(@Param("threshold") LocalDateTime threshold)