Database
1. Overview
FT System Metrics connects to an existing FTACS database in read-only mode.
The service does not create or modify tables (ddl-auto: none).
Supported databases:
-
MySQL 8.0+ — primary, used by default
-
Oracle 21c+ — alternative, activated via the
oracleprofile
2. Database Schema
The service reads data from the following FTACS tables:
2.2. Tables Description
| Table | Description |
|---|---|
|
CPE device registry. Contains protocol, serial number, online status, and tenant binding ( |
|
ISP/tenant directory. Contains ID and name |
|
Tasks in pending status. |
|
Successfully completed tasks |
|
Tasks that ended with an error |
|
Rejected tasks |
|
CPE device event log |
|
CPE event type directory |
|
Next session time for TR069 devices. Used to determine online status for TR069 |
3. Repository Layer
3.1. CpeRepository
JPA repository for the cpe table with native queries:
// Total devices by protocol and tenant
@Query(value = """
SELECT c.protocol_id AS protocolId,
COUNT(*) AS count,
c.location_id AS ispId
FROM cpe c
GROUP BY c.protocol_id, c.location_id
""", nativeQuery = true)
List<ProtocolCountProjection> countByProtocol();
// Online status for non-TR069 devices (is_online = 1)
List<OnlineStatusProjection> countOnlineStatusByProtocolNonTR();
// Online status for TR069 via cpe_next_session_time
List<OnlineStatusProjection> countOnlineStatusTR(LocalDateTime endTime);
3.2. CpeTaskQueryRepository
Interface for task queries. MySQL and Oracle implementations use UNION ALL across four tables:
// Task count by status and date (previous 1-2 days)
List<TaskStatusProjection> countTasksByStatusAndDate(
LocalDateTime startTime, LocalDateTime endTime);
// Task count by type and hour (previous 24 hours)
List<TaskTypeProjection> countTasksByTypeAndHour(
LocalDateTime startTime, LocalDateTime endTime);
Example UNION query (MySQL):
SELECT 'Completed' AS status, COUNT(*) AS count,
DATE_FORMAT(created, '%Y-%m-%d') AS day, c.location_id AS ispId
FROM cpe_completed_task t JOIN cpe c ON t.cpe_id = c.id
WHERE t.created BETWEEN :start AND :end
GROUP BY day, c.location_id
UNION ALL
SELECT 'Failed' AS status, COUNT(*) AS count, ...
UNION ALL
SELECT 'Rejected' AS status, COUNT(*) AS count, ...
UNION ALL
SELECT 'Pending' AS status, COUNT(*) AS count, ... -- repeats = 0
UNION ALL
SELECT 'Sent' AS status, COUNT(*) AS count, ... -- repeats > 0
3.3. CpeLogQueryRepository
Interface for event queries:
// Event count by type and hour
List<EventTypeProjection> countEventsByTypeAndHour(
LocalDateTime startTime, LocalDateTime endTime);
// Total event count by tenant
List<EventCountProjection> countEventsInTimeRange(
LocalDateTime startTime, LocalDateTime endTime);
4. Projections
Projection interfaces map native SQL query results to Java objects:
| Projection | Fields |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Record implementations (TaskStatusProjectionImpl, TaskTypeProjectionImpl, EventCountProjectionImpl, EventTypeProjectionImpl) are used in database-specific query repositories.
5. Multi-Database Query Strategy
Queries containing database-specific SQL functions are placed in separate repositories selected via Spring Profile:
src/main/java/com/friendly/repository/
+-- CpeTaskQueryRepository.java # Interface
+-- CpeLogQueryRepository.java # Interface
+-- mysql/
| +-- MysqlCpeTaskQueryRepository.java # @Profile("mysql")
| +-- MysqlCpeLogQueryRepository.java # @Profile("mysql")
+-- oracle/
+-- OracleCpeTaskQueryRepository.java # @Profile("oracle")
+-- OracleCpeLogQueryRepository.java # @Profile("oracle")
Metrics collectors depend only on interfaces (CpeTaskQueryRepository, CpeLogQueryRepository) and are unaware of the specific database engine.
6. Best Practices
6.1. Database Access
-
Read-only access — the service uses
ddl-auto: noneand does not modify data -
Native queries — complex aggregations (UNION ALL, GROUP BY) are executed via native SQL for maximum performance
-
Projection pattern — results are mapped to lightweight interfaces/records instead of full entities
-
Database abstraction — database-specific SQL is isolated in separate implementations
6.2. Performance Considerations
-
Connection pooling — HikariCP with a configurable pool size (default max=10, min=2)
-
Prepared statement caching — enabled by default (
cachePrepStmts=true, cache size=512) -
Time-bounded queries — all queries are limited to time windows (previous hour/day)
-
Lazy loading — JoinFetch is used only for required associations