Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion obp-api/src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ import code.metadata.tags.MappedTag
import code.metadata.transactionimages.MappedTransactionImage
import code.metadata.wheretags.MappedWhereTag
import code.methodrouting.MethodRouting
import code.metrics.{ConnectorTrace, MappedConnectorMetric, MappedMetric, MetricArchive}
import code.metrics.{ConnectorTrace, MappedConnectorMetric, MappedMetric, MetricArchive, MetricsArchiveRun}
import code.migration.MigrationScriptLog
import code.model._
import code.model.dataAccess._
Expand Down Expand Up @@ -994,6 +994,7 @@ object ToSchemify extends MdcLoggable {
TransactionRequestAttribute,
MappedMetric,
MetricArchive,
MetricsArchiveRun,
MapperAccountHolders,
MappedEntitlement,
MappedConnectorMetric,
Expand Down
6 changes: 6 additions & 0 deletions obp-api/src/main/scala/code/api/util/ApiRole.scala
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,12 @@ object ApiRole extends MdcLoggable{
case class CanGetDatabasePoolInfo(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetDatabasePoolInfo = CanGetDatabasePoolInfo()

case class CanGetMetricsDiagnostics(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetMetricsDiagnostics = CanGetMetricsDiagnostics()

case class CanCreateMetricsArchiveRun(requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateMetricsArchiveRun = CanCreateMetricsArchiveRun()

case class CanGetConnectorHealth(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetConnectorHealth = CanGetConnectorHealth()

Expand Down
122 changes: 121 additions & 1 deletion obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import code.api.Constant._
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.util.APIUtil.{EmptyBody, _}
import code.api.util.{APIUtil, ApiRole, CallContext, CustomJsonFormats, Glossary, NewStyle}
import code.api.util.ApiRole.{canCreateEntitlementAtAnyBank, canCreateEntitlementAtOneBank, canCreateOrganisation, canCreateRoutingScheme, canCreateTestEmail, canDeleteEntitlementAtAnyBank, canDeleteOrganisation, canDeleteRoutingScheme, canGetAccountAccessTrace, canGetAnyOrganisation, canGetAnyUser, canGetCacheConfig, canGetCacheInfo, canGetCacheNamespaces, canGetConnectorHealth, canGetCustomersAtOneBank, canGetDatabasePoolInfo, canGetMigrations, canUpdateBankSupportedRoutingScheme, canUpdateOrganisation, canUpdateRoutingScheme, canUpdateSystemView}
import code.api.util.ApiRole.{canCreateEntitlementAtAnyBank, canCreateEntitlementAtOneBank, canCreateMetricsArchiveRun, canCreateOrganisation, canCreateRoutingScheme, canCreateTestEmail, canDeleteEntitlementAtAnyBank, canDeleteOrganisation, canDeleteRoutingScheme, canGetAccountAccessTrace, canGetAnyOrganisation, canGetAnyUser, canGetCacheConfig, canGetCacheInfo, canGetCacheNamespaces, canGetConnectorHealth, canGetCustomersAtOneBank, canGetDatabasePoolInfo, canGetMetricsDiagnostics, canGetMigrations, canUpdateBankSupportedRoutingScheme, canUpdateOrganisation, canUpdateRoutingScheme, canUpdateSystemView}
import code.api.util.CommonsEmailWrapper
import code.model.dataAccess.AuthUser
import code.api.util.ApiTag._
Expand Down Expand Up @@ -3240,6 +3240,126 @@ object Http4s700 {
http4sPartialFunction = Some(factoryResetSystemView)
)

// Route: GET /obp/v7.0.0/management/system/diagnostics/metrics
//
// Operator diagnostic for the metrics-archiving pipeline. Reports the
// archiving props plus row counts and the oldest/newest record in both the
// `metric` and `metricarchive` tables, then runs integrity checks that
// surface whether MetricsArchiveScheduler is keeping each table inside its
// configured retention window. Intended for use from the API Manager.
val getMetricsDiagnostics: HttpRoutes[IO] = HttpRoutes.of[IO] {
case req @ GET -> `prefixPath` / "management" / "system" / "diagnostics" / "metrics" =>
EndpointHelpers.withUser(req) { (_, _) =>
Future {
JSONFactory700.createMetricsAndArchiveMetricsDiagnosticsJsonV700()
}
}
}

resourceDocs += ResourceDoc(
implementedInApiVersion,
nameOf(getMetricsDiagnostics),
"GET",
"/management/system/diagnostics/metrics",
"Get Metrics and Archive Metrics Diagnostics",
s"""Diagnostic for the metrics-archiving pipeline (`MetricsArchiveScheduler`).
|
|Returns:
|
|* `config` — the relevant props and their *effective* values after the
| scheduler's floors are applied (`retain_metrics_days` floored to 60,
| `retain_archive_metrics_days` floored to 365): `write_metrics`,
| `enable_metrics_scheduler`, `retain_metrics_scheduler_interval_in_seconds`,
| `retain_metrics_days`, `retain_archive_metrics_days`,
| `retain_metrics_move_limit`.
|* `metric` — row count and oldest/newest record (date + age in days) of
| the live `metric` table.
|* `metric_archive` — the same for the `metricarchive` table.
|* `last_run` / `last_successful_run` — the most recent (and most recent
| successful) scheduler run from the `metricsarchiverun` audit log, including
| rows moved, rows deleted, duration and success. Absent if no run has been
| recorded yet.
|* `checks` — a list of integrity checks, each with a `status` of `OK`,
| `WARNING`, or `ERROR`:
| * `write_metrics_enabled`
| * `metrics_scheduler_enabled`
| * `metric_oldest_within_retention` — flags if the oldest live metric
| is older than the retention window (move job not keeping up / stopped).
| * `archive_oldest_within_retention` — flags if the oldest archived
| metric is older than the archive retention (cleanup not keeping up / stopped).
| * `archive_recently_updated` — flags if a backlog exists but the newest
| archived record is stale (move job stopped).
| * `archive_job_last_run` — reports the outcome of the most recent run
| from the run log (errors if the last run failed; warns if none recorded).
|* `everything_as_expected` — `true` only when every check is `OK`.
|
|${userAuthenticationMessage(true)}""".stripMargin,
EmptyBody,
JSONFactory700.metricsAndArchiveMetricsDiagnosticsJsonV700Example,
List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError),
apiTagMetric :: apiTagSystem :: apiTagApi :: Nil,
Some(List(canGetMetricsDiagnostics)),
http4sPartialFunction = Some(getMetricsDiagnostics)
)

// Route: POST /obp/v7.0.0/management/system/diagnostics/metrics/run
//
// Manually trigger one metrics-archive run. This calls the exact same
// `MetricsArchiveScheduler.runOnce()` the timer uses, so it honours the same
// concurrency lock (won't start if a run is already in progress), the same
// retention props/floors, and records the run in the `metricsarchiverun` log.
// The run executes synchronously and may take a while for large backlogs
// (it moves up to `retain_metrics_move_limit` rows).
val triggerMetricsArchiveRun: HttpRoutes[IO] = HttpRoutes.of[IO] {
case req @ POST -> `prefixPath` / "management" / "system" / "diagnostics" / "metrics" / "run" =>
EndpointHelpers.withUser(req) { (user, _) =>
Future {
val outcome = code.scheduler.MetricsArchiveScheduler.runOnce()
logger.info(s"AUDIT triggerMetricsArchiveRun: user_id=${user.userId} outcome=${outcome.getClass.getSimpleName}")
JSONFactory700.createTriggerMetricsArchiveRunResponseJsonV700(outcome)
}
}
}

resourceDocs += ResourceDoc(
implementedInApiVersion,
nameOf(triggerMetricsArchiveRun),
"POST",
"/management/system/diagnostics/metrics/run",
"Trigger a Metrics Archive Run",
s"""Manually run the metrics-archiving job once, on demand.
|
|This invokes the **same** code path as the scheduled
|`MetricsArchiveScheduler` run, so it respects all the same checks:
|
|* **Concurrency lock** — if an archive run is already in progress (the
| `JobScheduler` lock is held, on this or another node), no new run is
| started and the response `status` is `skipped_already_in_progress`.
|* **Retention rules** — moves `metric` rows older than the effective
| `retain_metrics_days` (floored to 60) to `metricarchive`, up to
| `retain_metrics_move_limit` rows; then deletes `metricarchive` rows
| older than the effective `retain_archive_metrics_days` (floored to 365).
|* **Run log** — the outcome is written to the `metricsarchiverun` audit
| log, exactly as a scheduled run would be.
|
|Response fields:
|* `status` — `completed` (a run executed — inspect `run.success`) or
| `skipped_already_in_progress`.
|* `message` — human-readable summary.
|* `run` — the recorded run (run id, counts, duration, success, remark);
| absent when skipped.
|
|Note: the run executes synchronously, so a large backlog may take a while.
|
|${userAuthenticationMessage(true)}""".stripMargin,
EmptyBody,
JSONFactory700.triggerMetricsArchiveRunResponseJsonV700Example,
List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError),
apiTagMetric :: apiTagSystem :: apiTagApi :: Nil,
Some(List(canCreateMetricsArchiveRun)),
http4sPartialFunction = Some(triggerMetricsArchiveRun)
)

// Enabled only in Lift test mode (Props.testMode == true, i.e. -Drun.mode=test).
// Props.testMode is set from the JVM system property before any props file loads,
// so it is reliably available at object-initialization time unlike file-based props.
Expand Down
Loading
Loading