Loading…
Loading…
Explains the scheduled background service that aggregates raw F5, runtime, and generic traffic logs into hourly summaries for observability dashboards.
The Aggregation Refresh Service is a background process responsible for transforming high-volume, raw traffic logs into pre-aggregated, hourly summary tables. This process is critical for the performance of observability dashboards, which query these summary tables instead of the raw logs directly.
The service operates on three primary data sources:
s2r_traffic_log.s2r_runtime_log.s2r_generic_request_log.It runs as a scheduled, multi-tenant job that processes data for all enabled sources. It can also be triggered on-demand to refresh data for a specific F5 instance if it is detected to be stale, ensuring that dashboards remain reasonably up-to-date without incurring the performance penalty of synchronous, on-demand aggregation.
The service employs two primary mechanisms for refreshing data: a periodic scheduled refresh for all sources and an on-demand asynchronous refresh for stale F5 instance data.
flowchart TD
subgraph "Scheduled Refresh (Periodic)"
direction TD
Scheduler["@Scheduled Timer\n(e.g., every 30 min)"] --> ScheduledRun[scheduledRefresh]
ScheduledRun --> TenantLoop{Per-Tenant Loop}
TenantLoop --> F5Refresh["F5 Incremental Refresh\n(for each enabled instance)"]
TenantLoop --> RuntimeRefresh[Runtime Aggregation Refresh]
TenantLoop --> GenericRefresh[Generic Aggregation Refresh]
F5Refresh --> AggTables[(Aggregation Tables)]
RuntimeRefresh --> AggTables
GenericRefresh --> AggTables
end
subgraph "On-Demand Stale Refresh (F5 Only)"
direction TD
APIConsumer["API Consumer\n(e.g., Dashboard UI)"] --> StaleCheck{refreshIfStale}
StaleCheck -- "Data is fresh" --> ReturnStale["Returns immediately\n(with existing data)"]
StaleCheck -- "Data is stale" --> SubmitAsync["Submits background job"]
SubmitAsync --> ReturnStale
SubmitAsync --> AsyncExecutor["Single-thread executor"]
AsyncExecutor --> BackgroundRefresh["runStaleCheckAndRefresh"]
BackgroundRefresh --> F5RefreshAsync["F5 Incremental or Full Refresh"]
F5RefreshAsync --> AggTables
endThe primary refresh mechanism is a scheduled job that runs at a configurable interval (defaulting to every 30 minutes).
scheduledRefresh method is triggered by the platform's scheduler. It uses a lock (AtomicBoolean running) to prevent multiple scheduled jobs from running concurrently.PerTenantJobRunner. All subsequent database queries for that iteration are scoped to the current tenant.refreshAllSourcesForCurrentTenant method executes the aggregation for each data source independently:
F5InstanceRecords and calls incrementalRefresh for each.refreshRuntimeServiceAggregation.refreshGenericServiceAggregation.S2R-AGG-REFRESH with success and failure counts, which can be used for monitoring the health of the aggregation process.To provide fresher data for interactive dashboards without blocking API requests, the service implements an asynchronous, on-demand refresh mechanism for F5 instances.
refreshIfStale(instanceId).STALE_THRESHOLD_MS, 30 minutes), it does nothing.asyncRefreshExecutor) and returns immediately. The calling API proceeds with the currently available (stale) data.runStaleCheckAndRefresh) then performs a more rigorous check against the s2r_f5_agg_refresh_state table in the database. Based on this state, it triggers either an incrementalRefresh or a fullRefresh for the specified F5 instance.This design ensures that API response times are not affected by long-running aggregation queries, while still providing a mechanism to update data that has become significantly out-of-date between scheduled runs.
The refresh process consists of several distinct aggregation stages, each populating a specific summary table. For F5 traffic, an incremental refresh processes data from the last known point (or the last 8 days on a cold start), while a full refresh always processes the last 8 days.
The refreshServiceAggregation method aggregates data from s2r_observed_traffic_hourly into s2r_agg_service_hourly. It also performs a second pass to join against the raw s2r_traffic_log to calculate latency sketches (sum, count, max), as latency data is not present in the intermediate hourly table.
IMPORTANT
To prevent database deadlocks in multi-node deployments where multiple service instances might attempt to refresh the same F5 instance concurrently, this operation is wrapped in a transaction that acquires a PostgreSQL advisory lock (pg_advisory_xact_lock). The lock is keyed on the F5 instance ID, serializing refreshes for the same instance while allowing refreshes for different instances to proceed in parallel.
The refreshConsumerAggregation method populates the s2r_agg_consumer_hourly table, summarizing traffic by the consumer's IP address (client_ip and gateway_client_ip). It aggregates hit counts and compiles a list of service URIs accessed by each consumer within the hour.
The refreshVsFailureAggregation method is specifically designed to power dashboards monitoring VS-level connection failures. It reads the raw s2r_traffic_log and counts events that indicate a failure at the virtual server level (e.g., service_id is null and frontend_uri is empty or /). It stores these hourly counts in s2r_f5_agg_vs_failure_hourly.
The refreshUriFailureAttribution method solves a specific problem where F5 logs for backend errors often lack the original request URI. To correctly attribute these failures, it performs a multi-step process:
s2r_traffic_log, joining failure events to other log events that share the same global_transaction_id. It finds the earliest "sibling" event that has a non-empty frontend_uri and uses that as the "real" URI for the failure.MasterDiscoveryAggregator.canonicalUri to produce a standardized, canonical form. This ensures that different raw URIs that represent the same logical endpoint (e.g., due to case differences) are grouped together.s2r_f5_agg_uri_failure_hourly table.The refreshObservedTopology method rebuilds the s2r_observed_topology table, which maps the observed relationships between front-end virtual servers, back-end virtual servers, service URIs, and backend hosts. It aggregates hit counts over 24-hour and 7-day windows from s2r_observed_traffic_hourly.
The refreshRuntimeServiceAggregation and refreshGenericServiceAggregation methods perform aggregations for runtime and generic gateway traffic, respectively. They read from their corresponding raw log tables (s2r_runtime_log, s2r_generic_request_log) and write to dedicated hourly summary tables (s2r_runtime_agg_service_hourly, s2r_generic_agg_service_hourly). This allows observability dashboards to present a unified view of traffic across all sources by taking the UNION of the F5, runtime, and generic aggregation tables.
After each refresh cycle for an F5 instance, a cleanup process runs. It deletes records older than a defined retention period from the aggregation tables to manage storage growth.
s2r_agg_service_hourly, s2r_agg_consumer_hourly, s2r_f5_agg_vs_failure_hourly, s2r_f5_agg_uri_failure_hourly. The same retention is applied to s2r_runtime_agg_service_hourly and s2r_generic_agg_service_hourly.s2r_observed_traffic_hourly.The service's behavior is controlled by the following application properties and internal constants.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
s2r.f5.agg-refresh.enabled | Boolean | true | No | Enables or disables the entire aggregation service. |
s2r.f5.agg-refresh.initial-delay-ms | Long | 120000 | No | Milliseconds to wait after startup before the first scheduled refresh. |
s2r.f5.agg-refresh.delay-ms | Long | 1800000 | No | Milliseconds between the completion of one scheduled refresh and the start of the next. |
f5_agg_refresh_delay_ms (System Setting) | Long | 1800000 | No | A system setting that can override s2r.f5.agg-refresh.delay-ms. A restart is required for changes to take effect. |
STALE_THRESHOLD_MS (Constant) | Long | 1800000 | No | Internal constant (30 minutes) defining the age at which F5 instance data is considered stale for the on-demand refreshIfStale check. |
The service exposes public methods that can be invoked by other components within the platform.
refreshIfStaleTriggers a non-blocking, asynchronous refresh for a specific F5 instance if its aggregated data is older than 30 minutes.
public void refreshIfStale(UUID instanceId)instanceId (UUID): The ID of the F5 instance to check and potentially refresh.fullRefreshTriggers a blocking, full refresh for a specific F5 instance, reprocessing data from the last 8 days.
public void fullRefresh(UUID instanceId)instanceId (UUID): The ID of the F5 instance to refresh.The service provides structured logs to aid in monitoring and troubleshooting.
| Log Token | Level | Description |
|---|---|---|
S2R-AGG-REFRESH | INFO | Logged upon successful completion of a scheduled refresh cycle. Includes counts of successful and failed source aggregations. The absence of this log over a time window may indicate a stalled service. |
S2R-AGG-REFRESH-FAIL | WARN | Logged when the aggregation for a specific source fails. Includes source (e.g., f5, runtime), tenant, instance (for F5), and the error message. |
refreshIfStale mechanism helps close this gap for frequently viewed F5 instances but is not instantaneous.s2r_f5_agg_service_hourly table. This is now mitigated by a pg_advisory_xact_lock. If deadlock errors reappear, it could indicate a problem with this locking mechanism or contention on other tables.refreshUriFailureAttribution process. Failures in this stage can prevent errors from being correctly counted. Check for uri_failure attribution refresh failed warnings in the logs.