Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3814a8a95
|
||
|
|
d91a02675c
|
||
|
|
99330035fe
|
||
|
|
8b02386898
|
||
|
|
2ff64f4558 | ||
|
|
510271c51c
|
No files matched your search
@@ -5,12 +5,13 @@
|
||||
|
||||
> Database performance for QQQ apps: slow and expensive queries from pg_stat_statements with delta snapshots, table scan and index analysis, live activity and blocking chains, connection capacity, maintenance, QQQ query attribution with N+1 detection, and ranked suggestions, all as standard QQQ tables.
|
||||
|
||||
**Status:** design: docs/SPEC.md in this repository; implementation in progress. Nothing is published yet.
|
||||
**Status:** Phase A is implemented: schema and secured metadata, PostgreSQL statistics collection, daily rollups and retention. Attribution listeners, suggestions, explain actions, dashboard/API integration and help are later phases. Nothing is published yet. Approved design: [docs/SPEC.md](docs/SPEC.md).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java 21.
|
||||
- QQQ 4.1 (built against `4.1.0-RC.10`).
|
||||
- QQQ 4.1 (built against `4.1.0-RC.11`).
|
||||
- PostgreSQL 13+ for collection; PostgreSQL 17 and H2 2.2 are migration test targets.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -18,13 +19,45 @@ Not yet published. Once released, add `com.kingsrook.qbits:qbit-db-performance`
|
||||
|
||||
```java
|
||||
new DbPerfQBitProducer()
|
||||
.withQBitConfig(new DbPerfQBitConfig().withBackendName("rdbms"))
|
||||
.withQBitConfig(new DbPerfQBitConfig()
|
||||
.withBackendName("rdbms")
|
||||
.withSchedulerName("scheduler")) // omit to register no schedules
|
||||
.produce(qInstance);
|
||||
```
|
||||
|
||||
The backend must already exist. For PostgreSQL, use QQQ's
|
||||
`PostgreSQLBackendMetaData` from `qqq-backend-module-postgres` so table actions
|
||||
use the PostgreSQL SQL dialect. The qbit reads statistics through the backend's
|
||||
connection manager and writes its records through QQQ actions as the system user.
|
||||
|
||||
Include `db/liquibase/qbit-db-performance/db_performance.changelog.sql` in the
|
||||
host's Liquibase changelog. It creates 17 tables and seeds 14 suggestion rules
|
||||
without overwriting administrator edits. The runtime creates its Settings row
|
||||
on first use. It does not run migrations itself.
|
||||
|
||||
Enable `pg_stat_statements` in PostgreSQL's `shared_preload_libraries`, create
|
||||
the extension in the monitored database, and grant `pg_read_all_stats` to the
|
||||
application database role. Missing statistics access produces a PARTIAL snapshot
|
||||
and a Settings message; unsupported database vendors remain usable for metadata.
|
||||
|
||||
## Naming
|
||||
|
||||
Tables are `db_perf_<name>` (lowercase snake_case, backend name equal to the table name). Permissions are `db_perf.<action>`.
|
||||
Tables are `db_perf_<name>` (lowercase snake_case, backend name equal to the table name). Permissions are `db_performance.<action>`; the complete names are in `DbPerfPermissions.ALL`.
|
||||
|
||||
The app and tables require `db_performance.view`. Settings updates, rule changes
|
||||
and Collect now require `db_performance.manage_settings`. Other tables are
|
||||
read-only to users. Settings and rules have no AI exposure.
|
||||
|
||||
Collection defaults to every five minutes, with table/index samples hourly.
|
||||
New statements establish a baseline instead of booking historical counts into
|
||||
one interval. Snapshot totals include all statement deltas; only the top 200
|
||||
details are stored by default. Therefore a statement's 24-hour totals and daily
|
||||
rollup reflect its retained details, while its share uses the full database total.
|
||||
|
||||
The daily process rolls up yesterday and missing days in the preceding week,
|
||||
then purges details after 30 days and daily rows after 365 days. These thresholds
|
||||
are editable in Settings. Days use the configured time zone, UTC by default.
|
||||
Processes are scheduled or exposed as table actions, never navigation children.
|
||||
|
||||
## Adopting in another app
|
||||
|
||||
@@ -33,10 +66,14 @@ Follow the QQQ [adoption checklist](https://github.com/QRun-IO/qqq/blob/develop/
|
||||
## Build
|
||||
|
||||
```bash
|
||||
./mvnw verify
|
||||
./mvnw -o verify
|
||||
```
|
||||
|
||||
Always use the Maven wrapper (Maven 3.9.11); Maven 3.10 cannot read the parent POM.
|
||||
On Windows use `./mvnw.cmd -o verify`. Validation includes main/test checkstyle,
|
||||
JUnit tests and JaCoCo gates of 70% instructions and 90% classes. PostgreSQL
|
||||
Testcontainers tests (including `PostgresStatsProviderIT`) run with the suite and
|
||||
skip when Docker is unavailable.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
# QBit DB Performance: design
|
||||
|
||||
The design for this qbit is tracked in docs/SPEC.md in this repository. This repository implements it; the implementation is in progress.
|
||||
The approved specification is [SPEC.md](SPEC.md) (owner decisions 2026-10-15).
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
# QBit DB Performance: specification
|
||||
|
||||
Status: **approved for build** (owner decisions 2026-10-15, below). Author: James Maes (drafted with Claude).
|
||||
Repository: `QRun-IO/qbit-db-performance` (Apache-2.0), package `com.kingsrook.qbits.dbperformance`,
|
||||
Maven `com.kingsrook.qbits:qbit-db-performance`. First adopter: kof22 (`KofTwentyTwo/Website-Backend`).
|
||||
|
||||
## Owner decisions (2026-10-15)
|
||||
|
||||
| # | Decision |
|
||||
|---|---|
|
||||
| D1 | A **separate qbit**, not part of SQL Workbench. The two link both ways: a statement opens "Explain in SQL Workbench", and SQL Workbench can open a statement's history here. |
|
||||
| D2 | **Retention:** 30 days of detailed snapshots plus 1 year of daily rollups, both configurable per monitored database. |
|
||||
| D3 | **v1 scope is everything in this document.** PostgreSQL first, behind a provider interface so MySQL can follow. |
|
||||
|
||||
Standing owner standards apply (all recommendations below accepted as written): data in standard
|
||||
visible QQQ tables, `db_perf_` snake_case table prefix, permissions under the full name
|
||||
`db_performance.*`, processes as table or record actions with standard Review/Result screens and
|
||||
plain labels, UI only through `/qqq/v1`, durable jobs for heavy work, no filesystem (database only),
|
||||
central health, alerts through core `QAlerts`, data-driven help, readable at every width.
|
||||
|
||||
## 1. Summary
|
||||
|
||||
A system-administration tool that answers "why is the database slow, and what should we change?"
|
||||
for a QQQ application. It records what the database itself knows (statement statistics, table and
|
||||
index usage, sessions, locks, connections, maintenance), what QQQ knows about who issued each
|
||||
statement (route, process, scheduled job, table), and turns both into ranked, deduplicated
|
||||
findings with evidence and a ready-to-commit fix. It never changes the live database: fixes are
|
||||
Liquibase snippets for a developer to commit.
|
||||
|
||||
It lives under **Admin → Operations → Database Performance** (an admin tool, not a business app).
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
```
|
||||
┌──────────── per pod ────────────┐ ┌──── one pod (scheduler) ────┐
|
||||
QQQ RDBMS ──► StatementExecutionListener (core) │ │ Collect snapshot (5 min) │
|
||||
actions └► Attribution aggregator (memory) │ │ Analyze → findings (hourly) │
|
||||
└► flush every N s, 1 batch │ │ Roll up + purge (daily) │
|
||||
insert per window ───────────┼──► DB ◄┤ (durable-eligible processes)│
|
||||
└──────────────────────────────────┘ └─────────────┬───────────────┘
|
||||
│ DbStatsProvider
|
||||
PostgresStatsProvider (pg_stat_*)
|
||||
```
|
||||
|
||||
- **`DbStatsProvider`** (SPI): `capabilities()`, `readDatabase`, `readStatements`, `readTables`,
|
||||
`readIndexes`, `readActivity` (sessions plus lock graph), `readConnections`, `readSettings`,
|
||||
`explain(...)`. `PostgresStatsProvider` is the v1 implementation (PostgreSQL 13+, features that
|
||||
need 16+/17 such as `EXPLAIN (GENERIC_PLAN)` and per-entry `stats_since` are detected).
|
||||
The provider is chosen per monitored backend from the JDBC vendor; an unsupported vendor is
|
||||
reported in the Setup card, never a crash.
|
||||
- **Connections** are the backend's own (QQQ `ConnectionManager`), so they run as the app's
|
||||
database user. That user needs the built-in `pg_read_all_stats` role to see other sessions'
|
||||
query text and all statements; without it the qbit runs in a reduced mode and the Setup card and
|
||||
health check say exactly what to grant.
|
||||
- **Core hook** (QQQ, next 4.1 RC): `StatementExecutionListener` SPI in `qqq-backend-core`, fired by
|
||||
every `qqq-backend-module-rdbms` action (query, count, aggregate, insert, update, delete) with
|
||||
backend, table, operation, parameterized SQL, elapsed nanoseconds, row count and success, plus
|
||||
`UnitOfWork` scopes opened by core: HTTP request (route template), process run, scheduled job.
|
||||
Listeners must be cheap and never throw; core catches and rate-limits their errors. With no
|
||||
listener registered the hook costs one volatile read.
|
||||
|
||||
## 3. Tables
|
||||
|
||||
All tables are standard QQQ tables on the host's backend, created by the qbit's Liquibase
|
||||
changelog (`db/liquibase/qbit-db-performance/db_performance.changelog.sql`, PostgreSQL and H2),
|
||||
with foreign keys between the qbit's own tables (never into WORM tables), indexes on every foreign
|
||||
key and on every time column that retention or a widget filters on.
|
||||
|
||||
| Table | Label | Written by | Notes |
|
||||
|---|---|---|---|
|
||||
| `db_perf_settings` | Settings | seeded per backend at first run (insert-only), admins | one row per monitored backend: enabled, intervals, retention (30 days detailed, 365 days daily), top-N, attribution on/sample rate/flush seconds/N+1 threshold, EXPLAIN timeout, alert thresholds. Field-audited. Hidden from the assistant. |
|
||||
| `db_perf_rule` | Suggestion Rules | Liquibase seed (insert-only), admins | one row per rule: code, label, enabled, severity, thresholds. Every rule reads its row. Field-audited. Hidden from the assistant. |
|
||||
| `db_perf_snapshot` | Snapshots | collector | one row per collection pass: status, duration, reset detection, database-level deltas (commits, rollbacks, blocks hit/read, cache hit ratio, temp files/bytes, deadlocks), connection counts vs `max_connections`, database size, total statement time. Doubles as the collection run log. |
|
||||
| `db_perf_statement` | Queries | collector | one row per distinct statement (backend + `queryid` + user + top-level): normalized text, fingerprint, operation, main table, first/last seen, the last cumulative counters used for deltas, and 24-hour totals for sorting. **Main nav table.** |
|
||||
| `db_perf_query_snapshot` | Query Snapshots | collector | per snapshot per statement **delta**: calls, total/mean time, max time (since reset), rows, shared hit/read, hit ratio, temp blocks, % of database time. Top N per pass (default 200) by delta time. Child list on Queries. |
|
||||
| `db_perf_query_daily` | Query Daily | rollup | per statement per day: calls, total/mean/max time, rows, hit ratio, % of database time. |
|
||||
| `db_perf_table_stat` | Tables | collector | one row per user table: latest scans, tuples, live/dead, sizes, maintenance times, growth. Nav. |
|
||||
| `db_perf_table_snapshot` | Table Snapshots | collector (hourly) | per table **delta**: seq/idx scans, tuples read/fetched, inserts/updates/deletes/HOT, live/dead tuples, sizes, autovacuum/analyze counts, freeze age. |
|
||||
| `db_perf_table_daily` | Table Daily | rollup | per table per day: size, live/dead, scans, writes. |
|
||||
| `db_perf_index_stat` | Indexes | collector (hourly) | one row per index: definition, columns, unique/primary/valid, scans, size, write cost (table writes since stats reset). Nav. |
|
||||
| `db_perf_activity_sample` | Activity Samples | collector | notable sessions only (long-running, idle in transaction, blocked or blocking): state, wait event, durations, blocking pids, application, user, `queryid` and **fingerprinted text, never raw text**. |
|
||||
| `db_perf_connection_sample` | Connection Samples | collector | per snapshot per (database user, application, state): count. |
|
||||
| `db_perf_attribution` | QQQ Attribution | per-pod flush | per window per (pod, unit kind, unit name, table, operation, statement fingerprint): units, statements, total/max time, rows, errors, sampled flag. |
|
||||
| `db_perf_attribution_daily` | QQQ Attribution Daily | rollup | the same per day, pods merged. |
|
||||
| `db_perf_repeat_pattern` | Repeated Statements (N+1) | per-pod flush | per window per (unit kind, unit name, fingerprint): units affected, max and total repeats in one unit, table. |
|
||||
| `db_perf_finding` | Findings | analyzer, people (status) | rule, severity, score (rank), title, summary, evidence, recommendation, Liquibase snippet, subject table/statement/index, dedupe key (unique), status OPEN/ACKNOWLEDGED/RESOLVED/IGNORED, first/last seen, occurrences, note. Field-audited. **Nav.** |
|
||||
| `db_perf_explain` | Query Plans | plan viewer | audit of every plan request: who, when, statement, mode (PLAN, GENERIC, ANALYZE), duration, outcome. Plain/generic plans of literal-free statements are kept as plan history; ANALYZE plans are shown, never stored. |
|
||||
|
||||
Retention (`db_perf_settings`): `detail_retention_days` (30) purges snapshots, query/table
|
||||
snapshots, activity and connection samples, attribution, repeat patterns and query plans;
|
||||
`rollup_retention_days` (365) purges the `*_daily` tables. Statements, tables and indexes not seen
|
||||
within the rollup retention are deleted with their history. Findings are kept (resolved findings
|
||||
older than the rollup retention are purged).
|
||||
|
||||
Seeded defaults (insert-only, so admin edits survive): every `db_perf_rule` row. `db_perf_settings`
|
||||
is seeded by the runtime the first time it sees a backend (also insert-only). Tables that start
|
||||
empty carry help saying what fills them and when; the app home's Setup card shows each step.
|
||||
|
||||
## 4. Collection (delta snapshots)
|
||||
|
||||
`pg_stat_statements` counters are cumulative. Each pass:
|
||||
|
||||
1. Read `pg_stat_statements_info` (`stats_reset`, `dealloc`) and every `pg_stat_statements` row of
|
||||
this database (`toplevel` rows; `pg_stat_statements.track = top` on kof22).
|
||||
2. For each entry, compare with the statement's stored last counters. **Delta = current − last.**
|
||||
A reset is detected when `calls` went down, the entry's `stats_since` changed (PostgreSQL 17),
|
||||
or `pg_stat_statements_info.stats_reset` moved; then the delta is the current value (activity
|
||||
since the reset) and the snapshot records `stats_reset_detected`. New entries count from zero
|
||||
only when their `stats_since` falls inside the interval; otherwise they are baselined
|
||||
(no delta) so a restart of the qbit never books months of history into one interval.
|
||||
3. Keep the top N by delta time (default 200); the remainder is summed into the snapshot's totals,
|
||||
so % of database time stays exact.
|
||||
4. Database counters (`pg_stat_database`) use the same delta and reset rules (`stats_reset`).
|
||||
5. Tables and indexes (`pg_stat_user_tables`, `pg_statio_user_tables`, `pg_stat_user_indexes`,
|
||||
`pg_index`, sizes) are collected every `table_snapshot_minutes` (60).
|
||||
6. Sessions (`pg_stat_activity` with `pg_blocking_pids`) are sampled each pass; only notable
|
||||
sessions are stored.
|
||||
|
||||
**Text and PHI.** `pg_stat_statements` already replaces constants with `$n`. The qbit
|
||||
re-normalizes every stored statement text (string and numeric literals become placeholders,
|
||||
`IN` lists collapse) and stores a SHA-256 fingerprint of the normalized text. Raw text from
|
||||
`pg_stat_activity` is never persisted: samples keep the `queryid` and the normalized text of the
|
||||
matching statement, or a fingerprint of the normalized live text. Live text is shown only to users
|
||||
with `db_performance.view_live_text`, and only in the live activity response.
|
||||
|
||||
## 5. QQQ attribution and N+1
|
||||
|
||||
The qbit registers a `StatementExecutionListener` at startup (when `attribution_enabled`).
|
||||
|
||||
- On each statement: normalize the SQL (QQQ binds parameters, so text is literal-free; `?` lists
|
||||
collapse), fingerprint it (cached), and add to an in-memory `LongAdder` aggregate keyed by
|
||||
(unit kind, unit name, table, operation, fingerprint). Sampling (`attribution_sample_rate`,
|
||||
default 1.0) applies per unit of work. The qbit's own tables are ignored, and the flush thread
|
||||
suppresses itself, so attribution never measures itself.
|
||||
- Per unit of work, a small per-thread map counts each fingerprint; when the unit ends, any
|
||||
fingerprint repeated at least `n_plus_one_threshold` times (rule `N_PLUS_ONE`, default 10) is
|
||||
added to the repeat-pattern aggregate.
|
||||
- Every `attribution_flush_seconds` (60) each pod swaps its aggregate and writes it in one batch
|
||||
insert per table. This flush is an in-JVM timer, not a durable job: the counts exist only in that
|
||||
pod's memory, so no other pod could resume them. A crash loses at most one window of counts,
|
||||
which is acceptable for statistics. Memory is bounded (`attribution_max_keys`, 10,000; overflow is
|
||||
counted in an `(other)` key).
|
||||
|
||||
## 6. Suggestions engine
|
||||
|
||||
Rules read their `db_perf_rule` row (enabled, severity, thresholds) and produce findings with
|
||||
evidence. Each finding has a stable dedupe key (rule + subject), so a rerun updates `last_seen`
|
||||
and `occurrences` instead of adding rows. A finding that is no longer produced is marked RESOLVED
|
||||
automatically unless a person IGNORED it; an IGNORED finding stays ignored.
|
||||
|
||||
| Rule | Finds | Evidence | Recommendation |
|
||||
|---|---|---|---|
|
||||
| `SLOW_QUERY` | statements with high total or mean time | calls, mean, total, % of DB time (24 h) | plan link, index candidates |
|
||||
| `MISSING_INDEX` | tables with heavy sequential scans and statements filtering on unindexed columns | seq vs idx scans, tuples read, statement predicates | **ready-to-commit Liquibase changeset** (`CREATE INDEX CONCURRENTLY IF NOT EXISTS ...`, `runInTransaction:false`) |
|
||||
| `UNUSED_INDEX` | non-unique, non-primary indexes with zero scans since stats reset (minimum age) | size, write cost | Liquibase `DROP INDEX CONCURRENTLY IF EXISTS` snippet |
|
||||
| `DUPLICATE_INDEX` | identical or prefix-redundant indexes on the same table | both definitions, sizes | drop snippet for the redundant one |
|
||||
| `N_PLUS_ONE` | repeated statement shapes in one unit of work | unit, statement, max/total repeats | batch the lookups (one query with `IN`) |
|
||||
| `HOT_JOB` | scheduled jobs and processes with the most database time | attribution totals | review schedule or batching |
|
||||
| `OVER_AUDITED` | QQQ audit tables with high write volume relative to their source tables | inserts per day, size | lower audit level on noisy tables |
|
||||
| `BLOAT` | tables with high dead-tuple ratio | n_dead/n_live, size | autovacuum settings per table |
|
||||
| `VACUUM_STALE` | tables not vacuumed or analyzed for long, or near freeze age | last (auto)vacuum/analyze, age | maintenance settings |
|
||||
| `POOL_SIZING` | connection usage near `max_connections` or many idle connections | peaks by app/user | pool size changes |
|
||||
| `IDLE_IN_TRANSACTION` | sessions idle in transaction too long | samples | fix the transaction scope; `idle_in_transaction_session_timeout` |
|
||||
| `CACHE_HIT_LOW` | database cache hit ratio below threshold | hit ratio trend | memory or query fixes |
|
||||
| `TEMP_SPILL` | statements writing temp blocks | temp blocks, statements | `work_mem` or query fixes |
|
||||
| `DEADLOCKS` | deadlocks in the interval | count | lock ordering |
|
||||
|
||||
Score = severity weight × normalized impact (share of database time, size or connections), so the
|
||||
Findings table and the home list sort by real impact. Findings never offer a button that changes
|
||||
the live database.
|
||||
|
||||
## 7. Plan viewer
|
||||
|
||||
- **Plain EXPLAIN** by default: `EXPLAIN (FORMAT JSON)` for SQL with no parameters, or
|
||||
`EXPLAIN (GENERIC_PLAN, FORMAT JSON)` (PostgreSQL 16+) for a normalized statement with `$n`.
|
||||
- **EXPLAIN (ANALYZE, BUFFERS)** only for a single `SELECT` (or `WITH` without data-modifying
|
||||
CTEs), with parameter values the user supplies, in a `READ ONLY` transaction with
|
||||
`SET LOCAL statement_timeout` (`explain_timeout_ms`, 10 s) that is always **rolled back**.
|
||||
It needs the separate `db_performance.explain_analyze` permission.
|
||||
- Every request writes a `db_perf_explain` audit row (who, statement, mode, duration, outcome).
|
||||
Parameter values are never stored.
|
||||
- Entry points: the **Query plan** record action on a statement (standard process screens), and
|
||||
`POST /qqq/v1/qbit/db-performance/explain` for any client.
|
||||
- **Explain in SQL Workbench**: each statement links to the Workbench runner with `EXPLAIN` and its
|
||||
text (`sqlWorkbenchRunnerPath`, configurable). The reverse link, Workbench → this qbit's query
|
||||
history, uses `GET /qqq/v1/qbit/db-performance/statements/lookup?sql=...`, which normalizes the
|
||||
SQL the same way and returns the statement's history URL.
|
||||
- **Explain this plan (assistant, optional):** the plan response carries an `assistantPrompt`
|
||||
(the statement's normalized text and the plan, nothing else). A client sends it to the assistant
|
||||
through the assistant's own `/qqq/v1` operations with the user's session; the server never calls
|
||||
a model. `db_perf_settings` and `db_perf_rule` are marked `AiExposure.NONE`, so the assistant's
|
||||
tools cannot read the qbit's configuration.
|
||||
|
||||
## 8. UI
|
||||
|
||||
**App home (Database Performance)**, full-width widgets only, top to bottom:
|
||||
1. **Setup** - monitored backend, provider, `pg_read_all_stats` and `pg_stat_statements` access,
|
||||
last snapshot, attribution on/off; each with its state and next step.
|
||||
2. **Database time** - line chart of statement time per snapshot (24 h) with calls.
|
||||
3. **Top queries** - by total time over 24 h: statement, calls, mean, total, % of DB time, hit ratio.
|
||||
4. **Findings** - open findings by score with severity and status.
|
||||
5. **Live activity** - long-running, blocked/blocking chains, idle in transaction (live, not stored).
|
||||
6. **Connections** - current by application/user vs `max_connections`, cache hit ratio, temp, deadlocks.
|
||||
7. **QQQ attribution** - routes, processes and jobs by database time; repeated-statement (N+1) patterns.
|
||||
8. **Tables** - sequential-scan heavy and fastest-growing tables.
|
||||
|
||||
Each table widget is full width, paginates instead of scrolling inside, and stacks into cards when
|
||||
narrow. Record screens: Queries (snapshots and daily history child lists, findings, plan history),
|
||||
Tables (snapshots, indexes, findings), Indexes, Findings (evidence, snippet).
|
||||
|
||||
**Record actions** (never in the nav, standard Review/Result screens):
|
||||
- Queries: **Query plan**.
|
||||
- Findings: **Acknowledge**, **Mark resolved**, **Ignore**, **Reopen** (note required for Ignore).
|
||||
- Snapshots: **Collect now** (`manage_settings`).
|
||||
- Settings: **Apply default thresholds** (insert-only reseed of missing rules).
|
||||
|
||||
**UI API extension** `db-performance` (`/qqq/v1/qbit/db-performance`, semver 1.0.0, committed
|
||||
OpenAPI fragment with contract test): `GET overview`, `GET activity`, `POST explain`,
|
||||
`GET statements/lookup`.
|
||||
|
||||
## 9. Durable jobs, alerts, health
|
||||
|
||||
**Scheduled processes** (host scheduler, `schedulerName` in the config):
|
||||
`dbPerfCollectSnapshot` (every `snapshot_interval_minutes`, 5), `dbPerfAnalyze` (hourly),
|
||||
`dbPerfRollupAndPurge` (daily 03:15). Each declares `DurableProcessMetaData` (eligible, ATOMIC,
|
||||
idempotent), so a host with scheduled durable jobs runs them as crash-proof jobs with job records,
|
||||
retries and the Jobs app's failure and stall alerts and email. Every step is idempotent: a
|
||||
snapshot is keyed by backend and capture time, rollups replace the day's row, purges are keyset
|
||||
batches, so a rerun after a crash never double counts. Each pass also writes its own
|
||||
`db_perf_snapshot` row (status, duration, error), which is the report even where the host runs
|
||||
scheduled processes inline.
|
||||
|
||||
**Alerts** (core `QAlerts`; delivered by qbit-alerts when installed, logged otherwise; keyed, and
|
||||
resolved when clear): `db_perf.collection_failed`, `db_perf.query_spike` (a statement's total time
|
||||
in an interval above `spike_factor` × its 7-day interval median and above a floor),
|
||||
`db_perf.connection_saturation` (connections ≥ `connection_alert_percent` of `max_connections`),
|
||||
`db_perf.blocked_chain` (a session blocked longer than `blocked_alert_seconds`).
|
||||
|
||||
**Health** (core health SPI, Admin → Health): `db_performance.collector` (last successful snapshot
|
||||
age vs interval), `db_performance.stats_access` (pg_stat_statements present, `pg_read_all_stats`
|
||||
held), `db_performance.connections` (saturation).
|
||||
|
||||
## 10. Permissions
|
||||
|
||||
| Permission | Grants |
|
||||
|---|---|
|
||||
| `db_performance.view` | the app, its tables (read), widgets, overview and activity (fingerprinted text) |
|
||||
| `db_performance.view_live_text` | live query text in the activity view (never persisted) |
|
||||
| `db_performance.explain` | plain and generic EXPLAIN |
|
||||
| `db_performance.explain_analyze` | EXPLAIN (ANALYZE, BUFFERS) on SELECTs |
|
||||
| `db_performance.manage_findings` | finding status actions |
|
||||
| `db_performance.manage_settings` | edit settings and rules, Collect now |
|
||||
|
||||
## 11. Configuration (`DbPerfQBitConfig`)
|
||||
|
||||
`backendName` (required, the monitored backend and the tables' backend), `schedulerName`,
|
||||
`appName` (`dbPerformance`), `parentAppName`, `permissionBaseName` (`db_performance`),
|
||||
`attributionEnabled` (true), `sqlWorkbenchRunnerPath` (`/app/sqlWorkbenchRunner`), `podName`
|
||||
(env `HOSTNAME`). Runtime thresholds live in `db_perf_settings` and `db_perf_rule`.
|
||||
|
||||
## 12. Host setup (kof22)
|
||||
|
||||
1. Grant the app role `pg_read_all_stats` declaratively: cluster-gitops
|
||||
`tenants/kof22/website/pg-cluster.yaml`, CNPG managed role `kof22_website` `inRoles`.
|
||||
`pg_stat_statements` is already preloaded and created (cluster-gitops#133, #136).
|
||||
2. Website-Backend: add the dependency, include the qbit changelog, produce the qbit, register the
|
||||
listener, grant the permissions to kof22-admins, place the app under Admin → Operations.
|
||||
Migrations run as `kof22_website` (ChangelogDefaultRolePostgresIntegrationTest).
|
||||
|
||||
## 13. Acceptance
|
||||
|
||||
- Delta math: reset (calls down, `stats_since` change, `stats_reset`), new entries, evictions; unit tested.
|
||||
- Normalizer: literals, IN lists, comments, whitespace; identical fingerprints for QQQ `?` SQL and
|
||||
`pg_stat_statements` `$n` SQL of the same statement.
|
||||
- Postgres Testcontainers: the changelog applies, collection against a live `pg_stat_statements`,
|
||||
plan viewer READ ONLY rollback (a write in ANALYZE fails and nothing persists), statement timeout.
|
||||
- H2: metadata, changelog, every table read by the runtime with a test proving a row changes behavior.
|
||||
- Attribution: N+1 detection, flush batching (one insert per table per window), self-suppression.
|
||||
- Findings dedupe, auto-resolve, IGNORED stays ignored, Liquibase snippet content.
|
||||
- UI API fragment contract test; help bundle coverage test; FK guardrail test.
|
||||
- JaCoCo ≥ 70% instructions, checkstyle clean.
|
||||
@@ -67,6 +67,46 @@
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-module-postgres</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-module-rdbms</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.2.224</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<version>4.33.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.7.14</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>1.21.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>1.21.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
@@ -87,6 +127,44 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
<include>**/*IT.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.12.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<configLocation>checkstyle/config.xml</configLocation>
|
||||
<headerLocation>checkstyle/license.txt</headerLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals><goal>check</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
@@ -138,14 +216,14 @@
|
||||
<profiles>
|
||||
<!-- Built against qqq 4.1 (QBitMetaDataProducer). Until qbit-build-parent -->
|
||||
<!-- imports the released 4.1 BOM, this profile is active by default and -->
|
||||
<!-- imports qqq-bom-pom 4.1.0-RC.10 ahead of the parent's 4.0.0 BOM. -->
|
||||
<!-- imports qqq-bom-pom 4.1.0-RC.11 ahead of the parent's 4.0.0 BOM. -->
|
||||
<profile>
|
||||
<id>qqq-snapshot</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-RC.10</qqq.snapshot.version>
|
||||
<qqq.snapshot.version>4.1.0-RC.11</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.UseOtherPermissionNameCustomPermissionChecker;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stable public permission names for host role configuration.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfPermissions
|
||||
{
|
||||
public static final String VIEW = "db_performance.view";
|
||||
public static final String VIEW_LIVE_TEXT = "db_performance.view_live_text";
|
||||
public static final String EXPLAIN = "db_performance.explain";
|
||||
public static final String EXPLAIN_ANALYZE = "db_performance.explain_analyze";
|
||||
public static final String MANAGE_FINDINGS = "db_performance.manage_findings";
|
||||
public static final String MANAGE_SETTINGS = "db_performance.manage_settings";
|
||||
public static final List<String> ALL = List.of(VIEW, VIEW_LIVE_TEXT, EXPLAIN, EXPLAIN_ANALYZE, MANAGE_FINDINGS, MANAGE_SETTINGS);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constants only.
|
||||
*******************************************************************************/
|
||||
private DbPerfPermissions()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Require an exact permission name, without a generated suffix.
|
||||
*******************************************************************************/
|
||||
public static QPermissionRules rules(String permission)
|
||||
{
|
||||
return (new QPermissionRules().withLevel(PermissionLevel.HAS_ACCESS_PERMISSION).withPermissionBaseName(permission)
|
||||
.withCustomPermissionChecker(UseOtherPermissionNameCustomPermissionChecker.build(permission)));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
/*
|
||||
* Copyright 2026 Kingsrook, LLC
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -20,37 +24,54 @@ package com.kingsrook.qbits.dbperformance;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfig;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Host-facing configuration for the DB Performance QBit.
|
||||
**
|
||||
** Tables are named db_perf_<name> (lowercase snake_case, backend name equal
|
||||
** to the table name); permissions are db_perf.<action>.
|
||||
** Host configuration; runtime thresholds live in the Settings table.
|
||||
*******************************************************************************/
|
||||
public class DbPerfQBitConfig implements QBitConfig
|
||||
{
|
||||
public static final String TABLE_NAME_PREFIX = "db_perf_";
|
||||
public static final String PERMISSION_BASE_NAME = "db_perf";
|
||||
public static final String TABLE_NAME_PREFIX = "db_perf_";
|
||||
public static final String PERMISSION_BASE_NAME = "db_performance";
|
||||
|
||||
private String backendName;
|
||||
|
||||
private String schedulerName;
|
||||
private String appName = "dbPerformance";
|
||||
private String parentAppName;
|
||||
private String permissionBaseName = "db_performance";
|
||||
private Boolean attributionEnabled = true;
|
||||
private String sqlWorkbenchRunnerPath = "/app/sqlWorkbenchRunner";
|
||||
private String podName = System.getenv("HOSTNAME") == null ? "local" : System.getenv("HOSTNAME");
|
||||
private String snapshotCron = "0 */5 * * * ?";
|
||||
private String analyzeCron = "0 7 * * * ?";
|
||||
private String rollupCron = "0 15 3 * * ?";
|
||||
private String timeZone = "UTC";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The backend must be named.
|
||||
** Validate backend and application identity before registration.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void validate(QInstance qInstance, List<String> errors)
|
||||
{
|
||||
assertCondition(StringUtils.hasContent(backendName), "backendName is required for the DB Performance QBit", errors);
|
||||
assertCondition(backendName != null && qInstance.getBackend(backendName) instanceof RDBMSBackendMetaData, "backendName must name an existing RDBMS backend", errors);
|
||||
assertCondition(appName != null && !appName.isBlank() && qInstance.getApp(appName) == null, "appName must be nonblank and unused", errors);
|
||||
assertCondition(permissionBaseName != null && !permissionBaseName.isBlank(), "permissionBaseName is required", errors);
|
||||
assertCondition(parentAppName == null || qInstance.getApp(parentAppName) != null, "parentAppName must name an existing app", errors);
|
||||
try
|
||||
{
|
||||
java.time.ZoneId.of(timeZone);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
errors.add("timeZone must be a valid zone ID");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Tables without their own backend use the configured backend.
|
||||
** Default storage backend.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getDefaultBackendNameForTables()
|
||||
@@ -59,9 +80,8 @@ public class DbPerfQBitConfig implements QBitConfig
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName
|
||||
** Getter for backendName.
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
@@ -69,9 +89,8 @@ public class DbPerfQBitConfig implements QBitConfig
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendName
|
||||
** Setter for backendName.
|
||||
*******************************************************************************/
|
||||
public void setBackendName(String backendName)
|
||||
{
|
||||
@@ -79,9 +98,8 @@ public class DbPerfQBitConfig implements QBitConfig
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendName
|
||||
** Fluent setter for backendName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withBackendName(String backendName)
|
||||
{
|
||||
@@ -89,4 +107,311 @@ public class DbPerfQBitConfig implements QBitConfig
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for schedulerName.
|
||||
*******************************************************************************/
|
||||
public String getSchedulerName()
|
||||
{
|
||||
return (this.schedulerName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for schedulerName.
|
||||
*******************************************************************************/
|
||||
public void setSchedulerName(String schedulerName)
|
||||
{
|
||||
this.schedulerName = schedulerName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for schedulerName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withSchedulerName(String schedulerName)
|
||||
{
|
||||
this.schedulerName = schedulerName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for appName.
|
||||
*******************************************************************************/
|
||||
public String getAppName()
|
||||
{
|
||||
return (this.appName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for appName.
|
||||
*******************************************************************************/
|
||||
public void setAppName(String appName)
|
||||
{
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for appName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withAppName(String appName)
|
||||
{
|
||||
this.appName = appName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for parentAppName.
|
||||
*******************************************************************************/
|
||||
public String getParentAppName()
|
||||
{
|
||||
return (this.parentAppName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for parentAppName.
|
||||
*******************************************************************************/
|
||||
public void setParentAppName(String parentAppName)
|
||||
{
|
||||
this.parentAppName = parentAppName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for parentAppName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withParentAppName(String parentAppName)
|
||||
{
|
||||
this.parentAppName = parentAppName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for permissionBaseName.
|
||||
*******************************************************************************/
|
||||
public String getPermissionBaseName()
|
||||
{
|
||||
return (this.permissionBaseName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for permissionBaseName.
|
||||
*******************************************************************************/
|
||||
public void setPermissionBaseName(String permissionBaseName)
|
||||
{
|
||||
this.permissionBaseName = permissionBaseName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for permissionBaseName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withPermissionBaseName(String permissionBaseName)
|
||||
{
|
||||
this.permissionBaseName = permissionBaseName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public Boolean getAttributionEnabled()
|
||||
{
|
||||
return (this.attributionEnabled);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public void setAttributionEnabled(Boolean attributionEnabled)
|
||||
{
|
||||
this.attributionEnabled = attributionEnabled;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withAttributionEnabled(Boolean attributionEnabled)
|
||||
{
|
||||
this.attributionEnabled = attributionEnabled;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlWorkbenchRunnerPath.
|
||||
*******************************************************************************/
|
||||
public String getSqlWorkbenchRunnerPath()
|
||||
{
|
||||
return (this.sqlWorkbenchRunnerPath);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlWorkbenchRunnerPath.
|
||||
*******************************************************************************/
|
||||
public void setSqlWorkbenchRunnerPath(String sqlWorkbenchRunnerPath)
|
||||
{
|
||||
this.sqlWorkbenchRunnerPath = sqlWorkbenchRunnerPath;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlWorkbenchRunnerPath.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withSqlWorkbenchRunnerPath(String sqlWorkbenchRunnerPath)
|
||||
{
|
||||
this.sqlWorkbenchRunnerPath = sqlWorkbenchRunnerPath;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for podName.
|
||||
*******************************************************************************/
|
||||
public String getPodName()
|
||||
{
|
||||
return (this.podName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for podName.
|
||||
*******************************************************************************/
|
||||
public void setPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for podName.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotCron.
|
||||
*******************************************************************************/
|
||||
public String getSnapshotCron()
|
||||
{
|
||||
return (this.snapshotCron);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotCron.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotCron(String snapshotCron)
|
||||
{
|
||||
this.snapshotCron = snapshotCron;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotCron.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withSnapshotCron(String snapshotCron)
|
||||
{
|
||||
this.snapshotCron = snapshotCron;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for analyzeCron.
|
||||
*******************************************************************************/
|
||||
public String getAnalyzeCron()
|
||||
{
|
||||
return (this.analyzeCron);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for analyzeCron.
|
||||
*******************************************************************************/
|
||||
public void setAnalyzeCron(String analyzeCron)
|
||||
{
|
||||
this.analyzeCron = analyzeCron;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for analyzeCron.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withAnalyzeCron(String analyzeCron)
|
||||
{
|
||||
this.analyzeCron = analyzeCron;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rollupCron.
|
||||
*******************************************************************************/
|
||||
public String getRollupCron()
|
||||
{
|
||||
return (this.rollupCron);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rollupCron.
|
||||
*******************************************************************************/
|
||||
public void setRollupCron(String rollupCron)
|
||||
{
|
||||
this.rollupCron = rollupCron;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rollupCron.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withRollupCron(String rollupCron)
|
||||
{
|
||||
this.rollupCron = rollupCron;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeZone.
|
||||
*******************************************************************************/
|
||||
public String getTimeZone()
|
||||
{
|
||||
return (this.timeZone);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeZone.
|
||||
*******************************************************************************/
|
||||
public void setTimeZone(String timeZone)
|
||||
{
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeZone.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig withTimeZone(String timeZone)
|
||||
{
|
||||
this.timeZone = timeZone;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
/*
|
||||
* Copyright 2026 Kingsrook, LLC
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,6 +21,13 @@
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.metadata.DbPerfMetaData;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfigValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaDataProducer;
|
||||
|
||||
@@ -100,4 +111,33 @@ public class DbPerfQBitProducer implements QBitMetaDataProducer<DbPerfQBitConfig
|
||||
return (qBitConfig != null);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Validate and register the Phase A metadata.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public MetaDataProducerMultiOutput produce(QInstance instance) throws QException
|
||||
{
|
||||
if(qBitConfig == null)
|
||||
{
|
||||
throw (new QException("DB Performance configuration is required"));
|
||||
}
|
||||
List<String> errors = new ArrayList<>();
|
||||
qBitConfig.validate(instance, errors);
|
||||
for(String table : DbPerfMetaData.TABLES)
|
||||
{
|
||||
if(instance.getTable(table) != null)
|
||||
{
|
||||
errors.add("Table already registered: " + table);
|
||||
}
|
||||
}
|
||||
if(!errors.isEmpty())
|
||||
{
|
||||
throw (new QBitConfigValidationException(qBitConfig, errors));
|
||||
}
|
||||
instance.addQBit(getQBitMetaData());
|
||||
DbPerfMetaData.addTo(instance, qBitConfig);
|
||||
new DbPerfRuntime(instance, qBitConfig).addSelfToInstance(instance);
|
||||
com.kingsrook.qbits.dbperformance.processes.DbPerfProcesses.addTo(instance, qBitConfig);
|
||||
return (new MetaDataProducerMultiOutput());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.context.CapturedContext;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Per-instance configuration and service boundary; no global singleton or filesystem.
|
||||
*******************************************************************************/
|
||||
public class DbPerfRuntime implements QSupplementalInstanceMetaData
|
||||
{
|
||||
public static final String NAME = "dbPerformance";
|
||||
private final QInstance instance;
|
||||
private final DbPerfQBitConfig config;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runtime bound to a single host instance.
|
||||
*******************************************************************************/
|
||||
public DbPerfRuntime(QInstance instance, DbPerfQBitConfig config)
|
||||
{
|
||||
this.instance = instance;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Find the registered runtime.
|
||||
*******************************************************************************/
|
||||
public static DbPerfRuntime of(QInstance instance)
|
||||
{
|
||||
DbPerfRuntime runtime = QSupplementalInstanceMetaData.of(instance, NAME);
|
||||
if(runtime == null)
|
||||
{
|
||||
throw (new IllegalStateException("Database Performance is not registered"));
|
||||
}
|
||||
return (runtime);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Supplemental metadata name.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return (NAME);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Register on the owning instance.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void addSelfToInstance(QInstance instance)
|
||||
{
|
||||
instance.withSupplementalMetaData(this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Host configuration.
|
||||
*******************************************************************************/
|
||||
public DbPerfQBitConfig getConfig()
|
||||
{
|
||||
return (config);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Owning instance.
|
||||
*******************************************************************************/
|
||||
public QInstance getInstance()
|
||||
{
|
||||
return (instance);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Execute as the system user, always restoring the caller context.
|
||||
*******************************************************************************/
|
||||
public <T> T asSystem(Work<T> work) throws Exception
|
||||
{
|
||||
AtomicReference<T> result = new AtomicReference<>();
|
||||
QContext.withTemporaryContext(new CapturedContext(instance, new QSystemUserSession(instance)), () -> result.set(work.run()));
|
||||
return (result.get());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Atomic QQQ action transaction, rolled back on failure.
|
||||
*******************************************************************************/
|
||||
public <T> T transaction(Work<T> work) throws Exception
|
||||
{
|
||||
return (asSystem(() ->
|
||||
{
|
||||
try(QBackendTransaction transaction = QBackendTransaction.openFor(new QueryInput("db_perf_snapshot")))
|
||||
{
|
||||
AtomicReference<T> result = new AtomicReference<>();
|
||||
try
|
||||
{
|
||||
QContext.withTemporaryContext(new CapturedContext(instance, new QSystemUserSession(instance), transaction, null), () -> result.set(work.run()));
|
||||
transaction.commit();
|
||||
return (result.get());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
transaction.rollback();
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Checked service operation.
|
||||
*******************************************************************************/
|
||||
public interface Work<T>
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Execute the operation.
|
||||
*******************************************************************************/
|
||||
T run() throws Exception;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Insert-only per-backend defaults; user edits are never overwritten.
|
||||
*******************************************************************************/
|
||||
public class DbPerfSettingsService
|
||||
{
|
||||
private final DbPerfRuntime runtime;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runtime dependency.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettingsService(DbPerfRuntime runtime)
|
||||
{
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load or insert the settings row. A concurrent winner is re-read.
|
||||
*******************************************************************************/
|
||||
public QRecord ensure(String backendName) throws Exception
|
||||
{
|
||||
return (runtime.asSystem(() ->
|
||||
{
|
||||
QRecord existing = DbPerfStore.first("db_perf_settings", DbPerfStore.eq("backendName", backendName));
|
||||
if(existing != null)
|
||||
{
|
||||
return (existing);
|
||||
}
|
||||
try
|
||||
{
|
||||
return (DbPerfStore.insert("db_perf_settings", List.of(new QRecord().withValue("backendName", backendName)
|
||||
.withValue("attributionEnabled", runtime.getConfig().getAttributionEnabled()))).getFirst());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QRecord winner = DbPerfStore.first("db_perf_settings", DbPerfStore.eq("backendName", backendName));
|
||||
if(winner != null)
|
||||
{
|
||||
return (winner);
|
||||
}
|
||||
throw (e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Small QQQ action adapter. All mutations preserve audit/customizer semantics.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfStore
|
||||
{
|
||||
public static final int BATCH_SIZE = 1000;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfStore()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Read rows using the ambient system transaction.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> query(String table, QQueryFilter filter) throws QException
|
||||
{
|
||||
return (new QueryAction().execute(new QueryInput(table).withFilter(filter).withTransaction(QContext.getQBackendTransaction())).getRecords());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** First matching record.
|
||||
*******************************************************************************/
|
||||
public static QRecord first(String table, QQueryFilter filter) throws QException
|
||||
{
|
||||
List<QRecord> rows = query(table, filter.withLimit(1));
|
||||
return (rows.isEmpty() ? null : rows.getFirst());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A field equality predicate.
|
||||
*******************************************************************************/
|
||||
public static QQueryFilter eq(String field, Serializable value)
|
||||
{
|
||||
return (new QQueryFilter(new QFilterCriteria(field, QCriteriaOperator.EQUALS, value)));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Bounded batch insertion, returning generated IDs and rejecting row errors.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> insert(String table, List<QRecord> rows) throws QException
|
||||
{
|
||||
List<QRecord> result = new ArrayList<>();
|
||||
for(int i = 0; i < rows.size(); i += BATCH_SIZE)
|
||||
{
|
||||
List<QRecord> inserted = InsertAction.executeForRecords(new InsertInput(table).withRecords(new ArrayList<>(rows.subList(i, Math.min(rows.size(), i + BATCH_SIZE)))).withTransaction(QContext.getQBackendTransaction()));
|
||||
check(inserted);
|
||||
result.addAll(inserted);
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Batch updates, throwing on row errors rather than silently losing data.
|
||||
*******************************************************************************/
|
||||
public static void update(String table, List<QRecord> rows) throws QException
|
||||
{
|
||||
for(int i = 0; i < rows.size(); i += BATCH_SIZE)
|
||||
{
|
||||
check(UpdateAction.executeForRecords(new UpdateInput(table).withRecords(new ArrayList<>(rows.subList(i, Math.min(rows.size(), i + BATCH_SIZE)))).withTransaction(QContext.getQBackendTransaction())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Keyset deletion: select at most 1000 matching IDs, delete through QQQ,
|
||||
** then advance the key. No OFFSET skips and no unbounded ID collection.
|
||||
*******************************************************************************/
|
||||
public static int delete(String table, QQueryFilter filter) throws QException
|
||||
{
|
||||
int count = 0;
|
||||
int lastId = 0;
|
||||
while(true)
|
||||
{
|
||||
QQueryFilter page = new QQueryFilter().withSubFilter(filter).withCriteria(new QFilterCriteria("id", QCriteriaOperator.GREATER_THAN, lastId))
|
||||
.withOrderBy(new QFilterOrderBy("id", true)).withLimit(BATCH_SIZE);
|
||||
List<QRecord> rows = query(table, page);
|
||||
if(rows.isEmpty())
|
||||
{
|
||||
return (count);
|
||||
}
|
||||
List<Serializable> ids = rows.stream().map(row -> (Serializable) row.getValueInteger("id")).toList();
|
||||
var deleted = new DeleteAction().execute(new DeleteInput(table).withQueryFilter(new QQueryFilter().withSubFilter(filter).withCriteria(new QFilterCriteria("id", QCriteriaOperator.IN, ids))).withTransaction(QContext.getQBackendTransaction()));
|
||||
if(deleted.getRecordsWithErrors() != null)
|
||||
{
|
||||
check(deleted.getRecordsWithErrors());
|
||||
}
|
||||
if(deleted.getDeletedRecordCount() != ids.size())
|
||||
{
|
||||
throw (new QException("Database Performance deletion did not remove all selected records"));
|
||||
}
|
||||
lastId = rows.getLast().getValueInteger("id");
|
||||
count += rows.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Reject action-level record errors without persisting potentially raw SQL.
|
||||
*******************************************************************************/
|
||||
private static void check(List<QRecord> rows) throws QException
|
||||
{
|
||||
for(QRecord row : rows)
|
||||
{
|
||||
if(row.getErrors() != null && !row.getErrors().isEmpty())
|
||||
{
|
||||
throw (new QException("Database Performance record write failed: " + row.getErrors().getFirst().getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qbits.dbperformance.provider.StatementStats;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Pure cumulative-counter arithmetic with explicit baselines and reset detection.
|
||||
*******************************************************************************/
|
||||
public final class DeltaCalculator
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DeltaCalculator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A first observation is not historical activity. Only an entry created
|
||||
** within the previous capture interval can count from zero immediately.
|
||||
*******************************************************************************/
|
||||
public static Delta calculate(StatementStats previous, StatementStats current, Instant previousCapture, Instant globalReset)
|
||||
{
|
||||
boolean fresh = previous == null;
|
||||
boolean baseline = fresh && (previousCapture == null || current.statsSince() == null || !current.statsSince().isAfter(previousCapture));
|
||||
boolean reset = !fresh && (current.calls() < previous.calls()
|
||||
|| current.statsSince() != null && !Objects.equals(current.statsSince(), previous.statsSince())
|
||||
|| globalReset != null && previousCapture != null && globalReset.isAfter(previousCapture));
|
||||
if(baseline)
|
||||
{
|
||||
return (new Delta(0, BigDecimal.ZERO, 0, 0, 0, 0, false, true));
|
||||
}
|
||||
boolean zero = fresh || reset;
|
||||
return (new Delta(counter(zero ? 0L : previous.calls(), current.calls(), false),
|
||||
current.totalExecMs().subtract(zero ? BigDecimal.ZERO : previous.totalExecMs()).max(BigDecimal.ZERO),
|
||||
counter(zero ? 0L : previous.rows(), current.rows(), false),
|
||||
counter(zero ? 0L : previous.sharedBlksHit(), current.sharedBlksHit(), false),
|
||||
counter(zero ? 0L : previous.sharedBlksRead(), current.sharedBlksRead(), false),
|
||||
counter(zero ? 0L : previous.tempBlksWritten(), current.tempBlksWritten(), false), reset, false));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Null means no baseline; a decrease means reset for this counter.
|
||||
*******************************************************************************/
|
||||
public static long counter(Long previous, long current, boolean reset)
|
||||
{
|
||||
return (previous == null ? 0 : reset || current < previous ? current : current - previous);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interval statement activity; maximum duration remains a since-reset gauge.
|
||||
*******************************************************************************/
|
||||
public record Delta(long calls, BigDecimal totalMs, long rows, long sharedBlksHit, long sharedBlksRead, long tempBlksWritten, boolean reset, boolean baseline)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Configurable retention through bounded QQQ keyset deletes.
|
||||
*******************************************************************************/
|
||||
public class RetentionService
|
||||
{
|
||||
private final DbPerfRuntime runtime;
|
||||
private final Clock clock;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Production clock.
|
||||
*******************************************************************************/
|
||||
public RetentionService(DbPerfRuntime runtime)
|
||||
{
|
||||
this(runtime, Clock.systemUTC());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Clock injection for exact boundary tests.
|
||||
*******************************************************************************/
|
||||
public RetentionService(DbPerfRuntime runtime, Clock clock)
|
||||
{
|
||||
this.runtime = runtime;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Only closed old findings are removed; FK actions preserve open findings.
|
||||
*******************************************************************************/
|
||||
public int purge() throws Exception
|
||||
{
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
Instant detailCutoff = clock.instant().minusSeconds(86400L * SnapshotCollector.positive(settings, "detailRetentionDays", 30));
|
||||
int rollupDays = SnapshotCollector.positive(settings, "rollupRetentionDays", 365);
|
||||
Instant staleCutoff = clock.instant().minusSeconds(86400L * rollupDays);
|
||||
LocalDate dayCutoff = LocalDate.now(clock.withZone(ZoneId.of(runtime.getConfig().getTimeZone()))).minusDays(rollupDays);
|
||||
return (runtime.asSystem(() ->
|
||||
{
|
||||
int count = 0;
|
||||
for(String table : List.of("db_perf_query_snapshot", "db_perf_table_snapshot", "db_perf_activity_sample", "db_perf_connection_sample"))
|
||||
{
|
||||
count += DbPerfStore.delete(table, before("capturedAt", detailCutoff));
|
||||
}
|
||||
for(String table : List.of("db_perf_attribution", "db_perf_repeat_pattern"))
|
||||
{
|
||||
count += DbPerfStore.delete(table, before("windowStart", detailCutoff));
|
||||
}
|
||||
count += DbPerfStore.delete("db_perf_explain", before("requestedAt", detailCutoff));
|
||||
count += DbPerfStore.delete("db_perf_snapshot", backend(before("capturedAt", detailCutoff)));
|
||||
for(String table : List.of("db_perf_query_daily", "db_perf_table_daily", "db_perf_attribution_daily"))
|
||||
{
|
||||
count += DbPerfStore.delete(table, new QQueryFilter(new QFilterCriteria("day", QCriteriaOperator.LESS_THAN, dayCutoff)));
|
||||
}
|
||||
for(String table : List.of("db_perf_index_stat", "db_perf_statement", "db_perf_table_stat"))
|
||||
{
|
||||
count += DbPerfStore.delete(table, backend(before("lastSeenAt", staleCutoff)));
|
||||
}
|
||||
count += DbPerfStore.delete("db_perf_finding", backend(before("lastSeenAt", staleCutoff))
|
||||
.withCriteria(new QFilterCriteria("status", QCriteriaOperator.IN, List.of("RESOLVED", "IGNORED"))));
|
||||
return (count);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Scope backend-bearing rows to the configured monitored backend.
|
||||
*******************************************************************************/
|
||||
private QQueryFilter backend(QQueryFilter filter)
|
||||
{
|
||||
return (filter.withCriteria(new QFilterCriteria("backendName", QCriteriaOperator.EQUALS, runtime.getConfig().getBackendName())));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Strict age boundary preserves records at the cutoff.
|
||||
*******************************************************************************/
|
||||
private static QQueryFilter before(String field, Instant cutoff)
|
||||
{
|
||||
return (new QQueryFilter(new QFilterCriteria(field, QCriteriaOperator.LESS_THAN, cutoff)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qbits.dbperformance.normalize.SqlNormalizer;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Daily aggregates replaced atomically; reruns include late arriving detail rows.
|
||||
*******************************************************************************/
|
||||
public class RollupService
|
||||
{
|
||||
private final DbPerfRuntime runtime;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runtime dependency.
|
||||
*******************************************************************************/
|
||||
public RollupService(DbPerfRuntime runtime)
|
||||
{
|
||||
this.runtime = runtime;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Replace all three rollups for a local calendar day in one transaction.
|
||||
*******************************************************************************/
|
||||
public void rollupDay(LocalDate day) throws Exception
|
||||
{
|
||||
synchronized(runtime)
|
||||
{
|
||||
runtime.transaction(() ->
|
||||
{
|
||||
ZoneId zone = ZoneId.of(runtime.getConfig().getTimeZone());
|
||||
Instant start = day.atStartOfDay(zone).toInstant();
|
||||
Instant end = day.plusDays(1).atStartOfDay(zone).toInstant();
|
||||
rollupQueries(day, start, end);
|
||||
rollupTables(day, start, end);
|
||||
rollupAttribution(day, start, end);
|
||||
return (null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A day needs backfill only if retained detail exists without its daily rows.
|
||||
** Existing history must survive after a short detail-retention window expires.
|
||||
*******************************************************************************/
|
||||
public boolean needsBackfill(LocalDate day) throws Exception
|
||||
{
|
||||
return (runtime.asSystem(() ->
|
||||
{
|
||||
ZoneId zone = ZoneId.of(runtime.getConfig().getTimeZone());
|
||||
Instant start = day.atStartOfDay(zone).toInstant();
|
||||
Instant end = day.plusDays(1).atStartOfDay(zone).toInstant();
|
||||
for(String kind : List.of("query", "table", "attribution"))
|
||||
{
|
||||
String detail = "db_perf_" + kind + (kind.equals("attribution") ? "" : "_snapshot");
|
||||
String field = kind.equals("attribution") ? "windowStart" : "capturedAt";
|
||||
if(DbPerfStore.first("db_perf_" + kind + "_daily", DbPerfStore.eq("day", day)) == null
|
||||
&& DbPerfStore.first(detail, range(field, start, end)) != null)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Weighted statement means/hit ratios, since-reset maximum, exact day share.
|
||||
*******************************************************************************/
|
||||
private void rollupQueries(LocalDate day, Instant start, Instant end) throws Exception
|
||||
{
|
||||
Map<Integer, QRecord> groups = new LinkedHashMap<>();
|
||||
Map<Integer, long[]> hits = new LinkedHashMap<>();
|
||||
BigDecimal denominator = BigDecimal.ZERO;
|
||||
for(QRecord snapshot : DbPerfStore.query("db_perf_snapshot", range("capturedAt", start, end)
|
||||
.withCriteria(new QFilterCriteria("backendName", QCriteriaOperator.EQUALS, runtime.getConfig().getBackendName()))
|
||||
.withCriteria(new QFilterCriteria("status", QCriteriaOperator.IN, List.of("OK", "PARTIAL")))))
|
||||
{
|
||||
denominator = denominator.add(SnapshotCollector.decimal(snapshot, "statementTotalMs"));
|
||||
}
|
||||
for(QRecord row : DbPerfStore.query("db_perf_query_snapshot", range("capturedAt", start, end)))
|
||||
{
|
||||
int id = row.getValueInteger("statementId");
|
||||
QRecord aggregate = groups.computeIfAbsent(id, key -> new QRecord().withValue("statementId", key).withValue("day", day));
|
||||
sum(aggregate, row, "calls", "rowsCount");
|
||||
sumDecimal(aggregate, row, "totalMs");
|
||||
aggregate.setValue("maxMs", SnapshotCollector.decimal(aggregate, "maxMs").max(SnapshotCollector.decimal(row, "maxMs")));
|
||||
aggregate.setValue("snapshots", (int) SnapshotCollector.number(aggregate, "snapshots") + 1);
|
||||
long[] counts = hits.computeIfAbsent(id, key -> new long[2]);
|
||||
counts[0] += SnapshotCollector.number(row, "sharedBlksHit");
|
||||
counts[1] += SnapshotCollector.number(row, "sharedBlksRead");
|
||||
}
|
||||
for(Map.Entry<Integer, QRecord> entry : groups.entrySet())
|
||||
{
|
||||
QRecord row = entry.getValue();
|
||||
long[] counts = hits.get(entry.getKey());
|
||||
row.withValue("meanMs", SnapshotCollector.divide(SnapshotCollector.decimal(row, "totalMs"), SnapshotCollector.number(row, "calls")))
|
||||
.withValue("hitRatio", SnapshotCollector.ratio(counts[0], counts[0] + counts[1]))
|
||||
.withValue("shareOfDbTime", denominator.signum() == 0 ? BigDecimal.ZERO : SnapshotCollector.decimal(row, "totalMs").multiply(BigDecimal.valueOf(100)).divide(denominator, 4, RoundingMode.HALF_UP));
|
||||
}
|
||||
replace("db_perf_query_daily", day, new ArrayList<>(groups.values()));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Sum scan/write deltas, keeping the last size and tuple gauges of the day.
|
||||
*******************************************************************************/
|
||||
private void rollupTables(LocalDate day, Instant start, Instant end) throws Exception
|
||||
{
|
||||
Map<Integer, QRecord> groups = new LinkedHashMap<>();
|
||||
for(QRecord row : DbPerfStore.query("db_perf_table_snapshot", range("capturedAt", start, end).withOrderBy(new QFilterOrderBy("capturedAt", true)).withOrderBy(new QFilterOrderBy("id", true))))
|
||||
{
|
||||
QRecord aggregate = groups.computeIfAbsent(row.getValueInteger("tableStatId"), key -> new QRecord().withValue("tableStatId", key).withValue("day", day));
|
||||
sum(aggregate, row, "seqScan", "idxScan", "seqTupRead", "nTupIns", "nTupUpd", "nTupDel");
|
||||
for(String gauge : List.of("nLiveTup", "nDeadTup", "totalBytes"))
|
||||
{
|
||||
aggregate.setValue(gauge, row.getValue(gauge));
|
||||
}
|
||||
}
|
||||
replace("db_perf_table_daily", day, new ArrayList<>(groups.values()));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Merge pods by all attribution dimensions, using an unambiguous length-
|
||||
** prefixed digest for null-safe uniqueness and bounded index size.
|
||||
*******************************************************************************/
|
||||
private void rollupAttribution(LocalDate day, Instant start, Instant end) throws Exception
|
||||
{
|
||||
Map<String, QRecord> groups = new LinkedHashMap<>();
|
||||
List<String> dimensions = List.of("unitKind", "unitName", "processName", "tableName", "operation", "fingerprint");
|
||||
for(QRecord row : DbPerfStore.query("db_perf_attribution", range("windowStart", start, end)))
|
||||
{
|
||||
StringBuilder key = new StringBuilder();
|
||||
for(String field : dimensions)
|
||||
{
|
||||
String value = row.getValueString(field);
|
||||
key.append(value == null ? "-1:" : value.length() + ":" + value);
|
||||
}
|
||||
String hash = SqlNormalizer.fingerprint(key.toString());
|
||||
QRecord aggregate = groups.computeIfAbsent(hash, ignored -> new QRecord().withValue("day", day).withValue("rollupKey", hash));
|
||||
for(String field : dimensions)
|
||||
{
|
||||
aggregate.setValue(field, row.getValue(field));
|
||||
}
|
||||
aggregate.setValue("sqlText", row.getValueString("sqlText"));
|
||||
sum(aggregate, row, "units", "statements", "rowsCount", "errors");
|
||||
sumDecimal(aggregate, row, "totalMs");
|
||||
aggregate.setValue("maxMs", SnapshotCollector.decimal(aggregate, "maxMs").max(SnapshotCollector.decimal(row, "maxMs")));
|
||||
}
|
||||
replace("db_perf_attribution_daily", day, new ArrayList<>(groups.values()));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Half-open day window works on 23/25-hour local days too.
|
||||
*******************************************************************************/
|
||||
private static QQueryFilter range(String field, Instant start, Instant end)
|
||||
{
|
||||
return (new QQueryFilter(new QFilterCriteria(field, QCriteriaOperator.GREATER_THAN_OR_EQUALS, start), new QFilterCriteria(field, QCriteriaOperator.LESS_THAN, end)));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Replace inside the caller's transaction.
|
||||
*******************************************************************************/
|
||||
private static void replace(String table, LocalDate day, List<QRecord> rows) throws Exception
|
||||
{
|
||||
DbPerfStore.delete(table, DbPerfStore.eq("day", day));
|
||||
DbPerfStore.insert(table, rows);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Sum integral counters.
|
||||
*******************************************************************************/
|
||||
private static void sum(QRecord target, QRecord source, String... fields)
|
||||
{
|
||||
for(String field : fields)
|
||||
{
|
||||
target.setValue(field, SnapshotCollector.number(target, field) + SnapshotCollector.number(source, field));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Sum elapsed time without floating-point precision loss.
|
||||
*******************************************************************************/
|
||||
private static void sumDecimal(QRecord target, QRecord source, String field)
|
||||
{
|
||||
target.setValue(field, SnapshotCollector.decimal(target, field).add(SnapshotCollector.decimal(source, field)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.collect;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Connection;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qbits.dbperformance.normalize.SqlNormalizer;
|
||||
import com.kingsrook.qbits.dbperformance.provider.ConnectionCount;
|
||||
import com.kingsrook.qbits.dbperformance.provider.DatabaseStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.DbStatsProvider;
|
||||
import com.kingsrook.qbits.dbperformance.provider.DbStatsProviders;
|
||||
import com.kingsrook.qbits.dbperformance.provider.IndexStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.ProviderCapabilities;
|
||||
import com.kingsrook.qbits.dbperformance.provider.SessionInfo;
|
||||
import com.kingsrook.qbits.dbperformance.provider.StatementStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.TableStats;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Transactional delta collection; raw JDBC is used only for statistics reads.
|
||||
*******************************************************************************/
|
||||
public class SnapshotCollector
|
||||
{
|
||||
private final DbPerfRuntime runtime;
|
||||
private final Clock clock;
|
||||
private final DbStatsProvider providerOverride;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Production provider is chosen from the backend connection.
|
||||
*******************************************************************************/
|
||||
public SnapshotCollector(DbPerfRuntime runtime)
|
||||
{
|
||||
this(runtime, Clock.systemUTC(), null);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Explicit clock and provider allow deterministic reset/failure tests.
|
||||
*******************************************************************************/
|
||||
public SnapshotCollector(DbPerfRuntime runtime, Clock clock, DbStatsProvider providerOverride)
|
||||
{
|
||||
this.runtime = runtime;
|
||||
this.clock = clock;
|
||||
this.providerOverride = providerOverride;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Serialize within an instance and commit counters with their detail rows.
|
||||
** A failure leaves a FAILED run record and never escapes to the scheduler.
|
||||
*******************************************************************************/
|
||||
public QRecord collect(String backendName, boolean forceTables)
|
||||
{
|
||||
synchronized(runtime)
|
||||
{
|
||||
long started = System.nanoTime();
|
||||
QRecord snapshot = new QRecord().withValue("backendName", backendName).withValue("capturedAt", clock.instant()).withValue("status", "RUNNING");
|
||||
try
|
||||
{
|
||||
if(!Objects.equals(backendName, runtime.getConfig().getBackendName()))
|
||||
{
|
||||
throw (new IllegalArgumentException("Backend is not configured for this runtime"));
|
||||
}
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(backendName);
|
||||
QRecord inserted = runtime.asSystem(() -> DbPerfStore.insert("db_perf_snapshot", List.of(snapshot)).getFirst());
|
||||
snapshot.setValue("id", inserted.getValueInteger("id"));
|
||||
runtime.transaction(() ->
|
||||
{
|
||||
if(!Boolean.TRUE.equals(settings.getValueBoolean("enabled")))
|
||||
{
|
||||
snapshot.withValue("status", "PARTIAL").withValue("errorMessage", "Collection is disabled in Settings.");
|
||||
}
|
||||
else
|
||||
{
|
||||
collectInTransaction(backendName, forceTables, settings, snapshot);
|
||||
}
|
||||
snapshot.setValue("durationMs", (int) Math.min(Integer.MAX_VALUE, (System.nanoTime() - started) / 1000000));
|
||||
DbPerfStore.update("db_perf_snapshot", List.of(snapshot));
|
||||
return (null);
|
||||
});
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// Never persist a JDBC exception message: it may contain SQL literals.
|
||||
snapshot.withValue("status", "FAILED").withValue("errorMessage", "Collection failed (" + e.getClass().getSimpleName() + "). Check database connectivity, schema and statistics access.")
|
||||
.withValue("durationMs", (int) Math.min(Integer.MAX_VALUE, (System.nanoTime() - started) / 1000000));
|
||||
if(snapshot.getValueInteger("id") != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
runtime.asSystem(() ->
|
||||
{
|
||||
DbPerfStore.update("db_perf_snapshot", List.of(new QRecord().withValue("id", snapshot.getValueInteger("id"))
|
||||
.withValue("status", "FAILED").withValue("errorMessage", snapshot.getValueString("errorMessage")).withValue("durationMs", snapshot.getValueInteger("durationMs"))));
|
||||
return (null);
|
||||
});
|
||||
}
|
||||
catch(Exception persistenceFailure)
|
||||
{
|
||||
snapshot.setValue("errorMessage", snapshot.getValueString("errorMessage") + " Failure status could not be saved.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return (snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Statistics read and writes under the ambient system transaction.
|
||||
*******************************************************************************/
|
||||
private void collectInTransaction(String backendName, boolean forceTables, QRecord settings, QRecord snapshot) throws Exception
|
||||
{
|
||||
RDBMSBackendMetaData storageBackend = (RDBMSBackendMetaData) runtime.getInstance().getBackend(backendName);
|
||||
if(storageBackend.getActionStrategy().supportsForUpdateSkipLocked())
|
||||
{
|
||||
var locked = new com.kingsrook.qqq.backend.core.actions.tables.QueryAction().execute(new com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput("db_perf_settings")
|
||||
.withFilter(DbPerfStore.eq("backendName", backendName)).withTransaction(com.kingsrook.qqq.backend.core.context.QContext.getQBackendTransaction())
|
||||
.withLockMode(com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryLockMode.FOR_UPDATE_SKIP_LOCKED)).getRecords();
|
||||
if(locked.isEmpty())
|
||||
{
|
||||
snapshot.withValue("status", "PARTIAL").withValue("errorMessage", "Another collector is already running for this backend.");
|
||||
return;
|
||||
}
|
||||
settings = locked.getFirst();
|
||||
if(!Boolean.TRUE.equals(settings.getValueBoolean("enabled")))
|
||||
{
|
||||
snapshot.withValue("status", "PARTIAL").withValue("errorMessage", "Collection is disabled in Settings.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Instant now = snapshot.getValueInstant("capturedAt");
|
||||
QQueryFilter filter = DbPerfStore.eq("backendName", backendName)
|
||||
.withCriteria(new QFilterCriteria("status", QCriteriaOperator.IN, List.of("OK", "PARTIAL")))
|
||||
.withCriteria(new QFilterCriteria("rawXactCommit", QCriteriaOperator.IS_NOT_BLANK))
|
||||
.withOrderBy(new QFilterOrderBy("capturedAt", false));
|
||||
QRecord previous = DbPerfStore.first("db_perf_snapshot", filter);
|
||||
Instant previousCapture = previous == null ? null : previous.getValueInstant("capturedAt");
|
||||
snapshot.withValue("intervalSeconds", previousCapture == null ? 0 : Math.max(0, (int) Duration.between(previousCapture, now).toSeconds()))
|
||||
.withValue("statsResetDetected", false).withValue("includesTables", false);
|
||||
RDBMSBackendMetaData backend = (RDBMSBackendMetaData) runtime.getInstance().getBackend(backendName);
|
||||
try(Connection connection = ConnectionManager.getConnection(backend))
|
||||
{
|
||||
connection.setReadOnly(true);
|
||||
DbStatsProvider provider = providerOverride == null ? DbStatsProviders.forConnection(connection) : providerOverride;
|
||||
ProviderCapabilities capability = provider.capabilities(connection);
|
||||
settings.withValue("provider", provider.getName()).withValue("statsAccess", capability.statsAccess()).withValue("statsAccessMessage", capability.message());
|
||||
if(capability.supported())
|
||||
{
|
||||
DatabaseStats database = provider.readDatabase(connection);
|
||||
boolean databaseReset = previous != null && database.statsReset() != null && !Objects.equals(database.statsReset(), previous.getValueInstant("rawStatsReset"));
|
||||
if(previous != null)
|
||||
{
|
||||
for(Map.Entry<String, Long> entry : database.counters().entrySet())
|
||||
{
|
||||
String raw = "raw" + Character.toUpperCase(entry.getKey().charAt(0)) + entry.getKey().substring(1);
|
||||
if(previous.getValueLong(raw) != null && entry.getValue() < previous.getValueLong(raw))
|
||||
{
|
||||
databaseReset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(Map.Entry<String, Long> entry : database.counters().entrySet())
|
||||
{
|
||||
String raw = "raw" + Character.toUpperCase(entry.getKey().charAt(0)) + entry.getKey().substring(1);
|
||||
Long before = previous == null ? null : previous.getValueLong(raw);
|
||||
snapshot.withValue(entry.getKey(), DeltaCalculator.counter(before, entry.getValue(), databaseReset)).withValue(raw, entry.getValue());
|
||||
if(before != null && entry.getValue() < before)
|
||||
{
|
||||
databaseReset = true;
|
||||
}
|
||||
}
|
||||
snapshot.withValue("rawStatsReset", database.statsReset()).withValue("statsResetDetected", databaseReset)
|
||||
.withValue("databaseSizeBytes", database.sizeBytes()).withValue("maxConnections", database.maxConnections())
|
||||
.withValue("cacheHitRatio", ratio(number(snapshot, "blksHit"), number(snapshot, "blksHit") + number(snapshot, "blksRead")));
|
||||
if(capability.statements())
|
||||
{
|
||||
Instant reset = provider.readStatementResetInfo(connection).statsReset();
|
||||
snapshot.setValue("rawStatementStatsReset", reset);
|
||||
if(reset != null && previousCapture != null && reset.isAfter(previousCapture))
|
||||
{
|
||||
snapshot.setValue("statsResetDetected", true);
|
||||
}
|
||||
collectStatements(backendName, provider.readStatements(connection), previousCapture, reset, snapshot, settings);
|
||||
}
|
||||
List<ConnectionCount> connections = provider.readConnections(connection);
|
||||
collectConnections(connections, snapshot);
|
||||
collectSessions(provider.readSessions(connection), snapshot, settings, backendName);
|
||||
Instant lastTables = settings.getValueInstant("lastTableSnapshotAt");
|
||||
if(forceTables || lastTables == null || !lastTables.plusSeconds(60L * positive(settings, "tableSnapshotMinutes", 60)).isAfter(now))
|
||||
{
|
||||
collectTables(backendName, provider.readTables(connection), provider.readIndexes(connection), snapshot, databaseReset);
|
||||
settings.setValue("lastTableSnapshotAt", now);
|
||||
snapshot.setValue("includesTables", true);
|
||||
}
|
||||
}
|
||||
snapshot.withValue("status", "FULL".equals(capability.statsAccess()) ? "OK" : "PARTIAL")
|
||||
.withValue("errorMessage", "FULL".equals(capability.statsAccess()) ? null : capability.message());
|
||||
QRecord runtimeSettings = new QRecord().withValue("id", settings.getValueInteger("id")).withValue("lastSnapshotAt", now)
|
||||
.withValue("provider", settings.getValueString("provider")).withValue("statsAccess", settings.getValueString("statsAccess"))
|
||||
.withValue("statsAccessMessage", settings.getValueString("statsAccessMessage"));
|
||||
if(Boolean.TRUE.equals(snapshot.getValueBoolean("includesTables")))
|
||||
{
|
||||
runtimeSettings.setValue("lastTableSnapshotAt", now);
|
||||
}
|
||||
DbPerfStore.update("db_perf_settings", List.of(runtimeSettings));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Upsert all statement baselines, but retain only the top N interval rows.
|
||||
** The denominator and total calls include every observed statement delta.
|
||||
*******************************************************************************/
|
||||
private void collectStatements(String backend, List<StatementStats> current, Instant previousCapture, Instant reset, QRecord snapshot, QRecord settings) throws Exception
|
||||
{
|
||||
Map<String, QRecord> previous = new HashMap<>();
|
||||
for(QRecord row : DbPerfStore.query("db_perf_statement", DbPerfStore.eq("backendName", backend)))
|
||||
{
|
||||
previous.put(statementKey(row.getValueLong("queryId"), row.getValueString("dbUser"), Boolean.TRUE.equals(row.getValueBoolean("toplevel"))), row);
|
||||
}
|
||||
List<QRecord> inserts = new ArrayList<>();
|
||||
List<QRecord> updates = new ArrayList<>();
|
||||
List<QRecord> deltas = new ArrayList<>();
|
||||
Map<QRecord, String> keys = new LinkedHashMap<>();
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
long calls = 0;
|
||||
Instant now = snapshot.getValueInstant("capturedAt");
|
||||
for(StatementStats entry : current)
|
||||
{
|
||||
String key = statementKey(entry.queryId(), entry.dbUser(), entry.toplevel());
|
||||
QRecord old = previous.get(key);
|
||||
DeltaCalculator.Delta delta = DeltaCalculator.calculate(old == null ? null : storedStatement(old), entry, previousCapture, reset);
|
||||
String text = SqlNormalizer.normalize(entry.queryText());
|
||||
QRecord row = new QRecord().withValue("backendName", backend).withValue("queryId", entry.queryId()).withValue("dbUser", entry.dbUser()).withValue("toplevel", entry.toplevel())
|
||||
.withValue("queryText", text).withValue("fingerprint", SqlNormalizer.fingerprint(text)).withValue("operation", SqlNormalizer.operation(text)).withValue("mainTable", SqlNormalizer.mainTable(text))
|
||||
.withValue("lastSeenAt", now).withValue("lastCalls", entry.calls()).withValue("lastTotalExecMs", entry.totalExecMs()).withValue("lastRows", entry.rows())
|
||||
.withValue("lastSharedBlksHit", entry.sharedBlksHit()).withValue("lastSharedBlksRead", entry.sharedBlksRead()).withValue("lastTempBlksWritten", entry.tempBlksWritten())
|
||||
.withValue("lastMaxExecMs", entry.maxExecMs()).withValue("lastStatsSince", entry.statsSince());
|
||||
if(old == null)
|
||||
{
|
||||
row.setValue("firstSeenAt", now);
|
||||
inserts.add(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.setValue("id", old.getValueInteger("id"));
|
||||
updates.add(row);
|
||||
}
|
||||
if(delta.reset())
|
||||
{
|
||||
snapshot.setValue("statsResetDetected", true);
|
||||
}
|
||||
total = total.add(delta.totalMs());
|
||||
calls += delta.calls();
|
||||
if(!delta.baseline() && (delta.calls() > 0 || delta.totalMs().signum() > 0))
|
||||
{
|
||||
QRecord detail = new QRecord().withValue("snapshotId", snapshot.getValueInteger("id")).withValue("capturedAt", now)
|
||||
.withValue("calls", delta.calls()).withValue("totalMs", delta.totalMs()).withValue("meanMs", divide(delta.totalMs(), delta.calls()))
|
||||
.withValue("maxMs", entry.maxExecMs()).withValue("rowsCount", delta.rows()).withValue("sharedBlksHit", delta.sharedBlksHit())
|
||||
.withValue("sharedBlksRead", delta.sharedBlksRead()).withValue("hitRatio", ratio(delta.sharedBlksHit(), delta.sharedBlksHit() + delta.sharedBlksRead()))
|
||||
.withValue("tempBlksWritten", delta.tempBlksWritten());
|
||||
deltas.add(detail);
|
||||
keys.put(detail, key);
|
||||
}
|
||||
}
|
||||
for(QRecord inserted : DbPerfStore.insert("db_perf_statement", inserts))
|
||||
{
|
||||
previous.put(statementKey(inserted.getValueLong("queryId"), inserted.getValueString("dbUser"), Boolean.TRUE.equals(inserted.getValueBoolean("toplevel"))), inserted);
|
||||
}
|
||||
DbPerfStore.update("db_perf_statement", updates);
|
||||
deltas.sort(Comparator.comparing((QRecord row) -> row.getValueBigDecimal("totalMs")).reversed());
|
||||
List<QRecord> top = deltas.subList(0, Math.min(deltas.size(), positive(settings, "topStatementsPerSnapshot", 200)));
|
||||
for(QRecord row : top)
|
||||
{
|
||||
row.withValue("statementId", previous.get(keys.get(row)).getValueInteger("id"))
|
||||
.withValue("shareOfDbTime", total.signum() == 0 ? BigDecimal.ZERO : row.getValueBigDecimal("totalMs").multiply(BigDecimal.valueOf(100)).divide(total, 4, RoundingMode.HALF_UP));
|
||||
}
|
||||
DbPerfStore.insert("db_perf_query_snapshot", top);
|
||||
snapshot.withValue("statementCount", current.size()).withValue("statementCalls", calls).withValue("statementTotalMs", total).withValue("storedStatementCount", top.size());
|
||||
refresh24hours(backend, previous, current, snapshot);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored counters have the same shape as provider counters.
|
||||
*******************************************************************************/
|
||||
private StatementStats storedStatement(QRecord row)
|
||||
{
|
||||
return (new StatementStats(number(row, "queryId"), row.getValueString("dbUser"), Boolean.TRUE.equals(row.getValueBoolean("toplevel")), row.getValueString("queryText"), number(row, "lastCalls"), decimal(row, "lastTotalExecMs"), BigDecimal.ZERO, decimal(row, "lastMaxExecMs"), number(row, "lastRows"), number(row, "lastSharedBlksHit"), number(row, "lastSharedBlksRead"), number(row, "lastTempBlksWritten"), row.getValueInstant("lastStatsSince"), null));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Recompute touched statements against the exact all-statement denominator.
|
||||
*******************************************************************************/
|
||||
private void refresh24hours(String backend, Map<String, QRecord> known, List<StatementStats> touched, QRecord snapshot) throws Exception
|
||||
{
|
||||
Instant since = snapshot.getValueInstant("capturedAt").minusSeconds(86400);
|
||||
BigDecimal denominator = decimal(snapshot, "statementTotalMs");
|
||||
for(QRecord row : DbPerfStore.query("db_perf_snapshot", DbPerfStore.eq("backendName", backend).withCriteria(new QFilterCriteria("capturedAt", QCriteriaOperator.GREATER_THAN_OR_EQUALS, since))
|
||||
.withCriteria(new QFilterCriteria("status", QCriteriaOperator.IN, List.of("OK", "PARTIAL")))))
|
||||
{
|
||||
denominator = denominator.add(decimal(row, "statementTotalMs"));
|
||||
}
|
||||
Map<Integer, List<QRecord>> details = DbPerfStore.query("db_perf_query_snapshot", new QQueryFilter(new QFilterCriteria("capturedAt", QCriteriaOperator.GREATER_THAN_OR_EQUALS, since)))
|
||||
.stream().collect(Collectors.groupingBy(row -> row.getValueInteger("statementId")));
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// refresh every statement with activity in the window, plus any whose //
|
||||
// stored 24h numbers are non-zero (so idle statements decay to zero) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Set<Integer> ids = new LinkedHashSet<>(details.keySet());
|
||||
for(StatementStats entry : touched)
|
||||
{
|
||||
ids.add(known.get(statementKey(entry.queryId(), entry.dbUser(), entry.toplevel())).getValueInteger("id"));
|
||||
}
|
||||
for(QRecord row : known.values())
|
||||
{
|
||||
if(row.getValueInteger("id") != null && number(row, "calls24h") != 0)
|
||||
{
|
||||
ids.add(row.getValueInteger("id"));
|
||||
}
|
||||
}
|
||||
List<QRecord> updates = new ArrayList<>();
|
||||
for(Integer id : ids)
|
||||
{
|
||||
List<QRecord> rows = details.getOrDefault(id, List.of());
|
||||
long calls = rows.stream().mapToLong(row -> number(row, "calls")).sum();
|
||||
BigDecimal total = rows.stream().map(row -> decimal(row, "totalMs")).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
updates.add(new QRecord().withValue("id", id).withValue("calls24h", calls).withValue("totalMs24h", total).withValue("meanMs24h", divide(total, calls))
|
||||
.withValue("shareOfDbTime24h", denominator.signum() == 0 ? BigDecimal.ZERO : total.multiply(BigDecimal.valueOf(100)).divide(denominator, 4, RoundingMode.HALF_UP)));
|
||||
}
|
||||
DbPerfStore.update("db_perf_statement", updates);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Upsert table/index gauges; snapshot scan/write/maintenance counters as deltas.
|
||||
*******************************************************************************/
|
||||
private void collectTables(String backend, List<TableStats> tables, List<IndexStats> indexes, QRecord snapshot, boolean reset) throws Exception
|
||||
{
|
||||
Map<String, QRecord> known = new HashMap<>();
|
||||
for(QRecord row : DbPerfStore.query("db_perf_table_stat", DbPerfStore.eq("backendName", backend)))
|
||||
{
|
||||
known.put(row.getValueString("schemaName") + "." + row.getValueString("tableName"), row);
|
||||
}
|
||||
List<QRecord> inserts = new ArrayList<>();
|
||||
List<QRecord> updates = new ArrayList<>();
|
||||
List<QRecord> details = new ArrayList<>();
|
||||
Map<QRecord, String> detailKeys = new LinkedHashMap<>();
|
||||
Instant now = snapshot.getValueInstant("capturedAt");
|
||||
for(TableStats entry : tables)
|
||||
{
|
||||
String key = entry.schemaName() + "." + entry.tableName();
|
||||
QRecord old = known.get(key);
|
||||
QRecord row = new QRecord().withValue("backendName", backend).withValue("schemaName", entry.schemaName()).withValue("tableName", entry.tableName()).withValue("relid", entry.relid()).withValue("lastSeenAt", now);
|
||||
entry.counters().forEach(row::setValue);
|
||||
entry.maintenance().forEach(row::setValue);
|
||||
row.withValue("deadRatio", ratio(number(row, "nDeadTup"), number(row, "nLiveTup") + number(row, "nDeadTup")))
|
||||
.withValue("seqScanShare", ratio(number(row, "seqScan"), number(row, "seqScan") + number(row, "idxScan")));
|
||||
if(old == null)
|
||||
{
|
||||
inserts.add(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.setValue("id", old.getValueInteger("id"));
|
||||
updates.add(row);
|
||||
}
|
||||
if(old != null)
|
||||
{
|
||||
QRecord weekAgo = DbPerfStore.first("db_perf_table_snapshot", DbPerfStore.eq("tableStatId", old.getValueInteger("id"))
|
||||
.withCriteria(new QFilterCriteria("capturedAt", QCriteriaOperator.LESS_THAN_OR_EQUALS, now.minusSeconds(7 * 86400L)))
|
||||
.withOrderBy(new QFilterOrderBy("capturedAt", false)));
|
||||
row.setValue("growthBytes7d", weekAgo == null ? null : number(row, "totalBytes") - number(weekAgo, "totalBytes"));
|
||||
}
|
||||
QRecord detail = new QRecord().withValue("snapshotId", snapshot.getValueInteger("id")).withValue("capturedAt", now);
|
||||
boolean tableReset = reset || old != null && number(old, "relid") != entry.relid();
|
||||
for(String counter : List.of("seqScan", "seqTupRead", "idxScan", "idxTupFetch", "nTupIns", "nTupUpd", "nTupDel", "nTupHotUpd", "autovacuumCount", "autoanalyzeCount"))
|
||||
{
|
||||
detail.setValue(counter, DeltaCalculator.counter(old == null ? null : old.getValueLong(counter), number(row, counter), tableReset));
|
||||
}
|
||||
for(String gauge : List.of("nLiveTup", "nDeadTup", "totalBytes", "tableBytes", "indexBytes", "freezeAge"))
|
||||
{
|
||||
detail.setValue(gauge, number(row, gauge));
|
||||
}
|
||||
details.add(detail);
|
||||
detailKeys.put(detail, key);
|
||||
}
|
||||
for(QRecord inserted : DbPerfStore.insert("db_perf_table_stat", inserts))
|
||||
{
|
||||
known.put(inserted.getValueString("schemaName") + "." + inserted.getValueString("tableName"), inserted);
|
||||
}
|
||||
DbPerfStore.update("db_perf_table_stat", updates);
|
||||
for(QRecord detail : details)
|
||||
{
|
||||
detail.setValue("tableStatId", known.get(detailKeys.get(detail)).getValueInteger("id"));
|
||||
}
|
||||
DbPerfStore.insert("db_perf_table_snapshot", details);
|
||||
collectIndexes(backend, indexes, known, now);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Indexes reference collected tables and are written in batches.
|
||||
*******************************************************************************/
|
||||
private void collectIndexes(String backend, List<IndexStats> entries, Map<String, QRecord> tables, Instant now) throws Exception
|
||||
{
|
||||
Map<String, QRecord> old = new HashMap<>();
|
||||
for(QRecord row : DbPerfStore.query("db_perf_index_stat", DbPerfStore.eq("backendName", backend)))
|
||||
{
|
||||
old.put(row.getValueString("schemaName") + "." + row.getValueString("indexName"), row);
|
||||
}
|
||||
List<QRecord> inserts = new ArrayList<>();
|
||||
List<QRecord> updates = new ArrayList<>();
|
||||
for(IndexStats entry : entries)
|
||||
{
|
||||
QRecord table = tables.get(entry.schemaName() + "." + entry.tableName());
|
||||
if(table == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QRecord row = new QRecord().withValue("backendName", backend).withValue("tableStatId", table.getValueInteger("id"))
|
||||
.withValue("schemaName", entry.schemaName()).withValue("tableName", entry.tableName()).withValue("indexName", entry.indexName()).withValue("relid", entry.relid())
|
||||
.withValue("definition", truncate(entry.definition(), 2000)).withValue("columns", truncate(entry.columns(), 1000)).withValue("isUnique", entry.unique()).withValue("isPrimary", entry.primary()).withValue("isValid", entry.valid())
|
||||
.withValue("idxScan", entry.idxScan()).withValue("idxTupRead", entry.idxTupRead()).withValue("idxTupFetch", entry.idxTupFetch()).withValue("sizeBytes", entry.sizeBytes()).withValue("writeCost", entry.writeCost())
|
||||
.withValue("scansSinceReset", entry.idxScan()).withValue("lastSeenAt", now);
|
||||
QRecord previous = old.get(entry.schemaName() + "." + entry.indexName());
|
||||
if(previous == null)
|
||||
{
|
||||
inserts.add(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.setValue("id", previous.getValueInteger("id"));
|
||||
updates.add(row);
|
||||
}
|
||||
}
|
||||
DbPerfStore.insert("db_perf_index_stat", inserts);
|
||||
DbPerfStore.update("db_perf_index_stat", updates);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** All-database connection counts for capacity comparison.
|
||||
*******************************************************************************/
|
||||
private void collectConnections(List<ConnectionCount> connections, QRecord snapshot) throws Exception
|
||||
{
|
||||
List<QRecord> rows = new ArrayList<>();
|
||||
int total = 0;
|
||||
int active = 0;
|
||||
int idle = 0;
|
||||
int idleTx = 0;
|
||||
for(ConnectionCount entry : connections)
|
||||
{
|
||||
total += entry.connectionCount();
|
||||
active += "active".equals(entry.state()) ? entry.connectionCount() : 0;
|
||||
idle += "idle".equals(entry.state()) ? entry.connectionCount() : 0;
|
||||
idleTx += entry.state() != null && entry.state().startsWith("idle in transaction") ? entry.connectionCount() : 0;
|
||||
rows.add(new QRecord().withValue("snapshotId", snapshot.getValueInteger("id")).withValue("capturedAt", snapshot.getValueInstant("capturedAt"))
|
||||
.withValue("dbUser", entry.dbUser()).withValue("applicationName", truncate(entry.applicationName(), 200)).withValue("state", entry.state()).withValue("connectionCount", entry.connectionCount()));
|
||||
}
|
||||
DbPerfStore.insert("db_perf_connection_sample", rows);
|
||||
snapshot.withValue("connectionsTotal", total).withValue("connectionsActive", active).withValue("connectionsIdle", idle).withValue("connectionsIdleInTx", idleTx);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Persist only notable sessions; normalize before truncating or hashing.
|
||||
*******************************************************************************/
|
||||
private void collectSessions(List<SessionInfo> sessions, QRecord snapshot, QRecord settings, String backend) throws Exception
|
||||
{
|
||||
Map<Integer, Integer> blocking = new HashMap<>();
|
||||
for(SessionInfo session : sessions)
|
||||
{
|
||||
session.blockedBy().forEach(pid -> blocking.merge(pid, 1, Integer::sum));
|
||||
}
|
||||
Map<String, QRecord> statements = new HashMap<>();
|
||||
for(QRecord row : DbPerfStore.query("db_perf_statement", DbPerfStore.eq("backendName", backend)))
|
||||
{
|
||||
if(Boolean.TRUE.equals(row.getValueBoolean("toplevel")))
|
||||
{
|
||||
statements.put(row.getValueString("queryId") + "/" + row.getValueString("dbUser"), row);
|
||||
}
|
||||
}
|
||||
List<QRecord> rows = new ArrayList<>();
|
||||
for(SessionInfo entry : sessions)
|
||||
{
|
||||
String reason = !entry.blockedBy().isEmpty() ? "BLOCKED" : blocking.containsKey(entry.pid()) ? "BLOCKING"
|
||||
: entry.state() != null && entry.state().startsWith("idle in transaction") && entry.stateChange() != null && Duration.between(entry.stateChange(), snapshot.getValueInstant("capturedAt")).getSeconds() >= positive(settings, "idleInTransactionSeconds", 60) ? "IDLE_IN_TRANSACTION"
|
||||
: "active".equals(entry.state()) && entry.querySeconds() != null && entry.querySeconds().doubleValue() >= positive(settings, "activityLongRunningSeconds", 30) ? "LONG_RUNNING" : null;
|
||||
if(reason == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QRecord statement = statements.get(entry.queryId() + "/" + entry.dbUser());
|
||||
String normalized = SqlNormalizer.normalize(statement == null ? entry.queryText() : statement.getValueString("queryText"));
|
||||
rows.add(new QRecord().withValue("snapshotId", snapshot.getValueInteger("id")).withValue("capturedAt", snapshot.getValueInstant("capturedAt"))
|
||||
.withValue("pid", entry.pid()).withValue("dbUser", entry.dbUser()).withValue("applicationName", truncate(entry.applicationName(), 200)).withValue("clientAddr", entry.clientAddr())
|
||||
.withValue("state", entry.state()).withValue("waitEventType", entry.waitEventType()).withValue("waitEvent", entry.waitEvent()).withValue("backendType", entry.backendType())
|
||||
.withValue("xactStart", entry.xactStart()).withValue("queryStart", entry.queryStart()).withValue("stateChange", entry.stateChange()).withValue("querySeconds", entry.querySeconds()).withValue("xactSeconds", entry.xactSeconds())
|
||||
.withValue("blockedBy", truncate(entry.blockedBy().stream().map(String::valueOf).collect(Collectors.joining(",")), 500)).withValue("blockingCount", blocking.getOrDefault(entry.pid(), 0))
|
||||
.withValue("reason", reason).withValue("queryId", entry.queryId()).withValue("statementId", statement == null ? null : statement.getValueInteger("id"))
|
||||
.withValue("queryFingerprint", SqlNormalizer.fingerprint(normalized)).withValue("queryTextNormalized", truncate(normalized, 4000)));
|
||||
}
|
||||
DbPerfStore.insert("db_perf_activity_sample", rows);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stable statement identity including role and nesting level.
|
||||
*******************************************************************************/
|
||||
private static String statementKey(long queryId, String user, boolean top)
|
||||
{
|
||||
return (queryId + "/" + top + "/" + user);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Null counters are zero.
|
||||
*******************************************************************************/
|
||||
public static long number(QRecord row, String field)
|
||||
{
|
||||
Long value = row.getValueLong(field);
|
||||
return (value == null ? 0 : value);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Null decimal counters are zero.
|
||||
*******************************************************************************/
|
||||
public static BigDecimal decimal(QRecord row, String field)
|
||||
{
|
||||
BigDecimal value = row.getValueBigDecimal(field);
|
||||
return (value == null ? BigDecimal.ZERO : value);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Percentage, not a fractional ratio; matches seeded rule thresholds.
|
||||
*******************************************************************************/
|
||||
public static BigDecimal ratio(long numerator, long denominator)
|
||||
{
|
||||
return (denominator == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(numerator).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(denominator), 4, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Weighted mean with a zero-call guard.
|
||||
*******************************************************************************/
|
||||
public static BigDecimal divide(BigDecimal total, long count)
|
||||
{
|
||||
return (count == 0 ? BigDecimal.ZERO : total.divide(BigDecimal.valueOf(count), 3, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Invalid persisted intervals fall back to safe positive defaults.
|
||||
*******************************************************************************/
|
||||
public static int positive(QRecord settings, String field, int fallback)
|
||||
{
|
||||
Integer value = settings.getValueInteger(field);
|
||||
return (value == null || value < 1 ? fallback : value);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Bound display strings after normalization where applicable.
|
||||
*******************************************************************************/
|
||||
private static String truncate(String value, int length)
|
||||
{
|
||||
return (value == null ? null : value.substring(0, Math.min(length, value.length())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.metadata;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfPermissions;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfQBitConfig;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfActivitySample;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfAttribution;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfAttributionDaily;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfConnectionSample;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfExplain;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfFinding;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfIndexStat;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfQueryDaily;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfQuerySnapshot;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfRepeatPattern;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfRule;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfSettings;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfSnapshot;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfStatement;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfTableDaily;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfTableSnapshot;
|
||||
import com.kingsrook.qbits.dbperformance.model.DbPerfTableStat;
|
||||
import com.kingsrook.qqq.backend.core.actions.ai.AiExposure;
|
||||
import com.kingsrook.qqq.backend.core.actions.ai.AiTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ tables, exact permissions, sections and parent-child record lists.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfMetaData
|
||||
{
|
||||
public static final String CHANGELOG = "db/liquibase/qbit-db-performance/db_performance.changelog.sql";
|
||||
public static final List<String> TABLES = List.of("db_perf_settings", "db_perf_rule", "db_perf_snapshot", "db_perf_statement", "db_perf_query_snapshot", "db_perf_query_daily", "db_perf_table_stat", "db_perf_table_snapshot", "db_perf_table_daily", "db_perf_index_stat", "db_perf_activity_sample", "db_perf_connection_sample", "db_perf_attribution", "db_perf_attribution_daily", "db_perf_repeat_pattern", "db_perf_finding", "db_perf_explain");
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfMetaData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Register the complete Phase A metadata.
|
||||
*******************************************************************************/
|
||||
public static void addTo(QInstance instance, DbPerfQBitConfig config) throws QException
|
||||
{
|
||||
QTableMetaData settings = base(config, DbPerfSettings.TABLE_NAME, "Settings", DbPerfSettings.class);
|
||||
settings.withRecordLabelFormat("%s").withRecordLabelFields("backendName");
|
||||
settings.withUniqueKey(new UniqueKey("backendName"));
|
||||
settings.getField("id").setBackendName("id");
|
||||
settings.getField("backendName").setBackendName("backend_name");
|
||||
settings.getField("enabled").setBackendName("enabled");
|
||||
settings.getField("enabled").setDefaultValue(true);
|
||||
settings.getField("provider").setBackendName("provider");
|
||||
settings.getField("provider").setPossibleValueSourceName("db_perf_Provider");
|
||||
settings.getField("snapshotIntervalMinutes").setBackendName("snapshot_interval_minutes");
|
||||
settings.getField("snapshotIntervalMinutes").setDefaultValue(5);
|
||||
settings.getField("tableSnapshotMinutes").setBackendName("table_snapshot_minutes");
|
||||
settings.getField("tableSnapshotMinutes").setDefaultValue(60);
|
||||
settings.getField("detailRetentionDays").setBackendName("detail_retention_days");
|
||||
settings.getField("detailRetentionDays").setDefaultValue(30);
|
||||
settings.getField("rollupRetentionDays").setBackendName("rollup_retention_days");
|
||||
settings.getField("rollupRetentionDays").setDefaultValue(365);
|
||||
settings.getField("topStatementsPerSnapshot").setBackendName("top_statements_per_snapshot");
|
||||
settings.getField("topStatementsPerSnapshot").setDefaultValue(200);
|
||||
settings.getField("activityLongRunningSeconds").setBackendName("activity_long_running_seconds");
|
||||
settings.getField("activityLongRunningSeconds").setDefaultValue(30);
|
||||
settings.getField("idleInTransactionSeconds").setBackendName("idle_in_transaction_seconds");
|
||||
settings.getField("idleInTransactionSeconds").setDefaultValue(60);
|
||||
settings.getField("attributionEnabled").setBackendName("attribution_enabled");
|
||||
settings.getField("attributionEnabled").setDefaultValue(true);
|
||||
settings.getField("attributionSampleRate").setBackendName("attribution_sample_rate");
|
||||
settings.getField("attributionSampleRate").setDefaultValue(new java.math.BigDecimal("1.0"));
|
||||
settings.getField("attributionFlushSeconds").setBackendName("attribution_flush_seconds");
|
||||
settings.getField("attributionFlushSeconds").setDefaultValue(60);
|
||||
settings.getField("attributionMaxKeys").setBackendName("attribution_max_keys");
|
||||
settings.getField("attributionMaxKeys").setDefaultValue(10000);
|
||||
settings.getField("nPlusOneThreshold").setBackendName("n_plus_one_threshold");
|
||||
settings.getField("nPlusOneThreshold").setDefaultValue(10);
|
||||
settings.getField("explainTimeoutMs").setBackendName("explain_timeout_ms");
|
||||
settings.getField("explainTimeoutMs").setDefaultValue(10000);
|
||||
settings.getField("alertsEnabled").setBackendName("alerts_enabled");
|
||||
settings.getField("alertsEnabled").setDefaultValue(true);
|
||||
settings.getField("spikeFactor").setBackendName("spike_factor");
|
||||
settings.getField("spikeFactor").setDefaultValue(new java.math.BigDecimal("3"));
|
||||
settings.getField("spikeMinTotalMs").setBackendName("spike_min_total_ms");
|
||||
settings.getField("spikeMinTotalMs").setDefaultValue(5000);
|
||||
settings.getField("connectionAlertPercent").setBackendName("connection_alert_percent");
|
||||
settings.getField("connectionAlertPercent").setDefaultValue(80);
|
||||
settings.getField("blockedAlertSeconds").setBackendName("blocked_alert_seconds");
|
||||
settings.getField("blockedAlertSeconds").setDefaultValue(30);
|
||||
settings.getField("lastSnapshotAt").setBackendName("last_snapshot_at");
|
||||
settings.getField("lastTableSnapshotAt").setBackendName("last_table_snapshot_at");
|
||||
settings.getField("statsAccess").setBackendName("stats_access");
|
||||
settings.getField("statsAccess").setPossibleValueSourceName("db_perf_StatsAccess");
|
||||
settings.getField("statsAccessMessage").setBackendName("stats_access_message");
|
||||
settings.getField("createDate").setBackendName("create_date");
|
||||
settings.getField("modifyDate").setBackendName("modify_date");
|
||||
settings.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.FIELD));
|
||||
settings.withSupplementalMetaData(new AiTableMetaData().withExposure(AiExposure.NONE));
|
||||
settings.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_DELETE);
|
||||
settings.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "enabled", "provider", "snapshotIntervalMinutes", "tableSnapshotMinutes", "detailRetentionDays")));
|
||||
settings.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("rollupRetentionDays", "topStatementsPerSnapshot", "activityLongRunningSeconds", "idleInTransactionSeconds", "attributionEnabled", "attributionSampleRate", "attributionFlushSeconds", "attributionMaxKeys", "nPlusOneThreshold", "explainTimeoutMs", "alertsEnabled", "spikeFactor", "spikeMinTotalMs", "connectionAlertPercent", "blockedAlertSeconds", "lastSnapshotAt", "lastTableSnapshotAt", "statsAccess", "statsAccessMessage")));
|
||||
settings.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(settings);
|
||||
|
||||
QTableMetaData rule = base(config, DbPerfRule.TABLE_NAME, "Suggestion Rules", DbPerfRule.class);
|
||||
rule.withRecordLabelFormat("%s").withRecordLabelFields("label");
|
||||
rule.withUniqueKey(new UniqueKey("code"));
|
||||
rule.getField("id").setBackendName("id");
|
||||
rule.getField("code").setBackendName("code");
|
||||
rule.getField("label").setBackendName("label");
|
||||
rule.getField("description").setBackendName("description");
|
||||
rule.getField("enabled").setBackendName("enabled");
|
||||
rule.getField("enabled").setDefaultValue(true);
|
||||
rule.getField("severity").setBackendName("severity");
|
||||
rule.getField("severity").setPossibleValueSourceName("db_perf_Severity");
|
||||
rule.getField("threshold1").setBackendName("threshold_1");
|
||||
rule.getField("threshold2").setBackendName("threshold_2");
|
||||
rule.getField("threshold1Label").setBackendName("threshold_1_label");
|
||||
rule.getField("threshold2Label").setBackendName("threshold_2_label");
|
||||
rule.getField("createDate").setBackendName("create_date");
|
||||
rule.getField("modifyDate").setBackendName("modify_date");
|
||||
rule.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.FIELD));
|
||||
rule.withSupplementalMetaData(new AiTableMetaData().withExposure(AiExposure.NONE));
|
||||
rule.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "code", "label", "description", "enabled", "severity", "threshold1")));
|
||||
rule.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("threshold2", "threshold1Label", "threshold2Label")));
|
||||
rule.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(rule);
|
||||
|
||||
QTableMetaData snapshot = base(config, DbPerfSnapshot.TABLE_NAME, "Snapshots", DbPerfSnapshot.class);
|
||||
snapshot.withRecordLabelFormat("%s").withRecordLabelFields("capturedAt");
|
||||
snapshot.withUniqueKey(new UniqueKey("backendName", "capturedAt"));
|
||||
snapshot.getField("id").setBackendName("id");
|
||||
snapshot.getField("backendName").setBackendName("backend_name");
|
||||
snapshot.getField("capturedAt").setBackendName("captured_at");
|
||||
snapshot.getField("intervalSeconds").setBackendName("interval_seconds");
|
||||
snapshot.getField("status").setBackendName("status");
|
||||
snapshot.getField("status").setPossibleValueSourceName("db_perf_SnapshotStatus");
|
||||
snapshot.getField("durationMs").setBackendName("duration_ms");
|
||||
snapshot.getField("errorMessage").setBackendName("error_message");
|
||||
snapshot.getField("statsResetDetected").setBackendName("stats_reset_detected");
|
||||
snapshot.getField("includesTables").setBackendName("includes_tables");
|
||||
snapshot.getField("xactCommit").setBackendName("xact_commit");
|
||||
snapshot.getField("xactRollback").setBackendName("xact_rollback");
|
||||
snapshot.getField("blksHit").setBackendName("blks_hit");
|
||||
snapshot.getField("blksRead").setBackendName("blks_read");
|
||||
snapshot.getField("cacheHitRatio").setBackendName("cache_hit_ratio");
|
||||
snapshot.getField("tempFiles").setBackendName("temp_files");
|
||||
snapshot.getField("tempBytes").setBackendName("temp_bytes");
|
||||
snapshot.getField("deadlocks").setBackendName("deadlocks");
|
||||
snapshot.getField("conflicts").setBackendName("conflicts");
|
||||
snapshot.getField("connectionsTotal").setBackendName("connections_total");
|
||||
snapshot.getField("connectionsActive").setBackendName("connections_active");
|
||||
snapshot.getField("connectionsIdle").setBackendName("connections_idle");
|
||||
snapshot.getField("connectionsIdleInTx").setBackendName("connections_idle_in_tx");
|
||||
snapshot.getField("maxConnections").setBackendName("max_connections");
|
||||
snapshot.getField("databaseSizeBytes").setBackendName("database_size_bytes");
|
||||
snapshot.getField("statementCount").setBackendName("statement_count");
|
||||
snapshot.getField("statementCalls").setBackendName("statement_calls");
|
||||
snapshot.getField("statementTotalMs").setBackendName("statement_total_ms");
|
||||
snapshot.getField("storedStatementCount").setBackendName("stored_statement_count");
|
||||
snapshot.getField("createDate").setBackendName("create_date");
|
||||
snapshot.getField("rawXactCommit").setBackendName("raw_xact_commit");
|
||||
snapshot.getField("rawXactRollback").setBackendName("raw_xact_rollback");
|
||||
snapshot.getField("rawBlksHit").setBackendName("raw_blks_hit");
|
||||
snapshot.getField("rawBlksRead").setBackendName("raw_blks_read");
|
||||
snapshot.getField("rawTempFiles").setBackendName("raw_temp_files");
|
||||
snapshot.getField("rawTempBytes").setBackendName("raw_temp_bytes");
|
||||
snapshot.getField("rawDeadlocks").setBackendName("raw_deadlocks");
|
||||
snapshot.getField("rawConflicts").setBackendName("raw_conflicts");
|
||||
snapshot.getField("rawStatsReset").setBackendName("raw_stats_reset");
|
||||
snapshot.getField("rawStatementStatsReset").setBackendName("raw_statement_stats_reset");
|
||||
snapshot.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
snapshot.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "capturedAt", "intervalSeconds", "status", "durationMs", "errorMessage")));
|
||||
snapshot.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("statsResetDetected", "includesTables", "xactCommit", "xactRollback", "blksHit", "blksRead", "cacheHitRatio", "tempFiles", "tempBytes", "deadlocks", "conflicts", "connectionsTotal", "connectionsActive", "connectionsIdle", "connectionsIdleInTx", "maxConnections", "databaseSizeBytes", "statementCount", "statementCalls", "statementTotalMs", "storedStatementCount")));
|
||||
snapshot.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "rawXactCommit", "rawXactRollback", "rawBlksHit", "rawBlksRead", "rawTempFiles", "rawTempBytes", "rawDeadlocks", "rawConflicts", "rawStatsReset", "rawStatementStatsReset")));
|
||||
instance.addTable(snapshot);
|
||||
|
||||
QTableMetaData statement = base(config, DbPerfStatement.TABLE_NAME, "Queries", DbPerfStatement.class);
|
||||
statement.withRecordLabelFormat("%s").withRecordLabelFields("queryText");
|
||||
statement.withUniqueKey(new UniqueKey("backendName", "queryId", "dbUser", "toplevel"));
|
||||
statement.getField("id").setBackendName("id");
|
||||
statement.getField("backendName").setBackendName("backend_name");
|
||||
statement.getField("queryId").setBackendName("query_id");
|
||||
statement.getField("dbUser").setBackendName("db_user");
|
||||
statement.getField("toplevel").setBackendName("toplevel");
|
||||
statement.getField("fingerprint").setBackendName("fingerprint");
|
||||
statement.getField("queryText").setBackendName("query_text");
|
||||
statement.getField("queryText").setType(QFieldType.TEXT);
|
||||
statement.getField("operation").setBackendName("operation");
|
||||
statement.getField("operation").setPossibleValueSourceName("db_perf_Operation");
|
||||
statement.getField("mainTable").setBackendName("main_table");
|
||||
statement.getField("firstSeenAt").setBackendName("first_seen_at");
|
||||
statement.getField("lastSeenAt").setBackendName("last_seen_at");
|
||||
statement.getField("lastCalls").setBackendName("last_calls");
|
||||
statement.getField("lastTotalExecMs").setBackendName("last_total_exec_ms");
|
||||
statement.getField("lastRows").setBackendName("last_rows");
|
||||
statement.getField("lastSharedBlksHit").setBackendName("last_shared_blks_hit");
|
||||
statement.getField("lastSharedBlksRead").setBackendName("last_shared_blks_read");
|
||||
statement.getField("lastTempBlksWritten").setBackendName("last_temp_blks_written");
|
||||
statement.getField("lastMaxExecMs").setBackendName("last_max_exec_ms");
|
||||
statement.getField("lastStatsSince").setBackendName("last_stats_since");
|
||||
statement.getField("calls24h").setBackendName("calls_24h");
|
||||
statement.getField("totalMs24h").setBackendName("total_ms_24h");
|
||||
statement.getField("meanMs24h").setBackendName("mean_ms_24h");
|
||||
statement.getField("shareOfDbTime24h").setBackendName("share_of_db_time_24h");
|
||||
statement.getField("createDate").setBackendName("create_date");
|
||||
statement.getField("modifyDate").setBackendName("modify_date");
|
||||
statement.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
statement.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "queryId", "dbUser", "toplevel", "fingerprint", "queryText")));
|
||||
statement.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("operation", "mainTable", "firstSeenAt", "lastSeenAt", "lastCalls", "lastTotalExecMs", "lastRows", "lastSharedBlksHit", "lastSharedBlksRead", "lastTempBlksWritten", "lastMaxExecMs", "lastStatsSince", "calls24h", "totalMs24h", "meanMs24h", "shareOfDbTime24h")));
|
||||
statement.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(statement);
|
||||
|
||||
QTableMetaData querySnapshot = base(config, DbPerfQuerySnapshot.TABLE_NAME, "Query Snapshots", DbPerfQuerySnapshot.class);
|
||||
querySnapshot.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
querySnapshot.getField("id").setBackendName("id");
|
||||
querySnapshot.getField("snapshotId").setBackendName("snapshot_id");
|
||||
querySnapshot.getField("snapshotId").setPossibleValueSourceName("db_perf_snapshot");
|
||||
querySnapshot.getField("statementId").setBackendName("statement_id");
|
||||
querySnapshot.getField("statementId").setPossibleValueSourceName("db_perf_statement");
|
||||
querySnapshot.getField("capturedAt").setBackendName("captured_at");
|
||||
querySnapshot.getField("calls").setBackendName("calls");
|
||||
querySnapshot.getField("totalMs").setBackendName("total_ms");
|
||||
querySnapshot.getField("meanMs").setBackendName("mean_ms");
|
||||
querySnapshot.getField("maxMs").setBackendName("max_ms");
|
||||
querySnapshot.getField("rowsCount").setBackendName("rows_count");
|
||||
querySnapshot.getField("sharedBlksHit").setBackendName("shared_blks_hit");
|
||||
querySnapshot.getField("sharedBlksRead").setBackendName("shared_blks_read");
|
||||
querySnapshot.getField("hitRatio").setBackendName("hit_ratio");
|
||||
querySnapshot.getField("tempBlksWritten").setBackendName("temp_blks_written");
|
||||
querySnapshot.getField("shareOfDbTime").setBackendName("share_of_db_time");
|
||||
querySnapshot.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
querySnapshot.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "snapshotId", "statementId", "capturedAt", "calls", "totalMs", "meanMs")));
|
||||
querySnapshot.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("maxMs", "rowsCount", "sharedBlksHit", "sharedBlksRead", "hitRatio", "tempBlksWritten", "shareOfDbTime")));
|
||||
instance.addTable(querySnapshot);
|
||||
|
||||
QTableMetaData queryDaily = base(config, DbPerfQueryDaily.TABLE_NAME, "Query Daily", DbPerfQueryDaily.class);
|
||||
queryDaily.withRecordLabelFormat("%s").withRecordLabelFields("day");
|
||||
queryDaily.withUniqueKey(new UniqueKey("statementId", "day"));
|
||||
queryDaily.getField("id").setBackendName("id");
|
||||
queryDaily.getField("statementId").setBackendName("statement_id");
|
||||
queryDaily.getField("statementId").setPossibleValueSourceName("db_perf_statement");
|
||||
queryDaily.getField("day").setBackendName("day");
|
||||
queryDaily.getField("calls").setBackendName("calls");
|
||||
queryDaily.getField("totalMs").setBackendName("total_ms");
|
||||
queryDaily.getField("meanMs").setBackendName("mean_ms");
|
||||
queryDaily.getField("maxMs").setBackendName("max_ms");
|
||||
queryDaily.getField("rowsCount").setBackendName("rows_count");
|
||||
queryDaily.getField("hitRatio").setBackendName("hit_ratio");
|
||||
queryDaily.getField("shareOfDbTime").setBackendName("share_of_db_time");
|
||||
queryDaily.getField("snapshots").setBackendName("snapshots");
|
||||
queryDaily.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
queryDaily.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "statementId", "day", "calls", "totalMs", "meanMs", "maxMs")));
|
||||
queryDaily.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("rowsCount", "hitRatio", "shareOfDbTime", "snapshots")));
|
||||
instance.addTable(queryDaily);
|
||||
|
||||
QTableMetaData tableStat = base(config, DbPerfTableStat.TABLE_NAME, "Tables", DbPerfTableStat.class);
|
||||
tableStat.withRecordLabelFormat("%s").withRecordLabelFields("tableName");
|
||||
tableStat.withUniqueKey(new UniqueKey("backendName", "schemaName", "tableName"));
|
||||
tableStat.getField("id").setBackendName("id");
|
||||
tableStat.getField("backendName").setBackendName("backend_name");
|
||||
tableStat.getField("schemaName").setBackendName("schema_name");
|
||||
tableStat.getField("tableName").setBackendName("table_name");
|
||||
tableStat.getField("relid").setBackendName("relid");
|
||||
tableStat.getField("seqScan").setBackendName("seq_scan");
|
||||
tableStat.getField("seqTupRead").setBackendName("seq_tup_read");
|
||||
tableStat.getField("idxScan").setBackendName("idx_scan");
|
||||
tableStat.getField("idxTupFetch").setBackendName("idx_tup_fetch");
|
||||
tableStat.getField("nTupIns").setBackendName("n_tup_ins");
|
||||
tableStat.getField("nTupUpd").setBackendName("n_tup_upd");
|
||||
tableStat.getField("nTupDel").setBackendName("n_tup_del");
|
||||
tableStat.getField("nTupHotUpd").setBackendName("n_tup_hot_upd");
|
||||
tableStat.getField("nLiveTup").setBackendName("n_live_tup");
|
||||
tableStat.getField("nDeadTup").setBackendName("n_dead_tup");
|
||||
tableStat.getField("deadRatio").setBackendName("dead_ratio");
|
||||
tableStat.getField("totalBytes").setBackendName("total_bytes");
|
||||
tableStat.getField("tableBytes").setBackendName("table_bytes");
|
||||
tableStat.getField("indexBytes").setBackendName("index_bytes");
|
||||
tableStat.getField("toastBytes").setBackendName("toast_bytes");
|
||||
tableStat.getField("growthBytes7d").setBackendName("growth_bytes_7d");
|
||||
tableStat.getField("lastVacuum").setBackendName("last_vacuum");
|
||||
tableStat.getField("lastAutovacuum").setBackendName("last_autovacuum");
|
||||
tableStat.getField("lastAnalyze").setBackendName("last_analyze");
|
||||
tableStat.getField("lastAutoanalyze").setBackendName("last_autoanalyze");
|
||||
tableStat.getField("vacuumCount").setBackendName("vacuum_count");
|
||||
tableStat.getField("autovacuumCount").setBackendName("autovacuum_count");
|
||||
tableStat.getField("analyzeCount").setBackendName("analyze_count");
|
||||
tableStat.getField("autoanalyzeCount").setBackendName("autoanalyze_count");
|
||||
tableStat.getField("freezeAge").setBackendName("freeze_age");
|
||||
tableStat.getField("seqScanShare").setBackendName("seq_scan_share");
|
||||
tableStat.getField("lastSeenAt").setBackendName("last_seen_at");
|
||||
tableStat.getField("createDate").setBackendName("create_date");
|
||||
tableStat.getField("modifyDate").setBackendName("modify_date");
|
||||
tableStat.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
tableStat.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "schemaName", "tableName", "relid", "seqScan", "seqTupRead")));
|
||||
tableStat.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("idxScan", "idxTupFetch", "nTupIns", "nTupUpd", "nTupDel", "nTupHotUpd", "nLiveTup", "nDeadTup", "deadRatio", "totalBytes", "tableBytes", "indexBytes", "toastBytes", "growthBytes7d", "lastVacuum", "lastAutovacuum", "lastAnalyze", "lastAutoanalyze", "vacuumCount", "autovacuumCount", "analyzeCount", "autoanalyzeCount", "freezeAge", "seqScanShare", "lastSeenAt")));
|
||||
tableStat.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(tableStat);
|
||||
|
||||
QTableMetaData tableSnapshot = base(config, DbPerfTableSnapshot.TABLE_NAME, "Table Snapshots", DbPerfTableSnapshot.class);
|
||||
tableSnapshot.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
tableSnapshot.getField("id").setBackendName("id");
|
||||
tableSnapshot.getField("snapshotId").setBackendName("snapshot_id");
|
||||
tableSnapshot.getField("snapshotId").setPossibleValueSourceName("db_perf_snapshot");
|
||||
tableSnapshot.getField("tableStatId").setBackendName("table_stat_id");
|
||||
tableSnapshot.getField("tableStatId").setPossibleValueSourceName("db_perf_table_stat");
|
||||
tableSnapshot.getField("capturedAt").setBackendName("captured_at");
|
||||
tableSnapshot.getField("seqScan").setBackendName("seq_scan");
|
||||
tableSnapshot.getField("seqTupRead").setBackendName("seq_tup_read");
|
||||
tableSnapshot.getField("idxScan").setBackendName("idx_scan");
|
||||
tableSnapshot.getField("idxTupFetch").setBackendName("idx_tup_fetch");
|
||||
tableSnapshot.getField("nTupIns").setBackendName("n_tup_ins");
|
||||
tableSnapshot.getField("nTupUpd").setBackendName("n_tup_upd");
|
||||
tableSnapshot.getField("nTupDel").setBackendName("n_tup_del");
|
||||
tableSnapshot.getField("nTupHotUpd").setBackendName("n_tup_hot_upd");
|
||||
tableSnapshot.getField("nLiveTup").setBackendName("n_live_tup");
|
||||
tableSnapshot.getField("nDeadTup").setBackendName("n_dead_tup");
|
||||
tableSnapshot.getField("totalBytes").setBackendName("total_bytes");
|
||||
tableSnapshot.getField("tableBytes").setBackendName("table_bytes");
|
||||
tableSnapshot.getField("indexBytes").setBackendName("index_bytes");
|
||||
tableSnapshot.getField("autovacuumCount").setBackendName("autovacuum_count");
|
||||
tableSnapshot.getField("autoanalyzeCount").setBackendName("autoanalyze_count");
|
||||
tableSnapshot.getField("freezeAge").setBackendName("freeze_age");
|
||||
tableSnapshot.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
tableSnapshot.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "snapshotId", "tableStatId", "capturedAt", "seqScan", "seqTupRead", "idxScan")));
|
||||
tableSnapshot.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("idxTupFetch", "nTupIns", "nTupUpd", "nTupDel", "nTupHotUpd", "nLiveTup", "nDeadTup", "totalBytes", "tableBytes", "indexBytes", "autovacuumCount", "autoanalyzeCount", "freezeAge")));
|
||||
instance.addTable(tableSnapshot);
|
||||
|
||||
QTableMetaData tableDaily = base(config, DbPerfTableDaily.TABLE_NAME, "Table Daily", DbPerfTableDaily.class);
|
||||
tableDaily.withRecordLabelFormat("%s").withRecordLabelFields("day");
|
||||
tableDaily.withUniqueKey(new UniqueKey("tableStatId", "day"));
|
||||
tableDaily.getField("id").setBackendName("id");
|
||||
tableDaily.getField("tableStatId").setBackendName("table_stat_id");
|
||||
tableDaily.getField("tableStatId").setPossibleValueSourceName("db_perf_table_stat");
|
||||
tableDaily.getField("day").setBackendName("day");
|
||||
tableDaily.getField("seqScan").setBackendName("seq_scan");
|
||||
tableDaily.getField("idxScan").setBackendName("idx_scan");
|
||||
tableDaily.getField("seqTupRead").setBackendName("seq_tup_read");
|
||||
tableDaily.getField("nTupIns").setBackendName("n_tup_ins");
|
||||
tableDaily.getField("nTupUpd").setBackendName("n_tup_upd");
|
||||
tableDaily.getField("nTupDel").setBackendName("n_tup_del");
|
||||
tableDaily.getField("nLiveTup").setBackendName("n_live_tup");
|
||||
tableDaily.getField("nDeadTup").setBackendName("n_dead_tup");
|
||||
tableDaily.getField("totalBytes").setBackendName("total_bytes");
|
||||
tableDaily.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
tableDaily.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "tableStatId", "day", "seqScan", "idxScan", "seqTupRead", "nTupIns")));
|
||||
tableDaily.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("nTupUpd", "nTupDel", "nLiveTup", "nDeadTup", "totalBytes")));
|
||||
instance.addTable(tableDaily);
|
||||
|
||||
QTableMetaData indexStat = base(config, DbPerfIndexStat.TABLE_NAME, "Indexes", DbPerfIndexStat.class);
|
||||
indexStat.withRecordLabelFormat("%s").withRecordLabelFields("indexName");
|
||||
indexStat.withUniqueKey(new UniqueKey("backendName", "schemaName", "indexName"));
|
||||
indexStat.getField("id").setBackendName("id");
|
||||
indexStat.getField("backendName").setBackendName("backend_name");
|
||||
indexStat.getField("tableStatId").setBackendName("table_stat_id");
|
||||
indexStat.getField("tableStatId").setPossibleValueSourceName("db_perf_table_stat");
|
||||
indexStat.getField("schemaName").setBackendName("schema_name");
|
||||
indexStat.getField("tableName").setBackendName("table_name");
|
||||
indexStat.getField("indexName").setBackendName("index_name");
|
||||
indexStat.getField("relid").setBackendName("relid");
|
||||
indexStat.getField("definition").setBackendName("definition");
|
||||
indexStat.getField("columns").setBackendName("columns");
|
||||
indexStat.getField("isUnique").setBackendName("is_unique");
|
||||
indexStat.getField("isPrimary").setBackendName("is_primary");
|
||||
indexStat.getField("isValid").setBackendName("is_valid");
|
||||
indexStat.getField("idxScan").setBackendName("idx_scan");
|
||||
indexStat.getField("idxTupRead").setBackendName("idx_tup_read");
|
||||
indexStat.getField("idxTupFetch").setBackendName("idx_tup_fetch");
|
||||
indexStat.getField("sizeBytes").setBackendName("size_bytes");
|
||||
indexStat.getField("writeCost").setBackendName("write_cost");
|
||||
indexStat.getField("scansSinceReset").setBackendName("scans_since_reset");
|
||||
indexStat.getField("lastSeenAt").setBackendName("last_seen_at");
|
||||
indexStat.getField("createDate").setBackendName("create_date");
|
||||
indexStat.getField("modifyDate").setBackendName("modify_date");
|
||||
indexStat.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
indexStat.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "tableStatId", "schemaName", "tableName", "indexName", "relid")));
|
||||
indexStat.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("definition", "columns", "isUnique", "isPrimary", "isValid", "idxScan", "idxTupRead", "idxTupFetch", "sizeBytes", "writeCost", "scansSinceReset", "lastSeenAt")));
|
||||
indexStat.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(indexStat);
|
||||
|
||||
QTableMetaData activitySample = base(config, DbPerfActivitySample.TABLE_NAME, "Activity Samples", DbPerfActivitySample.class);
|
||||
activitySample.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
activitySample.getField("id").setBackendName("id");
|
||||
activitySample.getField("snapshotId").setBackendName("snapshot_id");
|
||||
activitySample.getField("snapshotId").setPossibleValueSourceName("db_perf_snapshot");
|
||||
activitySample.getField("capturedAt").setBackendName("captured_at");
|
||||
activitySample.getField("pid").setBackendName("pid");
|
||||
activitySample.getField("dbUser").setBackendName("db_user");
|
||||
activitySample.getField("applicationName").setBackendName("application_name");
|
||||
activitySample.getField("clientAddr").setBackendName("client_addr");
|
||||
activitySample.getField("state").setBackendName("state");
|
||||
activitySample.getField("state").setPossibleValueSourceName("db_perf_State");
|
||||
activitySample.getField("waitEventType").setBackendName("wait_event_type");
|
||||
activitySample.getField("waitEvent").setBackendName("wait_event");
|
||||
activitySample.getField("backendType").setBackendName("backend_type");
|
||||
activitySample.getField("xactStart").setBackendName("xact_start");
|
||||
activitySample.getField("queryStart").setBackendName("query_start");
|
||||
activitySample.getField("stateChange").setBackendName("state_change");
|
||||
activitySample.getField("querySeconds").setBackendName("query_seconds");
|
||||
activitySample.getField("xactSeconds").setBackendName("xact_seconds");
|
||||
activitySample.getField("blockedBy").setBackendName("blocked_by");
|
||||
activitySample.getField("blockingCount").setBackendName("blocking_count");
|
||||
activitySample.getField("reason").setBackendName("reason");
|
||||
activitySample.getField("reason").setPossibleValueSourceName("db_perf_Reason");
|
||||
activitySample.getField("queryId").setBackendName("query_id");
|
||||
activitySample.getField("statementId").setBackendName("statement_id");
|
||||
activitySample.getField("statementId").setPossibleValueSourceName("db_perf_statement");
|
||||
activitySample.getField("queryFingerprint").setBackendName("query_fingerprint");
|
||||
activitySample.getField("queryTextNormalized").setBackendName("query_text_normalized");
|
||||
activitySample.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
activitySample.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "snapshotId", "capturedAt", "pid", "dbUser", "applicationName", "clientAddr")));
|
||||
activitySample.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("state", "waitEventType", "waitEvent", "backendType", "xactStart", "queryStart", "stateChange", "querySeconds", "xactSeconds", "blockedBy", "blockingCount", "reason", "queryId", "statementId", "queryFingerprint", "queryTextNormalized")));
|
||||
instance.addTable(activitySample);
|
||||
|
||||
QTableMetaData connectionSample = base(config, DbPerfConnectionSample.TABLE_NAME, "Connection Samples", DbPerfConnectionSample.class);
|
||||
connectionSample.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
connectionSample.getField("id").setBackendName("id");
|
||||
connectionSample.getField("snapshotId").setBackendName("snapshot_id");
|
||||
connectionSample.getField("snapshotId").setPossibleValueSourceName("db_perf_snapshot");
|
||||
connectionSample.getField("capturedAt").setBackendName("captured_at");
|
||||
connectionSample.getField("dbUser").setBackendName("db_user");
|
||||
connectionSample.getField("applicationName").setBackendName("application_name");
|
||||
connectionSample.getField("state").setBackendName("state");
|
||||
connectionSample.getField("state").setPossibleValueSourceName("db_perf_State");
|
||||
connectionSample.getField("connectionCount").setBackendName("connection_count");
|
||||
connectionSample.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
connectionSample.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "snapshotId", "capturedAt", "dbUser", "applicationName", "state", "connectionCount")));
|
||||
instance.addTable(connectionSample);
|
||||
|
||||
QTableMetaData attribution = base(config, DbPerfAttribution.TABLE_NAME, "QQQ Attribution", DbPerfAttribution.class);
|
||||
attribution.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
attribution.getField("id").setBackendName("id");
|
||||
attribution.getField("windowStart").setBackendName("window_start");
|
||||
attribution.getField("windowEnd").setBackendName("window_end");
|
||||
attribution.getField("podName").setBackendName("pod_name");
|
||||
attribution.getField("unitKind").setBackendName("unit_kind");
|
||||
attribution.getField("unitKind").setPossibleValueSourceName("db_perf_UnitKind");
|
||||
attribution.getField("unitName").setBackendName("unit_name");
|
||||
attribution.getField("processName").setBackendName("process_name");
|
||||
attribution.getField("tableName").setBackendName("table_name");
|
||||
attribution.getField("operation").setBackendName("operation");
|
||||
attribution.getField("operation").setPossibleValueSourceName("db_perf_Operation");
|
||||
attribution.getField("fingerprint").setBackendName("fingerprint");
|
||||
attribution.getField("sqlText").setBackendName("sql_text");
|
||||
attribution.getField("units").setBackendName("units");
|
||||
attribution.getField("statements").setBackendName("statements");
|
||||
attribution.getField("totalMs").setBackendName("total_ms");
|
||||
attribution.getField("maxMs").setBackendName("max_ms");
|
||||
attribution.getField("rowsCount").setBackendName("rows_count");
|
||||
attribution.getField("errors").setBackendName("errors");
|
||||
attribution.getField("sampleRate").setBackendName("sample_rate");
|
||||
attribution.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
attribution.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "windowStart", "windowEnd", "podName", "unitKind", "unitName", "processName")));
|
||||
attribution.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("tableName", "operation", "fingerprint", "sqlText", "units", "statements", "totalMs", "maxMs", "rowsCount", "errors", "sampleRate")));
|
||||
instance.addTable(attribution);
|
||||
|
||||
QTableMetaData attributionDaily = base(config, DbPerfAttributionDaily.TABLE_NAME, "QQQ Attribution Daily", DbPerfAttributionDaily.class);
|
||||
attributionDaily.withRecordLabelFormat("%s").withRecordLabelFields("day");
|
||||
attributionDaily.withUniqueKey(new UniqueKey("day", "rollupKey"));
|
||||
attributionDaily.getField("id").setBackendName("id");
|
||||
attributionDaily.getField("day").setBackendName("day");
|
||||
attributionDaily.getField("rollupKey").setBackendName("rollup_key");
|
||||
attributionDaily.getField("unitKind").setBackendName("unit_kind");
|
||||
attributionDaily.getField("unitKind").setPossibleValueSourceName("db_perf_UnitKind");
|
||||
attributionDaily.getField("unitName").setBackendName("unit_name");
|
||||
attributionDaily.getField("processName").setBackendName("process_name");
|
||||
attributionDaily.getField("tableName").setBackendName("table_name");
|
||||
attributionDaily.getField("operation").setBackendName("operation");
|
||||
attributionDaily.getField("operation").setPossibleValueSourceName("db_perf_Operation");
|
||||
attributionDaily.getField("fingerprint").setBackendName("fingerprint");
|
||||
attributionDaily.getField("sqlText").setBackendName("sql_text");
|
||||
attributionDaily.getField("units").setBackendName("units");
|
||||
attributionDaily.getField("statements").setBackendName("statements");
|
||||
attributionDaily.getField("totalMs").setBackendName("total_ms");
|
||||
attributionDaily.getField("maxMs").setBackendName("max_ms");
|
||||
attributionDaily.getField("rowsCount").setBackendName("rows_count");
|
||||
attributionDaily.getField("errors").setBackendName("errors");
|
||||
attributionDaily.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
attributionDaily.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "day", "rollupKey", "unitKind", "unitName", "processName", "tableName")));
|
||||
attributionDaily.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("operation", "fingerprint", "sqlText", "units", "statements", "totalMs", "maxMs", "rowsCount", "errors")));
|
||||
instance.addTable(attributionDaily);
|
||||
|
||||
QTableMetaData repeatPattern = base(config, DbPerfRepeatPattern.TABLE_NAME, "Repeated Statements", DbPerfRepeatPattern.class);
|
||||
repeatPattern.withRecordLabelFormat("%s").withRecordLabelFields("id");
|
||||
repeatPattern.getField("id").setBackendName("id");
|
||||
repeatPattern.getField("windowStart").setBackendName("window_start");
|
||||
repeatPattern.getField("windowEnd").setBackendName("window_end");
|
||||
repeatPattern.getField("podName").setBackendName("pod_name");
|
||||
repeatPattern.getField("unitKind").setBackendName("unit_kind");
|
||||
repeatPattern.getField("unitKind").setPossibleValueSourceName("db_perf_UnitKind");
|
||||
repeatPattern.getField("unitName").setBackendName("unit_name");
|
||||
repeatPattern.getField("processName").setBackendName("process_name");
|
||||
repeatPattern.getField("tableName").setBackendName("table_name");
|
||||
repeatPattern.getField("fingerprint").setBackendName("fingerprint");
|
||||
repeatPattern.getField("sqlText").setBackendName("sql_text");
|
||||
repeatPattern.getField("unitsAffected").setBackendName("units_affected");
|
||||
repeatPattern.getField("maxRepeats").setBackendName("max_repeats");
|
||||
repeatPattern.getField("totalRepeats").setBackendName("total_repeats");
|
||||
repeatPattern.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
repeatPattern.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "windowStart", "windowEnd", "podName", "unitKind", "unitName", "processName")));
|
||||
repeatPattern.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("tableName", "fingerprint", "sqlText", "unitsAffected", "maxRepeats", "totalRepeats")));
|
||||
instance.addTable(repeatPattern);
|
||||
|
||||
QTableMetaData finding = base(config, DbPerfFinding.TABLE_NAME, "Findings", DbPerfFinding.class);
|
||||
finding.withRecordLabelFormat("%s").withRecordLabelFields("title");
|
||||
finding.withUniqueKey(new UniqueKey("dedupeKey"));
|
||||
finding.getField("id").setBackendName("id");
|
||||
finding.getField("backendName").setBackendName("backend_name");
|
||||
finding.getField("ruleId").setBackendName("rule_id");
|
||||
finding.getField("ruleId").setPossibleValueSourceName("db_perf_rule");
|
||||
finding.getField("ruleCode").setBackendName("rule_code");
|
||||
finding.getField("dedupeKey").setBackendName("dedupe_key");
|
||||
finding.getField("status").setBackendName("status");
|
||||
finding.getField("status").setDefaultValue("OPEN");
|
||||
finding.getField("status").setPossibleValueSourceName("db_perf_FindingStatus");
|
||||
finding.getField("severity").setBackendName("severity");
|
||||
finding.getField("severity").setPossibleValueSourceName("db_perf_Severity");
|
||||
finding.getField("score").setBackendName("score");
|
||||
finding.getField("title").setBackendName("title");
|
||||
finding.getField("summary").setBackendName("summary");
|
||||
finding.getField("evidence").setBackendName("evidence");
|
||||
finding.getField("evidence").setType(QFieldType.TEXT);
|
||||
finding.getField("recommendation").setBackendName("recommendation");
|
||||
finding.getField("recommendation").setType(QFieldType.TEXT);
|
||||
finding.getField("migrationSnippet").setBackendName("migration_snippet");
|
||||
finding.getField("migrationSnippet").setType(QFieldType.TEXT);
|
||||
finding.getField("subjectTable").setBackendName("subject_table");
|
||||
finding.getField("statementId").setBackendName("statement_id");
|
||||
finding.getField("statementId").setPossibleValueSourceName("db_perf_statement");
|
||||
finding.getField("tableStatId").setBackendName("table_stat_id");
|
||||
finding.getField("tableStatId").setPossibleValueSourceName("db_perf_table_stat");
|
||||
finding.getField("indexStatId").setBackendName("index_stat_id");
|
||||
finding.getField("indexStatId").setPossibleValueSourceName("db_perf_index_stat");
|
||||
finding.getField("unitName").setBackendName("unit_name");
|
||||
finding.getField("firstSeenAt").setBackendName("first_seen_at");
|
||||
finding.getField("lastSeenAt").setBackendName("last_seen_at");
|
||||
finding.getField("occurrences").setBackendName("occurrences");
|
||||
finding.getField("resolvedAt").setBackendName("resolved_at");
|
||||
finding.getField("statusNote").setBackendName("status_note");
|
||||
finding.getField("statusChangedBy").setBackendName("status_changed_by");
|
||||
finding.getField("createDate").setBackendName("create_date");
|
||||
finding.getField("modifyDate").setBackendName("modify_date");
|
||||
finding.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.FIELD));
|
||||
finding.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
finding.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "backendName", "ruleId", "ruleCode", "dedupeKey", "status", "severity")));
|
||||
finding.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("score", "title", "summary", "evidence", "recommendation", "migrationSnippet", "subjectTable", "statementId", "tableStatId", "indexStatId", "unitName", "firstSeenAt", "lastSeenAt", "occurrences", "resolvedAt", "statusNote", "statusChangedBy")));
|
||||
finding.withSection(new QFieldSection("system", new QIcon("table_rows"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
instance.addTable(finding);
|
||||
|
||||
QTableMetaData explain = base(config, DbPerfExplain.TABLE_NAME, "Query Plans", DbPerfExplain.class);
|
||||
explain.withRecordLabelFormat("%s").withRecordLabelFields("requestedAt");
|
||||
explain.getField("id").setBackendName("id");
|
||||
explain.getField("statementId").setBackendName("statement_id");
|
||||
explain.getField("statementId").setPossibleValueSourceName("db_perf_statement");
|
||||
explain.getField("requestedBy").setBackendName("requested_by");
|
||||
explain.getField("requestedAt").setBackendName("requested_at");
|
||||
explain.getField("mode").setBackendName("mode");
|
||||
explain.getField("mode").setPossibleValueSourceName("db_perf_ExplainMode");
|
||||
explain.getField("sqlFingerprint").setBackendName("sql_fingerprint");
|
||||
explain.getField("sqlText").setBackendName("sql_text");
|
||||
explain.getField("sqlText").setType(QFieldType.TEXT);
|
||||
explain.getField("parameterCount").setBackendName("parameter_count");
|
||||
explain.getField("durationMs").setBackendName("duration_ms");
|
||||
explain.getField("outcome").setBackendName("outcome");
|
||||
explain.getField("outcome").setPossibleValueSourceName("db_perf_Outcome");
|
||||
explain.getField("errorMessage").setBackendName("error_message");
|
||||
explain.getField("planJson").setBackendName("plan_json");
|
||||
explain.getField("planJson").setType(QFieldType.TEXT);
|
||||
explain.getField("planText").setBackendName("plan_text");
|
||||
explain.getField("planText").setType(QFieldType.TEXT);
|
||||
explain.getField("totalCost").setBackendName("total_cost");
|
||||
explain.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
explain.withSection(new QFieldSection("identity", new QIcon("table_rows"), Tier.T1, List.of("id", "statementId", "requestedBy", "requestedAt", "mode", "sqlFingerprint", "sqlText")));
|
||||
explain.withSection(new QFieldSection("details", new QIcon("table_rows"), Tier.T2, List.of("parameterCount", "durationMs", "outcome", "errorMessage", "planJson", "planText", "totalCost")));
|
||||
instance.addTable(explain);
|
||||
|
||||
instance.addPossibleValueSource(source("db_perf_Provider", DbPerfPossibleValues.Provider.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_StatsAccess", DbPerfPossibleValues.StatsAccess.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_SnapshotStatus", DbPerfPossibleValues.SnapshotStatus.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_Severity", DbPerfPossibleValues.Severity.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_Operation", DbPerfPossibleValues.Operation.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_UnitKind", DbPerfPossibleValues.UnitKind.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_FindingStatus", DbPerfPossibleValues.FindingStatus.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_ExplainMode", DbPerfPossibleValues.ExplainMode.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_Outcome", DbPerfPossibleValues.Outcome.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_Reason", DbPerfPossibleValues.Reason.values()));
|
||||
instance.addPossibleValueSource(source("db_perf_State", DbPerfPossibleValues.State.values()));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_settings"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_rule"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_snapshot"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_statement"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_query_snapshot"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_query_daily"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_table_stat"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_table_snapshot"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_table_daily"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_index_stat"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_activity_sample"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_connection_sample"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_attribution"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_attribution_daily"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_repeat_pattern"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_finding"));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable("db_perf_explain"));
|
||||
child(instance, "db_perf_snapshot", "db_perf_query_snapshot", "snapshotId");
|
||||
child(instance, "db_perf_statement", "db_perf_query_snapshot", "statementId");
|
||||
child(instance, "db_perf_statement", "db_perf_query_daily", "statementId");
|
||||
child(instance, "db_perf_snapshot", "db_perf_table_snapshot", "snapshotId");
|
||||
child(instance, "db_perf_table_stat", "db_perf_table_snapshot", "tableStatId");
|
||||
child(instance, "db_perf_table_stat", "db_perf_table_daily", "tableStatId");
|
||||
child(instance, "db_perf_table_stat", "db_perf_index_stat", "tableStatId");
|
||||
child(instance, "db_perf_snapshot", "db_perf_activity_sample", "snapshotId");
|
||||
child(instance, "db_perf_snapshot", "db_perf_connection_sample", "snapshotId");
|
||||
child(instance, "db_perf_rule", "db_perf_finding", "ruleId");
|
||||
child(instance, "db_perf_statement", "db_perf_finding", "statementId");
|
||||
child(instance, "db_perf_table_stat", "db_perf_finding", "tableStatId");
|
||||
child(instance, "db_perf_statement", "db_perf_explain", "statementId");
|
||||
QAppMetaData app = new QAppMetaData().withName(config.getAppName()).withLabel("Database Performance").withIcon(new QIcon("speed"))
|
||||
.withPermissionRules(DbPerfPermissions.rules(config.getPermissionBaseName() + ".view"))
|
||||
.withSection(new QAppSection().withName("data").withLabel("Data").withIcon(new QIcon("table_rows"))
|
||||
.withTables(List.of("db_perf_finding", "db_perf_statement", "db_perf_table_stat", "db_perf_index_stat", "db_perf_snapshot", "db_perf_settings", "db_perf_rule")));
|
||||
instance.addApp(app);
|
||||
if(config.getParentAppName() != null)
|
||||
{
|
||||
instance.getApp(config.getParentAppName()).withChild(app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Common storage, permissions and write guard.
|
||||
*******************************************************************************/
|
||||
private static QTableMetaData base(DbPerfQBitConfig config, String name, String label, Class<? extends QRecordEntity> entity) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData().withName(name).withLabel(label).withIcon(new QIcon("speed"))
|
||||
.withBackendName(config.getBackendName()).withBackendDetails(new RDBMSTableBackendDetails().withTableName(name))
|
||||
.withPrimaryKeyField("id").withFieldsFromEntity(entity)
|
||||
.withPermissionRules(DbPerfPermissions.rules(config.getPermissionBaseName() + ".view"));
|
||||
table.getPermissionRules().setCustomPermissionChecker(new com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceWithProperties(DbPerfTablePermissionChecker.class,
|
||||
java.util.Map.of("permissionName", config.getPermissionBaseName() + ".view")));
|
||||
QInstanceEnricher.setInferredFieldBackendNames(table);
|
||||
for(TableCustomizers role : List.of(TableCustomizers.PRE_INSERT_RECORD, TableCustomizers.PRE_UPDATE_RECORD, TableCustomizers.PRE_DELETE_RECORD))
|
||||
{
|
||||
table.withCustomizer(role, new QCodeReference(DbPerfWriteGuard.class));
|
||||
}
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Enum-backed stored values.
|
||||
*******************************************************************************/
|
||||
private static QPossibleValueSource source(String name, PossibleValueEnum<String>[] values)
|
||||
{
|
||||
List<QPossibleValue<?>> choices = new ArrayList<>();
|
||||
for(PossibleValueEnum<String> value : values)
|
||||
{
|
||||
choices.add(new QPossibleValue<>(value.getPossibleValueId(), value.getPossibleValueLabel()));
|
||||
}
|
||||
return (new QPossibleValueSource().withName(name).withType(QPossibleValueSourceType.ENUM).withEnumValues(choices));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A join and its record-screen child list.
|
||||
*******************************************************************************/
|
||||
private static void child(QInstance instance, String parent, String child, String field)
|
||||
{
|
||||
String name = parent + "_" + child;
|
||||
QJoinMetaData join = new QJoinMetaData().withName(name).withLeftTable(parent).withRightTable(child)
|
||||
.withType(JoinType.ONE_TO_MANY).withJoinOn(new JoinOn("id", field));
|
||||
instance.addJoin(join);
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(join).withLabel(instance.getTable(child).getLabel())
|
||||
.withMaxRows(25).withOrderBys(List.of(new QFilterOrderBy("id", false))).getWidgetMetaData()
|
||||
.withPermissionRules(instance.getTable(parent).getPermissionRules()));
|
||||
instance.getTable(parent).withSection(new QFieldSection(name, instance.getTable(child).getLabel(), new QIcon("list"), Tier.T2, null).withWidgetName(name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.metadata;
|
||||
|
||||
|
||||
import java.util.Locale;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Enum-backed possible values with readable labels.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfPossibleValues
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfPossibleValues()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for Provider.
|
||||
*******************************************************************************/
|
||||
public enum Provider implements PossibleValueEnum<String>
|
||||
{
|
||||
POSTGRES, MYSQL, UNSUPPORTED;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for StatsAccess.
|
||||
*******************************************************************************/
|
||||
public enum StatsAccess implements PossibleValueEnum<String>
|
||||
{
|
||||
FULL, NO_READ_ALL_STATS, NO_PG_STAT_STATEMENTS, UNSUPPORTED;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for SnapshotStatus.
|
||||
*******************************************************************************/
|
||||
public enum SnapshotStatus implements PossibleValueEnum<String>
|
||||
{
|
||||
RUNNING, OK, PARTIAL, FAILED;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for Severity.
|
||||
*******************************************************************************/
|
||||
public enum Severity implements PossibleValueEnum<String>
|
||||
{
|
||||
INFO, WARNING, ERROR, CRITICAL;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for Operation.
|
||||
*******************************************************************************/
|
||||
public enum Operation implements PossibleValueEnum<String>
|
||||
{
|
||||
SELECT, INSERT, UPDATE, DELETE, WITH, OTHER;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for UnitKind.
|
||||
*******************************************************************************/
|
||||
public enum UnitKind implements PossibleValueEnum<String>
|
||||
{
|
||||
HTTP_REQUEST, PROCESS, SCHEDULED_JOB, OTHER;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for FindingStatus.
|
||||
*******************************************************************************/
|
||||
public enum FindingStatus implements PossibleValueEnum<String>
|
||||
{
|
||||
OPEN, ACKNOWLEDGED, RESOLVED, IGNORED;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for ExplainMode.
|
||||
*******************************************************************************/
|
||||
public enum ExplainMode implements PossibleValueEnum<String>
|
||||
{
|
||||
PLAN, GENERIC, ANALYZE;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for Outcome.
|
||||
*******************************************************************************/
|
||||
public enum Outcome implements PossibleValueEnum<String>
|
||||
{
|
||||
OK, ERROR, DENIED, TIMEOUT;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for Reason.
|
||||
*******************************************************************************/
|
||||
public enum Reason implements PossibleValueEnum<String>
|
||||
{
|
||||
LONG_RUNNING, IDLE_IN_TRANSACTION, BLOCKED, BLOCKING;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Values for State.
|
||||
*******************************************************************************/
|
||||
public enum State implements PossibleValueEnum<String>
|
||||
{
|
||||
active, idle, idle_in_transaction, idle_in_transaction_aborted, fastpath_function_call, disabled;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stored value.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return (name().replace('_', ' '));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Human-readable label.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
String text = name().toLowerCase(Locale.ROOT).replace('_', ' ');
|
||||
return (Character.toUpperCase(text.charAt(0)) + text.substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.AvailablePermission;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionSubType;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.UseOtherPermissionNameCustomPermissionChecker;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithName;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Exact view and settings-write permissions without READ_WRITE permission names.
|
||||
*******************************************************************************/
|
||||
public class DbPerfTablePermissionChecker extends UseOtherPermissionNameCustomPermissionChecker
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Reads require view; only settings/rules have human write permissions.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void checkPermissionsThrowing(AbstractActionInput input, MetaDataWithPermissionRules metadata) throws QPermissionDeniedException
|
||||
{
|
||||
if(QContext.getQSession() instanceof QSystemUserSession)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(input instanceof InsertInput || input instanceof UpdateInput || input instanceof DeleteInput)
|
||||
{
|
||||
boolean settings = "db_perf_settings".equals(metadata.getName());
|
||||
boolean rule = "db_perf_rule".equals(metadata.getName());
|
||||
if((!settings && !rule) || settings && !(input instanceof UpdateInput)
|
||||
|| QContext.getQSession() == null || !QContext.getQSession().hasPermission(managePermission()))
|
||||
{
|
||||
throw (new QPermissionDeniedException("Database Performance write permission denied"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.checkPermissionsThrowing(input, metadata);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Publish the exact write permission in host role editors.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public AvailablePermission buildAvailablePermission(QPermissionRules rules, PermissionSubType subtype, String base, MetaDataWithName metadata, String objectType)
|
||||
{
|
||||
AvailablePermission permission = super.buildAvailablePermission(rules, subtype, base, metadata, objectType);
|
||||
if(permission != null && (subtype == TablePermissionSubType.INSERT || subtype == TablePermissionSubType.EDIT || subtype == TablePermissionSubType.DELETE))
|
||||
{
|
||||
permission.setName(managePermission());
|
||||
}
|
||||
return (permission);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The configurable base is shared with the view permission.
|
||||
*******************************************************************************/
|
||||
private String managePermission()
|
||||
{
|
||||
String view = getPermissionName();
|
||||
return (view.substring(0, view.length() - "view".length()) + "manage_settings");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.metadata;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.PermissionDeniedMessage;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Enforces, in the core actions, what a table's capabilities hide in the UI
|
||||
** and API: an insert, update or delete the table does not offer is rejected
|
||||
** unless the qbit itself (the system user) makes it. So performance records only change through the engine and record actions,
|
||||
** whoever calls the table actions (admins included).
|
||||
*******************************************************************************/
|
||||
public class DbPerfWriteGuard implements TableCustomizerInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<QRecord> preInsert(InsertInput insertInput, List<QRecord> records, boolean isPreview) throws QException
|
||||
{
|
||||
return (deny(insertInput.getTableName(), Capability.TABLE_INSERT, records));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<QRecord> preUpdate(UpdateInput updateInput, List<QRecord> records, boolean isPreview, Optional<List<QRecord>> oldRecordList) throws QException
|
||||
{
|
||||
return (deny(updateInput.getTableName(), Capability.TABLE_UPDATE, records));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<QRecord> preDelete(DeleteInput deleteInput, List<QRecord> records, boolean isPreview) throws QException
|
||||
{
|
||||
return (deny(deleteInput.getTableName(), Capability.TABLE_DELETE, records));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Add an error to every record when the table lacks the capability and the
|
||||
** caller is not the system user.
|
||||
*******************************************************************************/
|
||||
static List<QRecord> deny(String tableName, Capability capability, List<QRecord> records)
|
||||
{
|
||||
if(QContext.getQSession() instanceof QSystemUserSession)
|
||||
{
|
||||
return (records);
|
||||
}
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
if(table != null && table.getDisabledCapabilities() != null && table.getDisabledCapabilities().contains(capability))
|
||||
{
|
||||
for(QRecord record : records)
|
||||
{
|
||||
record.addError(new PermissionDeniedMessage("Only the Database Performance runtime changes this table."));
|
||||
}
|
||||
}
|
||||
if(table != null && (tableName.equals("db_perf_settings") || tableName.equals("db_perf_rule")))
|
||||
{
|
||||
String view = table.getPermissionRules().getPermissionBaseName();
|
||||
String manage = view.substring(0, view.length() - "view".length()) + "manage_settings";
|
||||
if(QContext.getQSession() == null || !QContext.getQSession().hasPermission(manage))
|
||||
{
|
||||
for(QRecord record : records)
|
||||
{
|
||||
record.addError(new PermissionDeniedMessage("Managing settings requires " + manage));
|
||||
}
|
||||
}
|
||||
}
|
||||
return (records);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Activity Samples persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfActivitySample extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_activity_sample";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer snapshotId;
|
||||
|
||||
@QField
|
||||
private Instant capturedAt;
|
||||
|
||||
@QField
|
||||
private Integer pid;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String dbUser;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String applicationName;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String clientAddr;
|
||||
|
||||
@QField(maxLength = 40)
|
||||
private String state;
|
||||
|
||||
@QField(maxLength = 60)
|
||||
private String waitEventType;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String waitEvent;
|
||||
|
||||
@QField(maxLength = 60)
|
||||
private String backendType;
|
||||
|
||||
@QField
|
||||
private Instant xactStart;
|
||||
|
||||
@QField
|
||||
private Instant queryStart;
|
||||
|
||||
@QField
|
||||
private Instant stateChange;
|
||||
|
||||
@QField
|
||||
private BigDecimal querySeconds;
|
||||
|
||||
@QField
|
||||
private BigDecimal xactSeconds;
|
||||
|
||||
@QField(maxLength = 500)
|
||||
private String blockedBy;
|
||||
|
||||
@QField
|
||||
private Integer blockingCount;
|
||||
|
||||
@QField(maxLength = 30)
|
||||
private String reason;
|
||||
|
||||
@QField
|
||||
private Long queryId;
|
||||
|
||||
@QField
|
||||
private Integer statementId;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String queryFingerprint;
|
||||
|
||||
@QField(maxLength = 4000)
|
||||
private String queryTextNormalized;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshotId()
|
||||
{
|
||||
return (this.snapshotId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getCapturedAt()
|
||||
{
|
||||
return (this.capturedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public void setCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for pid.
|
||||
*******************************************************************************/
|
||||
public Integer getPid()
|
||||
{
|
||||
return (this.pid);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for pid.
|
||||
*******************************************************************************/
|
||||
public void setPid(Integer pid)
|
||||
{
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for pid.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withPid(Integer pid)
|
||||
{
|
||||
this.pid = pid;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for dbUser.
|
||||
*******************************************************************************/
|
||||
public String getDbUser()
|
||||
{
|
||||
return (this.dbUser);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public void setDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for applicationName.
|
||||
*******************************************************************************/
|
||||
public String getApplicationName()
|
||||
{
|
||||
return (this.applicationName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for applicationName.
|
||||
*******************************************************************************/
|
||||
public void setApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for applicationName.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for clientAddr.
|
||||
*******************************************************************************/
|
||||
public String getClientAddr()
|
||||
{
|
||||
return (this.clientAddr);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for clientAddr.
|
||||
*******************************************************************************/
|
||||
public void setClientAddr(String clientAddr)
|
||||
{
|
||||
this.clientAddr = clientAddr;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for clientAddr.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withClientAddr(String clientAddr)
|
||||
{
|
||||
this.clientAddr = clientAddr;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for state.
|
||||
*******************************************************************************/
|
||||
public String getState()
|
||||
{
|
||||
return (this.state);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for state.
|
||||
*******************************************************************************/
|
||||
public void setState(String state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for state.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withState(String state)
|
||||
{
|
||||
this.state = state;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for waitEventType.
|
||||
*******************************************************************************/
|
||||
public String getWaitEventType()
|
||||
{
|
||||
return (this.waitEventType);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for waitEventType.
|
||||
*******************************************************************************/
|
||||
public void setWaitEventType(String waitEventType)
|
||||
{
|
||||
this.waitEventType = waitEventType;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for waitEventType.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withWaitEventType(String waitEventType)
|
||||
{
|
||||
this.waitEventType = waitEventType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for waitEvent.
|
||||
*******************************************************************************/
|
||||
public String getWaitEvent()
|
||||
{
|
||||
return (this.waitEvent);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for waitEvent.
|
||||
*******************************************************************************/
|
||||
public void setWaitEvent(String waitEvent)
|
||||
{
|
||||
this.waitEvent = waitEvent;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for waitEvent.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withWaitEvent(String waitEvent)
|
||||
{
|
||||
this.waitEvent = waitEvent;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendType.
|
||||
*******************************************************************************/
|
||||
public String getBackendType()
|
||||
{
|
||||
return (this.backendType);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendType.
|
||||
*******************************************************************************/
|
||||
public void setBackendType(String backendType)
|
||||
{
|
||||
this.backendType = backendType;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendType.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withBackendType(String backendType)
|
||||
{
|
||||
this.backendType = backendType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for xactStart.
|
||||
*******************************************************************************/
|
||||
public Instant getXactStart()
|
||||
{
|
||||
return (this.xactStart);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for xactStart.
|
||||
*******************************************************************************/
|
||||
public void setXactStart(Instant xactStart)
|
||||
{
|
||||
this.xactStart = xactStart;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for xactStart.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withXactStart(Instant xactStart)
|
||||
{
|
||||
this.xactStart = xactStart;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStart.
|
||||
*******************************************************************************/
|
||||
public Instant getQueryStart()
|
||||
{
|
||||
return (this.queryStart);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStart.
|
||||
*******************************************************************************/
|
||||
public void setQueryStart(Instant queryStart)
|
||||
{
|
||||
this.queryStart = queryStart;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStart.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withQueryStart(Instant queryStart)
|
||||
{
|
||||
this.queryStart = queryStart;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for stateChange.
|
||||
*******************************************************************************/
|
||||
public Instant getStateChange()
|
||||
{
|
||||
return (this.stateChange);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for stateChange.
|
||||
*******************************************************************************/
|
||||
public void setStateChange(Instant stateChange)
|
||||
{
|
||||
this.stateChange = stateChange;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for stateChange.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withStateChange(Instant stateChange)
|
||||
{
|
||||
this.stateChange = stateChange;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for querySeconds.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getQuerySeconds()
|
||||
{
|
||||
return (this.querySeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for querySeconds.
|
||||
*******************************************************************************/
|
||||
public void setQuerySeconds(BigDecimal querySeconds)
|
||||
{
|
||||
this.querySeconds = querySeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for querySeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withQuerySeconds(BigDecimal querySeconds)
|
||||
{
|
||||
this.querySeconds = querySeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for xactSeconds.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getXactSeconds()
|
||||
{
|
||||
return (this.xactSeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for xactSeconds.
|
||||
*******************************************************************************/
|
||||
public void setXactSeconds(BigDecimal xactSeconds)
|
||||
{
|
||||
this.xactSeconds = xactSeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for xactSeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withXactSeconds(BigDecimal xactSeconds)
|
||||
{
|
||||
this.xactSeconds = xactSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for blockedBy.
|
||||
*******************************************************************************/
|
||||
public String getBlockedBy()
|
||||
{
|
||||
return (this.blockedBy);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for blockedBy.
|
||||
*******************************************************************************/
|
||||
public void setBlockedBy(String blockedBy)
|
||||
{
|
||||
this.blockedBy = blockedBy;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for blockedBy.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withBlockedBy(String blockedBy)
|
||||
{
|
||||
this.blockedBy = blockedBy;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for blockingCount.
|
||||
*******************************************************************************/
|
||||
public Integer getBlockingCount()
|
||||
{
|
||||
return (this.blockingCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for blockingCount.
|
||||
*******************************************************************************/
|
||||
public void setBlockingCount(Integer blockingCount)
|
||||
{
|
||||
this.blockingCount = blockingCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for blockingCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withBlockingCount(Integer blockingCount)
|
||||
{
|
||||
this.blockingCount = blockingCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for reason.
|
||||
*******************************************************************************/
|
||||
public String getReason()
|
||||
{
|
||||
return (this.reason);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for reason.
|
||||
*******************************************************************************/
|
||||
public void setReason(String reason)
|
||||
{
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for reason.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withReason(String reason)
|
||||
{
|
||||
this.reason = reason;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryId.
|
||||
*******************************************************************************/
|
||||
public Long getQueryId()
|
||||
{
|
||||
return (this.queryId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryId.
|
||||
*******************************************************************************/
|
||||
public void setQueryId(Long queryId)
|
||||
{
|
||||
this.queryId = queryId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryId.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withQueryId(Long queryId)
|
||||
{
|
||||
this.queryId = queryId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statementId.
|
||||
*******************************************************************************/
|
||||
public Integer getStatementId()
|
||||
{
|
||||
return (this.statementId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statementId.
|
||||
*******************************************************************************/
|
||||
public void setStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statementId.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryFingerprint.
|
||||
*******************************************************************************/
|
||||
public String getQueryFingerprint()
|
||||
{
|
||||
return (this.queryFingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryFingerprint.
|
||||
*******************************************************************************/
|
||||
public void setQueryFingerprint(String queryFingerprint)
|
||||
{
|
||||
this.queryFingerprint = queryFingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryFingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withQueryFingerprint(String queryFingerprint)
|
||||
{
|
||||
this.queryFingerprint = queryFingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryTextNormalized.
|
||||
*******************************************************************************/
|
||||
public String getQueryTextNormalized()
|
||||
{
|
||||
return (this.queryTextNormalized);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryTextNormalized.
|
||||
*******************************************************************************/
|
||||
public void setQueryTextNormalized(String queryTextNormalized)
|
||||
{
|
||||
this.queryTextNormalized = queryTextNormalized;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryTextNormalized.
|
||||
*******************************************************************************/
|
||||
public DbPerfActivitySample withQueryTextNormalized(String queryTextNormalized)
|
||||
{
|
||||
this.queryTextNormalized = queryTextNormalized;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ Attribution persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfAttribution extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_attribution";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Instant windowStart;
|
||||
|
||||
@QField
|
||||
private Instant windowEnd;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String podName;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String unitKind;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String unitName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String processName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String tableName;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String operation;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String fingerprint;
|
||||
|
||||
@QField(maxLength = 4000)
|
||||
private String sqlText;
|
||||
|
||||
@QField
|
||||
private Long units;
|
||||
|
||||
@QField
|
||||
private Long statements;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal maxMs;
|
||||
|
||||
@QField
|
||||
private Long rowsCount;
|
||||
|
||||
@QField
|
||||
private Long errors;
|
||||
|
||||
@QField
|
||||
private BigDecimal sampleRate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for windowStart.
|
||||
*******************************************************************************/
|
||||
public Instant getWindowStart()
|
||||
{
|
||||
return (this.windowStart);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for windowStart.
|
||||
*******************************************************************************/
|
||||
public void setWindowStart(Instant windowStart)
|
||||
{
|
||||
this.windowStart = windowStart;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for windowStart.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withWindowStart(Instant windowStart)
|
||||
{
|
||||
this.windowStart = windowStart;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public Instant getWindowEnd()
|
||||
{
|
||||
return (this.windowEnd);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public void setWindowEnd(Instant windowEnd)
|
||||
{
|
||||
this.windowEnd = windowEnd;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withWindowEnd(Instant windowEnd)
|
||||
{
|
||||
this.windowEnd = windowEnd;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for podName.
|
||||
*******************************************************************************/
|
||||
public String getPodName()
|
||||
{
|
||||
return (this.podName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for podName.
|
||||
*******************************************************************************/
|
||||
public void setPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for podName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitKind.
|
||||
*******************************************************************************/
|
||||
public String getUnitKind()
|
||||
{
|
||||
return (this.unitKind);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public void setUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitName.
|
||||
*******************************************************************************/
|
||||
public String getUnitName()
|
||||
{
|
||||
return (this.unitName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitName.
|
||||
*******************************************************************************/
|
||||
public void setUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName.
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return (this.processName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName.
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName.
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.tableName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName.
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for operation.
|
||||
*******************************************************************************/
|
||||
public String getOperation()
|
||||
{
|
||||
return (this.operation);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for operation.
|
||||
*******************************************************************************/
|
||||
public void setOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for operation.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public String getFingerprint()
|
||||
{
|
||||
return (this.fingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public void setFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlText.
|
||||
*******************************************************************************/
|
||||
public String getSqlText()
|
||||
{
|
||||
return (this.sqlText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public void setSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for units.
|
||||
*******************************************************************************/
|
||||
public Long getUnits()
|
||||
{
|
||||
return (this.units);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for units.
|
||||
*******************************************************************************/
|
||||
public void setUnits(Long units)
|
||||
{
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for units.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withUnits(Long units)
|
||||
{
|
||||
this.units = units;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statements.
|
||||
*******************************************************************************/
|
||||
public Long getStatements()
|
||||
{
|
||||
return (this.statements);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statements.
|
||||
*******************************************************************************/
|
||||
public void setStatements(Long statements)
|
||||
{
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statements.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withStatements(Long statements)
|
||||
{
|
||||
this.statements = statements;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalMs()
|
||||
{
|
||||
return (this.totalMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public void setTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMaxMs()
|
||||
{
|
||||
return (this.maxMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public void setMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public Long getRowsCount()
|
||||
{
|
||||
return (this.rowsCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public void setRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for errors.
|
||||
*******************************************************************************/
|
||||
public Long getErrors()
|
||||
{
|
||||
return (this.errors);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for errors.
|
||||
*******************************************************************************/
|
||||
public void setErrors(Long errors)
|
||||
{
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for errors.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withErrors(Long errors)
|
||||
{
|
||||
this.errors = errors;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sampleRate.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getSampleRate()
|
||||
{
|
||||
return (this.sampleRate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sampleRate.
|
||||
*******************************************************************************/
|
||||
public void setSampleRate(BigDecimal sampleRate)
|
||||
{
|
||||
this.sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sampleRate.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttribution withSampleRate(BigDecimal sampleRate)
|
||||
{
|
||||
this.sampleRate = sampleRate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QQQ Attribution Daily persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfAttributionDaily extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_attribution_daily";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private LocalDate day;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String rollupKey;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String unitKind;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String unitName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String processName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String tableName;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String operation;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String fingerprint;
|
||||
|
||||
@QField(maxLength = 4000)
|
||||
private String sqlText;
|
||||
|
||||
@QField
|
||||
private Long units;
|
||||
|
||||
@QField
|
||||
private Long statements;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal maxMs;
|
||||
|
||||
@QField
|
||||
private Long rowsCount;
|
||||
|
||||
@QField
|
||||
private Long errors;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for day.
|
||||
*******************************************************************************/
|
||||
public LocalDate getDay()
|
||||
{
|
||||
return (this.day);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for day.
|
||||
*******************************************************************************/
|
||||
public void setDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for day.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rollupKey.
|
||||
*******************************************************************************/
|
||||
public String getRollupKey()
|
||||
{
|
||||
return (this.rollupKey);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rollupKey.
|
||||
*******************************************************************************/
|
||||
public void setRollupKey(String rollupKey)
|
||||
{
|
||||
this.rollupKey = rollupKey;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rollupKey.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withRollupKey(String rollupKey)
|
||||
{
|
||||
this.rollupKey = rollupKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitKind.
|
||||
*******************************************************************************/
|
||||
public String getUnitKind()
|
||||
{
|
||||
return (this.unitKind);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public void setUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitName.
|
||||
*******************************************************************************/
|
||||
public String getUnitName()
|
||||
{
|
||||
return (this.unitName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitName.
|
||||
*******************************************************************************/
|
||||
public void setUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName.
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return (this.processName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName.
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName.
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.tableName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName.
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for operation.
|
||||
*******************************************************************************/
|
||||
public String getOperation()
|
||||
{
|
||||
return (this.operation);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for operation.
|
||||
*******************************************************************************/
|
||||
public void setOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for operation.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public String getFingerprint()
|
||||
{
|
||||
return (this.fingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public void setFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlText.
|
||||
*******************************************************************************/
|
||||
public String getSqlText()
|
||||
{
|
||||
return (this.sqlText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public void setSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for units.
|
||||
*******************************************************************************/
|
||||
public Long getUnits()
|
||||
{
|
||||
return (this.units);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for units.
|
||||
*******************************************************************************/
|
||||
public void setUnits(Long units)
|
||||
{
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for units.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withUnits(Long units)
|
||||
{
|
||||
this.units = units;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statements.
|
||||
*******************************************************************************/
|
||||
public Long getStatements()
|
||||
{
|
||||
return (this.statements);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statements.
|
||||
*******************************************************************************/
|
||||
public void setStatements(Long statements)
|
||||
{
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statements.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withStatements(Long statements)
|
||||
{
|
||||
this.statements = statements;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalMs()
|
||||
{
|
||||
return (this.totalMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public void setTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMaxMs()
|
||||
{
|
||||
return (this.maxMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public void setMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public Long getRowsCount()
|
||||
{
|
||||
return (this.rowsCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public void setRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for errors.
|
||||
*******************************************************************************/
|
||||
public Long getErrors()
|
||||
{
|
||||
return (this.errors);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for errors.
|
||||
*******************************************************************************/
|
||||
public void setErrors(Long errors)
|
||||
{
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for errors.
|
||||
*******************************************************************************/
|
||||
public DbPerfAttributionDaily withErrors(Long errors)
|
||||
{
|
||||
this.errors = errors;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Connection Samples persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfConnectionSample extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_connection_sample";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer snapshotId;
|
||||
|
||||
@QField
|
||||
private Instant capturedAt;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String dbUser;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String applicationName;
|
||||
|
||||
@QField(maxLength = 40)
|
||||
private String state;
|
||||
|
||||
@QField
|
||||
private Integer connectionCount;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshotId()
|
||||
{
|
||||
return (this.snapshotId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getCapturedAt()
|
||||
{
|
||||
return (this.capturedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public void setCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for dbUser.
|
||||
*******************************************************************************/
|
||||
public String getDbUser()
|
||||
{
|
||||
return (this.dbUser);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public void setDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for applicationName.
|
||||
*******************************************************************************/
|
||||
public String getApplicationName()
|
||||
{
|
||||
return (this.applicationName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for applicationName.
|
||||
*******************************************************************************/
|
||||
public void setApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for applicationName.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withApplicationName(String applicationName)
|
||||
{
|
||||
this.applicationName = applicationName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for state.
|
||||
*******************************************************************************/
|
||||
public String getState()
|
||||
{
|
||||
return (this.state);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for state.
|
||||
*******************************************************************************/
|
||||
public void setState(String state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for state.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withState(String state)
|
||||
{
|
||||
this.state = state;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for connectionCount.
|
||||
*******************************************************************************/
|
||||
public Integer getConnectionCount()
|
||||
{
|
||||
return (this.connectionCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for connectionCount.
|
||||
*******************************************************************************/
|
||||
public void setConnectionCount(Integer connectionCount)
|
||||
{
|
||||
this.connectionCount = connectionCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for connectionCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfConnectionSample withConnectionCount(Integer connectionCount)
|
||||
{
|
||||
this.connectionCount = connectionCount;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Query Plans persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfExplain extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_explain";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer statementId;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String requestedBy;
|
||||
|
||||
@QField
|
||||
private Instant requestedAt;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String mode;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String sqlFingerprint;
|
||||
|
||||
@QField
|
||||
private String sqlText;
|
||||
|
||||
@QField
|
||||
private Integer parameterCount;
|
||||
|
||||
@QField
|
||||
private Integer durationMs;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String outcome;
|
||||
|
||||
@QField(maxLength = 2000)
|
||||
private String errorMessage;
|
||||
|
||||
@QField
|
||||
private String planJson;
|
||||
|
||||
@QField
|
||||
private String planText;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalCost;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statementId.
|
||||
*******************************************************************************/
|
||||
public Integer getStatementId()
|
||||
{
|
||||
return (this.statementId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statementId.
|
||||
*******************************************************************************/
|
||||
public void setStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statementId.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for requestedBy.
|
||||
*******************************************************************************/
|
||||
public String getRequestedBy()
|
||||
{
|
||||
return (this.requestedBy);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for requestedBy.
|
||||
*******************************************************************************/
|
||||
public void setRequestedBy(String requestedBy)
|
||||
{
|
||||
this.requestedBy = requestedBy;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for requestedBy.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withRequestedBy(String requestedBy)
|
||||
{
|
||||
this.requestedBy = requestedBy;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for requestedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getRequestedAt()
|
||||
{
|
||||
return (this.requestedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for requestedAt.
|
||||
*******************************************************************************/
|
||||
public void setRequestedAt(Instant requestedAt)
|
||||
{
|
||||
this.requestedAt = requestedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for requestedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withRequestedAt(Instant requestedAt)
|
||||
{
|
||||
this.requestedAt = requestedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for mode.
|
||||
*******************************************************************************/
|
||||
public String getMode()
|
||||
{
|
||||
return (this.mode);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for mode.
|
||||
*******************************************************************************/
|
||||
public void setMode(String mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for mode.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withMode(String mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlFingerprint.
|
||||
*******************************************************************************/
|
||||
public String getSqlFingerprint()
|
||||
{
|
||||
return (this.sqlFingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlFingerprint.
|
||||
*******************************************************************************/
|
||||
public void setSqlFingerprint(String sqlFingerprint)
|
||||
{
|
||||
this.sqlFingerprint = sqlFingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlFingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withSqlFingerprint(String sqlFingerprint)
|
||||
{
|
||||
this.sqlFingerprint = sqlFingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlText.
|
||||
*******************************************************************************/
|
||||
public String getSqlText()
|
||||
{
|
||||
return (this.sqlText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public void setSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for parameterCount.
|
||||
*******************************************************************************/
|
||||
public Integer getParameterCount()
|
||||
{
|
||||
return (this.parameterCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for parameterCount.
|
||||
*******************************************************************************/
|
||||
public void setParameterCount(Integer parameterCount)
|
||||
{
|
||||
this.parameterCount = parameterCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for parameterCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withParameterCount(Integer parameterCount)
|
||||
{
|
||||
this.parameterCount = parameterCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for durationMs.
|
||||
*******************************************************************************/
|
||||
public Integer getDurationMs()
|
||||
{
|
||||
return (this.durationMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for durationMs.
|
||||
*******************************************************************************/
|
||||
public void setDurationMs(Integer durationMs)
|
||||
{
|
||||
this.durationMs = durationMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for durationMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withDurationMs(Integer durationMs)
|
||||
{
|
||||
this.durationMs = durationMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for outcome.
|
||||
*******************************************************************************/
|
||||
public String getOutcome()
|
||||
{
|
||||
return (this.outcome);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for outcome.
|
||||
*******************************************************************************/
|
||||
public void setOutcome(String outcome)
|
||||
{
|
||||
this.outcome = outcome;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for outcome.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withOutcome(String outcome)
|
||||
{
|
||||
this.outcome = outcome;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for errorMessage.
|
||||
*******************************************************************************/
|
||||
public String getErrorMessage()
|
||||
{
|
||||
return (this.errorMessage);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for errorMessage.
|
||||
*******************************************************************************/
|
||||
public void setErrorMessage(String errorMessage)
|
||||
{
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for errorMessage.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withErrorMessage(String errorMessage)
|
||||
{
|
||||
this.errorMessage = errorMessage;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for planJson.
|
||||
*******************************************************************************/
|
||||
public String getPlanJson()
|
||||
{
|
||||
return (this.planJson);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for planJson.
|
||||
*******************************************************************************/
|
||||
public void setPlanJson(String planJson)
|
||||
{
|
||||
this.planJson = planJson;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for planJson.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withPlanJson(String planJson)
|
||||
{
|
||||
this.planJson = planJson;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for planText.
|
||||
*******************************************************************************/
|
||||
public String getPlanText()
|
||||
{
|
||||
return (this.planText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for planText.
|
||||
*******************************************************************************/
|
||||
public void setPlanText(String planText)
|
||||
{
|
||||
this.planText = planText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for planText.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withPlanText(String planText)
|
||||
{
|
||||
this.planText = planText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalCost.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalCost()
|
||||
{
|
||||
return (this.totalCost);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalCost.
|
||||
*******************************************************************************/
|
||||
public void setTotalCost(BigDecimal totalCost)
|
||||
{
|
||||
this.totalCost = totalCost;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalCost.
|
||||
*******************************************************************************/
|
||||
public DbPerfExplain withTotalCost(BigDecimal totalCost)
|
||||
{
|
||||
this.totalCost = totalCost;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,860 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Findings persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfFinding extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_finding";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String backendName;
|
||||
|
||||
@QField
|
||||
private Integer ruleId;
|
||||
|
||||
@QField(maxLength = 40)
|
||||
private String ruleCode;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String dedupeKey;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String status;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String severity;
|
||||
|
||||
@QField
|
||||
private BigDecimal score;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String title;
|
||||
|
||||
@QField(maxLength = 2000)
|
||||
private String summary;
|
||||
|
||||
@QField
|
||||
private String evidence;
|
||||
|
||||
@QField
|
||||
private String recommendation;
|
||||
|
||||
@QField
|
||||
private String migrationSnippet;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String subjectTable;
|
||||
|
||||
@QField
|
||||
private Integer statementId;
|
||||
|
||||
@QField
|
||||
private Integer tableStatId;
|
||||
|
||||
@QField
|
||||
private Integer indexStatId;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String unitName;
|
||||
|
||||
@QField
|
||||
private Instant firstSeenAt;
|
||||
|
||||
@QField
|
||||
private Instant lastSeenAt;
|
||||
|
||||
@QField
|
||||
private Integer occurrences;
|
||||
|
||||
@QField
|
||||
private Instant resolvedAt;
|
||||
|
||||
@QField(maxLength = 2000)
|
||||
private String statusNote;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String statusChangedBy;
|
||||
|
||||
@QField
|
||||
private Instant createDate;
|
||||
|
||||
@QField
|
||||
private Instant modifyDate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName.
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
return (this.backendName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendName.
|
||||
*******************************************************************************/
|
||||
public void setBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendName.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for ruleId.
|
||||
*******************************************************************************/
|
||||
public Integer getRuleId()
|
||||
{
|
||||
return (this.ruleId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for ruleId.
|
||||
*******************************************************************************/
|
||||
public void setRuleId(Integer ruleId)
|
||||
{
|
||||
this.ruleId = ruleId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for ruleId.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withRuleId(Integer ruleId)
|
||||
{
|
||||
this.ruleId = ruleId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for ruleCode.
|
||||
*******************************************************************************/
|
||||
public String getRuleCode()
|
||||
{
|
||||
return (this.ruleCode);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for ruleCode.
|
||||
*******************************************************************************/
|
||||
public void setRuleCode(String ruleCode)
|
||||
{
|
||||
this.ruleCode = ruleCode;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for ruleCode.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withRuleCode(String ruleCode)
|
||||
{
|
||||
this.ruleCode = ruleCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for dedupeKey.
|
||||
*******************************************************************************/
|
||||
public String getDedupeKey()
|
||||
{
|
||||
return (this.dedupeKey);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for dedupeKey.
|
||||
*******************************************************************************/
|
||||
public void setDedupeKey(String dedupeKey)
|
||||
{
|
||||
this.dedupeKey = dedupeKey;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for dedupeKey.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withDedupeKey(String dedupeKey)
|
||||
{
|
||||
this.dedupeKey = dedupeKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for status.
|
||||
*******************************************************************************/
|
||||
public String getStatus()
|
||||
{
|
||||
return (this.status);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for status.
|
||||
*******************************************************************************/
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for status.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for severity.
|
||||
*******************************************************************************/
|
||||
public String getSeverity()
|
||||
{
|
||||
return (this.severity);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for severity.
|
||||
*******************************************************************************/
|
||||
public void setSeverity(String severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for severity.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withSeverity(String severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for score.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getScore()
|
||||
{
|
||||
return (this.score);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for score.
|
||||
*******************************************************************************/
|
||||
public void setScore(BigDecimal score)
|
||||
{
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for score.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withScore(BigDecimal score)
|
||||
{
|
||||
this.score = score;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for title.
|
||||
*******************************************************************************/
|
||||
public String getTitle()
|
||||
{
|
||||
return (this.title);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for title.
|
||||
*******************************************************************************/
|
||||
public void setTitle(String title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for title.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withTitle(String title)
|
||||
{
|
||||
this.title = title;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for summary.
|
||||
*******************************************************************************/
|
||||
public String getSummary()
|
||||
{
|
||||
return (this.summary);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for summary.
|
||||
*******************************************************************************/
|
||||
public void setSummary(String summary)
|
||||
{
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for summary.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withSummary(String summary)
|
||||
{
|
||||
this.summary = summary;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for evidence.
|
||||
*******************************************************************************/
|
||||
public String getEvidence()
|
||||
{
|
||||
return (this.evidence);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for evidence.
|
||||
*******************************************************************************/
|
||||
public void setEvidence(String evidence)
|
||||
{
|
||||
this.evidence = evidence;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for evidence.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withEvidence(String evidence)
|
||||
{
|
||||
this.evidence = evidence;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recommendation.
|
||||
*******************************************************************************/
|
||||
public String getRecommendation()
|
||||
{
|
||||
return (this.recommendation);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recommendation.
|
||||
*******************************************************************************/
|
||||
public void setRecommendation(String recommendation)
|
||||
{
|
||||
this.recommendation = recommendation;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recommendation.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withRecommendation(String recommendation)
|
||||
{
|
||||
this.recommendation = recommendation;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for migrationSnippet.
|
||||
*******************************************************************************/
|
||||
public String getMigrationSnippet()
|
||||
{
|
||||
return (this.migrationSnippet);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for migrationSnippet.
|
||||
*******************************************************************************/
|
||||
public void setMigrationSnippet(String migrationSnippet)
|
||||
{
|
||||
this.migrationSnippet = migrationSnippet;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for migrationSnippet.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withMigrationSnippet(String migrationSnippet)
|
||||
{
|
||||
this.migrationSnippet = migrationSnippet;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for subjectTable.
|
||||
*******************************************************************************/
|
||||
public String getSubjectTable()
|
||||
{
|
||||
return (this.subjectTable);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for subjectTable.
|
||||
*******************************************************************************/
|
||||
public void setSubjectTable(String subjectTable)
|
||||
{
|
||||
this.subjectTable = subjectTable;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for subjectTable.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withSubjectTable(String subjectTable)
|
||||
{
|
||||
this.subjectTable = subjectTable;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statementId.
|
||||
*******************************************************************************/
|
||||
public Integer getStatementId()
|
||||
{
|
||||
return (this.statementId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statementId.
|
||||
*******************************************************************************/
|
||||
public void setStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statementId.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public Integer getTableStatId()
|
||||
{
|
||||
return (this.tableStatId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public void setTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for indexStatId.
|
||||
*******************************************************************************/
|
||||
public Integer getIndexStatId()
|
||||
{
|
||||
return (this.indexStatId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for indexStatId.
|
||||
*******************************************************************************/
|
||||
public void setIndexStatId(Integer indexStatId)
|
||||
{
|
||||
this.indexStatId = indexStatId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for indexStatId.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withIndexStatId(Integer indexStatId)
|
||||
{
|
||||
this.indexStatId = indexStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitName.
|
||||
*******************************************************************************/
|
||||
public String getUnitName()
|
||||
{
|
||||
return (this.unitName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitName.
|
||||
*******************************************************************************/
|
||||
public void setUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitName.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public Instant getFirstSeenAt()
|
||||
{
|
||||
return (this.firstSeenAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public void setFirstSeenAt(Instant firstSeenAt)
|
||||
{
|
||||
this.firstSeenAt = firstSeenAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withFirstSeenAt(Instant firstSeenAt)
|
||||
{
|
||||
this.firstSeenAt = firstSeenAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public Instant getLastSeenAt()
|
||||
{
|
||||
return (this.lastSeenAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public void setLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for occurrences.
|
||||
*******************************************************************************/
|
||||
public Integer getOccurrences()
|
||||
{
|
||||
return (this.occurrences);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for occurrences.
|
||||
*******************************************************************************/
|
||||
public void setOccurrences(Integer occurrences)
|
||||
{
|
||||
this.occurrences = occurrences;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for occurrences.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withOccurrences(Integer occurrences)
|
||||
{
|
||||
this.occurrences = occurrences;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for resolvedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getResolvedAt()
|
||||
{
|
||||
return (this.resolvedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for resolvedAt.
|
||||
*******************************************************************************/
|
||||
public void setResolvedAt(Instant resolvedAt)
|
||||
{
|
||||
this.resolvedAt = resolvedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for resolvedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withResolvedAt(Instant resolvedAt)
|
||||
{
|
||||
this.resolvedAt = resolvedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statusNote.
|
||||
*******************************************************************************/
|
||||
public String getStatusNote()
|
||||
{
|
||||
return (this.statusNote);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statusNote.
|
||||
*******************************************************************************/
|
||||
public void setStatusNote(String statusNote)
|
||||
{
|
||||
this.statusNote = statusNote;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statusNote.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withStatusNote(String statusNote)
|
||||
{
|
||||
this.statusNote = statusNote;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statusChangedBy.
|
||||
*******************************************************************************/
|
||||
public String getStatusChangedBy()
|
||||
{
|
||||
return (this.statusChangedBy);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statusChangedBy.
|
||||
*******************************************************************************/
|
||||
public void setStatusChangedBy(String statusChangedBy)
|
||||
{
|
||||
this.statusChangedBy = statusChangedBy;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statusChangedBy.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withStatusChangedBy(String statusChangedBy)
|
||||
{
|
||||
this.statusChangedBy = statusChangedBy;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate.
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate.
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfFinding withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Indexes persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfIndexStat extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_index_stat";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String backendName;
|
||||
|
||||
@QField
|
||||
private Integer tableStatId;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String schemaName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String tableName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String indexName;
|
||||
|
||||
@QField
|
||||
private Long relid;
|
||||
|
||||
@QField(maxLength = 2000)
|
||||
private String definition;
|
||||
|
||||
@QField(maxLength = 1000)
|
||||
private String columns;
|
||||
|
||||
@QField
|
||||
private Boolean isUnique;
|
||||
|
||||
@QField
|
||||
private Boolean isPrimary;
|
||||
|
||||
@QField
|
||||
private Boolean isValid;
|
||||
|
||||
@QField
|
||||
private Long idxScan;
|
||||
|
||||
@QField
|
||||
private Long idxTupRead;
|
||||
|
||||
@QField
|
||||
private Long idxTupFetch;
|
||||
|
||||
@QField
|
||||
private Long sizeBytes;
|
||||
|
||||
@QField
|
||||
private Long writeCost;
|
||||
|
||||
@QField
|
||||
private Long scansSinceReset;
|
||||
|
||||
@QField
|
||||
private Instant lastSeenAt;
|
||||
|
||||
@QField
|
||||
private Instant createDate;
|
||||
|
||||
@QField
|
||||
private Instant modifyDate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName.
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
return (this.backendName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendName.
|
||||
*******************************************************************************/
|
||||
public void setBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendName.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public Integer getTableStatId()
|
||||
{
|
||||
return (this.tableStatId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public void setTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for schemaName.
|
||||
*******************************************************************************/
|
||||
public String getSchemaName()
|
||||
{
|
||||
return (this.schemaName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for schemaName.
|
||||
*******************************************************************************/
|
||||
public void setSchemaName(String schemaName)
|
||||
{
|
||||
this.schemaName = schemaName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for schemaName.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withSchemaName(String schemaName)
|
||||
{
|
||||
this.schemaName = schemaName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName.
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.tableName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName.
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for indexName.
|
||||
*******************************************************************************/
|
||||
public String getIndexName()
|
||||
{
|
||||
return (this.indexName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for indexName.
|
||||
*******************************************************************************/
|
||||
public void setIndexName(String indexName)
|
||||
{
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for indexName.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIndexName(String indexName)
|
||||
{
|
||||
this.indexName = indexName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for relid.
|
||||
*******************************************************************************/
|
||||
public Long getRelid()
|
||||
{
|
||||
return (this.relid);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for relid.
|
||||
*******************************************************************************/
|
||||
public void setRelid(Long relid)
|
||||
{
|
||||
this.relid = relid;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for relid.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withRelid(Long relid)
|
||||
{
|
||||
this.relid = relid;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for definition.
|
||||
*******************************************************************************/
|
||||
public String getDefinition()
|
||||
{
|
||||
return (this.definition);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for definition.
|
||||
*******************************************************************************/
|
||||
public void setDefinition(String definition)
|
||||
{
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for definition.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withDefinition(String definition)
|
||||
{
|
||||
this.definition = definition;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for columns.
|
||||
*******************************************************************************/
|
||||
public String getColumns()
|
||||
{
|
||||
return (this.columns);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for columns.
|
||||
*******************************************************************************/
|
||||
public void setColumns(String columns)
|
||||
{
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for columns.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withColumns(String columns)
|
||||
{
|
||||
this.columns = columns;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isUnique.
|
||||
*******************************************************************************/
|
||||
public Boolean getIsUnique()
|
||||
{
|
||||
return (this.isUnique);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isUnique.
|
||||
*******************************************************************************/
|
||||
public void setIsUnique(Boolean isUnique)
|
||||
{
|
||||
this.isUnique = isUnique;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for isUnique.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIsUnique(Boolean isUnique)
|
||||
{
|
||||
this.isUnique = isUnique;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isPrimary.
|
||||
*******************************************************************************/
|
||||
public Boolean getIsPrimary()
|
||||
{
|
||||
return (this.isPrimary);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isPrimary.
|
||||
*******************************************************************************/
|
||||
public void setIsPrimary(Boolean isPrimary)
|
||||
{
|
||||
this.isPrimary = isPrimary;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for isPrimary.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIsPrimary(Boolean isPrimary)
|
||||
{
|
||||
this.isPrimary = isPrimary;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isValid.
|
||||
*******************************************************************************/
|
||||
public Boolean getIsValid()
|
||||
{
|
||||
return (this.isValid);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isValid.
|
||||
*******************************************************************************/
|
||||
public void setIsValid(Boolean isValid)
|
||||
{
|
||||
this.isValid = isValid;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for isValid.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIsValid(Boolean isValid)
|
||||
{
|
||||
this.isValid = isValid;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxScan.
|
||||
*******************************************************************************/
|
||||
public Long getIdxScan()
|
||||
{
|
||||
return (this.idxScan);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public void setIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxTupRead.
|
||||
*******************************************************************************/
|
||||
public Long getIdxTupRead()
|
||||
{
|
||||
return (this.idxTupRead);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxTupRead.
|
||||
*******************************************************************************/
|
||||
public void setIdxTupRead(Long idxTupRead)
|
||||
{
|
||||
this.idxTupRead = idxTupRead;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxTupRead.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIdxTupRead(Long idxTupRead)
|
||||
{
|
||||
this.idxTupRead = idxTupRead;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public Long getIdxTupFetch()
|
||||
{
|
||||
return (this.idxTupFetch);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public void setIdxTupFetch(Long idxTupFetch)
|
||||
{
|
||||
this.idxTupFetch = idxTupFetch;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withIdxTupFetch(Long idxTupFetch)
|
||||
{
|
||||
this.idxTupFetch = idxTupFetch;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sizeBytes.
|
||||
*******************************************************************************/
|
||||
public Long getSizeBytes()
|
||||
{
|
||||
return (this.sizeBytes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sizeBytes.
|
||||
*******************************************************************************/
|
||||
public void setSizeBytes(Long sizeBytes)
|
||||
{
|
||||
this.sizeBytes = sizeBytes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sizeBytes.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withSizeBytes(Long sizeBytes)
|
||||
{
|
||||
this.sizeBytes = sizeBytes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for writeCost.
|
||||
*******************************************************************************/
|
||||
public Long getWriteCost()
|
||||
{
|
||||
return (this.writeCost);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for writeCost.
|
||||
*******************************************************************************/
|
||||
public void setWriteCost(Long writeCost)
|
||||
{
|
||||
this.writeCost = writeCost;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for writeCost.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withWriteCost(Long writeCost)
|
||||
{
|
||||
this.writeCost = writeCost;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scansSinceReset.
|
||||
*******************************************************************************/
|
||||
public Long getScansSinceReset()
|
||||
{
|
||||
return (this.scansSinceReset);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scansSinceReset.
|
||||
*******************************************************************************/
|
||||
public void setScansSinceReset(Long scansSinceReset)
|
||||
{
|
||||
this.scansSinceReset = scansSinceReset;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scansSinceReset.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withScansSinceReset(Long scansSinceReset)
|
||||
{
|
||||
this.scansSinceReset = scansSinceReset;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public Instant getLastSeenAt()
|
||||
{
|
||||
return (this.lastSeenAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public void setLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate.
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate.
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfIndexStat withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Query Daily persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfQueryDaily extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_query_daily";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer statementId;
|
||||
|
||||
@QField
|
||||
private LocalDate day;
|
||||
|
||||
@QField
|
||||
private Long calls;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal meanMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal maxMs;
|
||||
|
||||
@QField
|
||||
private Long rowsCount;
|
||||
|
||||
@QField
|
||||
private BigDecimal hitRatio;
|
||||
|
||||
@QField
|
||||
private BigDecimal shareOfDbTime;
|
||||
|
||||
@QField
|
||||
private Integer snapshots;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statementId.
|
||||
*******************************************************************************/
|
||||
public Integer getStatementId()
|
||||
{
|
||||
return (this.statementId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statementId.
|
||||
*******************************************************************************/
|
||||
public void setStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statementId.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for day.
|
||||
*******************************************************************************/
|
||||
public LocalDate getDay()
|
||||
{
|
||||
return (this.day);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for day.
|
||||
*******************************************************************************/
|
||||
public void setDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for day.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for calls.
|
||||
*******************************************************************************/
|
||||
public Long getCalls()
|
||||
{
|
||||
return (this.calls);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for calls.
|
||||
*******************************************************************************/
|
||||
public void setCalls(Long calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for calls.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withCalls(Long calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalMs()
|
||||
{
|
||||
return (this.totalMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public void setTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for meanMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMeanMs()
|
||||
{
|
||||
return (this.meanMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for meanMs.
|
||||
*******************************************************************************/
|
||||
public void setMeanMs(BigDecimal meanMs)
|
||||
{
|
||||
this.meanMs = meanMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for meanMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withMeanMs(BigDecimal meanMs)
|
||||
{
|
||||
this.meanMs = meanMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMaxMs()
|
||||
{
|
||||
return (this.maxMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public void setMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public Long getRowsCount()
|
||||
{
|
||||
return (this.rowsCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public void setRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getHitRatio()
|
||||
{
|
||||
return (this.hitRatio);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public void setHitRatio(BigDecimal hitRatio)
|
||||
{
|
||||
this.hitRatio = hitRatio;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withHitRatio(BigDecimal hitRatio)
|
||||
{
|
||||
this.hitRatio = hitRatio;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getShareOfDbTime()
|
||||
{
|
||||
return (this.shareOfDbTime);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public void setShareOfDbTime(BigDecimal shareOfDbTime)
|
||||
{
|
||||
this.shareOfDbTime = shareOfDbTime;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withShareOfDbTime(BigDecimal shareOfDbTime)
|
||||
{
|
||||
this.shareOfDbTime = shareOfDbTime;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshots.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshots()
|
||||
{
|
||||
return (this.snapshots);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshots.
|
||||
*******************************************************************************/
|
||||
public void setSnapshots(Integer snapshots)
|
||||
{
|
||||
this.snapshots = snapshots;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshots.
|
||||
*******************************************************************************/
|
||||
public DbPerfQueryDaily withSnapshots(Integer snapshots)
|
||||
{
|
||||
this.snapshots = snapshots;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Query Snapshots persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfQuerySnapshot extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_query_snapshot";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer snapshotId;
|
||||
|
||||
@QField
|
||||
private Integer statementId;
|
||||
|
||||
@QField
|
||||
private Instant capturedAt;
|
||||
|
||||
@QField
|
||||
private Long calls;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal meanMs;
|
||||
|
||||
@QField
|
||||
private BigDecimal maxMs;
|
||||
|
||||
@QField
|
||||
private Long rowsCount;
|
||||
|
||||
@QField
|
||||
private Long sharedBlksHit;
|
||||
|
||||
@QField
|
||||
private Long sharedBlksRead;
|
||||
|
||||
@QField
|
||||
private BigDecimal hitRatio;
|
||||
|
||||
@QField
|
||||
private Long tempBlksWritten;
|
||||
|
||||
@QField
|
||||
private BigDecimal shareOfDbTime;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshotId()
|
||||
{
|
||||
return (this.snapshotId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statementId.
|
||||
*******************************************************************************/
|
||||
public Integer getStatementId()
|
||||
{
|
||||
return (this.statementId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statementId.
|
||||
*******************************************************************************/
|
||||
public void setStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statementId.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withStatementId(Integer statementId)
|
||||
{
|
||||
this.statementId = statementId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getCapturedAt()
|
||||
{
|
||||
return (this.capturedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public void setCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for calls.
|
||||
*******************************************************************************/
|
||||
public Long getCalls()
|
||||
{
|
||||
return (this.calls);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for calls.
|
||||
*******************************************************************************/
|
||||
public void setCalls(Long calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for calls.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withCalls(Long calls)
|
||||
{
|
||||
this.calls = calls;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalMs()
|
||||
{
|
||||
return (this.totalMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public void setTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withTotalMs(BigDecimal totalMs)
|
||||
{
|
||||
this.totalMs = totalMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for meanMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMeanMs()
|
||||
{
|
||||
return (this.meanMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for meanMs.
|
||||
*******************************************************************************/
|
||||
public void setMeanMs(BigDecimal meanMs)
|
||||
{
|
||||
this.meanMs = meanMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for meanMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withMeanMs(BigDecimal meanMs)
|
||||
{
|
||||
this.meanMs = meanMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMaxMs()
|
||||
{
|
||||
return (this.maxMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public void setMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withMaxMs(BigDecimal maxMs)
|
||||
{
|
||||
this.maxMs = maxMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public Long getRowsCount()
|
||||
{
|
||||
return (this.rowsCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public void setRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rowsCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withRowsCount(Long rowsCount)
|
||||
{
|
||||
this.rowsCount = rowsCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public Long getSharedBlksHit()
|
||||
{
|
||||
return (this.sharedBlksHit);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public void setSharedBlksHit(Long sharedBlksHit)
|
||||
{
|
||||
this.sharedBlksHit = sharedBlksHit;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withSharedBlksHit(Long sharedBlksHit)
|
||||
{
|
||||
this.sharedBlksHit = sharedBlksHit;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public Long getSharedBlksRead()
|
||||
{
|
||||
return (this.sharedBlksRead);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public void setSharedBlksRead(Long sharedBlksRead)
|
||||
{
|
||||
this.sharedBlksRead = sharedBlksRead;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withSharedBlksRead(Long sharedBlksRead)
|
||||
{
|
||||
this.sharedBlksRead = sharedBlksRead;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getHitRatio()
|
||||
{
|
||||
return (this.hitRatio);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public void setHitRatio(BigDecimal hitRatio)
|
||||
{
|
||||
this.hitRatio = hitRatio;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hitRatio.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withHitRatio(BigDecimal hitRatio)
|
||||
{
|
||||
this.hitRatio = hitRatio;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public Long getTempBlksWritten()
|
||||
{
|
||||
return (this.tempBlksWritten);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public void setTempBlksWritten(Long tempBlksWritten)
|
||||
{
|
||||
this.tempBlksWritten = tempBlksWritten;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withTempBlksWritten(Long tempBlksWritten)
|
||||
{
|
||||
this.tempBlksWritten = tempBlksWritten;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getShareOfDbTime()
|
||||
{
|
||||
return (this.shareOfDbTime);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public void setShareOfDbTime(BigDecimal shareOfDbTime)
|
||||
{
|
||||
this.shareOfDbTime = shareOfDbTime;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for shareOfDbTime.
|
||||
*******************************************************************************/
|
||||
public DbPerfQuerySnapshot withShareOfDbTime(BigDecimal shareOfDbTime)
|
||||
{
|
||||
this.shareOfDbTime = shareOfDbTime;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Repeated Statements persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfRepeatPattern extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_repeat_pattern";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Instant windowStart;
|
||||
|
||||
@QField
|
||||
private Instant windowEnd;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String podName;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String unitKind;
|
||||
|
||||
@QField(maxLength = 300)
|
||||
private String unitName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String processName;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String tableName;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String fingerprint;
|
||||
|
||||
@QField(maxLength = 4000)
|
||||
private String sqlText;
|
||||
|
||||
@QField
|
||||
private Long unitsAffected;
|
||||
|
||||
@QField
|
||||
private Integer maxRepeats;
|
||||
|
||||
@QField
|
||||
private Long totalRepeats;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for windowStart.
|
||||
*******************************************************************************/
|
||||
public Instant getWindowStart()
|
||||
{
|
||||
return (this.windowStart);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for windowStart.
|
||||
*******************************************************************************/
|
||||
public void setWindowStart(Instant windowStart)
|
||||
{
|
||||
this.windowStart = windowStart;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for windowStart.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withWindowStart(Instant windowStart)
|
||||
{
|
||||
this.windowStart = windowStart;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public Instant getWindowEnd()
|
||||
{
|
||||
return (this.windowEnd);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public void setWindowEnd(Instant windowEnd)
|
||||
{
|
||||
this.windowEnd = windowEnd;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for windowEnd.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withWindowEnd(Instant windowEnd)
|
||||
{
|
||||
this.windowEnd = windowEnd;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for podName.
|
||||
*******************************************************************************/
|
||||
public String getPodName()
|
||||
{
|
||||
return (this.podName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for podName.
|
||||
*******************************************************************************/
|
||||
public void setPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for podName.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withPodName(String podName)
|
||||
{
|
||||
this.podName = podName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitKind.
|
||||
*******************************************************************************/
|
||||
public String getUnitKind()
|
||||
{
|
||||
return (this.unitKind);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public void setUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitKind.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withUnitKind(String unitKind)
|
||||
{
|
||||
this.unitKind = unitKind;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitName.
|
||||
*******************************************************************************/
|
||||
public String getUnitName()
|
||||
{
|
||||
return (this.unitName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitName.
|
||||
*******************************************************************************/
|
||||
public void setUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitName.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withUnitName(String unitName)
|
||||
{
|
||||
this.unitName = unitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processName.
|
||||
*******************************************************************************/
|
||||
public String getProcessName()
|
||||
{
|
||||
return (this.processName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processName.
|
||||
*******************************************************************************/
|
||||
public void setProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processName.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withProcessName(String processName)
|
||||
{
|
||||
this.processName = processName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName.
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.tableName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName.
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public String getFingerprint()
|
||||
{
|
||||
return (this.fingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public void setFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sqlText.
|
||||
*******************************************************************************/
|
||||
public String getSqlText()
|
||||
{
|
||||
return (this.sqlText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public void setSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sqlText.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withSqlText(String sqlText)
|
||||
{
|
||||
this.sqlText = sqlText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for unitsAffected.
|
||||
*******************************************************************************/
|
||||
public Long getUnitsAffected()
|
||||
{
|
||||
return (this.unitsAffected);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for unitsAffected.
|
||||
*******************************************************************************/
|
||||
public void setUnitsAffected(Long unitsAffected)
|
||||
{
|
||||
this.unitsAffected = unitsAffected;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for unitsAffected.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withUnitsAffected(Long unitsAffected)
|
||||
{
|
||||
this.unitsAffected = unitsAffected;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxRepeats.
|
||||
*******************************************************************************/
|
||||
public Integer getMaxRepeats()
|
||||
{
|
||||
return (this.maxRepeats);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxRepeats.
|
||||
*******************************************************************************/
|
||||
public void setMaxRepeats(Integer maxRepeats)
|
||||
{
|
||||
this.maxRepeats = maxRepeats;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxRepeats.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withMaxRepeats(Integer maxRepeats)
|
||||
{
|
||||
this.maxRepeats = maxRepeats;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalRepeats.
|
||||
*******************************************************************************/
|
||||
public Long getTotalRepeats()
|
||||
{
|
||||
return (this.totalRepeats);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalRepeats.
|
||||
*******************************************************************************/
|
||||
public void setTotalRepeats(Long totalRepeats)
|
||||
{
|
||||
this.totalRepeats = totalRepeats;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalRepeats.
|
||||
*******************************************************************************/
|
||||
public DbPerfRepeatPattern withTotalRepeats(Long totalRepeats)
|
||||
{
|
||||
this.totalRepeats = totalRepeats;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Suggestion Rules persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfRule extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_rule";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isRequired = true, maxLength = 40)
|
||||
private String code;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String label;
|
||||
|
||||
@QField(maxLength = 1000)
|
||||
private String description;
|
||||
|
||||
@QField
|
||||
private Boolean enabled;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String severity;
|
||||
|
||||
@QField
|
||||
private BigDecimal threshold1;
|
||||
|
||||
@QField
|
||||
private BigDecimal threshold2;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String threshold1Label;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String threshold2Label;
|
||||
|
||||
@QField
|
||||
private Instant createDate;
|
||||
|
||||
@QField
|
||||
private Instant modifyDate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for code.
|
||||
*******************************************************************************/
|
||||
public String getCode()
|
||||
{
|
||||
return (this.code);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for code.
|
||||
*******************************************************************************/
|
||||
public void setCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for code.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label.
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.label);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for label.
|
||||
*******************************************************************************/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for label.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for description.
|
||||
*******************************************************************************/
|
||||
public String getDescription()
|
||||
{
|
||||
return (this.description);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for description.
|
||||
*******************************************************************************/
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for description.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabled.
|
||||
*******************************************************************************/
|
||||
public Boolean getEnabled()
|
||||
{
|
||||
return (this.enabled);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabled.
|
||||
*******************************************************************************/
|
||||
public void setEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabled.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for severity.
|
||||
*******************************************************************************/
|
||||
public String getSeverity()
|
||||
{
|
||||
return (this.severity);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for severity.
|
||||
*******************************************************************************/
|
||||
public void setSeverity(String severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for severity.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withSeverity(String severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for threshold1.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getThreshold1()
|
||||
{
|
||||
return (this.threshold1);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for threshold1.
|
||||
*******************************************************************************/
|
||||
public void setThreshold1(BigDecimal threshold1)
|
||||
{
|
||||
this.threshold1 = threshold1;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for threshold1.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withThreshold1(BigDecimal threshold1)
|
||||
{
|
||||
this.threshold1 = threshold1;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for threshold2.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getThreshold2()
|
||||
{
|
||||
return (this.threshold2);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for threshold2.
|
||||
*******************************************************************************/
|
||||
public void setThreshold2(BigDecimal threshold2)
|
||||
{
|
||||
this.threshold2 = threshold2;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for threshold2.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withThreshold2(BigDecimal threshold2)
|
||||
{
|
||||
this.threshold2 = threshold2;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for threshold1Label.
|
||||
*******************************************************************************/
|
||||
public String getThreshold1Label()
|
||||
{
|
||||
return (this.threshold1Label);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for threshold1Label.
|
||||
*******************************************************************************/
|
||||
public void setThreshold1Label(String threshold1Label)
|
||||
{
|
||||
this.threshold1Label = threshold1Label;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for threshold1Label.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withThreshold1Label(String threshold1Label)
|
||||
{
|
||||
this.threshold1Label = threshold1Label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for threshold2Label.
|
||||
*******************************************************************************/
|
||||
public String getThreshold2Label()
|
||||
{
|
||||
return (this.threshold2Label);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for threshold2Label.
|
||||
*******************************************************************************/
|
||||
public void setThreshold2Label(String threshold2Label)
|
||||
{
|
||||
this.threshold2Label = threshold2Label;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for threshold2Label.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withThreshold2Label(String threshold2Label)
|
||||
{
|
||||
this.threshold2Label = threshold2Label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate.
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate.
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfRule withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,922 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Settings persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfSettings extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_settings";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isRequired = true, maxLength = 100)
|
||||
private String backendName;
|
||||
|
||||
@QField
|
||||
private Boolean enabled;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String provider;
|
||||
|
||||
@QField
|
||||
private Integer snapshotIntervalMinutes;
|
||||
|
||||
@QField
|
||||
private Integer tableSnapshotMinutes;
|
||||
|
||||
@QField
|
||||
private Integer detailRetentionDays;
|
||||
|
||||
@QField
|
||||
private Integer rollupRetentionDays;
|
||||
|
||||
@QField
|
||||
private Integer topStatementsPerSnapshot;
|
||||
|
||||
@QField
|
||||
private Integer activityLongRunningSeconds;
|
||||
|
||||
@QField
|
||||
private Integer idleInTransactionSeconds;
|
||||
|
||||
@QField
|
||||
private Boolean attributionEnabled;
|
||||
|
||||
@QField
|
||||
private BigDecimal attributionSampleRate;
|
||||
|
||||
@QField
|
||||
private Integer attributionFlushSeconds;
|
||||
|
||||
@QField
|
||||
private Integer attributionMaxKeys;
|
||||
|
||||
@QField
|
||||
private Integer nPlusOneThreshold;
|
||||
|
||||
@QField
|
||||
private Integer explainTimeoutMs;
|
||||
|
||||
@QField
|
||||
private Boolean alertsEnabled;
|
||||
|
||||
@QField
|
||||
private BigDecimal spikeFactor;
|
||||
|
||||
@QField
|
||||
private Integer spikeMinTotalMs;
|
||||
|
||||
@QField
|
||||
private Integer connectionAlertPercent;
|
||||
|
||||
@QField
|
||||
private Integer blockedAlertSeconds;
|
||||
|
||||
@QField
|
||||
private Instant lastSnapshotAt;
|
||||
|
||||
@QField
|
||||
private Instant lastTableSnapshotAt;
|
||||
|
||||
@QField(maxLength = 30)
|
||||
private String statsAccess;
|
||||
|
||||
@QField(maxLength = 500)
|
||||
private String statsAccessMessage;
|
||||
|
||||
@QField
|
||||
private Instant createDate;
|
||||
|
||||
@QField
|
||||
private Instant modifyDate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName.
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
return (this.backendName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendName.
|
||||
*******************************************************************************/
|
||||
public void setBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendName.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabled.
|
||||
*******************************************************************************/
|
||||
public Boolean getEnabled()
|
||||
{
|
||||
return (this.enabled);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabled.
|
||||
*******************************************************************************/
|
||||
public void setEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabled.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for provider.
|
||||
*******************************************************************************/
|
||||
public String getProvider()
|
||||
{
|
||||
return (this.provider);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for provider.
|
||||
*******************************************************************************/
|
||||
public void setProvider(String provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for provider.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withProvider(String provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotIntervalMinutes.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshotIntervalMinutes()
|
||||
{
|
||||
return (this.snapshotIntervalMinutes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotIntervalMinutes.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotIntervalMinutes(Integer snapshotIntervalMinutes)
|
||||
{
|
||||
this.snapshotIntervalMinutes = snapshotIntervalMinutes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotIntervalMinutes.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withSnapshotIntervalMinutes(Integer snapshotIntervalMinutes)
|
||||
{
|
||||
this.snapshotIntervalMinutes = snapshotIntervalMinutes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableSnapshotMinutes.
|
||||
*******************************************************************************/
|
||||
public Integer getTableSnapshotMinutes()
|
||||
{
|
||||
return (this.tableSnapshotMinutes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableSnapshotMinutes.
|
||||
*******************************************************************************/
|
||||
public void setTableSnapshotMinutes(Integer tableSnapshotMinutes)
|
||||
{
|
||||
this.tableSnapshotMinutes = tableSnapshotMinutes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableSnapshotMinutes.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withTableSnapshotMinutes(Integer tableSnapshotMinutes)
|
||||
{
|
||||
this.tableSnapshotMinutes = tableSnapshotMinutes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for detailRetentionDays.
|
||||
*******************************************************************************/
|
||||
public Integer getDetailRetentionDays()
|
||||
{
|
||||
return (this.detailRetentionDays);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for detailRetentionDays.
|
||||
*******************************************************************************/
|
||||
public void setDetailRetentionDays(Integer detailRetentionDays)
|
||||
{
|
||||
this.detailRetentionDays = detailRetentionDays;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for detailRetentionDays.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withDetailRetentionDays(Integer detailRetentionDays)
|
||||
{
|
||||
this.detailRetentionDays = detailRetentionDays;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for rollupRetentionDays.
|
||||
*******************************************************************************/
|
||||
public Integer getRollupRetentionDays()
|
||||
{
|
||||
return (this.rollupRetentionDays);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for rollupRetentionDays.
|
||||
*******************************************************************************/
|
||||
public void setRollupRetentionDays(Integer rollupRetentionDays)
|
||||
{
|
||||
this.rollupRetentionDays = rollupRetentionDays;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for rollupRetentionDays.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withRollupRetentionDays(Integer rollupRetentionDays)
|
||||
{
|
||||
this.rollupRetentionDays = rollupRetentionDays;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for topStatementsPerSnapshot.
|
||||
*******************************************************************************/
|
||||
public Integer getTopStatementsPerSnapshot()
|
||||
{
|
||||
return (this.topStatementsPerSnapshot);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for topStatementsPerSnapshot.
|
||||
*******************************************************************************/
|
||||
public void setTopStatementsPerSnapshot(Integer topStatementsPerSnapshot)
|
||||
{
|
||||
this.topStatementsPerSnapshot = topStatementsPerSnapshot;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for topStatementsPerSnapshot.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withTopStatementsPerSnapshot(Integer topStatementsPerSnapshot)
|
||||
{
|
||||
this.topStatementsPerSnapshot = topStatementsPerSnapshot;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for activityLongRunningSeconds.
|
||||
*******************************************************************************/
|
||||
public Integer getActivityLongRunningSeconds()
|
||||
{
|
||||
return (this.activityLongRunningSeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for activityLongRunningSeconds.
|
||||
*******************************************************************************/
|
||||
public void setActivityLongRunningSeconds(Integer activityLongRunningSeconds)
|
||||
{
|
||||
this.activityLongRunningSeconds = activityLongRunningSeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for activityLongRunningSeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withActivityLongRunningSeconds(Integer activityLongRunningSeconds)
|
||||
{
|
||||
this.activityLongRunningSeconds = activityLongRunningSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idleInTransactionSeconds.
|
||||
*******************************************************************************/
|
||||
public Integer getIdleInTransactionSeconds()
|
||||
{
|
||||
return (this.idleInTransactionSeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idleInTransactionSeconds.
|
||||
*******************************************************************************/
|
||||
public void setIdleInTransactionSeconds(Integer idleInTransactionSeconds)
|
||||
{
|
||||
this.idleInTransactionSeconds = idleInTransactionSeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idleInTransactionSeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withIdleInTransactionSeconds(Integer idleInTransactionSeconds)
|
||||
{
|
||||
this.idleInTransactionSeconds = idleInTransactionSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public Boolean getAttributionEnabled()
|
||||
{
|
||||
return (this.attributionEnabled);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public void setAttributionEnabled(Boolean attributionEnabled)
|
||||
{
|
||||
this.attributionEnabled = attributionEnabled;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for attributionEnabled.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withAttributionEnabled(Boolean attributionEnabled)
|
||||
{
|
||||
this.attributionEnabled = attributionEnabled;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for attributionSampleRate.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getAttributionSampleRate()
|
||||
{
|
||||
return (this.attributionSampleRate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for attributionSampleRate.
|
||||
*******************************************************************************/
|
||||
public void setAttributionSampleRate(BigDecimal attributionSampleRate)
|
||||
{
|
||||
this.attributionSampleRate = attributionSampleRate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for attributionSampleRate.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withAttributionSampleRate(BigDecimal attributionSampleRate)
|
||||
{
|
||||
this.attributionSampleRate = attributionSampleRate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for attributionFlushSeconds.
|
||||
*******************************************************************************/
|
||||
public Integer getAttributionFlushSeconds()
|
||||
{
|
||||
return (this.attributionFlushSeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for attributionFlushSeconds.
|
||||
*******************************************************************************/
|
||||
public void setAttributionFlushSeconds(Integer attributionFlushSeconds)
|
||||
{
|
||||
this.attributionFlushSeconds = attributionFlushSeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for attributionFlushSeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withAttributionFlushSeconds(Integer attributionFlushSeconds)
|
||||
{
|
||||
this.attributionFlushSeconds = attributionFlushSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for attributionMaxKeys.
|
||||
*******************************************************************************/
|
||||
public Integer getAttributionMaxKeys()
|
||||
{
|
||||
return (this.attributionMaxKeys);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for attributionMaxKeys.
|
||||
*******************************************************************************/
|
||||
public void setAttributionMaxKeys(Integer attributionMaxKeys)
|
||||
{
|
||||
this.attributionMaxKeys = attributionMaxKeys;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for attributionMaxKeys.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withAttributionMaxKeys(Integer attributionMaxKeys)
|
||||
{
|
||||
this.attributionMaxKeys = attributionMaxKeys;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nPlusOneThreshold.
|
||||
*******************************************************************************/
|
||||
public Integer getNPlusOneThreshold()
|
||||
{
|
||||
return (this.nPlusOneThreshold);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nPlusOneThreshold.
|
||||
*******************************************************************************/
|
||||
public void setNPlusOneThreshold(Integer nPlusOneThreshold)
|
||||
{
|
||||
this.nPlusOneThreshold = nPlusOneThreshold;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nPlusOneThreshold.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withNPlusOneThreshold(Integer nPlusOneThreshold)
|
||||
{
|
||||
this.nPlusOneThreshold = nPlusOneThreshold;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for explainTimeoutMs.
|
||||
*******************************************************************************/
|
||||
public Integer getExplainTimeoutMs()
|
||||
{
|
||||
return (this.explainTimeoutMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for explainTimeoutMs.
|
||||
*******************************************************************************/
|
||||
public void setExplainTimeoutMs(Integer explainTimeoutMs)
|
||||
{
|
||||
this.explainTimeoutMs = explainTimeoutMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for explainTimeoutMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withExplainTimeoutMs(Integer explainTimeoutMs)
|
||||
{
|
||||
this.explainTimeoutMs = explainTimeoutMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for alertsEnabled.
|
||||
*******************************************************************************/
|
||||
public Boolean getAlertsEnabled()
|
||||
{
|
||||
return (this.alertsEnabled);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for alertsEnabled.
|
||||
*******************************************************************************/
|
||||
public void setAlertsEnabled(Boolean alertsEnabled)
|
||||
{
|
||||
this.alertsEnabled = alertsEnabled;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for alertsEnabled.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withAlertsEnabled(Boolean alertsEnabled)
|
||||
{
|
||||
this.alertsEnabled = alertsEnabled;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for spikeFactor.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getSpikeFactor()
|
||||
{
|
||||
return (this.spikeFactor);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for spikeFactor.
|
||||
*******************************************************************************/
|
||||
public void setSpikeFactor(BigDecimal spikeFactor)
|
||||
{
|
||||
this.spikeFactor = spikeFactor;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for spikeFactor.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withSpikeFactor(BigDecimal spikeFactor)
|
||||
{
|
||||
this.spikeFactor = spikeFactor;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for spikeMinTotalMs.
|
||||
*******************************************************************************/
|
||||
public Integer getSpikeMinTotalMs()
|
||||
{
|
||||
return (this.spikeMinTotalMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for spikeMinTotalMs.
|
||||
*******************************************************************************/
|
||||
public void setSpikeMinTotalMs(Integer spikeMinTotalMs)
|
||||
{
|
||||
this.spikeMinTotalMs = spikeMinTotalMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for spikeMinTotalMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withSpikeMinTotalMs(Integer spikeMinTotalMs)
|
||||
{
|
||||
this.spikeMinTotalMs = spikeMinTotalMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for connectionAlertPercent.
|
||||
*******************************************************************************/
|
||||
public Integer getConnectionAlertPercent()
|
||||
{
|
||||
return (this.connectionAlertPercent);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for connectionAlertPercent.
|
||||
*******************************************************************************/
|
||||
public void setConnectionAlertPercent(Integer connectionAlertPercent)
|
||||
{
|
||||
this.connectionAlertPercent = connectionAlertPercent;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for connectionAlertPercent.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withConnectionAlertPercent(Integer connectionAlertPercent)
|
||||
{
|
||||
this.connectionAlertPercent = connectionAlertPercent;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for blockedAlertSeconds.
|
||||
*******************************************************************************/
|
||||
public Integer getBlockedAlertSeconds()
|
||||
{
|
||||
return (this.blockedAlertSeconds);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for blockedAlertSeconds.
|
||||
*******************************************************************************/
|
||||
public void setBlockedAlertSeconds(Integer blockedAlertSeconds)
|
||||
{
|
||||
this.blockedAlertSeconds = blockedAlertSeconds;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for blockedAlertSeconds.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withBlockedAlertSeconds(Integer blockedAlertSeconds)
|
||||
{
|
||||
this.blockedAlertSeconds = blockedAlertSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public Instant getLastSnapshotAt()
|
||||
{
|
||||
return (this.lastSnapshotAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public void setLastSnapshotAt(Instant lastSnapshotAt)
|
||||
{
|
||||
this.lastSnapshotAt = lastSnapshotAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withLastSnapshotAt(Instant lastSnapshotAt)
|
||||
{
|
||||
this.lastSnapshotAt = lastSnapshotAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastTableSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public Instant getLastTableSnapshotAt()
|
||||
{
|
||||
return (this.lastTableSnapshotAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastTableSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public void setLastTableSnapshotAt(Instant lastTableSnapshotAt)
|
||||
{
|
||||
this.lastTableSnapshotAt = lastTableSnapshotAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastTableSnapshotAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withLastTableSnapshotAt(Instant lastTableSnapshotAt)
|
||||
{
|
||||
this.lastTableSnapshotAt = lastTableSnapshotAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statsAccess.
|
||||
*******************************************************************************/
|
||||
public String getStatsAccess()
|
||||
{
|
||||
return (this.statsAccess);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statsAccess.
|
||||
*******************************************************************************/
|
||||
public void setStatsAccess(String statsAccess)
|
||||
{
|
||||
this.statsAccess = statsAccess;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statsAccess.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withStatsAccess(String statsAccess)
|
||||
{
|
||||
this.statsAccess = statsAccess;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for statsAccessMessage.
|
||||
*******************************************************************************/
|
||||
public String getStatsAccessMessage()
|
||||
{
|
||||
return (this.statsAccessMessage);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for statsAccessMessage.
|
||||
*******************************************************************************/
|
||||
public void setStatsAccessMessage(String statsAccessMessage)
|
||||
{
|
||||
this.statsAccessMessage = statsAccessMessage;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for statsAccessMessage.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withStatsAccessMessage(String statsAccessMessage)
|
||||
{
|
||||
this.statsAccessMessage = statsAccessMessage;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate.
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate.
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfSettings withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Queries persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfStatement extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_statement";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String backendName;
|
||||
|
||||
@QField
|
||||
private Long queryId;
|
||||
|
||||
@QField(maxLength = 100)
|
||||
private String dbUser;
|
||||
|
||||
@QField
|
||||
private Boolean toplevel;
|
||||
|
||||
@QField(maxLength = 64)
|
||||
private String fingerprint;
|
||||
|
||||
@QField
|
||||
private String queryText;
|
||||
|
||||
@QField(maxLength = 20)
|
||||
private String operation;
|
||||
|
||||
@QField(maxLength = 200)
|
||||
private String mainTable;
|
||||
|
||||
@QField
|
||||
private Instant firstSeenAt;
|
||||
|
||||
@QField
|
||||
private Instant lastSeenAt;
|
||||
|
||||
@QField
|
||||
private Long lastCalls;
|
||||
|
||||
@QField
|
||||
private BigDecimal lastTotalExecMs;
|
||||
|
||||
@QField
|
||||
private Long lastRows;
|
||||
|
||||
@QField
|
||||
private Long lastSharedBlksHit;
|
||||
|
||||
@QField
|
||||
private Long lastSharedBlksRead;
|
||||
|
||||
@QField
|
||||
private Long lastTempBlksWritten;
|
||||
|
||||
@QField
|
||||
private BigDecimal lastMaxExecMs;
|
||||
|
||||
@QField
|
||||
private Instant lastStatsSince;
|
||||
|
||||
@QField
|
||||
private Long calls24h;
|
||||
|
||||
@QField
|
||||
private BigDecimal totalMs24h;
|
||||
|
||||
@QField
|
||||
private BigDecimal meanMs24h;
|
||||
|
||||
@QField
|
||||
private BigDecimal shareOfDbTime24h;
|
||||
|
||||
@QField
|
||||
private Instant createDate;
|
||||
|
||||
@QField
|
||||
private Instant modifyDate;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName.
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
return (this.backendName);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendName.
|
||||
*******************************************************************************/
|
||||
public void setBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendName.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withBackendName(String backendName)
|
||||
{
|
||||
this.backendName = backendName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryId.
|
||||
*******************************************************************************/
|
||||
public Long getQueryId()
|
||||
{
|
||||
return (this.queryId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryId.
|
||||
*******************************************************************************/
|
||||
public void setQueryId(Long queryId)
|
||||
{
|
||||
this.queryId = queryId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryId.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withQueryId(Long queryId)
|
||||
{
|
||||
this.queryId = queryId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for dbUser.
|
||||
*******************************************************************************/
|
||||
public String getDbUser()
|
||||
{
|
||||
return (this.dbUser);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public void setDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for dbUser.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withDbUser(String dbUser)
|
||||
{
|
||||
this.dbUser = dbUser;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for toplevel.
|
||||
*******************************************************************************/
|
||||
public Boolean getToplevel()
|
||||
{
|
||||
return (this.toplevel);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for toplevel.
|
||||
*******************************************************************************/
|
||||
public void setToplevel(Boolean toplevel)
|
||||
{
|
||||
this.toplevel = toplevel;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for toplevel.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withToplevel(Boolean toplevel)
|
||||
{
|
||||
this.toplevel = toplevel;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public String getFingerprint()
|
||||
{
|
||||
return (this.fingerprint);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public void setFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fingerprint.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withFingerprint(String fingerprint)
|
||||
{
|
||||
this.fingerprint = fingerprint;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryText.
|
||||
*******************************************************************************/
|
||||
public String getQueryText()
|
||||
{
|
||||
return (this.queryText);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryText.
|
||||
*******************************************************************************/
|
||||
public void setQueryText(String queryText)
|
||||
{
|
||||
this.queryText = queryText;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryText.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withQueryText(String queryText)
|
||||
{
|
||||
this.queryText = queryText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for operation.
|
||||
*******************************************************************************/
|
||||
public String getOperation()
|
||||
{
|
||||
return (this.operation);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for operation.
|
||||
*******************************************************************************/
|
||||
public void setOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for operation.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withOperation(String operation)
|
||||
{
|
||||
this.operation = operation;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for mainTable.
|
||||
*******************************************************************************/
|
||||
public String getMainTable()
|
||||
{
|
||||
return (this.mainTable);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for mainTable.
|
||||
*******************************************************************************/
|
||||
public void setMainTable(String mainTable)
|
||||
{
|
||||
this.mainTable = mainTable;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for mainTable.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withMainTable(String mainTable)
|
||||
{
|
||||
this.mainTable = mainTable;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public Instant getFirstSeenAt()
|
||||
{
|
||||
return (this.firstSeenAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public void setFirstSeenAt(Instant firstSeenAt)
|
||||
{
|
||||
this.firstSeenAt = firstSeenAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for firstSeenAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withFirstSeenAt(Instant firstSeenAt)
|
||||
{
|
||||
this.firstSeenAt = firstSeenAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public Instant getLastSeenAt()
|
||||
{
|
||||
return (this.lastSeenAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public void setLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSeenAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastSeenAt(Instant lastSeenAt)
|
||||
{
|
||||
this.lastSeenAt = lastSeenAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastCalls.
|
||||
*******************************************************************************/
|
||||
public Long getLastCalls()
|
||||
{
|
||||
return (this.lastCalls);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastCalls.
|
||||
*******************************************************************************/
|
||||
public void setLastCalls(Long lastCalls)
|
||||
{
|
||||
this.lastCalls = lastCalls;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastCalls.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastCalls(Long lastCalls)
|
||||
{
|
||||
this.lastCalls = lastCalls;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastTotalExecMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getLastTotalExecMs()
|
||||
{
|
||||
return (this.lastTotalExecMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastTotalExecMs.
|
||||
*******************************************************************************/
|
||||
public void setLastTotalExecMs(BigDecimal lastTotalExecMs)
|
||||
{
|
||||
this.lastTotalExecMs = lastTotalExecMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastTotalExecMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastTotalExecMs(BigDecimal lastTotalExecMs)
|
||||
{
|
||||
this.lastTotalExecMs = lastTotalExecMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastRows.
|
||||
*******************************************************************************/
|
||||
public Long getLastRows()
|
||||
{
|
||||
return (this.lastRows);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastRows.
|
||||
*******************************************************************************/
|
||||
public void setLastRows(Long lastRows)
|
||||
{
|
||||
this.lastRows = lastRows;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastRows.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastRows(Long lastRows)
|
||||
{
|
||||
this.lastRows = lastRows;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public Long getLastSharedBlksHit()
|
||||
{
|
||||
return (this.lastSharedBlksHit);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public void setLastSharedBlksHit(Long lastSharedBlksHit)
|
||||
{
|
||||
this.lastSharedBlksHit = lastSharedBlksHit;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSharedBlksHit.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastSharedBlksHit(Long lastSharedBlksHit)
|
||||
{
|
||||
this.lastSharedBlksHit = lastSharedBlksHit;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastSharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public Long getLastSharedBlksRead()
|
||||
{
|
||||
return (this.lastSharedBlksRead);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastSharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public void setLastSharedBlksRead(Long lastSharedBlksRead)
|
||||
{
|
||||
this.lastSharedBlksRead = lastSharedBlksRead;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastSharedBlksRead.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastSharedBlksRead(Long lastSharedBlksRead)
|
||||
{
|
||||
this.lastSharedBlksRead = lastSharedBlksRead;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastTempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public Long getLastTempBlksWritten()
|
||||
{
|
||||
return (this.lastTempBlksWritten);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastTempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public void setLastTempBlksWritten(Long lastTempBlksWritten)
|
||||
{
|
||||
this.lastTempBlksWritten = lastTempBlksWritten;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastTempBlksWritten.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastTempBlksWritten(Long lastTempBlksWritten)
|
||||
{
|
||||
this.lastTempBlksWritten = lastTempBlksWritten;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastMaxExecMs.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getLastMaxExecMs()
|
||||
{
|
||||
return (this.lastMaxExecMs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastMaxExecMs.
|
||||
*******************************************************************************/
|
||||
public void setLastMaxExecMs(BigDecimal lastMaxExecMs)
|
||||
{
|
||||
this.lastMaxExecMs = lastMaxExecMs;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastMaxExecMs.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastMaxExecMs(BigDecimal lastMaxExecMs)
|
||||
{
|
||||
this.lastMaxExecMs = lastMaxExecMs;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastStatsSince.
|
||||
*******************************************************************************/
|
||||
public Instant getLastStatsSince()
|
||||
{
|
||||
return (this.lastStatsSince);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastStatsSince.
|
||||
*******************************************************************************/
|
||||
public void setLastStatsSince(Instant lastStatsSince)
|
||||
{
|
||||
this.lastStatsSince = lastStatsSince;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastStatsSince.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withLastStatsSince(Instant lastStatsSince)
|
||||
{
|
||||
this.lastStatsSince = lastStatsSince;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for calls24h.
|
||||
*******************************************************************************/
|
||||
public Long getCalls24h()
|
||||
{
|
||||
return (this.calls24h);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for calls24h.
|
||||
*******************************************************************************/
|
||||
public void setCalls24h(Long calls24h)
|
||||
{
|
||||
this.calls24h = calls24h;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for calls24h.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withCalls24h(Long calls24h)
|
||||
{
|
||||
this.calls24h = calls24h;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalMs24h.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getTotalMs24h()
|
||||
{
|
||||
return (this.totalMs24h);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalMs24h.
|
||||
*******************************************************************************/
|
||||
public void setTotalMs24h(BigDecimal totalMs24h)
|
||||
{
|
||||
this.totalMs24h = totalMs24h;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalMs24h.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withTotalMs24h(BigDecimal totalMs24h)
|
||||
{
|
||||
this.totalMs24h = totalMs24h;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for meanMs24h.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getMeanMs24h()
|
||||
{
|
||||
return (this.meanMs24h);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for meanMs24h.
|
||||
*******************************************************************************/
|
||||
public void setMeanMs24h(BigDecimal meanMs24h)
|
||||
{
|
||||
this.meanMs24h = meanMs24h;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for meanMs24h.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withMeanMs24h(BigDecimal meanMs24h)
|
||||
{
|
||||
this.meanMs24h = meanMs24h;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shareOfDbTime24h.
|
||||
*******************************************************************************/
|
||||
public BigDecimal getShareOfDbTime24h()
|
||||
{
|
||||
return (this.shareOfDbTime24h);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shareOfDbTime24h.
|
||||
*******************************************************************************/
|
||||
public void setShareOfDbTime24h(BigDecimal shareOfDbTime24h)
|
||||
{
|
||||
this.shareOfDbTime24h = shareOfDbTime24h;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for shareOfDbTime24h.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withShareOfDbTime24h(BigDecimal shareOfDbTime24h)
|
||||
{
|
||||
this.shareOfDbTime24h = shareOfDbTime24h;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate.
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate.
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate.
|
||||
*******************************************************************************/
|
||||
public DbPerfStatement withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Table Daily persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfTableDaily extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_table_daily";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer tableStatId;
|
||||
|
||||
@QField
|
||||
private LocalDate day;
|
||||
|
||||
@QField
|
||||
private Long seqScan;
|
||||
|
||||
@QField
|
||||
private Long idxScan;
|
||||
|
||||
@QField
|
||||
private Long seqTupRead;
|
||||
|
||||
@QField
|
||||
private Long nTupIns;
|
||||
|
||||
@QField
|
||||
private Long nTupUpd;
|
||||
|
||||
@QField
|
||||
private Long nTupDel;
|
||||
|
||||
@QField
|
||||
private Long nLiveTup;
|
||||
|
||||
@QField
|
||||
private Long nDeadTup;
|
||||
|
||||
@QField
|
||||
private Long totalBytes;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public Integer getTableStatId()
|
||||
{
|
||||
return (this.tableStatId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public void setTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for day.
|
||||
*******************************************************************************/
|
||||
public LocalDate getDay()
|
||||
{
|
||||
return (this.day);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for day.
|
||||
*******************************************************************************/
|
||||
public void setDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for day.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withDay(LocalDate day)
|
||||
{
|
||||
this.day = day;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for seqScan.
|
||||
*******************************************************************************/
|
||||
public Long getSeqScan()
|
||||
{
|
||||
return (this.seqScan);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for seqScan.
|
||||
*******************************************************************************/
|
||||
public void setSeqScan(Long seqScan)
|
||||
{
|
||||
this.seqScan = seqScan;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for seqScan.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withSeqScan(Long seqScan)
|
||||
{
|
||||
this.seqScan = seqScan;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxScan.
|
||||
*******************************************************************************/
|
||||
public Long getIdxScan()
|
||||
{
|
||||
return (this.idxScan);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public void setIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public Long getSeqTupRead()
|
||||
{
|
||||
return (this.seqTupRead);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public void setSeqTupRead(Long seqTupRead)
|
||||
{
|
||||
this.seqTupRead = seqTupRead;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withSeqTupRead(Long seqTupRead)
|
||||
{
|
||||
this.seqTupRead = seqTupRead;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public Long getNTupIns()
|
||||
{
|
||||
return (this.nTupIns);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public void setNTupIns(Long nTupIns)
|
||||
{
|
||||
this.nTupIns = nTupIns;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withNTupIns(Long nTupIns)
|
||||
{
|
||||
this.nTupIns = nTupIns;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public Long getNTupUpd()
|
||||
{
|
||||
return (this.nTupUpd);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public void setNTupUpd(Long nTupUpd)
|
||||
{
|
||||
this.nTupUpd = nTupUpd;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withNTupUpd(Long nTupUpd)
|
||||
{
|
||||
this.nTupUpd = nTupUpd;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public Long getNTupDel()
|
||||
{
|
||||
return (this.nTupDel);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public void setNTupDel(Long nTupDel)
|
||||
{
|
||||
this.nTupDel = nTupDel;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withNTupDel(Long nTupDel)
|
||||
{
|
||||
this.nTupDel = nTupDel;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public Long getNLiveTup()
|
||||
{
|
||||
return (this.nLiveTup);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public void setNLiveTup(Long nLiveTup)
|
||||
{
|
||||
this.nLiveTup = nLiveTup;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withNLiveTup(Long nLiveTup)
|
||||
{
|
||||
this.nLiveTup = nLiveTup;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public Long getNDeadTup()
|
||||
{
|
||||
return (this.nDeadTup);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public void setNDeadTup(Long nDeadTup)
|
||||
{
|
||||
this.nDeadTup = nDeadTup;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withNDeadTup(Long nDeadTup)
|
||||
{
|
||||
this.nDeadTup = nDeadTup;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public Long getTotalBytes()
|
||||
{
|
||||
return (this.totalBytes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public void setTotalBytes(Long totalBytes)
|
||||
{
|
||||
this.totalBytes = totalBytes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableDaily withTotalBytes(Long totalBytes)
|
||||
{
|
||||
this.totalBytes = totalBytes;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.model;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Table Snapshots persisted through QQQ table actions.
|
||||
*******************************************************************************/
|
||||
public class DbPerfTableSnapshot extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "db_perf_table_snapshot";
|
||||
|
||||
@QField(isPrimaryKey = true, isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField
|
||||
private Integer snapshotId;
|
||||
|
||||
@QField
|
||||
private Integer tableStatId;
|
||||
|
||||
@QField
|
||||
private Instant capturedAt;
|
||||
|
||||
@QField
|
||||
private Long seqScan;
|
||||
|
||||
@QField
|
||||
private Long seqTupRead;
|
||||
|
||||
@QField
|
||||
private Long idxScan;
|
||||
|
||||
@QField
|
||||
private Long idxTupFetch;
|
||||
|
||||
@QField
|
||||
private Long nTupIns;
|
||||
|
||||
@QField
|
||||
private Long nTupUpd;
|
||||
|
||||
@QField
|
||||
private Long nTupDel;
|
||||
|
||||
@QField
|
||||
private Long nTupHotUpd;
|
||||
|
||||
@QField
|
||||
private Long nLiveTup;
|
||||
|
||||
@QField
|
||||
private Long nDeadTup;
|
||||
|
||||
@QField
|
||||
private Long totalBytes;
|
||||
|
||||
@QField
|
||||
private Long tableBytes;
|
||||
|
||||
@QField
|
||||
private Long indexBytes;
|
||||
|
||||
@QField
|
||||
private Long autovacuumCount;
|
||||
|
||||
@QField
|
||||
private Long autoanalyzeCount;
|
||||
|
||||
@QField
|
||||
private Long freezeAge;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Populate from a QQQ record.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id.
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id.
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public Integer getSnapshotId()
|
||||
{
|
||||
return (this.snapshotId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public void setSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for snapshotId.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withSnapshotId(Integer snapshotId)
|
||||
{
|
||||
this.snapshotId = snapshotId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public Integer getTableStatId()
|
||||
{
|
||||
return (this.tableStatId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public void setTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableStatId.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withTableStatId(Integer tableStatId)
|
||||
{
|
||||
this.tableStatId = tableStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public Instant getCapturedAt()
|
||||
{
|
||||
return (this.capturedAt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public void setCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for capturedAt.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withCapturedAt(Instant capturedAt)
|
||||
{
|
||||
this.capturedAt = capturedAt;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for seqScan.
|
||||
*******************************************************************************/
|
||||
public Long getSeqScan()
|
||||
{
|
||||
return (this.seqScan);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for seqScan.
|
||||
*******************************************************************************/
|
||||
public void setSeqScan(Long seqScan)
|
||||
{
|
||||
this.seqScan = seqScan;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for seqScan.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withSeqScan(Long seqScan)
|
||||
{
|
||||
this.seqScan = seqScan;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public Long getSeqTupRead()
|
||||
{
|
||||
return (this.seqTupRead);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public void setSeqTupRead(Long seqTupRead)
|
||||
{
|
||||
this.seqTupRead = seqTupRead;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for seqTupRead.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withSeqTupRead(Long seqTupRead)
|
||||
{
|
||||
this.seqTupRead = seqTupRead;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxScan.
|
||||
*******************************************************************************/
|
||||
public Long getIdxScan()
|
||||
{
|
||||
return (this.idxScan);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public void setIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxScan.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withIdxScan(Long idxScan)
|
||||
{
|
||||
this.idxScan = idxScan;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public Long getIdxTupFetch()
|
||||
{
|
||||
return (this.idxTupFetch);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public void setIdxTupFetch(Long idxTupFetch)
|
||||
{
|
||||
this.idxTupFetch = idxTupFetch;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for idxTupFetch.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withIdxTupFetch(Long idxTupFetch)
|
||||
{
|
||||
this.idxTupFetch = idxTupFetch;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public Long getNTupIns()
|
||||
{
|
||||
return (this.nTupIns);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public void setNTupIns(Long nTupIns)
|
||||
{
|
||||
this.nTupIns = nTupIns;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupIns.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNTupIns(Long nTupIns)
|
||||
{
|
||||
this.nTupIns = nTupIns;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public Long getNTupUpd()
|
||||
{
|
||||
return (this.nTupUpd);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public void setNTupUpd(Long nTupUpd)
|
||||
{
|
||||
this.nTupUpd = nTupUpd;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupUpd.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNTupUpd(Long nTupUpd)
|
||||
{
|
||||
this.nTupUpd = nTupUpd;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public Long getNTupDel()
|
||||
{
|
||||
return (this.nTupDel);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public void setNTupDel(Long nTupDel)
|
||||
{
|
||||
this.nTupDel = nTupDel;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupDel.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNTupDel(Long nTupDel)
|
||||
{
|
||||
this.nTupDel = nTupDel;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nTupHotUpd.
|
||||
*******************************************************************************/
|
||||
public Long getNTupHotUpd()
|
||||
{
|
||||
return (this.nTupHotUpd);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nTupHotUpd.
|
||||
*******************************************************************************/
|
||||
public void setNTupHotUpd(Long nTupHotUpd)
|
||||
{
|
||||
this.nTupHotUpd = nTupHotUpd;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nTupHotUpd.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNTupHotUpd(Long nTupHotUpd)
|
||||
{
|
||||
this.nTupHotUpd = nTupHotUpd;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public Long getNLiveTup()
|
||||
{
|
||||
return (this.nLiveTup);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public void setNLiveTup(Long nLiveTup)
|
||||
{
|
||||
this.nLiveTup = nLiveTup;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nLiveTup.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNLiveTup(Long nLiveTup)
|
||||
{
|
||||
this.nLiveTup = nLiveTup;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public Long getNDeadTup()
|
||||
{
|
||||
return (this.nDeadTup);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public void setNDeadTup(Long nDeadTup)
|
||||
{
|
||||
this.nDeadTup = nDeadTup;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for nDeadTup.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withNDeadTup(Long nDeadTup)
|
||||
{
|
||||
this.nDeadTup = nDeadTup;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public Long getTotalBytes()
|
||||
{
|
||||
return (this.totalBytes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public void setTotalBytes(Long totalBytes)
|
||||
{
|
||||
this.totalBytes = totalBytes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for totalBytes.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withTotalBytes(Long totalBytes)
|
||||
{
|
||||
this.totalBytes = totalBytes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableBytes.
|
||||
*******************************************************************************/
|
||||
public Long getTableBytes()
|
||||
{
|
||||
return (this.tableBytes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableBytes.
|
||||
*******************************************************************************/
|
||||
public void setTableBytes(Long tableBytes)
|
||||
{
|
||||
this.tableBytes = tableBytes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableBytes.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withTableBytes(Long tableBytes)
|
||||
{
|
||||
this.tableBytes = tableBytes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for indexBytes.
|
||||
*******************************************************************************/
|
||||
public Long getIndexBytes()
|
||||
{
|
||||
return (this.indexBytes);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for indexBytes.
|
||||
*******************************************************************************/
|
||||
public void setIndexBytes(Long indexBytes)
|
||||
{
|
||||
this.indexBytes = indexBytes;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for indexBytes.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withIndexBytes(Long indexBytes)
|
||||
{
|
||||
this.indexBytes = indexBytes;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for autovacuumCount.
|
||||
*******************************************************************************/
|
||||
public Long getAutovacuumCount()
|
||||
{
|
||||
return (this.autovacuumCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for autovacuumCount.
|
||||
*******************************************************************************/
|
||||
public void setAutovacuumCount(Long autovacuumCount)
|
||||
{
|
||||
this.autovacuumCount = autovacuumCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for autovacuumCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withAutovacuumCount(Long autovacuumCount)
|
||||
{
|
||||
this.autovacuumCount = autovacuumCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for autoanalyzeCount.
|
||||
*******************************************************************************/
|
||||
public Long getAutoanalyzeCount()
|
||||
{
|
||||
return (this.autoanalyzeCount);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for autoanalyzeCount.
|
||||
*******************************************************************************/
|
||||
public void setAutoanalyzeCount(Long autoanalyzeCount)
|
||||
{
|
||||
this.autoanalyzeCount = autoanalyzeCount;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for autoanalyzeCount.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withAutoanalyzeCount(Long autoanalyzeCount)
|
||||
{
|
||||
this.autoanalyzeCount = autoanalyzeCount;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for freezeAge.
|
||||
*******************************************************************************/
|
||||
public Long getFreezeAge()
|
||||
{
|
||||
return (this.freezeAge);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for freezeAge.
|
||||
*******************************************************************************/
|
||||
public void setFreezeAge(Long freezeAge)
|
||||
{
|
||||
this.freezeAge = freezeAge;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for freezeAge.
|
||||
*******************************************************************************/
|
||||
public DbPerfTableSnapshot withFreezeAge(Long freezeAge)
|
||||
{
|
||||
this.freezeAge = freezeAge;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.normalize;
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Literal-free SQL shapes shared by PostgreSQL snapshots and QQQ attribution.
|
||||
*******************************************************************************/
|
||||
public final class SqlNormalizer
|
||||
{
|
||||
private static final Pattern NUMBER = Pattern.compile("(?:0[xX][0-9a-fA-F]+|(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?)");
|
||||
private static final Pattern OPERATOR = Pattern.compile("(?:->>|#>>|!~\\*|\\|\\|/|::|>=|<=|<>|!=|\\|\\||->|#>|!~|~\\*|@>|<@|&&|\\|/|:=|=>|\\?\\||\\?&)");
|
||||
private static final Pattern DOLLAR = Pattern.compile("\\$(?:[A-Za-z_][A-Za-z_0-9]*)?\\$");
|
||||
private static final Pattern TABLE = Pattern.compile("(?:^| )(?:(?:from|into|update) )((?:\"(?:[^\"]|\"\")*\"|[a-z_][a-z_0-9$]*)(?: \\. (?:\"(?:[^\"]|\"\")*\"|[a-z_][a-z_0-9$]*))?)");
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private SqlNormalizer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Lex SQL before folding whitespace, so quoted identifiers stay intact.
|
||||
** Unterminated literals and comments consume their remainder and never leak it.
|
||||
*******************************************************************************/
|
||||
public static String normalize(String sql)
|
||||
{
|
||||
if(sql == null || sql.isBlank())
|
||||
{
|
||||
return ("");
|
||||
}
|
||||
StringBuilder out = new StringBuilder();
|
||||
int i = 0;
|
||||
while(i < sql.length())
|
||||
{
|
||||
char c = sql.charAt(i);
|
||||
if(Character.isWhitespace(c))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if(sql.startsWith("--", i))
|
||||
{
|
||||
int end = sql.indexOf('\n', i);
|
||||
i = end < 0 ? sql.length() : end + 1;
|
||||
continue;
|
||||
}
|
||||
if(sql.startsWith("/*", i))
|
||||
{
|
||||
int depth = 1;
|
||||
i += 2;
|
||||
while(i < sql.length() && depth > 0)
|
||||
{
|
||||
if(sql.startsWith("/*", i))
|
||||
{
|
||||
depth++;
|
||||
i += 2;
|
||||
}
|
||||
else if(sql.startsWith("*/", i))
|
||||
{
|
||||
depth--;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
boolean escape = (c == 'e' || c == 'E') && i + 1 < sql.length() && sql.charAt(i + 1) == '\'';
|
||||
if(c == '\'' || escape)
|
||||
{
|
||||
i = quotedEnd(sql, i + (escape ? 1 : 0), '\'', escape);
|
||||
out.append("? ");
|
||||
continue;
|
||||
}
|
||||
if(c == '"')
|
||||
{
|
||||
int end = quotedEnd(sql, i, '"', false);
|
||||
out.append(sql, i, end).append(' ');
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
if(c == '$')
|
||||
{
|
||||
Matcher dollar = DOLLAR.matcher(sql).region(i, sql.length());
|
||||
if(dollar.lookingAt())
|
||||
{
|
||||
int end = sql.indexOf(dollar.group(), dollar.end());
|
||||
i = end < 0 ? sql.length() : end + dollar.group().length();
|
||||
out.append("? ");
|
||||
continue;
|
||||
}
|
||||
int end = i + 1;
|
||||
while(end < sql.length() && Character.isDigit(sql.charAt(end)))
|
||||
{
|
||||
end++;
|
||||
}
|
||||
if(end > i + 1)
|
||||
{
|
||||
out.append("? ");
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(Character.isDigit(c) || c == '.' && i + 1 < sql.length() && Character.isDigit(sql.charAt(i + 1)))
|
||||
{
|
||||
Matcher number = NUMBER.matcher(sql).region(i, sql.length());
|
||||
if(number.lookingAt())
|
||||
{
|
||||
i = number.end();
|
||||
out.append("? ");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(Character.isLetter(c) || c == '_')
|
||||
{
|
||||
int end = i + 1;
|
||||
while(end < sql.length() && (Character.isLetterOrDigit(sql.charAt(end)) || sql.charAt(end) == '_' || sql.charAt(end) == '$'))
|
||||
{
|
||||
end++;
|
||||
}
|
||||
out.append(sql.substring(i, end).toLowerCase(Locale.ROOT)).append(' ');
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
Matcher operator = OPERATOR.matcher(sql).region(i, sql.length());
|
||||
if(operator.lookingAt())
|
||||
{
|
||||
out.append(operator.group()).append(' ');
|
||||
i = operator.end();
|
||||
continue;
|
||||
}
|
||||
out.append(c).append(' ');
|
||||
i++;
|
||||
}
|
||||
String normalized = collapseOutsideIdentifiers(out.toString().trim());
|
||||
return (normalized.substring(0, Math.min(10000, normalized.length())));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** List folding applies only outside quoted identifiers, whose contents may
|
||||
** themselves resemble SQL. Escaped identifier quotes remain untouched.
|
||||
*******************************************************************************/
|
||||
private static String collapseOutsideIdentifiers(String sql)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
int start = 0;
|
||||
while(start < sql.length())
|
||||
{
|
||||
int quote = sql.indexOf('"', start);
|
||||
if(quote < 0)
|
||||
{
|
||||
result.append(collapseLists(sql.substring(start)));
|
||||
break;
|
||||
}
|
||||
result.append(collapseLists(sql.substring(start, quote)));
|
||||
int end = quotedEnd(sql, quote, '"', false);
|
||||
result.append(sql, quote, end);
|
||||
start = end;
|
||||
}
|
||||
return (result.toString());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Collapse placeholder repetitions, preserving VALUES column arity.
|
||||
*******************************************************************************/
|
||||
private static String collapseLists(String normalized)
|
||||
{
|
||||
normalized = normalized.replaceAll("\\bin \\( \\?(?: , \\?)* \\)", "in ( ? )");
|
||||
normalized = normalized.replaceAll("\\bvalues (\\( \\?(?: , \\?)* \\))(?: , \\( \\?(?: , \\?)* \\))+", "values $1");
|
||||
return (normalized);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Consume a quote with SQL doubled-quote and optional escape handling.
|
||||
*******************************************************************************/
|
||||
private static int quotedEnd(String sql, int start, char quote, boolean escapes)
|
||||
{
|
||||
int i = start + 1;
|
||||
while(i < sql.length())
|
||||
{
|
||||
if(escapes && sql.charAt(i) == '\\')
|
||||
{
|
||||
i = Math.min(i + 2, sql.length());
|
||||
}
|
||||
else if(sql.charAt(i++) == quote)
|
||||
{
|
||||
if(i < sql.length() && sql.charAt(i) == quote)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Stable hexadecimal SHA-256; callers supply the normalized shape.
|
||||
*******************************************************************************/
|
||||
public static String fingerprint(String normalized)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(normalized.getBytes(StandardCharsets.UTF_8))));
|
||||
}
|
||||
catch(NoSuchAlgorithmException e)
|
||||
{
|
||||
throw (new IllegalStateException("SHA-256 is required by Java", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Leading operation, including WITH as its own kind.
|
||||
*******************************************************************************/
|
||||
public static String operation(String sql)
|
||||
{
|
||||
String first = normalize(sql).split(" ", 2)[0].toUpperCase(Locale.ROOT);
|
||||
return (switch(first)
|
||||
{
|
||||
case "SELECT", "INSERT", "UPDATE", "DELETE", "WITH" -> first;
|
||||
default -> "OTHER";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Best-effort first target table, without schema or identifier quotes.
|
||||
*******************************************************************************/
|
||||
public static String mainTable(String sql)
|
||||
{
|
||||
Matcher match = TABLE.matcher(normalize(sql));
|
||||
if(!match.find())
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
String[] parts = match.group(1).split(" \\. ");
|
||||
return (parts[parts.length - 1].replace("\"", ""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.processes;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.SnapshotCollector;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Collect on schedule or explicitly from the Snapshots table.
|
||||
*******************************************************************************/
|
||||
public class DbPerfCollectStep implements BackendStep
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Respect runtime interval settings on scheduled retries; Collect now forces
|
||||
** table statistics and always returns the recorded outcome on its result screen.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(QContext.getQInstance());
|
||||
if(QContext.getQSession() == null || !QContext.getQSession().hasPermission(runtime.getConfig().getPermissionBaseName() + ".manage_settings"))
|
||||
{
|
||||
throw (new QException("Managing settings permission is required"));
|
||||
}
|
||||
ArrayList<ProcessSummaryLineInterface> lines = new ArrayList<>();
|
||||
boolean force = Boolean.TRUE.equals(input.getValueBoolean("forceTables"));
|
||||
try
|
||||
{
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
Instant last = settings.getValueInstant("lastSnapshotAt");
|
||||
if(!force && last != null && last.plusSeconds(60L * SnapshotCollector.positive(settings, "snapshotIntervalMinutes", 5)).isAfter(Instant.now()))
|
||||
{
|
||||
lines.add(new ProcessSummaryLine(Status.INFO, 0, "snapshots collected; the next interval is not due"));
|
||||
}
|
||||
else
|
||||
{
|
||||
QRecord snapshot = new SnapshotCollector(runtime).collect(runtime.getConfig().getBackendName(), force);
|
||||
output.addRecord(snapshot);
|
||||
lines.add(new ProcessSummaryLine("FAILED".equals(snapshot.getValueString("status")) ? Status.ERROR : Status.INFO, 1, "snapshot: " + snapshot.getValueString("status")));
|
||||
lines.add(new ProcessSummaryLine(Status.INFO, snapshot.getValueInteger("storedStatementCount") == null ? 0 : snapshot.getValueInteger("storedStatementCount"), "query details stored"));
|
||||
if(snapshot.getValueString("errorMessage") != null)
|
||||
{
|
||||
lines.add(new ProcessSummaryLine(Status.INFO, 0, snapshot.getValueString("errorMessage")));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Unable to collect database performance snapshot", e));
|
||||
}
|
||||
output.addValue("processResults", lines);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.processes;
|
||||
|
||||
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfPermissions;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfQBitConfig;
|
||||
import com.kingsrook.qqq.backend.core.jobs.DurableProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.jobs.model.JobIdempotency;
|
||||
import com.kingsrook.qqq.backend.core.jobs.model.JobSemantics;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Scheduled maintenance and the Snapshot table action; no process navigation.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfProcesses
|
||||
{
|
||||
public static final String COLLECT = "dbPerfCollectSnapshot";
|
||||
public static final String ROLLUP = "dbPerfRollupAndPurge";
|
||||
public static final String COLLECT_NOW = "dbPerfCollectNow";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfProcesses()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Add schedules only when the host supplies a scheduler name.
|
||||
*******************************************************************************/
|
||||
public static void addTo(QInstance instance, DbPerfQBitConfig config)
|
||||
{
|
||||
instance.addProcess(scheduled(config, COLLECT, "Collect database snapshot", DbPerfCollectStep.class, config.getSnapshotCron()));
|
||||
instance.addProcess(scheduled(config, ROLLUP, "Roll up and purge database performance", DbPerfRollupStep.class, config.getRollupCron()));
|
||||
instance.addProcess(base(config, COLLECT_NOW, "Collect now", DbPerfCollectStep.class, true)
|
||||
.withTableName("db_perf_snapshot")
|
||||
.withStep(new QFrontendStepMetaData().withName("result").withLabel("Result")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.PROCESS_SUMMARY_RESULTS))));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Hidden durable-eligible maintenance process.
|
||||
*******************************************************************************/
|
||||
private static QProcessMetaData scheduled(DbPerfQBitConfig config, String name, String label, Class<?> step, String cron)
|
||||
{
|
||||
QProcessMetaData process = base(config, name, label, step, false).withIsHidden(true)
|
||||
.withSupplementalMetaData(new DurableProcessMetaData().withEligible(true).withSemantics(JobSemantics.ATOMIC).withIdempotency(JobIdempotency.NATURAL).withMaxConcurrentJobs(1));
|
||||
if(config.getSchedulerName() != null)
|
||||
{
|
||||
process.withSchedule(new QScheduleMetaData().withSchedulerName(config.getSchedulerName()).withCronExpression(cron).withCronTimeZoneId(config.getTimeZone()));
|
||||
}
|
||||
return (process);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** One backend step with standard process result lines.
|
||||
*******************************************************************************/
|
||||
private static QProcessMetaData base(DbPerfQBitConfig config, String name, String label, Class<?> step, boolean force)
|
||||
{
|
||||
return (new QProcessMetaData().withName(name).withLabel(label).withIcon(new QIcon("speed"))
|
||||
.withPermissionRules(DbPerfPermissions.rules(config.getPermissionBaseName() + ".manage_settings"))
|
||||
.withStep(new QBackendStepMetaData().withName("run").withCode(new QCodeReference(step))
|
||||
.withInputData(new QFunctionInputMetaData().withField(new QFieldMetaData("forceTables", QFieldType.BOOLEAN).withDefaultValue(force)))
|
||||
.withOutputMetaData(new QFunctionOutputMetaData().withField(new QFieldMetaData("processResults", QFieldType.STRING)))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.processes;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import com.kingsrook.qbits.dbperformance.DbPerfRuntime;
|
||||
import com.kingsrook.qbits.dbperformance.collect.RetentionService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.RollupService;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Roll up yesterday and backfill missing recent days, then enforce retention.
|
||||
*******************************************************************************/
|
||||
public class DbPerfRollupStep implements BackendStep
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Yesterday includes late detail; missing older days are repaired only while
|
||||
** their detail is retained. Daily history outlives the detail-retention window.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(QContext.getQInstance());
|
||||
if(QContext.getQSession() == null || !QContext.getQSession().hasPermission(runtime.getConfig().getPermissionBaseName() + ".manage_settings"))
|
||||
{
|
||||
throw (new QException("Managing settings permission is required"));
|
||||
}
|
||||
try
|
||||
{
|
||||
LocalDate today = LocalDate.now(ZoneId.of(runtime.getConfig().getTimeZone()));
|
||||
RollupService rollup = new RollupService(runtime);
|
||||
int rolledUp = 0;
|
||||
for(int days = 1; days <= 7; days++)
|
||||
{
|
||||
LocalDate day = today.minusDays(days);
|
||||
if(days == 1 || rollup.needsBackfill(day))
|
||||
{
|
||||
rollup.rollupDay(day);
|
||||
rolledUp++;
|
||||
}
|
||||
}
|
||||
int removed = new RetentionService(runtime).purge();
|
||||
ArrayList<ProcessSummaryLineInterface> lines = new ArrayList<>();
|
||||
lines.add(new ProcessSummaryLine(Status.INFO, rolledUp, "days rolled up"));
|
||||
lines.add(new ProcessSummaryLine(Status.INFO, removed, "expired records deleted"));
|
||||
output.addValue("processResults", lines);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Database performance rollup and purge failed", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** ConnectionCount read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record ConnectionCount(String dbUser, String applicationName, String state, int connectionCount)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** DatabaseStats read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record DatabaseStats(Map<String, Long> counters, Instant statsReset, int maxConnections, long sizeBytes)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Read-only database statistics SPI. Implementations never change the database.
|
||||
*******************************************************************************/
|
||||
public interface DbStatsProvider
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Provider name.
|
||||
*******************************************************************************/
|
||||
String getName();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** capabilities using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
ProviderCapabilities capabilities(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readDatabase using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
DatabaseStats readDatabase(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readStatements using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
List<StatementStats> readStatements(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readStatementResetInfo using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
StatementResetInfo readStatementResetInfo(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readTables using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
List<TableStats> readTables(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readIndexes using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
List<IndexStats> readIndexes(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readSessions using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
List<SessionInfo> readSessions(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readConnections using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
List<ConnectionCount> readConnections(Connection connection) throws SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** readSettings using the supplied backend connection.
|
||||
*******************************************************************************/
|
||||
Map<String, String> readSettings(Connection connection) throws SQLException;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Select a provider from JDBC product identity.
|
||||
*******************************************************************************/
|
||||
public final class DbStatsProviders
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbStatsProviders()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** PostgreSQL first; other vendors are explicitly unsupported.
|
||||
*******************************************************************************/
|
||||
public static DbStatsProvider forConnection(Connection connection) throws SQLException
|
||||
{
|
||||
return ("PostgreSQL".equalsIgnoreCase(connection.getMetaData().getDatabaseProductName()) ? new PostgresStatsProvider() : new UnsupportedStatsProvider());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** IndexStats read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record IndexStats(String schemaName, String tableName, String indexName, long relid, String definition, String columns, boolean unique, boolean primary, boolean valid, long idxScan, long idxTupRead, long idxTupFetch, long sizeBytes, long writeCost)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** PostgreSQL 13+ statistics reader; every statement is a read-only SELECT.
|
||||
*******************************************************************************/
|
||||
public class PostgresStatsProvider implements DbStatsProvider
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Provider name.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return ("POSTGRES");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Server version and privileges, including explicit installation guidance.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public ProviderCapabilities capabilities(Connection connection) throws SQLException
|
||||
{
|
||||
int version = version(connection);
|
||||
boolean installed = scalar(connection, "SELECT count(*) FROM pg_extension WHERE extname = 'pg_stat_statements'") > 0;
|
||||
boolean privileged = scalar(connection, "SELECT CASE WHEN pg_has_role(current_user, 'pg_read_all_stats', 'MEMBER') OR (SELECT rolsuper FROM pg_roles WHERE rolname=current_user) THEN 1 ELSE 0 END") > 0;
|
||||
String access = version < 130000 ? "UNSUPPORTED" : !installed ? "NO_PG_STAT_STATEMENTS" : !privileged ? "NO_READ_ALL_STATS" : "FULL";
|
||||
String message = switch(access)
|
||||
{
|
||||
case "UNSUPPORTED" -> "PostgreSQL 13 or newer is required.";
|
||||
case "NO_PG_STAT_STATEMENTS" -> "Install pg_stat_statements in this database and preload it in shared_preload_libraries.";
|
||||
case "NO_READ_ALL_STATS" -> "Grant pg_read_all_stats to the application database role to see all statement statistics.";
|
||||
default -> "Full statistics access.";
|
||||
};
|
||||
if(installed && version >= 130000)
|
||||
{
|
||||
try
|
||||
{
|
||||
scalar(connection, "SELECT count(*) FROM " + statementsView(connection));
|
||||
}
|
||||
catch(SQLException e)
|
||||
{
|
||||
if("55000".equals(e.getSQLState()) || "42501".equals(e.getSQLState()))
|
||||
{
|
||||
installed = false;
|
||||
access = "NO_PG_STAT_STATEMENTS";
|
||||
message = "pg_stat_statements must be preloaded and readable by the application database role.";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (new ProviderCapabilities(version >= 130000, version, installed, privileged, access, message));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Current database cumulative counters and capacity.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DatabaseStats readDatabase(Connection connection) throws SQLException
|
||||
{
|
||||
return (read(connection, "SELECT d.*, pg_database_size(current_database()) AS size_bytes, current_setting('max_connections')::int AS max_connections FROM pg_stat_database d WHERE datname=current_database()", row ->
|
||||
new DatabaseStats(counters(row, "xact_commit", "xact_rollback", "blks_hit", "blks_read", "temp_files", "temp_bytes", "deadlocks", "conflicts"), instant(row, "stats_reset"), row.getInt("max_connections"), row.getLong("size_bytes"))).getFirst());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Current database statements only. Version-dependent columns are projected
|
||||
** as null or true when older PostgreSQL versions do not provide them.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<StatementStats> readStatements(Connection connection) throws SQLException
|
||||
{
|
||||
int version = version(connection);
|
||||
String sql = "SELECT s.queryid, r.rolname, s.query, s.calls, s.total_exec_time, s.mean_exec_time, s.max_exec_time, s.rows, s.shared_blks_hit, s.shared_blks_read, s.temp_blks_written, "
|
||||
+ (version >= 140000 ? "s.toplevel" : "true") + " AS top_level, "
|
||||
+ (version >= 170000 ? "s.stats_since, s.minmax_stats_since" : "NULL::timestamptz AS stats_since, NULL::timestamptz AS minmax_stats_since")
|
||||
+ " FROM " + statementsView(connection) + " s LEFT JOIN pg_roles r ON r.oid=s.userid WHERE s.dbid=(SELECT oid FROM pg_database WHERE datname=current_database()) AND s.queryid IS NOT NULL AND s.query IS NOT NULL";
|
||||
return (read(connection, sql, row -> new StatementStats(row.getLong("queryid"), row.getString("rolname"), row.getBoolean("top_level"), row.getString("query"), row.getLong("calls"), row.getBigDecimal("total_exec_time"), row.getBigDecimal("mean_exec_time"), row.getBigDecimal("max_exec_time"), row.getLong("rows"), row.getLong("shared_blks_hit"), row.getLong("shared_blks_read"), row.getLong("temp_blks_written"), instant(row, "stats_since"), instant(row, "minmax_stats_since"))));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Global statement reset is available starting with PostgreSQL 14.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public StatementResetInfo readStatementResetInfo(Connection connection) throws SQLException
|
||||
{
|
||||
if(version(connection) < 140000)
|
||||
{
|
||||
return (new StatementResetInfo(null, 0));
|
||||
}
|
||||
return (read(connection, "SELECT stats_reset, dealloc FROM " + statementsView(connection) + "_info", row -> new StatementResetInfo(instant(row, "stats_reset"), row.getLong("dealloc"))).getFirst());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Latest table counters, tuple gauges, maintenance times and sizes.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<TableStats> readTables(Connection connection) throws SQLException
|
||||
{
|
||||
String sql = "SELECT s.*, pg_total_relation_size(s.relid) AS total_bytes, pg_relation_size(s.relid) AS table_bytes, pg_indexes_size(s.relid) AS index_bytes, CASE WHEN c.reltoastrelid=0 THEN 0 ELSE pg_total_relation_size(c.reltoastrelid) END AS toast_bytes, age(c.relfrozenxid)::bigint AS freeze_age FROM pg_stat_user_tables s JOIN pg_class c ON c.oid=s.relid";
|
||||
return (read(connection, sql, row ->
|
||||
{
|
||||
Map<String, Instant> maintenance = new LinkedHashMap<>();
|
||||
for(String field : List.of("last_vacuum", "last_autovacuum", "last_analyze", "last_autoanalyze"))
|
||||
{
|
||||
maintenance.put(camel(field), instant(row, field));
|
||||
}
|
||||
return (new TableStats(row.getString("schemaname"), row.getString("relname"), row.getLong("relid"), counters(row, "seq_scan", "seq_tup_read", "idx_scan", "idx_tup_fetch", "n_tup_ins", "n_tup_upd", "n_tup_del", "n_tup_hot_upd", "n_live_tup", "n_dead_tup", "total_bytes", "table_bytes", "index_bytes", "toast_bytes", "vacuum_count", "autovacuum_count", "analyze_count", "autoanalyze_count", "freeze_age"), maintenance));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Index definitions, ordered keys (including expressions) and write cost.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<IndexStats> readIndexes(Connection connection) throws SQLException
|
||||
{
|
||||
String sql = "SELECT s.*, i.indisunique, i.indisprimary, i.indisvalid, pg_get_indexdef(s.indexrelid) AS definition, (SELECT string_agg(pg_get_indexdef(s.indexrelid, k, true), ', ' ORDER BY k) FROM generate_series(1, i.indnatts) AS k) AS column_names, pg_relation_size(s.indexrelid) AS size_bytes, t.n_tup_ins+t.n_tup_upd+t.n_tup_del AS write_cost FROM pg_stat_user_indexes s JOIN pg_index i ON i.indexrelid=s.indexrelid JOIN pg_stat_user_tables t ON t.relid=s.relid";
|
||||
return (read(connection, sql, row -> new IndexStats(row.getString("schemaname"), row.getString("relname"), row.getString("indexrelname"), row.getLong("indexrelid"), row.getString("definition"), row.getString("column_names"), row.getBoolean("indisunique"), row.getBoolean("indisprimary"), row.getBoolean("indisvalid"), row.getLong("idx_scan"), row.getLong("idx_tup_read"), row.getLong("idx_tup_fetch"), row.getLong("size_bytes"), row.getLong("write_cost"))));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Current database activity, excluding the collector's own session.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<SessionInfo> readSessions(Connection connection) throws SQLException
|
||||
{
|
||||
String sql = "SELECT pid, usename, application_name, client_addr::text, state, wait_event_type, wait_event, backend_type, xact_start, query_start, state_change, extract(epoch FROM clock_timestamp()-query_start) AS query_seconds, extract(epoch FROM clock_timestamp()-xact_start) AS xact_seconds, pg_blocking_pids(pid) AS blocked_by, "
|
||||
+ (version(connection) >= 140000 ? "query_id" : "NULL::bigint AS query_id") + ", query FROM pg_stat_activity WHERE datname=current_database() AND pid<>pg_backend_pid()";
|
||||
return (read(connection, sql, row ->
|
||||
{
|
||||
List<Integer> blockedBy = new ArrayList<>();
|
||||
java.sql.Array array = row.getArray("blocked_by");
|
||||
if(array != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
for(Object pid : (Object[]) array.getArray())
|
||||
{
|
||||
blockedBy.add(((Number) pid).intValue());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
array.free();
|
||||
}
|
||||
}
|
||||
Long queryId = row.getObject("query_id") == null ? null : row.getLong("query_id");
|
||||
return (new SessionInfo(row.getInt("pid"), row.getString("usename"), row.getString("application_name"), row.getString("client_addr"), row.getString("state"), row.getString("wait_event_type"), row.getString("wait_event"), row.getString("backend_type"), instant(row, "xact_start"), instant(row, "query_start"), instant(row, "state_change"), row.getBigDecimal("query_seconds"), row.getBigDecimal("xact_seconds"), blockedBy, queryId, row.getString("query")));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Cluster-wide capacity counts, including connections in other databases.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<ConnectionCount> readConnections(Connection connection) throws SQLException
|
||||
{
|
||||
return (read(connection, "SELECT usename, application_name, state, count(*) AS connections FROM pg_stat_activity GROUP BY usename, application_name, state", row -> new ConnectionCount(row.getString("usename"), row.getString("application_name"), row.getString("state"), row.getInt("connections"))));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Only operational, non-secret server settings relevant to this qbit.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Map<String, String> readSettings(Connection connection) throws SQLException
|
||||
{
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
read(connection, "SELECT name, setting FROM pg_settings WHERE name IN ('server_version_num','max_connections','shared_preload_libraries','pg_stat_statements.track','track_io_timing','block_size')", row -> result.put(row.getString("name"), row.getString("setting")));
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Extension schema is obtained from the catalog, then identifier-quoted.
|
||||
*******************************************************************************/
|
||||
private String statementsView(Connection connection) throws SQLException
|
||||
{
|
||||
List<String> schemas = read(connection, "SELECT n.nspname FROM pg_extension e JOIN pg_namespace n ON n.oid=e.extnamespace WHERE e.extname='pg_stat_statements'", row -> row.getString(1));
|
||||
if(schemas.isEmpty())
|
||||
{
|
||||
throw (new SQLException("pg_stat_statements is not installed", "42P01"));
|
||||
}
|
||||
return ("\"" + schemas.getFirst().replace("\"", "\"\"") + "\".pg_stat_statements");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Numeric server version avoids JDBC minor-version ambiguity.
|
||||
*******************************************************************************/
|
||||
private int version(Connection connection) throws SQLException
|
||||
{
|
||||
return ((int) scalar(connection, "SELECT current_setting('server_version_num')::int"));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A single numeric read.
|
||||
*******************************************************************************/
|
||||
private long scalar(Connection connection, String sql) throws SQLException
|
||||
{
|
||||
return (read(connection, sql, row -> row.getLong(1)).getFirst());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Timestamp conversion preserves null reset/maintenance values.
|
||||
*******************************************************************************/
|
||||
private static Instant instant(ResultSet row, String column) throws SQLException
|
||||
{
|
||||
Timestamp timestamp = row.getTimestamp(column);
|
||||
return (timestamp == null ? null : timestamp.toInstant());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Copy cumulative counters to the entity's camelCase names.
|
||||
*******************************************************************************/
|
||||
private static Map<String, Long> counters(ResultSet row, String... columns) throws SQLException
|
||||
{
|
||||
Map<String, Long> counters = new LinkedHashMap<>();
|
||||
for(String column : columns)
|
||||
{
|
||||
counters.put(camel(column), row.getLong(column));
|
||||
}
|
||||
return (counters);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** PostgreSQL column to entity field.
|
||||
*******************************************************************************/
|
||||
private static String camel(String column)
|
||||
{
|
||||
StringBuilder value = new StringBuilder();
|
||||
for(String part : column.split("_"))
|
||||
{
|
||||
value.append(value.isEmpty() ? part : Character.toUpperCase(part.charAt(0)) + part.substring(1));
|
||||
}
|
||||
return (value.toString());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Execute one bounded-time read and close JDBC resources.
|
||||
*******************************************************************************/
|
||||
private static <T> List<T> read(Connection connection, String sql, RowReader<T> mapper) throws SQLException
|
||||
{
|
||||
List<T> result = new ArrayList<>();
|
||||
try(Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.setQueryTimeout(30);
|
||||
try(ResultSet rows = statement.executeQuery(sql))
|
||||
{
|
||||
while(rows.next())
|
||||
{
|
||||
result.add(mapper.read(rows));
|
||||
}
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Checked JDBC row mapping.
|
||||
*******************************************************************************/
|
||||
private interface RowReader<T>
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Map one row.
|
||||
*******************************************************************************/
|
||||
T read(ResultSet row) throws SQLException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** ProviderCapabilities read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record ProviderCapabilities(boolean supported, int version, boolean statements, boolean readAllStats, String statsAccess, String message)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** SessionInfo read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record SessionInfo(int pid, String dbUser, String applicationName, String clientAddr, String state, String waitEventType, String waitEvent, String backendType, Instant xactStart, Instant queryStart, Instant stateChange, BigDecimal querySeconds, BigDecimal xactSeconds, List<Integer> blockedBy, Long queryId, String queryText)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** StatementResetInfo read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record StatementResetInfo(Instant statsReset, long dealloc)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** StatementStats read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record StatementStats(long queryId, String dbUser, boolean toplevel, String queryText, long calls, BigDecimal totalExecMs, BigDecimal meanExecMs, BigDecimal maxExecMs, long rows, long sharedBlksHit, long sharedBlksRead, long tempBlksWritten, Instant statsSince, Instant minmaxStatsSince)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** TableStats read from database statistics; no persistence or side effects.
|
||||
*******************************************************************************/
|
||||
public record TableStats(String schemaName, String tableName, long relid, Map<String, Long> counters, Map<String, Instant> maintenance)
|
||||
{
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance.provider;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unsupported vendors report reduced capability without reading vendor views.
|
||||
*******************************************************************************/
|
||||
public class UnsupportedStatsProvider implements DbStatsProvider
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Provider name.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return ("UNSUPPORTED");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public ProviderCapabilities capabilities(Connection connection)
|
||||
{
|
||||
return (new ProviderCapabilities(false, 0, false, false, "UNSUPPORTED", "This database vendor is not supported; PostgreSQL 13 or newer is required."));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DatabaseStats readDatabase(Connection connection)
|
||||
{
|
||||
return (new DatabaseStats(Map.of(), null, 0, 0));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<StatementStats> readStatements(Connection connection)
|
||||
{
|
||||
return (List.of());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public StatementResetInfo readStatementResetInfo(Connection connection)
|
||||
{
|
||||
return (new StatementResetInfo(null, 0));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<TableStats> readTables(Connection connection)
|
||||
{
|
||||
return (List.of());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<IndexStats> readIndexes(Connection connection)
|
||||
{
|
||||
return (List.of());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<SessionInfo> readSessions(Connection connection)
|
||||
{
|
||||
return (List.of());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<ConnectionCount> readConnections(Connection connection)
|
||||
{
|
||||
return (List.of());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Empty unsupported result.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Map<String, String> readSettings(Connection connection)
|
||||
{
|
||||
return (Map.of());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
-- liquibase formatted sql
|
||||
|
||||
-- changeset qbit-db-performance:1
|
||||
-- comment: Settings table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_settings (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100) NOT NULL,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
provider VARCHAR(20),
|
||||
snapshot_interval_minutes INT DEFAULT 5,
|
||||
table_snapshot_minutes INT DEFAULT 60,
|
||||
detail_retention_days INT DEFAULT 30,
|
||||
rollup_retention_days INT DEFAULT 365,
|
||||
top_statements_per_snapshot INT DEFAULT 200,
|
||||
activity_long_running_seconds INT DEFAULT 30,
|
||||
idle_in_transaction_seconds INT DEFAULT 60,
|
||||
attribution_enabled BOOLEAN DEFAULT TRUE,
|
||||
attribution_sample_rate DECIMAL(5,4) DEFAULT 1.0,
|
||||
attribution_flush_seconds INT DEFAULT 60,
|
||||
attribution_max_keys INT DEFAULT 10000,
|
||||
n_plus_one_threshold INT DEFAULT 10,
|
||||
explain_timeout_ms INT DEFAULT 10000,
|
||||
alerts_enabled BOOLEAN DEFAULT TRUE,
|
||||
spike_factor DECIMAL(6,2) DEFAULT 3,
|
||||
spike_min_total_ms INT DEFAULT 5000,
|
||||
connection_alert_percent INT DEFAULT 80,
|
||||
blocked_alert_seconds INT DEFAULT 30,
|
||||
last_snapshot_at TIMESTAMP,
|
||||
last_table_snapshot_at TIMESTAMP,
|
||||
stats_access VARCHAR(30),
|
||||
stats_access_message VARCHAR(500),
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_settings_0 UNIQUE (backend_name)
|
||||
);
|
||||
|
||||
|
||||
-- changeset qbit-db-performance:2
|
||||
-- comment: Suggestion Rules table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_rule (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
code VARCHAR(40) NOT NULL,
|
||||
label VARCHAR(100),
|
||||
description VARCHAR(1000),
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
severity VARCHAR(20),
|
||||
threshold_1 DECIMAL(18,4),
|
||||
threshold_2 DECIMAL(18,4),
|
||||
threshold_1_label VARCHAR(100),
|
||||
threshold_2_label VARCHAR(100),
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_rule_0 UNIQUE (code)
|
||||
);
|
||||
|
||||
|
||||
-- changeset qbit-db-performance:3
|
||||
-- comment: Snapshots table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_snapshot (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100),
|
||||
captured_at TIMESTAMP NOT NULL,
|
||||
interval_seconds INT,
|
||||
status VARCHAR(20),
|
||||
duration_ms INT,
|
||||
error_message VARCHAR(2000),
|
||||
stats_reset_detected BOOLEAN,
|
||||
includes_tables BOOLEAN,
|
||||
xact_commit BIGINT,
|
||||
xact_rollback BIGINT,
|
||||
blks_hit BIGINT,
|
||||
blks_read BIGINT,
|
||||
cache_hit_ratio DECIMAL(7,4),
|
||||
temp_files BIGINT,
|
||||
temp_bytes BIGINT,
|
||||
deadlocks BIGINT,
|
||||
conflicts BIGINT,
|
||||
connections_total INT,
|
||||
connections_active INT,
|
||||
connections_idle INT,
|
||||
connections_idle_in_tx INT,
|
||||
max_connections INT,
|
||||
database_size_bytes BIGINT,
|
||||
statement_count INT,
|
||||
statement_calls BIGINT,
|
||||
statement_total_ms DECIMAL(18,3),
|
||||
stored_statement_count INT,
|
||||
create_date TIMESTAMP,
|
||||
raw_xact_commit BIGINT,
|
||||
raw_xact_rollback BIGINT,
|
||||
raw_blks_hit BIGINT,
|
||||
raw_blks_read BIGINT,
|
||||
raw_temp_files BIGINT,
|
||||
raw_temp_bytes BIGINT,
|
||||
raw_deadlocks BIGINT,
|
||||
raw_conflicts BIGINT,
|
||||
raw_stats_reset TIMESTAMP,
|
||||
raw_statement_stats_reset TIMESTAMP,
|
||||
CONSTRAINT uq_snapshot_0 UNIQUE (backend_name,captured_at)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_snapshot_0 ON db_perf_snapshot (captured_at);
|
||||
|
||||
-- changeset qbit-db-performance:4
|
||||
-- comment: Queries table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_statement (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100),
|
||||
query_id BIGINT,
|
||||
db_user VARCHAR(100),
|
||||
toplevel BOOLEAN,
|
||||
fingerprint CHAR(64),
|
||||
query_text TEXT,
|
||||
operation VARCHAR(20),
|
||||
main_table VARCHAR(200),
|
||||
first_seen_at TIMESTAMP,
|
||||
last_seen_at TIMESTAMP,
|
||||
last_calls BIGINT,
|
||||
last_total_exec_ms DECIMAL(18,3),
|
||||
last_rows BIGINT,
|
||||
last_shared_blks_hit BIGINT,
|
||||
last_shared_blks_read BIGINT,
|
||||
last_temp_blks_written BIGINT,
|
||||
last_max_exec_ms DECIMAL(18,3),
|
||||
last_stats_since TIMESTAMP,
|
||||
calls_24h BIGINT,
|
||||
total_ms_24h DECIMAL(18,3),
|
||||
mean_ms_24h DECIMAL(18,3),
|
||||
share_of_db_time_24h DECIMAL(7,4),
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_statement_0 UNIQUE (backend_name,query_id,db_user,toplevel)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_statement_0 ON db_perf_statement (fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS ix_statement_1 ON db_perf_statement (last_seen_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_statement_2 ON db_perf_statement (total_ms_24h);
|
||||
|
||||
-- changeset qbit-db-performance:5
|
||||
-- comment: Query Snapshots table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_query_snapshot (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
snapshot_id BIGINT,
|
||||
statement_id BIGINT,
|
||||
captured_at TIMESTAMP,
|
||||
calls BIGINT,
|
||||
total_ms DECIMAL(18,3),
|
||||
mean_ms DECIMAL(18,3),
|
||||
max_ms DECIMAL(18,3),
|
||||
rows_count BIGINT,
|
||||
shared_blks_hit BIGINT,
|
||||
shared_blks_read BIGINT,
|
||||
hit_ratio DECIMAL(7,4),
|
||||
temp_blks_written BIGINT,
|
||||
share_of_db_time DECIMAL(7,4)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_query_snapshot_0 ON db_perf_query_snapshot (snapshot_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_query_snapshot_1 ON db_perf_query_snapshot (statement_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_query_snapshot_2 ON db_perf_query_snapshot (captured_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_query_snapshot_3 ON db_perf_query_snapshot (statement_id,captured_at);
|
||||
|
||||
-- changeset qbit-db-performance:6
|
||||
-- comment: Query Daily table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_query_daily (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
statement_id BIGINT,
|
||||
"day" DATE,
|
||||
calls BIGINT,
|
||||
total_ms DECIMAL(18,3),
|
||||
mean_ms DECIMAL(18,3),
|
||||
max_ms DECIMAL(18,3),
|
||||
rows_count BIGINT,
|
||||
hit_ratio DECIMAL(7,4),
|
||||
share_of_db_time DECIMAL(7,4),
|
||||
snapshots INT,
|
||||
CONSTRAINT uq_query_daily_0 UNIQUE (statement_id,"day")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_query_daily_0 ON db_perf_query_daily (statement_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_query_daily_1 ON db_perf_query_daily ("day");
|
||||
|
||||
-- changeset qbit-db-performance:7
|
||||
-- comment: Tables table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_table_stat (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100),
|
||||
schema_name VARCHAR(100),
|
||||
table_name VARCHAR(200),
|
||||
relid BIGINT,
|
||||
seq_scan BIGINT,
|
||||
seq_tup_read BIGINT,
|
||||
idx_scan BIGINT,
|
||||
idx_tup_fetch BIGINT,
|
||||
n_tup_ins BIGINT,
|
||||
n_tup_upd BIGINT,
|
||||
n_tup_del BIGINT,
|
||||
n_tup_hot_upd BIGINT,
|
||||
n_live_tup BIGINT,
|
||||
n_dead_tup BIGINT,
|
||||
dead_ratio DECIMAL(7,4),
|
||||
total_bytes BIGINT,
|
||||
table_bytes BIGINT,
|
||||
index_bytes BIGINT,
|
||||
toast_bytes BIGINT,
|
||||
growth_bytes_7d BIGINT,
|
||||
last_vacuum TIMESTAMP,
|
||||
last_autovacuum TIMESTAMP,
|
||||
last_analyze TIMESTAMP,
|
||||
last_autoanalyze TIMESTAMP,
|
||||
vacuum_count BIGINT,
|
||||
autovacuum_count BIGINT,
|
||||
analyze_count BIGINT,
|
||||
autoanalyze_count BIGINT,
|
||||
freeze_age BIGINT,
|
||||
seq_scan_share DECIMAL(7,4),
|
||||
last_seen_at TIMESTAMP,
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_table_stat_0 UNIQUE (backend_name,schema_name,table_name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_table_stat_0 ON db_perf_table_stat (last_seen_at);
|
||||
|
||||
-- changeset qbit-db-performance:8
|
||||
-- comment: Table Snapshots table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_table_snapshot (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
snapshot_id BIGINT,
|
||||
table_stat_id BIGINT,
|
||||
captured_at TIMESTAMP,
|
||||
seq_scan BIGINT,
|
||||
seq_tup_read BIGINT,
|
||||
idx_scan BIGINT,
|
||||
idx_tup_fetch BIGINT,
|
||||
n_tup_ins BIGINT,
|
||||
n_tup_upd BIGINT,
|
||||
n_tup_del BIGINT,
|
||||
n_tup_hot_upd BIGINT,
|
||||
n_live_tup BIGINT,
|
||||
n_dead_tup BIGINT,
|
||||
total_bytes BIGINT,
|
||||
table_bytes BIGINT,
|
||||
index_bytes BIGINT,
|
||||
autovacuum_count BIGINT,
|
||||
autoanalyze_count BIGINT,
|
||||
freeze_age BIGINT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_table_snapshot_0 ON db_perf_table_snapshot (snapshot_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_table_snapshot_1 ON db_perf_table_snapshot (table_stat_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_table_snapshot_2 ON db_perf_table_snapshot (captured_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_table_snapshot_3 ON db_perf_table_snapshot (table_stat_id,captured_at);
|
||||
|
||||
-- changeset qbit-db-performance:9
|
||||
-- comment: Table Daily table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_table_daily (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
table_stat_id BIGINT,
|
||||
"day" DATE,
|
||||
seq_scan BIGINT,
|
||||
idx_scan BIGINT,
|
||||
seq_tup_read BIGINT,
|
||||
n_tup_ins BIGINT,
|
||||
n_tup_upd BIGINT,
|
||||
n_tup_del BIGINT,
|
||||
n_live_tup BIGINT,
|
||||
n_dead_tup BIGINT,
|
||||
total_bytes BIGINT,
|
||||
CONSTRAINT uq_table_daily_0 UNIQUE (table_stat_id,"day")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_table_daily_0 ON db_perf_table_daily (table_stat_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_table_daily_1 ON db_perf_table_daily ("day");
|
||||
|
||||
-- changeset qbit-db-performance:10
|
||||
-- comment: Indexes table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_index_stat (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100),
|
||||
table_stat_id BIGINT,
|
||||
schema_name VARCHAR(100),
|
||||
table_name VARCHAR(200),
|
||||
index_name VARCHAR(200),
|
||||
relid BIGINT,
|
||||
definition VARCHAR(2000),
|
||||
"columns" VARCHAR(1000),
|
||||
is_unique BOOLEAN,
|
||||
is_primary BOOLEAN,
|
||||
is_valid BOOLEAN,
|
||||
idx_scan BIGINT,
|
||||
idx_tup_read BIGINT,
|
||||
idx_tup_fetch BIGINT,
|
||||
size_bytes BIGINT,
|
||||
write_cost BIGINT,
|
||||
scans_since_reset BIGINT,
|
||||
last_seen_at TIMESTAMP,
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_index_stat_0 UNIQUE (backend_name,schema_name,index_name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_index_stat_0 ON db_perf_index_stat (table_stat_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_index_stat_1 ON db_perf_index_stat (last_seen_at);
|
||||
|
||||
-- changeset qbit-db-performance:11
|
||||
-- comment: Activity Samples table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_activity_sample (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
snapshot_id BIGINT,
|
||||
captured_at TIMESTAMP,
|
||||
pid INT,
|
||||
db_user VARCHAR(100),
|
||||
application_name VARCHAR(200),
|
||||
client_addr VARCHAR(100),
|
||||
state VARCHAR(40),
|
||||
wait_event_type VARCHAR(60),
|
||||
wait_event VARCHAR(100),
|
||||
backend_type VARCHAR(60),
|
||||
xact_start TIMESTAMP,
|
||||
query_start TIMESTAMP,
|
||||
state_change TIMESTAMP,
|
||||
query_seconds DECIMAL(12,3),
|
||||
xact_seconds DECIMAL(12,3),
|
||||
blocked_by VARCHAR(500),
|
||||
blocking_count INT,
|
||||
reason VARCHAR(30),
|
||||
query_id BIGINT,
|
||||
statement_id BIGINT,
|
||||
query_fingerprint CHAR(64),
|
||||
query_text_normalized VARCHAR(4000)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_activity_sample_0 ON db_perf_activity_sample (snapshot_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_activity_sample_1 ON db_perf_activity_sample (captured_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_activity_sample_2 ON db_perf_activity_sample (statement_id);
|
||||
|
||||
-- changeset qbit-db-performance:12
|
||||
-- comment: Connection Samples table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_connection_sample (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
snapshot_id BIGINT,
|
||||
captured_at TIMESTAMP,
|
||||
db_user VARCHAR(100),
|
||||
application_name VARCHAR(200),
|
||||
state VARCHAR(40),
|
||||
connection_count INT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_connection_sample_0 ON db_perf_connection_sample (snapshot_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_connection_sample_1 ON db_perf_connection_sample (captured_at);
|
||||
|
||||
-- changeset qbit-db-performance:13
|
||||
-- comment: QQQ Attribution table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_attribution (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
window_start TIMESTAMP,
|
||||
window_end TIMESTAMP,
|
||||
pod_name VARCHAR(200),
|
||||
unit_kind VARCHAR(20),
|
||||
unit_name VARCHAR(300),
|
||||
process_name VARCHAR(200),
|
||||
table_name VARCHAR(200),
|
||||
operation VARCHAR(20),
|
||||
fingerprint CHAR(64),
|
||||
sql_text VARCHAR(4000),
|
||||
units BIGINT,
|
||||
statements BIGINT,
|
||||
total_ms DECIMAL(18,3),
|
||||
max_ms DECIMAL(18,3),
|
||||
rows_count BIGINT,
|
||||
errors BIGINT,
|
||||
sample_rate DECIMAL(5,4)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_attribution_0 ON db_perf_attribution (window_start);
|
||||
CREATE INDEX IF NOT EXISTS ix_attribution_1 ON db_perf_attribution (fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS ix_attribution_2 ON db_perf_attribution (unit_kind,unit_name);
|
||||
|
||||
-- changeset qbit-db-performance:14
|
||||
-- comment: QQQ Attribution Daily table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_attribution_daily (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
"day" DATE,
|
||||
rollup_key CHAR(64),
|
||||
unit_kind VARCHAR(20),
|
||||
unit_name VARCHAR(300),
|
||||
process_name VARCHAR(200),
|
||||
table_name VARCHAR(200),
|
||||
operation VARCHAR(20),
|
||||
fingerprint CHAR(64),
|
||||
sql_text VARCHAR(4000),
|
||||
units BIGINT,
|
||||
statements BIGINT,
|
||||
total_ms DECIMAL(18,3),
|
||||
max_ms DECIMAL(18,3),
|
||||
rows_count BIGINT,
|
||||
errors BIGINT,
|
||||
CONSTRAINT uq_attribution_daily_0 UNIQUE ("day",rollup_key)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_attribution_daily_0 ON db_perf_attribution_daily ("day");
|
||||
CREATE INDEX IF NOT EXISTS ix_attribution_daily_1 ON db_perf_attribution_daily (fingerprint);
|
||||
|
||||
-- changeset qbit-db-performance:15
|
||||
-- comment: Repeated Statements table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_repeat_pattern (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
window_start TIMESTAMP,
|
||||
window_end TIMESTAMP,
|
||||
pod_name VARCHAR(200),
|
||||
unit_kind VARCHAR(20),
|
||||
unit_name VARCHAR(300),
|
||||
process_name VARCHAR(200),
|
||||
table_name VARCHAR(200),
|
||||
fingerprint CHAR(64),
|
||||
sql_text VARCHAR(4000),
|
||||
units_affected BIGINT,
|
||||
max_repeats INT,
|
||||
total_repeats BIGINT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_repeat_pattern_0 ON db_perf_repeat_pattern (window_start);
|
||||
CREATE INDEX IF NOT EXISTS ix_repeat_pattern_1 ON db_perf_repeat_pattern (fingerprint);
|
||||
|
||||
-- changeset qbit-db-performance:16
|
||||
-- comment: Findings table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_finding (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
backend_name VARCHAR(100),
|
||||
rule_id BIGINT,
|
||||
rule_code VARCHAR(40),
|
||||
dedupe_key VARCHAR(300),
|
||||
status VARCHAR(20) DEFAULT 'OPEN',
|
||||
severity VARCHAR(20),
|
||||
score DECIMAL(12,4),
|
||||
title VARCHAR(300),
|
||||
summary VARCHAR(2000),
|
||||
evidence TEXT,
|
||||
recommendation TEXT,
|
||||
migration_snippet TEXT,
|
||||
subject_table VARCHAR(200),
|
||||
statement_id BIGINT,
|
||||
table_stat_id BIGINT,
|
||||
index_stat_id BIGINT,
|
||||
unit_name VARCHAR(300),
|
||||
first_seen_at TIMESTAMP,
|
||||
last_seen_at TIMESTAMP,
|
||||
occurrences INT,
|
||||
resolved_at TIMESTAMP,
|
||||
status_note VARCHAR(2000),
|
||||
status_changed_by VARCHAR(100),
|
||||
create_date TIMESTAMP,
|
||||
modify_date TIMESTAMP,
|
||||
CONSTRAINT uq_finding_0 UNIQUE (dedupe_key)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_0 ON db_perf_finding (rule_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_1 ON db_perf_finding (statement_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_2 ON db_perf_finding (table_stat_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_3 ON db_perf_finding (index_stat_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_4 ON db_perf_finding (last_seen_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_5 ON db_perf_finding (resolved_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_finding_6 ON db_perf_finding (status,score);
|
||||
|
||||
-- changeset qbit-db-performance:17
|
||||
-- comment: Query Plans table.
|
||||
CREATE TABLE IF NOT EXISTS db_perf_explain (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
statement_id BIGINT,
|
||||
requested_by VARCHAR(100),
|
||||
requested_at TIMESTAMP,
|
||||
mode VARCHAR(20),
|
||||
sql_fingerprint CHAR(64),
|
||||
sql_text TEXT,
|
||||
parameter_count INT,
|
||||
duration_ms INT,
|
||||
outcome VARCHAR(20),
|
||||
error_message VARCHAR(2000),
|
||||
plan_json TEXT,
|
||||
plan_text TEXT,
|
||||
total_cost DECIMAL(18,3)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_explain_0 ON db_perf_explain (statement_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_explain_1 ON db_perf_explain (requested_at);
|
||||
|
||||
-- changeset qbit-db-performance:18
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_query_snapshot_snapshot_id' AND LOWER(TABLE_NAME) = 'db_perf_query_snapshot' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Query Snapshots reference to Snapshots.
|
||||
ALTER TABLE db_perf_query_snapshot ADD CONSTRAINT fk_query_snapshot_snapshot_id FOREIGN KEY (snapshot_id) REFERENCES db_perf_snapshot(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:19
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_query_snapshot_statement_id' AND LOWER(TABLE_NAME) = 'db_perf_query_snapshot' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Query Snapshots reference to Queries.
|
||||
ALTER TABLE db_perf_query_snapshot ADD CONSTRAINT fk_query_snapshot_statement_id FOREIGN KEY (statement_id) REFERENCES db_perf_statement(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:20
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_query_daily_statement_id' AND LOWER(TABLE_NAME) = 'db_perf_query_daily' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Query Daily reference to Queries.
|
||||
ALTER TABLE db_perf_query_daily ADD CONSTRAINT fk_query_daily_statement_id FOREIGN KEY (statement_id) REFERENCES db_perf_statement(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:21
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_table_snapshot_snapshot_id' AND LOWER(TABLE_NAME) = 'db_perf_table_snapshot' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Table Snapshots reference to Snapshots.
|
||||
ALTER TABLE db_perf_table_snapshot ADD CONSTRAINT fk_table_snapshot_snapshot_id FOREIGN KEY (snapshot_id) REFERENCES db_perf_snapshot(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:22
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_table_snapshot_table_stat_id' AND LOWER(TABLE_NAME) = 'db_perf_table_snapshot' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Table Snapshots reference to Tables.
|
||||
ALTER TABLE db_perf_table_snapshot ADD CONSTRAINT fk_table_snapshot_table_stat_id FOREIGN KEY (table_stat_id) REFERENCES db_perf_table_stat(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:23
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_table_daily_table_stat_id' AND LOWER(TABLE_NAME) = 'db_perf_table_daily' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Table Daily reference to Tables.
|
||||
ALTER TABLE db_perf_table_daily ADD CONSTRAINT fk_table_daily_table_stat_id FOREIGN KEY (table_stat_id) REFERENCES db_perf_table_stat(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:24
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_index_stat_table_stat_id' AND LOWER(TABLE_NAME) = 'db_perf_index_stat' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Indexes reference to Tables.
|
||||
ALTER TABLE db_perf_index_stat ADD CONSTRAINT fk_index_stat_table_stat_id FOREIGN KEY (table_stat_id) REFERENCES db_perf_table_stat(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:25
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_activity_sample_snapshot_id' AND LOWER(TABLE_NAME) = 'db_perf_activity_sample' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Activity Samples reference to Snapshots.
|
||||
ALTER TABLE db_perf_activity_sample ADD CONSTRAINT fk_activity_sample_snapshot_id FOREIGN KEY (snapshot_id) REFERENCES db_perf_snapshot(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:26
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_activity_sample_statement_id' AND LOWER(TABLE_NAME) = 'db_perf_activity_sample' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Activity Samples reference to Queries.
|
||||
ALTER TABLE db_perf_activity_sample ADD CONSTRAINT fk_activity_sample_statement_id FOREIGN KEY (statement_id) REFERENCES db_perf_statement(id) ON DELETE SET NULL;
|
||||
|
||||
-- changeset qbit-db-performance:27
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_connection_sample_snapshot_id' AND LOWER(TABLE_NAME) = 'db_perf_connection_sample' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Connection Samples reference to Snapshots.
|
||||
ALTER TABLE db_perf_connection_sample ADD CONSTRAINT fk_connection_sample_snapshot_id FOREIGN KEY (snapshot_id) REFERENCES db_perf_snapshot(id) ON DELETE CASCADE;
|
||||
|
||||
-- changeset qbit-db-performance:28
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_finding_rule_id' AND LOWER(TABLE_NAME) = 'db_perf_finding' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Findings reference to Suggestion Rules.
|
||||
ALTER TABLE db_perf_finding ADD CONSTRAINT fk_finding_rule_id FOREIGN KEY (rule_id) REFERENCES db_perf_rule(id) ON DELETE RESTRICT;
|
||||
|
||||
-- changeset qbit-db-performance:29
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_finding_statement_id' AND LOWER(TABLE_NAME) = 'db_perf_finding' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Findings reference to Queries.
|
||||
ALTER TABLE db_perf_finding ADD CONSTRAINT fk_finding_statement_id FOREIGN KEY (statement_id) REFERENCES db_perf_statement(id) ON DELETE SET NULL;
|
||||
|
||||
-- changeset qbit-db-performance:30
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_finding_table_stat_id' AND LOWER(TABLE_NAME) = 'db_perf_finding' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Findings reference to Tables.
|
||||
ALTER TABLE db_perf_finding ADD CONSTRAINT fk_finding_table_stat_id FOREIGN KEY (table_stat_id) REFERENCES db_perf_table_stat(id) ON DELETE SET NULL;
|
||||
|
||||
-- changeset qbit-db-performance:31
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_finding_index_stat_id' AND LOWER(TABLE_NAME) = 'db_perf_finding' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Findings reference to Indexes.
|
||||
ALTER TABLE db_perf_finding ADD CONSTRAINT fk_finding_index_stat_id FOREIGN KEY (index_stat_id) REFERENCES db_perf_index_stat(id) ON DELETE SET NULL;
|
||||
|
||||
-- changeset qbit-db-performance:32
|
||||
-- preconditions onFail:MARK_RAN
|
||||
-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE LOWER(CONSTRAINT_NAME) = 'fk_explain_statement_id' AND LOWER(TABLE_NAME) = 'db_perf_explain' AND LOWER(TABLE_SCHEMA) = LOWER(CURRENT_SCHEMA())
|
||||
-- comment: Query Plans reference to Queries.
|
||||
ALTER TABLE db_perf_explain ADD CONSTRAINT fk_explain_statement_id FOREIGN KEY (statement_id) REFERENCES db_perf_statement(id) ON DELETE SET NULL;
|
||||
|
||||
-- changeset qbit-db-performance:33
|
||||
-- comment: Suggestion Rules insert-only defaults; administrator edits survive.
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'SLOW_QUERY', 'Slow query', 'Slow query.', TRUE, 'WARNING', 500, 'Mean execution time (ms)', 5, 'Database time (%)' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'SLOW_QUERY');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'MISSING_INDEX', 'Possible missing index', 'Possible missing index.', TRUE, 'WARNING', 50, 'Sequential scan share (%)', 1000000, 'Tuples read per "day"' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'MISSING_INDEX');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'UNUSED_INDEX', 'Unused index', 'Unused index.', TRUE, 'WARNING', 14, 'Days since statistics reset', 1, 'Index size (MB)' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'UNUSED_INDEX');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'DUPLICATE_INDEX', 'Duplicate index', 'Duplicate index.', TRUE, 'WARNING', NULL, NULL, NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'DUPLICATE_INDEX');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'N_PLUS_ONE', 'Repeated statements', 'Repeated statements.', TRUE, 'WARNING', 10, 'Repeats per unit', 5, 'Affected units' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'N_PLUS_ONE');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'HOT_JOB', 'Expensive job', 'Expensive job.', TRUE, 'WARNING', 10, 'Database time (%)', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'HOT_JOB');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'OVER_AUDITED', 'High audit volume', 'High audit volume.', TRUE, 'WARNING', 50000, 'Audit inserts per "day"', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'OVER_AUDITED');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'BLOAT', 'Dead tuples', 'Dead tuples.', TRUE, 'WARNING', 20, 'Dead tuple ratio (%)', 10000, 'Dead tuples' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'BLOAT');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'VACUUM_STALE', 'Stale vacuum', 'Stale vacuum.', TRUE, 'WARNING', 7, 'Days since vacuum', 150000000, 'Transaction freeze age' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'VACUUM_STALE');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'POOL_SIZING', 'Connection pool sizing', 'Connection pool sizing.', TRUE, 'WARNING', 80, 'Connections used (%)', 50, 'Idle connections (%)' WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'POOL_SIZING');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'IDLE_IN_TRANSACTION', 'Idle transaction', 'Idle transaction.', TRUE, 'WARNING', 60, 'Idle seconds', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'IDLE_IN_TRANSACTION');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'CACHE_HIT_LOW', 'Low cache hit ratio', 'Low cache hit ratio.', TRUE, 'WARNING', 95, 'Cache hit ratio (%)', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'CACHE_HIT_LOW');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'TEMP_SPILL', 'Temporary disk spill', 'Temporary disk spill.', TRUE, 'WARNING', 100, 'Temporary data per "day" (MB)', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'TEMP_SPILL');
|
||||
INSERT INTO db_perf_rule (code,label,description,enabled,severity,threshold_1,threshold_1_label,threshold_2,threshold_2_label)
|
||||
SELECT 'DEADLOCKS', 'Deadlocks', 'Deadlocks.', TRUE, 'WARNING', 1, 'Deadlocks per "day"', NULL, NULL WHERE NOT EXISTS (SELECT 1 FROM db_perf_rule WHERE code = 'DEADLOCKS');
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import com.kingsrook.qbits.dbperformance.metadata.DbPerfMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Real Liquibase migration and insert-only defaults on H2.
|
||||
*******************************************************************************/
|
||||
class DbPerfChangelogTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Tables exist, all defaults are seeded and reapplying preserves edits.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void migrateTwice() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
RDBMSBackendMetaData backend = (RDBMSBackendMetaData) instance.getBackends().values().iterator().next();
|
||||
try(Connection connection = DriverManager.getConnection(backend.getJdbcUrl(), "sa", ""); Statement statement = connection.createStatement())
|
||||
{
|
||||
for(String table : DbPerfMetaData.TABLES)
|
||||
{
|
||||
try(ResultSet rows = statement.executeQuery("SELECT count(*) FROM " + table))
|
||||
{
|
||||
rows.next();
|
||||
assertThat(rows.getInt(1)).isEqualTo(table.equals("db_perf_rule") ? 14 : 0);
|
||||
}
|
||||
}
|
||||
statement.executeUpdate("UPDATE db_perf_rule SET threshold_1=123 WHERE code='SLOW_QUERY'");
|
||||
DbPerfTestSupport.migrate(backend);
|
||||
try(var resource = getClass().getClassLoader().getResourceAsStream(DbPerfMetaData.CHANGELOG))
|
||||
{
|
||||
String script = new String(resource.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
|
||||
String seeds = script.substring(script.indexOf("INSERT INTO db_perf_rule"));
|
||||
for(String sql : seeds.split(";"))
|
||||
{
|
||||
if(!sql.isBlank())
|
||||
{
|
||||
statement.executeUpdate(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
try(ResultSet rows = statement.executeQuery("SELECT threshold_1 FROM db_perf_rule WHERE code='SLOW_QUERY'"))
|
||||
{
|
||||
rows.next();
|
||||
assertThat(rows.getInt(1)).isEqualTo(123);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qbits.dbperformance.provider.ConnectionCount;
|
||||
import com.kingsrook.qbits.dbperformance.provider.DatabaseStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.IndexStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.ProviderCapabilities;
|
||||
import com.kingsrook.qbits.dbperformance.provider.SessionInfo;
|
||||
import com.kingsrook.qbits.dbperformance.provider.StatementResetInfo;
|
||||
import com.kingsrook.qbits.dbperformance.provider.StatementStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.TableStats;
|
||||
import com.kingsrook.qbits.dbperformance.provider.UnsupportedStatsProvider;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Deterministic statistics stream for real QQQ/H2 service integration tests.
|
||||
*******************************************************************************/
|
||||
public class DbPerfFixtureProvider extends UnsupportedStatsProvider
|
||||
{
|
||||
public static final Instant START = Instant.parse("2026-10-01T12:00:00Z");
|
||||
public long calls = 10;
|
||||
public Instant reset = START.minusSeconds(3600);
|
||||
public boolean limited;
|
||||
public boolean fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Supported full or reduced access.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public ProviderCapabilities capabilities(Connection connection)
|
||||
{
|
||||
return (new ProviderCapabilities(true, 170000, true, !limited, limited ? "NO_READ_ALL_STATS" : "FULL", limited ? "Grant pg_read_all_stats" : "Full statistics access"));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** PostgreSQL identity.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return ("POSTGRES");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Cumulative database counters.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DatabaseStats readDatabase(Connection connection)
|
||||
{
|
||||
return (new DatabaseStats(Map.of("xactCommit", calls, "xactRollback", 0L, "blksHit", calls * 9, "blksRead", calls, "tempFiles", 0L, "tempBytes", 0L, "deadlocks", 0L, "conflicts", 0L), reset, 100, 4096));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Two shapes with different total time exercise top-N denominators.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<StatementStats> readStatements(Connection connection)
|
||||
{
|
||||
return (List.of(new StatementStats(1, "app", true, "select * from public.accounts where id=$1", calls, BigDecimal.valueOf(calls * 10), BigDecimal.TEN, BigDecimal.valueOf(15), calls, calls * 9, calls, 0, reset, reset),
|
||||
new StatementStats(2, "app", true, "select * from public.accounts where name='private'", calls, BigDecimal.valueOf(calls * 5), BigDecimal.valueOf(5), BigDecimal.TEN, calls, calls, calls, 2, reset, reset)));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Statement reset epoch.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public StatementResetInfo readStatementResetInfo(Connection connection)
|
||||
{
|
||||
return (new StatementResetInfo(reset, 0));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A failure here occurs after statement writes, proving atomic rollback.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<TableStats> readTables(Connection connection)
|
||||
{
|
||||
if(fail)
|
||||
{
|
||||
throw (new IllegalStateException("raw SQL contains private patient text"));
|
||||
}
|
||||
return (List.of(new TableStats("public", "accounts", 44, Map.of("seqScan", calls, "idxScan", calls * 3, "nTupIns", calls, "nLiveTup", calls, "nDeadTup", 1L, "totalBytes", calls * 100, "tableBytes", calls * 90, "indexBytes", calls * 10, "freezeAge", 100L), Map.of("lastAutovacuum", START))));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Index gauge linked to the collected table.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<IndexStats> readIndexes(Connection connection)
|
||||
{
|
||||
return (List.of(new IndexStats("public", "accounts", "accounts_pkey", 45, "CREATE UNIQUE INDEX accounts_pkey ON public.accounts(id)", "id", true, true, true, calls * 3, calls, calls, 1024, calls)));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A blocked query, its idle blocker, and a long idle transaction.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<SessionInfo> readSessions(Connection connection)
|
||||
{
|
||||
return (List.of(new SessionInfo(10, "app", "site", "127.0.0.1", "active", "Lock", "relation", "client backend", START, START, START, BigDecimal.valueOf(50), BigDecimal.valueOf(60), List.of(20), 1L, "select * from accounts where id=999"),
|
||||
new SessionInfo(20, "app", "site", null, "idle", null, null, "client backend", START, START, START, BigDecimal.TEN, BigDecimal.TEN, List.of(), null, "select 'private secret'"),
|
||||
new SessionInfo(30, "app", "site", null, "idle in transaction", null, null, "client backend", START, START, START.minusSeconds(120), BigDecimal.valueOf(100), BigDecimal.valueOf(100), List.of(), null, "select $$private secret$$")));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Capacity gauges.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<ConnectionCount> readConnections(Connection connection)
|
||||
{
|
||||
return (List.of(new ConnectionCount("app", "site", "active", 2), new ConnectionCount("app", "site", "idle", 3), new ConnectionCount("app", "site", "idle in transaction", 1)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qbits.dbperformance.metadata.DbPerfMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Every metadata reference is backed by an actual PostgreSQL foreign key.
|
||||
*******************************************************************************/
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class DbPerfForeignKeyTest
|
||||
{
|
||||
@Container
|
||||
private static final PostgreSQLContainer<?> POSTGRES = DbPerfPostgresSupport.container();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The host changelog creates all references and reruns without changes.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void everyReferenceHasForeignKey() throws Exception
|
||||
{
|
||||
RDBMSBackendMetaData backend = DbPerfPostgresSupport.backend(POSTGRES);
|
||||
QInstance instance = DbPerfTestSupport.instance(backend);
|
||||
DbPerfTestSupport.migrate(backend);
|
||||
try(Connection connection = DbPerfPostgresSupport.connect(POSTGRES))
|
||||
{
|
||||
for(QTableMetaData table : instance.getTables().values())
|
||||
{
|
||||
Set<String> references = new HashSet<>();
|
||||
try(ResultSet keys = connection.getMetaData().getImportedKeys(null, "public", table.getName()))
|
||||
{
|
||||
while(keys.next())
|
||||
{
|
||||
references.add(keys.getString("FKCOLUMN_NAME") + ":" + keys.getString("PKTABLE_NAME"));
|
||||
}
|
||||
}
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
if(field.getPossibleValueSourceName() != null && DbPerfMetaData.TABLES.contains(field.getPossibleValueSourceName()))
|
||||
{
|
||||
assertThat(references).as(table.getName() + "." + field.getBackendName()).contains(field.getBackendName() + ":" + field.getPossibleValueSourceName());
|
||||
}
|
||||
}
|
||||
}
|
||||
try(Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("INSERT INTO db_perf_statement(id) VALUES (999)");
|
||||
statement.execute("INSERT INTO db_perf_explain(statement_id) VALUES (999)");
|
||||
statement.execute("DELETE FROM db_perf_statement WHERE id=999");
|
||||
try(ResultSet row = statement.executeQuery("SELECT statement_id FROM db_perf_explain"))
|
||||
{
|
||||
row.next();
|
||||
assertThat(row.getObject(1)).isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfStore;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Exact read/write permissions enforced by core actions and write customizers.
|
||||
*******************************************************************************/
|
||||
class DbPerfPermissionTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Viewers can read configuration but cannot mutate it or runtime tables.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void viewAndManageAreDistinct() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
instance.setEnforcePermissionsInCoreActions(true);
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(instance);
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
QContext.setQSession(new QSession().withPermissions(DbPerfPermissions.VIEW));
|
||||
assertThat(DbPerfStore.query("db_perf_settings", new QQueryFilter())).hasSize(1);
|
||||
QRecord change = new QRecord().withValue("id", settings.getValueInteger("id")).withValue("topStatementsPerSnapshot", 7);
|
||||
assertThatThrownBy(() -> DbPerfStore.update("db_perf_settings", List.of(change))).hasMessageContaining("permission");
|
||||
QContext.setQSession(new QSession().withPermissions(DbPerfPermissions.VIEW, DbPerfPermissions.MANAGE_SETTINGS));
|
||||
DbPerfStore.update("db_perf_settings", List.of(change));
|
||||
assertThat(new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName()).getValueInteger("topStatementsPerSnapshot")).isEqualTo(7);
|
||||
assertThatThrownBy(() -> DbPerfStore.insert("db_perf_settings", List.of(new QRecord().withValue("backendName", "other")))).hasMessageContaining("permission");
|
||||
assertThatThrownBy(() -> DbPerfStore.insert("db_perf_snapshot", List.of(new QRecord().withValue("capturedAt", java.time.Instant.now())))).hasMessageContaining("permission");
|
||||
QRecord rule = DbPerfStore.insert("db_perf_rule", List.of(new QRecord().withValue("code", "CUSTOM").withValue("label", "Custom rule"))).getFirst();
|
||||
DbPerfStore.update("db_perf_rule", List.of(new QRecord().withValue("id", rule.getValueInteger("id")).withValue("enabled", false)));
|
||||
DbPerfStore.delete("db_perf_rule", DbPerfStore.eq("id", rule.getValueInteger("id")));
|
||||
assertThat(PermissionsHelper.getAllAvailablePermissions(instance)).extracting(permission -> permission.getName()).contains(DbPerfPermissions.VIEW, DbPerfPermissions.MANAGE_SETTINGS)
|
||||
.doesNotContain("db_performance.view.hasAccess", "db_performance.read", "db_performance.write");
|
||||
QContext.setQSession(new QSession());
|
||||
assertThatThrownBy(() -> DbPerfStore.query("db_perf_statement", new QQueryFilter())).hasMessageContaining("Permission");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runtime-only writes stay guarded when hosts disable core enforcement.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void guardWithoutCoreEnforcement() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
instance.setEnforcePermissionsInCoreActions(false);
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(instance);
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
QContext.setQSession(new QSession().withPermissions(DbPerfPermissions.VIEW));
|
||||
assertThatThrownBy(() -> DbPerfStore.update("db_perf_settings", List.of(new QRecord().withValue("id", settings.getValueInteger("id")).withValue("enabled", false))))
|
||||
.hasMessageContaining("record write failed");
|
||||
assertThatThrownBy(() -> DbPerfStore.insert("db_perf_statement", List.of(new QRecord().withValue("queryId", 1L)))).hasMessageContaining("record write failed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.Statement;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.module.postgres.model.metadata.PostgreSQLBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Shared PostgreSQL 17 test setup, using the same JDBC role as the host backend.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfPostgresSupport
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfPostgresSupport()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** pg_stat_statements must be loaded at server startup.
|
||||
*******************************************************************************/
|
||||
public static PostgreSQLContainer<?> container()
|
||||
{
|
||||
return (new PostgreSQLContainer<>("postgres:17-alpine")
|
||||
.withCommand("postgres", "-c", "shared_preload_libraries=pg_stat_statements", "-c", "pg_stat_statements.track=top"));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Install the extension in the current database.
|
||||
*******************************************************************************/
|
||||
public static RDBMSBackendMetaData backend(PostgreSQLContainer<?> postgres) throws Exception
|
||||
{
|
||||
try(Connection connection = connect(postgres); Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("CREATE EXTENSION IF NOT EXISTS pg_stat_statements");
|
||||
}
|
||||
RDBMSBackendMetaData backend = new PostgreSQLBackendMetaData().withName("postgres" + UUID.randomUUID().toString().replace("-", ""))
|
||||
.withVendor("postgresql").withHostName(postgres.getHost()).withDatabaseName(postgres.getDatabaseName())
|
||||
.withUsername(postgres.getUsername()).withPassword(postgres.getPassword());
|
||||
backend.setJdbcUrl(postgres.getJdbcUrl());
|
||||
return (backend);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Direct test fixture connection.
|
||||
*******************************************************************************/
|
||||
public static Connection connect(PostgreSQLContainer<?> postgres) throws Exception
|
||||
{
|
||||
return (DriverManager.getConnection(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.processes.DbPerfCollectStep;
|
||||
import com.kingsrook.qbits.dbperformance.processes.DbPerfProcesses;
|
||||
import com.kingsrook.qbits.dbperformance.processes.DbPerfRollupStep;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.jobs.DurableProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.jobs.model.JobIdempotency;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Process metadata, system execution, summaries and runtime permission guards.
|
||||
*******************************************************************************/
|
||||
class DbPerfProcessTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Scheduled processes stay out of navigation and register optional cron jobs.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void metadataAndExecution() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
assertThat(instance.getProcesses()).containsKeys(DbPerfProcesses.COLLECT, DbPerfProcesses.ROLLUP, DbPerfProcesses.COLLECT_NOW);
|
||||
for(String name : List.of(DbPerfProcesses.COLLECT, DbPerfProcesses.ROLLUP))
|
||||
{
|
||||
QProcessMetaData process = instance.getProcess(name);
|
||||
assertThat(process.getIsHidden()).isTrue();
|
||||
assertThat(process.getSchedule()).isNull();
|
||||
assertThat(DurableProcessMetaData.of(process).getEligible()).isTrue();
|
||||
assertThat(DurableProcessMetaData.of(process).getIdempotency()).isEqualTo(JobIdempotency.NATURAL);
|
||||
}
|
||||
assertThat(instance.getProcess(DbPerfProcesses.COLLECT_NOW).getTableName()).isEqualTo("db_perf_snapshot");
|
||||
RunBackendStepInput input = new RunBackendStepInput();
|
||||
input.addValue("forceTables", true);
|
||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||
new DbPerfCollectStep().run(input, output);
|
||||
assertThat(output.getValue("processResults")).isNotNull();
|
||||
input.addValue("forceTables", false);
|
||||
new DbPerfCollectStep().run(input, new RunBackendStepOutput());
|
||||
new DbPerfRollupStep().run(input, new RunBackendStepOutput());
|
||||
DbPerfRuntime.of(instance).getConfig().setSchedulerName("scheduler");
|
||||
QInstance scheduledInstance = new QInstance();
|
||||
DbPerfProcesses.addTo(scheduledInstance, DbPerfRuntime.of(instance).getConfig());
|
||||
assertThat(scheduledInstance.getProcess(DbPerfProcesses.COLLECT).getSchedule().getCronExpression()).isEqualTo("0 */5 * * * ?");
|
||||
QContext.setQSession(new QSession().withPermissions(DbPerfPermissions.VIEW));
|
||||
assertThatThrownBy(() -> new DbPerfCollectStep().run(input, output)).hasMessageContaining("permission");
|
||||
assertThatThrownBy(() -> new DbPerfRollupStep().run(input, output)).hasMessageContaining("permission");
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
/*
|
||||
* Copyright 2026 Kingsrook, LLC
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,66 +21,115 @@
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.metadata.DbPerfMetaData;
|
||||
import com.kingsrook.qqq.backend.core.actions.ai.AiExposure;
|
||||
import com.kingsrook.qqq.backend.core.actions.ai.AiTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfigValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for DbPerfQBitProducer
|
||||
** Metadata and entity storage contract tests.
|
||||
*******************************************************************************/
|
||||
class DbPerfQBitProducerTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** A configured qbit registers itself in the instance.
|
||||
** All tables use the exact storage names and protected navigation.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testProduce() throws Exception
|
||||
void metadata() throws Exception
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
DbPerfQBitConfig config = new DbPerfQBitConfig().withBackendName("rdbms");
|
||||
DbPerfQBitProducer producer = new DbPerfQBitProducer().withQBitConfig(config).withNamespace("db_perf");
|
||||
|
||||
assertThat(producer.isEnabled()).isTrue();
|
||||
assertThat(producer.getQBitConfig()).isSameAs(config);
|
||||
assertThat(producer.getNamespace()).isEqualTo("db_perf");
|
||||
assertThat(config.getDefaultBackendNameForTables()).isEqualTo("rdbms");
|
||||
|
||||
producer.produce(qInstance);
|
||||
assertThat(qInstance.getQBits()).hasSize(1);
|
||||
assertThat(qInstance.getQBits().values().iterator().next().getArtifactId()).isEqualTo("qbit-db-performance");
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
assertThat(instance.getTables()).hasSize(17);
|
||||
assertThat(instance.getQBits()).hasSize(1);
|
||||
for(String name : DbPerfMetaData.TABLES)
|
||||
{
|
||||
QTableMetaData table = instance.getTable(name);
|
||||
assertThat(((RDBMSTableBackendDetails) table.getBackendDetails()).getTableName()).isEqualTo(name);
|
||||
assertThat(table.getPermissionRules().getPermissionBaseName()).isEqualTo(DbPerfPermissions.VIEW);
|
||||
table.getFields().values().forEach(field -> assertThat(field.getBackendName()).matches("[a-z][a-z0-9_]*"));
|
||||
if(!List.of("db_perf_settings", "db_perf_rule").contains(name))
|
||||
{
|
||||
assertThat(table.getDisabledCapabilities()).contains(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
}
|
||||
}
|
||||
assertThat(AiTableMetaData.of(instance.getTable("db_perf_settings")).getExposure()).isEqualTo(AiExposure.NONE);
|
||||
assertThat(AiTableMetaData.of(instance.getTable("db_perf_rule")).getExposure()).isEqualTo(AiExposure.NONE);
|
||||
assertThat(instance.getApp("dbPerformance").getSections().getFirst().getTables()).containsExactly("db_perf_finding", "db_perf_statement", "db_perf_table_stat", "db_perf_index_stat", "db_perf_snapshot", "db_perf_settings", "db_perf_rule");
|
||||
assertThat(instance.getJoins()).hasSize(13);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Without a config the producer is disabled; without a backend it fails.
|
||||
** Fail before modifying the instance when configuration conflicts.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDisabledAndInvalid()
|
||||
void configuration() throws Exception
|
||||
{
|
||||
assertThat(new DbPerfQBitProducer().isEnabled()).isFalse();
|
||||
|
||||
DbPerfQBitConfig config = new DbPerfQBitConfig();
|
||||
config.setBackendName(null);
|
||||
assertThat(config.getBackendName()).isNull();
|
||||
assertThatThrownBy(() -> new DbPerfQBitProducer().withQBitConfig(config).produce(new QInstance()))
|
||||
.isInstanceOf(QBitConfigValidationException.class);
|
||||
assertThatThrownBy(() -> new DbPerfQBitProducer().produce(new QInstance())).hasMessageContaining("configuration");
|
||||
assertThatThrownBy(() -> new DbPerfQBitProducer().withQBitConfig(new DbPerfQBitConfig()).produce(new QInstance())).isInstanceOf(QBitConfigValidationException.class);
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
DbPerfQBitConfig config = new DbPerfQBitConfig().withBackendName(instance.getBackends().keySet().iterator().next());
|
||||
DbPerfQBitProducer producer = new DbPerfQBitProducer().withQBitConfig(config).withNamespace("db_perf");
|
||||
assertThat(producer.getNamespace()).isEqualTo("db_perf");
|
||||
assertThat(producer.getQBitConfig()).isSameAs(config);
|
||||
assertThat(producer.isEnabled()).isTrue();
|
||||
assertThat(config.getDefaultBackendNameForTables()).isEqualTo(config.getBackendName());
|
||||
assertThatThrownBy(() -> producer.produce(instance)).isInstanceOf(QBitConfigValidationException.class);
|
||||
for(Method method : DbPerfQBitConfig.class.getDeclaredMethods())
|
||||
{
|
||||
if(method.getName().startsWith("with"))
|
||||
{
|
||||
Object value = method.getParameterTypes()[0] == Boolean.class ? false : "configured";
|
||||
assertThat(method.invoke(config, value)).isSameAs(config);
|
||||
assertThat(DbPerfQBitConfig.class.getMethod("get" + method.getName().substring(4)).invoke(config)).isEqualTo(value);
|
||||
DbPerfQBitConfig.class.getMethod("set" + method.getName().substring(4), method.getParameterTypes()).invoke(config, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Names follow the owner rule: lowercase snake_case with the full qbit name.
|
||||
** Every annotated entity round-trips through QRecord without type loss.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNaming()
|
||||
void entityStorageContract() throws Exception
|
||||
{
|
||||
assertThat(DbPerfQBitConfig.TABLE_NAME_PREFIX).isEqualTo("db_perf_").matches("^[a-z][a-z0-9_]*_$");
|
||||
assertThat(DbPerfQBitConfig.PERMISSION_BASE_NAME).isEqualTo("db_perf");
|
||||
for(String name : DbPerfMetaData.TABLES)
|
||||
{
|
||||
StringBuilder typeName = new StringBuilder("com.kingsrook.qbits.dbperformance.model.DbPerf");
|
||||
for(String part : name.substring("db_perf_".length()).split("_"))
|
||||
{
|
||||
typeName.append(Character.toUpperCase(part.charAt(0))).append(part.substring(1));
|
||||
}
|
||||
Class<?> type = Class.forName(typeName.toString());
|
||||
QRecordEntity entity = (QRecordEntity) type.getConstructor().newInstance();
|
||||
for(Method method : type.getDeclaredMethods())
|
||||
{
|
||||
if(method.getName().startsWith("with"))
|
||||
{
|
||||
Class<?> fieldType = method.getParameterTypes()[0];
|
||||
Object value = fieldType == Integer.class ? 12 : fieldType == Long.class ? 9000000000L : fieldType == Boolean.class ? true : fieldType == Instant.class ? Instant.parse("2026-01-01T00:00:00Z") : fieldType == LocalDate.class ? LocalDate.of(2026, 1, 1) : fieldType == BigDecimal.class ? new BigDecimal("12.345") : "value";
|
||||
assertThat(method.invoke(entity, value)).isSameAs(entity);
|
||||
type.getMethod("set" + method.getName().substring(4), fieldType).invoke(entity, value);
|
||||
Object copy = type.getConstructor(QRecord.class).newInstance(entity.toQRecord());
|
||||
assertThat(type.getMethod("get" + method.getName().substring(4)).invoke(copy)).isEqualTo(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qbits.dbperformance.metadata.DbPerfMetaData;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import liquibase.Liquibase;
|
||||
import liquibase.database.DatabaseFactory;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.resource.ClassLoaderResourceAccessor;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Isolated H2 fixtures using the real host migration and QQQ actions.
|
||||
*******************************************************************************/
|
||||
public final class DbPerfTestSupport
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
*******************************************************************************/
|
||||
private DbPerfTestSupport()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** New isolated instance with schema and system context.
|
||||
*******************************************************************************/
|
||||
public static QInstance instance() throws Exception
|
||||
{
|
||||
String name = "dbperf" + UUID.randomUUID().toString().replace("-", "");
|
||||
RDBMSBackendMetaData backend = new RDBMSBackendMetaData().withName(name).withVendor("h2").withHostName("mem").withDatabaseName(name).withUsername("sa");
|
||||
backend.setJdbcUrl("jdbc:h2:mem:" + name + ";CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1;MODE=PostgreSQL");
|
||||
return (instance(backend));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Wire the producer onto an existing backend.
|
||||
*******************************************************************************/
|
||||
public static QInstance instance(RDBMSBackendMetaData backend) throws Exception
|
||||
{
|
||||
QInstance instance = new QInstance();
|
||||
instance.addBackend(backend);
|
||||
instance.withInstanceDefaultAuthentication(new QAuthenticationMetaData().withName("mock").withType(QAuthenticationType.MOCK));
|
||||
new DbPerfQBitProducer().withQBitConfig(new DbPerfQBitConfig().withBackendName(backend.getName())).produce(instance);
|
||||
migrate(backend);
|
||||
QContext.init(instance, new QSystemUserSession(instance));
|
||||
return (instance);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Apply or reapply the shipped changelog through Liquibase.
|
||||
*******************************************************************************/
|
||||
public static void migrate(RDBMSBackendMetaData backend) throws Exception
|
||||
{
|
||||
try(Connection connection = DriverManager.getConnection(backend.getJdbcUrl(), backend.getUsername(), backend.getPassword());
|
||||
Liquibase liquibase = new Liquibase(DbPerfMetaData.CHANGELOG, new ClassLoaderResourceAccessor(), DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection))))
|
||||
{
|
||||
liquibase.getDatabase().setDatabaseChangeLogTableName("databasechangelog");
|
||||
liquibase.getDatabase().setDatabaseChangeLogLockTableName("databasechangeloglock");
|
||||
liquibase.update("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DeltaCalculator;
|
||||
import com.kingsrook.qbits.dbperformance.provider.StatementStats;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Counter baseline, reset and monotonic interval arithmetic.
|
||||
*******************************************************************************/
|
||||
class DeltaCalculatorTest
|
||||
{
|
||||
private static final Instant CAPTURE = Instant.parse("2026-10-01T12:00:00Z");
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Old unknown entries baseline; new entries inside the interval count.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void baselines()
|
||||
{
|
||||
assertThat(DeltaCalculator.calculate(null, entry(100, null), CAPTURE, null).baseline()).isTrue();
|
||||
assertThat(DeltaCalculator.calculate(null, entry(100, CAPTURE.minusSeconds(1)), CAPTURE, null).calls()).isZero();
|
||||
assertThat(DeltaCalculator.calculate(null, entry(100, CAPTURE.plusSeconds(1)), CAPTURE, null).calls()).isEqualTo(100);
|
||||
assertThat(DeltaCalculator.calculate(null, entry(100, CAPTURE.plusSeconds(1)), null, null).calls()).isZero();
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Decreased calls, changed entry epoch and global resets all count from zero.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void resetsAndDeltas()
|
||||
{
|
||||
StatementStats before = entry(10, CAPTURE.minusSeconds(60));
|
||||
DeltaCalculator.Delta delta = DeltaCalculator.calculate(before, entry(15, before.statsSince()), CAPTURE, null);
|
||||
assertThat(delta.calls()).isEqualTo(5);
|
||||
assertThat(delta.totalMs()).isEqualByComparingTo("50");
|
||||
assertThat(delta.rows()).isEqualTo(5);
|
||||
assertThat(delta.reset()).isFalse();
|
||||
assertThat(DeltaCalculator.calculate(before, entry(3, before.statsSince()), CAPTURE, null).reset()).isTrue();
|
||||
assertThat(DeltaCalculator.calculate(before, entry(15, CAPTURE), CAPTURE, null).calls()).isEqualTo(15);
|
||||
assertThat(DeltaCalculator.calculate(before, entry(15, before.statsSince()), CAPTURE, CAPTURE.plusSeconds(1)).reset()).isTrue();
|
||||
assertThat(DeltaCalculator.counter(null, 100, false)).isZero();
|
||||
assertThat(DeltaCalculator.counter(10L, 3, false)).isEqualTo(3);
|
||||
assertThat(DeltaCalculator.counter(10L, 30, true)).isEqualTo(30);
|
||||
assertThat(DeltaCalculator.counter(10L, 30, false)).isEqualTo(20);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A coherent counter sample.
|
||||
*******************************************************************************/
|
||||
private static StatementStats entry(long calls, Instant since)
|
||||
{
|
||||
return (new StatementStats(1, "app", true, "select $1", calls, BigDecimal.valueOf(calls * 10), BigDecimal.TEN, BigDecimal.TEN, calls, calls, calls, calls, since, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
import com.kingsrook.qbits.dbperformance.provider.DbStatsProviders;
|
||||
import com.kingsrook.qbits.dbperformance.provider.PostgresStatsProvider;
|
||||
import com.kingsrook.qbits.dbperformance.provider.ProviderCapabilities;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Live PostgreSQL view contracts; skipped only when Docker is unavailable.
|
||||
*******************************************************************************/
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class PostgresStatsProviderIT
|
||||
{
|
||||
@Container
|
||||
private static final PostgreSQLContainer<?> POSTGRES = DbPerfPostgresSupport.container();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Read every provider surface against pg_stat_statements on PostgreSQL 17.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void readAllStatistics() throws Exception
|
||||
{
|
||||
DbPerfPostgresSupport.backend(POSTGRES);
|
||||
try(Connection connection = DbPerfPostgresSupport.connect(POSTGRES); Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("CREATE TABLE provider_sample (id int PRIMARY KEY, value text)");
|
||||
statement.execute("INSERT INTO provider_sample VALUES (1, 'private value')");
|
||||
statement.execute("SELECT * FROM provider_sample WHERE id=1");
|
||||
PostgresStatsProvider provider = (PostgresStatsProvider) DbStatsProviders.forConnection(connection);
|
||||
ProviderCapabilities capabilities = provider.capabilities(connection);
|
||||
assertThat(provider.getName()).isEqualTo("POSTGRES");
|
||||
assertThat(capabilities.supported()).isTrue();
|
||||
assertThat(capabilities.version()).isGreaterThanOrEqualTo(170000);
|
||||
assertThat(capabilities.statsAccess()).isEqualTo("FULL");
|
||||
assertThat(provider.readDatabase(connection).sizeBytes()).isPositive();
|
||||
assertThat(provider.readStatements(connection)).anySatisfy(row ->
|
||||
{
|
||||
assertThat(row.queryText()).contains("SELECT * FROM provider_sample");
|
||||
assertThat(row.toplevel()).isTrue();
|
||||
assertThat(row.statsSince()).isNotNull();
|
||||
});
|
||||
assertThat(provider.readStatementResetInfo(connection).statsReset()).isNotNull();
|
||||
assertThat(provider.readTables(connection)).anySatisfy(row -> assertThat(row.tableName()).isEqualTo("provider_sample"));
|
||||
assertThat(provider.readIndexes(connection)).anySatisfy(row -> assertThat(row.indexName()).isEqualTo("provider_sample_pkey"));
|
||||
try(Connection other = DbPerfPostgresSupport.connect(POSTGRES))
|
||||
{
|
||||
assertThat(provider.readSessions(connection)).isNotEmpty();
|
||||
}
|
||||
assertThat(provider.readConnections(connection)).isNotEmpty();
|
||||
assertThat(provider.readSettings(connection)).containsKeys("server_version_num", "max_connections", "block_size");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfStore;
|
||||
import com.kingsrook.qbits.dbperformance.collect.RetentionService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.RollupService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.SnapshotCollector;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Daily replacement and keyset retention integration using real QQQ/H2 writes.
|
||||
*******************************************************************************/
|
||||
class RollupRetentionTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Weighted query aggregates, last table gauges and null-safe attribution keys.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void rollupIsIdempotent() throws Exception
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(DbPerfTestSupport.instance());
|
||||
DbPerfFixtureProvider provider = new DbPerfFixtureProvider();
|
||||
snapshot(runtime, provider, 0);
|
||||
provider.calls = 15;
|
||||
snapshot(runtime, provider, 300);
|
||||
provider.calls = 25;
|
||||
snapshot(runtime, provider, 600);
|
||||
QRecord attribution = new QRecord().withValue("windowStart", DbPerfFixtureProvider.START).withValue("windowEnd", DbPerfFixtureProvider.START.plusSeconds(60))
|
||||
.withValue("podName", "one").withValue("unitKind", "PROCESS").withValue("unitName", "example").withValue("operation", "SELECT").withValue("fingerprint", "a".repeat(64))
|
||||
.withValue("sqlText", "select ?").withValue("units", 2L).withValue("statements", 5L).withValue("totalMs", BigDecimal.TEN).withValue("maxMs", BigDecimal.valueOf(4)).withValue("rowsCount", 3L).withValue("errors", 1L);
|
||||
QRecord second = new QRecord(attribution).withValue("podName", "two");
|
||||
DbPerfStore.insert("db_perf_attribution", List.of(attribution, second));
|
||||
RollupService rollup = new RollupService(runtime);
|
||||
LocalDate day = LocalDate.of(2026, 10, 1);
|
||||
assertThat(rollup.needsBackfill(day)).isTrue();
|
||||
rollup.rollupDay(day);
|
||||
assertThat(rollup.needsBackfill(day)).isFalse();
|
||||
rollup.rollupDay(day);
|
||||
List<QRecord> daily = DbPerfStore.query("db_perf_query_daily", new QQueryFilter());
|
||||
assertThat(daily).hasSize(2);
|
||||
QRecord first = daily.stream().filter(row -> row.getValueBigDecimal("totalMs").compareTo(BigDecimal.valueOf(150)) == 0).findFirst().orElseThrow();
|
||||
assertThat(first.getValueLong("calls")).isEqualTo(15);
|
||||
assertThat(first.getValueBigDecimal("meanMs")).isEqualByComparingTo("10");
|
||||
assertThat(first.getValueBigDecimal("hitRatio")).isEqualByComparingTo("90");
|
||||
assertThat(first.getValueBigDecimal("shareOfDbTime")).isEqualByComparingTo("66.6667");
|
||||
QRecord table = DbPerfStore.query("db_perf_table_daily", new QQueryFilter()).getFirst();
|
||||
assertThat(table.getValueLong("seqScan")).isEqualTo(15);
|
||||
assertThat(table.getValueLong("totalBytes")).isEqualTo(2500);
|
||||
List<QRecord> attributed = DbPerfStore.query("db_perf_attribution_daily", new QQueryFilter());
|
||||
assertThat(attributed).hasSize(1);
|
||||
assertThat(attributed.getFirst().getValueLong("units")).isEqualTo(4);
|
||||
assertThat(attributed.getFirst().getValueLong("statements")).isEqualTo(10);
|
||||
assertThat(attributed.getFirst().getValueBigDecimal("totalMs")).isEqualByComparingTo("20");
|
||||
assertThat(attributed.getFirst().getValueString("rollupKey")).hasSize(64);
|
||||
rollup.rollupDay(day.plusDays(1));
|
||||
assertThat(DbPerfStore.query("db_perf_query_daily", new QQueryFilter())).hasSize(2);
|
||||
QRecord nextWeek = new SnapshotCollector(runtime, Clock.fixed(DbPerfFixtureProvider.START.plusSeconds(7 * 86400L), ZoneOffset.UTC), provider)
|
||||
.collect(runtime.getConfig().getBackendName(), true);
|
||||
assertThat(nextWeek.getValueString("status")).isEqualTo("OK");
|
||||
assertThat(DbPerfStore.query("db_perf_table_stat", new QQueryFilter()).getFirst().getValueLong("growthBytes7d")).isEqualTo(1500);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** More than one deletion batch, strict cutoff boundaries and open findings.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void retentionBatchesAndClosedFindings() throws Exception
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(DbPerfTestSupport.instance());
|
||||
String backend = runtime.getConfig().getBackendName();
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(backend);
|
||||
settings.withValue("detailRetentionDays", 2).withValue("rollupRetentionDays", 5);
|
||||
DbPerfStore.update("db_perf_settings", List.of(settings));
|
||||
Instant now = DbPerfFixtureProvider.START;
|
||||
List<QRecord> rows = new ArrayList<>();
|
||||
for(int i = 0; i < 1005; i++)
|
||||
{
|
||||
rows.add(new QRecord().withValue("windowStart", now.minusSeconds(3 * 86400)).withValue("unitName", "old" + i));
|
||||
}
|
||||
rows.add(new QRecord().withValue("windowStart", now.minusSeconds(2 * 86400)).withValue("unitName", "boundary"));
|
||||
DbPerfStore.insert("db_perf_attribution", rows);
|
||||
DbPerfStore.insert("db_perf_attribution_daily", List.of(new QRecord().withValue("day", LocalDate.of(2026, 9, 20)).withValue("rollupKey", "old")));
|
||||
int rule = DbPerfStore.query("db_perf_rule", new QQueryFilter()).getFirst().getValueInteger("id");
|
||||
List<QRecord> findings = new ArrayList<>();
|
||||
for(String status : List.of("OPEN", "ACKNOWLEDGED", "RESOLVED", "IGNORED"))
|
||||
{
|
||||
findings.add(new QRecord().withValue("backendName", backend).withValue("ruleId", rule).withValue("dedupeKey", status).withValue("status", status).withValue("lastSeenAt", now.minusSeconds(6 * 86400)));
|
||||
}
|
||||
DbPerfStore.insert("db_perf_finding", findings);
|
||||
RetentionService retention = new RetentionService(runtime, Clock.fixed(now, ZoneOffset.UTC));
|
||||
assertThat(retention.purge()).isEqualTo(1008);
|
||||
assertThat(retention.purge()).isZero();
|
||||
assertThat(DbPerfStore.query("db_perf_attribution", new QQueryFilter())).singleElement().satisfies(row -> assertThat(row.getValueString("unitName")).isEqualTo("boundary"));
|
||||
assertThat(DbPerfStore.query("db_perf_finding", new QQueryFilter())).extracting(row -> row.getValueString("status")).containsExactlyInAnyOrder("OPEN", "ACKNOWLEDGED");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Timestamped forced table capture.
|
||||
*******************************************************************************/
|
||||
private static void snapshot(DbPerfRuntime runtime, DbPerfFixtureProvider provider, int seconds)
|
||||
{
|
||||
QRecord result = new SnapshotCollector(runtime, Clock.fixed(DbPerfFixtureProvider.START.plusSeconds(seconds), ZoneOffset.UTC), provider).collect(runtime.getConfig().getBackendName(), true);
|
||||
assertThat(result.getValueString("status")).as(result.getValueString("errorMessage")).isEqualTo("OK");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfStore;
|
||||
import com.kingsrook.qbits.dbperformance.collect.SnapshotCollector;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.module.postgres.model.metadata.PostgreSQLBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Live PostgreSQL collection, reset detection and reduced-role access.
|
||||
*******************************************************************************/
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class SnapshotCollectorPostgresTest
|
||||
{
|
||||
@Container
|
||||
private static final PostgreSQLContainer<?> POSTGRES = DbPerfPostgresSupport.container();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Two real passes retain deltas; reset is explicit and a limited role is PARTIAL.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void snapshotsAndReducedAccess() throws Exception
|
||||
{
|
||||
RDBMSBackendMetaData backend = DbPerfPostgresSupport.backend(POSTGRES);
|
||||
QInstance instance = DbPerfTestSupport.instance(backend);
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(instance);
|
||||
SnapshotCollector collector = new SnapshotCollector(runtime);
|
||||
assertThat(collector.collect(backend.getName(), true).getValueString("status")).isEqualTo("OK");
|
||||
try(Connection connection = DbPerfPostgresSupport.connect(POSTGRES); Statement statement = connection.createStatement())
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
statement.execute("SELECT sum(id) FROM db_perf_rule WHERE id > 0");
|
||||
}
|
||||
statement.execute("SELECT pg_stat_force_next_flush()");
|
||||
}
|
||||
QRecord second = collector.collect(backend.getName(), true);
|
||||
assertThat(second.getValueString("status")).as(second.getValueString("errorMessage")).isEqualTo("OK");
|
||||
assertThat(second.getValueLong("statementCalls")).isPositive();
|
||||
assertThat(DbPerfStore.query("db_perf_query_snapshot", new QQueryFilter())).isNotEmpty();
|
||||
assertThat(DbPerfStore.query("db_perf_table_stat", new QQueryFilter())).isNotEmpty();
|
||||
assertThat(DbPerfStore.query("db_perf_index_stat", new QQueryFilter())).isNotEmpty();
|
||||
try(Connection connection = DbPerfPostgresSupport.connect(POSTGRES); Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("SELECT pg_stat_statements_reset()");
|
||||
}
|
||||
assertThat(collector.collect(backend.getName(), true).getValueBoolean("statsResetDetected")).isTrue();
|
||||
try(Connection connection = DbPerfPostgresSupport.connect(POSTGRES); Statement statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("CREATE ROLE dbperf_limited LOGIN PASSWORD 'test-only'");
|
||||
statement.execute("GRANT USAGE ON SCHEMA public TO dbperf_limited");
|
||||
statement.execute("GRANT ALL ON ALL TABLES IN SCHEMA public TO dbperf_limited");
|
||||
statement.execute("GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO dbperf_limited");
|
||||
}
|
||||
RDBMSBackendMetaData limited = new PostgreSQLBackendMetaData().withName("limited" + UUID.randomUUID().toString().replace("-", ""))
|
||||
.withVendor("postgresql").withHostName(POSTGRES.getHost()).withDatabaseName(POSTGRES.getDatabaseName()).withUsername("dbperf_limited").withPassword("test-only");
|
||||
limited.setJdbcUrl(POSTGRES.getJdbcUrl());
|
||||
QInstance limitedInstance = DbPerfTestSupport.instance(limited);
|
||||
DbPerfRuntime limitedRuntime = DbPerfRuntime.of(limitedInstance);
|
||||
QRecord partial = new SnapshotCollector(limitedRuntime).collect(limited.getName(), true);
|
||||
assertThat(partial.getValueString("status")).as(partial.getValueString("errorMessage")).isEqualTo("PARTIAL");
|
||||
assertThat(new DbPerfSettingsService(limitedRuntime).ensure(limited.getName()).getValueString("statsAccessMessage")).contains("pg_read_all_stats");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfSettingsService;
|
||||
import com.kingsrook.qbits.dbperformance.collect.DbPerfStore;
|
||||
import com.kingsrook.qbits.dbperformance.collect.SnapshotCollector;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Real H2 QQQ actions with deterministic vendor samples: no mocked persistence.
|
||||
*******************************************************************************/
|
||||
class SnapshotCollectorTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Baseline, delta top-N, reset, tables, normalized activity and partial mode.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void collectionLifecycle() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(instance);
|
||||
String backend = runtime.getConfig().getBackendName();
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(backend);
|
||||
settings.setValue("topStatementsPerSnapshot", 1);
|
||||
DbPerfStore.update("db_perf_settings", List.of(settings));
|
||||
assertThat(new DbPerfSettingsService(runtime).ensure(backend).getValueInteger("topStatementsPerSnapshot")).isEqualTo(1);
|
||||
DbPerfFixtureProvider provider = new DbPerfFixtureProvider();
|
||||
QRecord baseline = collect(runtime, provider, 0, true);
|
||||
assertThat(baseline.getValueString("status")).as(baseline.getValueString("errorMessage")).isEqualTo("OK");
|
||||
assertThat(baseline.getValueLong("statementCalls")).isZero();
|
||||
provider.calls = 15;
|
||||
QRecord delta = collect(runtime, provider, 300, false);
|
||||
assertThat(delta.getValueString("status")).as(delta.getValueString("errorMessage")).isEqualTo("OK");
|
||||
assertThat(delta.getValueLong("statementCalls")).isEqualTo(10);
|
||||
assertThat(delta.getValueBigDecimal("statementTotalMs")).isEqualByComparingTo("75");
|
||||
assertThat(delta.getValueInteger("storedStatementCount")).isEqualTo(1);
|
||||
assertThat(delta.getValueBoolean("includesTables")).isFalse();
|
||||
assertThat(delta.getValueLong("xactCommit")).isEqualTo(5);
|
||||
assertThat(delta.getValueBigDecimal("cacheHitRatio")).isEqualByComparingTo("90");
|
||||
assertThat(delta.getValueInteger("connectionsTotal")).isEqualTo(6);
|
||||
QRecord detail = DbPerfStore.query("db_perf_query_snapshot", new QQueryFilter()).getFirst();
|
||||
assertThat(detail.getValueBigDecimal("shareOfDbTime")).isEqualByComparingTo("66.6667");
|
||||
QRecord statement = DbPerfStore.first("db_perf_statement", DbPerfStore.eq("queryId", 1L));
|
||||
assertThat(statement.getValueLong("calls24h")).isEqualTo(5);
|
||||
assertThat(statement.getValueBigDecimal("totalMs24h")).isEqualByComparingTo("50");
|
||||
assertThat(statement.getValueBigDecimal("meanMs24h")).isEqualByComparingTo("10");
|
||||
assertThat(statement.getValueBigDecimal("shareOfDbTime24h")).isEqualByComparingTo("66.6667");
|
||||
assertThat(DbPerfStore.query("db_perf_activity_sample", new QQueryFilter())).hasSize(6).allSatisfy(row -> assertThat(row.getValueString("queryTextNormalized")).doesNotContain("private", "999"));
|
||||
assertThat(DbPerfStore.query("db_perf_table_stat", new QQueryFilter())).hasSize(1);
|
||||
assertThat(DbPerfStore.query("db_perf_index_stat", new QQueryFilter())).hasSize(1);
|
||||
provider.calls = 2;
|
||||
provider.reset = DbPerfFixtureProvider.START.plusSeconds(400);
|
||||
provider.limited = true;
|
||||
QRecord reset = collect(runtime, provider, 600, true);
|
||||
assertThat(reset.getValueString("status")).isEqualTo("PARTIAL");
|
||||
assertThat(reset.getValueBoolean("statsResetDetected")).isTrue();
|
||||
assertThat(reset.getValueLong("statementCalls")).isEqualTo(4);
|
||||
assertThat(new DbPerfSettingsService(runtime).ensure(backend).getValueString("statsAccess")).isEqualTo("NO_READ_ALL_STATS");
|
||||
assertThat(DbPerfStore.query("db_perf_table_snapshot", new QQueryFilter())).hasSize(2);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Failed writes do not advance counters, and the caller session is restored.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void failureAndDisabled() throws Exception
|
||||
{
|
||||
QInstance instance = DbPerfTestSupport.instance();
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(instance);
|
||||
DbPerfFixtureProvider provider = new DbPerfFixtureProvider();
|
||||
assertThat(collect(runtime, provider, 0, true).getValueString("status")).isEqualTo("OK");
|
||||
QSession caller = new QSession().withPermissions(DbPerfPermissions.VIEW);
|
||||
QContext.setQSession(caller);
|
||||
provider.calls = 20;
|
||||
provider.fail = true;
|
||||
QRecord failed = collect(runtime, provider, 300, true);
|
||||
assertThat(failed.getValueString("status")).isEqualTo("FAILED");
|
||||
assertThat(failed.getValueString("errorMessage")).doesNotContain("patient", "private");
|
||||
assertThat(QContext.getQSession()).isSameAs(caller);
|
||||
runtime.asSystem(() ->
|
||||
{
|
||||
assertThat(DbPerfStore.query("db_perf_statement", new QQueryFilter())).allSatisfy(row -> assertThat(row.getValueLong("lastCalls")).isEqualTo(10));
|
||||
assertThat(DbPerfStore.query("db_perf_query_snapshot", new QQueryFilter())).isEmpty();
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
settings.setValue("enabled", false);
|
||||
DbPerfStore.update("db_perf_settings", List.of(settings));
|
||||
return (null);
|
||||
});
|
||||
assertThat(collect(runtime, provider, 600, true).getValueString("status")).isEqualTo("PARTIAL");
|
||||
assertThat(new SnapshotCollector(runtime).collect("wrong-backend", false).getValueString("status")).isEqualTo("FAILED");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** H2 remains usable and explicitly unsupported by the production selector.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void unsupportedVendor() throws Exception
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(DbPerfTestSupport.instance());
|
||||
QRecord snapshot = new SnapshotCollector(runtime).collect(runtime.getConfig().getBackendName(), false);
|
||||
assertThat(snapshot.getValueString("status")).isEqualTo("PARTIAL");
|
||||
assertThat(snapshot.getValueString("errorMessage")).contains("not supported");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** A concurrent administrator edit is not overwritten by runtime status writes.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void collectionPreservesConcurrentSettingsEdit() throws Exception
|
||||
{
|
||||
DbPerfRuntime runtime = DbPerfRuntime.of(DbPerfTestSupport.instance());
|
||||
QRecord settings = new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName());
|
||||
DbPerfFixtureProvider provider = new DbPerfFixtureProvider()
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Simulate an independently committed edit after settings were read.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public java.util.List<com.kingsrook.qbits.dbperformance.provider.ConnectionCount> readConnections(java.sql.Connection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
runtime.asSystem(() ->
|
||||
{
|
||||
DbPerfStore.update("db_perf_settings", List.of(new QRecord().withValue("id", settings.getValueInteger("id")).withValue("topStatementsPerSnapshot", 17)));
|
||||
return (null);
|
||||
});
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new IllegalStateException(e));
|
||||
}
|
||||
return (super.readConnections(connection));
|
||||
}
|
||||
};
|
||||
assertThat(collect(runtime, provider, 0, true).getValueString("status")).isEqualTo("OK");
|
||||
assertThat(new DbPerfSettingsService(runtime).ensure(runtime.getConfig().getBackendName()).getValueInteger("topStatementsPerSnapshot")).isEqualTo(17);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** One timestamped pass.
|
||||
*******************************************************************************/
|
||||
private static QRecord collect(DbPerfRuntime runtime, DbPerfFixtureProvider provider, int seconds, boolean tables)
|
||||
{
|
||||
return (new SnapshotCollector(runtime, Clock.fixed(DbPerfFixtureProvider.START.plusSeconds(seconds), ZoneOffset.UTC), provider).collect(runtime.getConfig().getBackendName(), tables));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qbits.dbperformance;
|
||||
|
||||
|
||||
import com.kingsrook.qbits.dbperformance.normalize.SqlNormalizer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Privacy and stable-shape regressions for literal normalization.
|
||||
*******************************************************************************/
|
||||
class SqlNormalizerTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Different bind syntaxes and literals must have the same fingerprint.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void equivalentParameters()
|
||||
{
|
||||
String qqq = SqlNormalizer.normalize("SELECT * FROM account WHERE id=? AND name=?");
|
||||
assertThat(SqlNormalizer.normalize("select * from account where id=$1 and name=$2")).isEqualTo(qqq);
|
||||
assertThat(SqlNormalizer.fingerprint(SqlNormalizer.normalize("SELECT * FROM account WHERE id=123 AND name='secret'")))
|
||||
.isEqualTo(SqlNormalizer.fingerprint(qqq)).hasSize(64);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Comments, escaped and dollar-quoted strings never leave literal content.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void literalsAndComments()
|
||||
{
|
||||
for(String literal : new String[] {"'private''name'", "E'private\\'name'", "$$private name$$", "$body$private 'name'$body$", "'unterminated private", "$x$unterminated private"})
|
||||
{
|
||||
assertThat(SqlNormalizer.normalize("select " + literal)).isEqualTo("select ?");
|
||||
}
|
||||
assertThat(SqlNormalizer.normalize("SELECT /* outer /* nested */ done */ col12 -- private\nFROM t WHERE n=-1.2e+3"))
|
||||
.isEqualTo("select col12 from t where n = - ?");
|
||||
assertThat(SqlNormalizer.normalize("select '/* literal */', 0xff, .5, 2e-4")).isEqualTo("select ? , ? , ? , ?");
|
||||
assertThat(SqlNormalizer.normalize("SELECT \"My Table\".\"Field2\" FROM \"My Table\""))
|
||||
.isEqualTo("select \"My Table\" . \"Field2\" from \"My Table\"");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Repeated list shapes collapse while column arity remains meaningful.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void listShapesAndClassification()
|
||||
{
|
||||
assertThat(SqlNormalizer.normalize("select * from t where id IN (1,2,3)"))
|
||||
.isEqualTo(SqlNormalizer.normalize("select * from t where id in (?)"));
|
||||
assertThat(SqlNormalizer.normalize("insert into t values (1,'a'),(2,'b')"))
|
||||
.isEqualTo(SqlNormalizer.normalize("insert into t values (?,?)"));
|
||||
for(String operation : new String[] {"select", "insert", "update", "delete", "with"})
|
||||
{
|
||||
assertThat(SqlNormalizer.operation(operation)).isEqualTo(operation.toUpperCase(java.util.Locale.ROOT));
|
||||
}
|
||||
assertThat(SqlNormalizer.normalize("SELECT id::bigint FROM t WHERE n>=1 AND name||'secret'=$1"))
|
||||
.isEqualTo("select id :: bigint from t where n >= ? and name || ? = ?");
|
||||
assertThat(SqlNormalizer.normalize("select payload->>'secret', n<>10 from t"))
|
||||
.isEqualTo("select payload ->> ? , n <> ? from t");
|
||||
assertThat(SqlNormalizer.operation("create table t(a int)")).isEqualTo("OTHER");
|
||||
assertThat(SqlNormalizer.mainTable("select x from public.\"Account\" where x=1")).isEqualTo("Account");
|
||||
assertThat(SqlNormalizer.mainTable("update public.account set x=1")).isEqualTo("account");
|
||||
assertThat(SqlNormalizer.mainTable("select 1")).isNull();
|
||||
assertThat(SqlNormalizer.normalize("select \"in ( ? , ? )\" from t")).isEqualTo("select \"in ( ? , ? )\" from t");
|
||||
assertThat(SqlNormalizer.normalize(null)).isEmpty();
|
||||
assertThat(SqlNormalizer.normalize(" ")).isEmpty();
|
||||
assertThat(SqlNormalizer.normalize("select " + "field, ".repeat(5000))).hasSize(10000);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user