pg_reports 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -0
  3. data/README.md +123 -370
  4. data/app/controllers/pg_reports/dashboard_controller.rb +21 -21
  5. data/app/views/layouts/pg_reports/application.html.erb +135 -69
  6. data/app/views/pg_reports/dashboard/_show_modals.html.erb +22 -22
  7. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +105 -55
  8. data/app/views/pg_reports/dashboard/_show_styles.html.erb +49 -11
  9. data/app/views/pg_reports/dashboard/index.html.erb +123 -114
  10. data/app/views/pg_reports/dashboard/show.html.erb +30 -26
  11. data/config/locales/en.yml +597 -0
  12. data/config/locales/ru.yml +562 -0
  13. data/config/locales/uk.yml +607 -0
  14. data/lib/pg_reports/compatibility.rb +63 -0
  15. data/lib/pg_reports/configuration.rb +2 -0
  16. data/lib/pg_reports/dashboard/reports_registry.rb +112 -5
  17. data/lib/pg_reports/definitions/indexes/fk_without_indexes.yml +30 -0
  18. data/lib/pg_reports/definitions/indexes/index_correlation.yml +31 -0
  19. data/lib/pg_reports/definitions/indexes/inefficient_indexes.yml +45 -0
  20. data/lib/pg_reports/definitions/queries/temp_file_queries.yml +39 -0
  21. data/lib/pg_reports/definitions/schema_analysis/always_null_columns.yml +31 -0
  22. data/lib/pg_reports/definitions/schema_analysis/unused_columns.yml +32 -0
  23. data/lib/pg_reports/definitions/system/wraparound_risk.yml +31 -0
  24. data/lib/pg_reports/definitions/tables/tables_without_pk.yml +28 -0
  25. data/lib/pg_reports/definitions/tables/unused_tables.yml +30 -0
  26. data/lib/pg_reports/definitions/tables/update_hotspots.yml +32 -0
  27. data/lib/pg_reports/engine.rb +6 -0
  28. data/lib/pg_reports/module_generator.rb +2 -1
  29. data/lib/pg_reports/modules/indexes.rb +3 -0
  30. data/lib/pg_reports/modules/queries.rb +1 -0
  31. data/lib/pg_reports/modules/schema_analysis.rb +261 -2
  32. data/lib/pg_reports/modules/system.rb +27 -0
  33. data/lib/pg_reports/modules/tables.rb +1 -0
  34. data/lib/pg_reports/query_monitor.rb +64 -36
  35. data/lib/pg_reports/report_definition.rb +20 -24
  36. data/lib/pg_reports/sql/indexes/fk_without_indexes.sql +23 -0
  37. data/lib/pg_reports/sql/indexes/index_correlation.sql +27 -0
  38. data/lib/pg_reports/sql/indexes/inefficient_indexes.sql +22 -0
  39. data/lib/pg_reports/sql/queries/temp_file_queries.sql +16 -0
  40. data/lib/pg_reports/sql/schema_analysis/always_null_columns.sql +25 -0
  41. data/lib/pg_reports/sql/schema_analysis/unused_columns.sql +36 -0
  42. data/lib/pg_reports/sql/system/checkpoint_stats.sql +20 -0
  43. data/lib/pg_reports/sql/system/checkpoint_stats_legacy.sql +19 -0
  44. data/lib/pg_reports/sql/system/wraparound_risk.sql +21 -0
  45. data/lib/pg_reports/sql/tables/tables_without_pk.sql +20 -0
  46. data/lib/pg_reports/sql/tables/unused_tables.sql +19 -0
  47. data/lib/pg_reports/sql/tables/update_hotspots.sql +26 -0
  48. data/lib/pg_reports/version.rb +1 -1
  49. data/lib/pg_reports.rb +5 -0
  50. metadata +24 -1
@@ -51,6 +51,16 @@ uk:
51
51
  - "Збільшення shared_buffers допомагає, але є межа ефективності (зазвичай 25% RAM)."
52
52
  - "Covering indexes (INCLUDE) дозволяють виконувати index-only scan без звернення до таблиці."
53
53
 
54
+ temp_file_queries:
55
+ title: "Temp File Queries"
56
+ what: "Запити, що скидають проміжні результати у тимчасові файли на диск."
57
+ how: "Аналізує temp_blks_written та temp_blks_read з pg_stat_statements. Тимчасові файли створюються, коли work_mem недостатній для сортування, хешування або матеріалізації."
58
+ nuances:
59
+ - "Запис у тимчасові файли на порядки повільніший за операції в пам'яті."
60
+ - "Збільшення work_mem може усунути тимчасові файли, але впливає на всі сесії — використовуйте SET LOCAL для конкретних запитів."
61
+ - "Часті причини: великі сортування (ORDER BY), hash join на великих таблицях, DISTINCT на багатьох рядках, складні CTE."
62
+ - "Розгляньте додавання індексів для усунення сортувань або переписування запитів для зменшення проміжних результатів."
63
+
54
64
  all_queries:
55
65
  title: "All Queries"
56
66
  what: "Повна статистика по всіх запитах з pg_stat_statements."
@@ -107,6 +117,17 @@ uk:
107
117
  - "idx_tup_read — рядки прочитані з індексу."
108
118
  - "idx_tup_fetch — рядки отримані з таблиці після index scan (для non-covering indexes)."
109
119
 
120
+ inefficient_indexes:
121
+ title: "Inefficient Indexes"
122
+ what: "Індекси, які використовуються, але сканують значно більше записів, ніж реально витягують."
123
+ how: "Порівнює idx_tup_read (записів прочитано з індексу) та idx_tup_fetch (рядків витягнуто з heap) з pg_stat_user_indexes. Високе співвідношення означає, що порядок колонок індексу не відповідає предикатам запиту, змушуючи PostgreSQL сканувати великі діапазони індексу."
124
+ nuances:
125
+ - "Співвідношення read/fetch >10 означає, що індекс читає в 10+ разів більше записів, ніж повертає — явна ознака неправильного порядку колонок."
126
+ - "Для складених індексів (a, b, c) запити, що фільтрують по (b) або (c) без (a), не можуть виконати точний seek і змушені сканувати широкі діапазони."
127
+ - "Рішення: створити цільовий індекс з колонками, що відповідають найбільш селективним WHERE-предикатам у першу чергу."
128
+ - "Використовуйте EXPLAIN ANALYZE для підтвердження, що index scan читає надлишкову кількість записів, перш ніж вносити зміни."
129
+ - "Index-only scans (де idx_tup_fetch = 0) виключені — вони вказують на ефективно працюючі covering indexes."
130
+
110
131
  bloated_indexes:
111
132
  title: "Bloated Indexes"
112
133
  what: "Індекси з високим рівнем bloat (роздування) через мертві кортежі."
@@ -117,6 +138,27 @@ uk:
117
138
  - "pg_repack дозволяє перебудувати індекси без блокувань."
118
139
  - "Bloat >30-50% — привід для дій."
119
140
 
141
+ fk_without_indexes:
142
+ title: "FK Without Indexes"
143
+ what: "Зовнішні ключі на дочірніх таблицях без підтримуючого індексу."
144
+ how: "Порівнює pg_constraint (зовнішні ключі) з pg_index для пошуку FK-колонок без відповідного ведучого індексу."
145
+ nuances:
146
+ - "Без індексу DELETE або UPDATE батьківської таблиці викликає sequential scan дочірньої таблиці."
147
+ - "На великих дочірніх таблицях це може спричинити серйозну конкуренцію блокувань та сповільнити каскадні операції."
148
+ - "Індекс повинен мати FK-колонку як ведучу (першу) для ефективності."
149
+ - "Маленькі таблиці (< 10K рядків) можуть не виграти від додавання індексу — PostgreSQL віддасть перевагу seq scan."
150
+
151
+ index_correlation:
152
+ title: "Index Correlation"
153
+ what: "Індекси, де фізичний порядок рядків погано відповідає порядку індексу."
154
+ how: "Читає pg_stats.correlation для ведучих колонок індексів. Кореляція близько 0 означає випадковий фізичний порядок відносно індексу, що спричиняє надлишковий random I/O при range scan."
155
+ nuances:
156
+ - "Кореляція від -1 до 1. Значення близько 0 — випадковий порядок; близько 1 або -1 — впорядкований."
157
+ - "Низька кореляція переважно впливає на range scan (BETWEEN, >, <) та ORDER BY — точкові пошуки зачіпаються менше."
158
+ - "CLUSTER table USING index фізично переупорядковує рядки, але блокує таблицю та не підтримується автоматично."
159
+ - "Для append-only таблиць з timestamp колонками кореляція природно висока — дій не потрібно."
160
+ - "Показані лише таблиці > 10MB з > 100 index scan для зниження шуму."
161
+
120
162
  index_sizes:
121
163
  title: "Index Sizes"
122
164
  what: "Розміри індексів на диску."
@@ -183,6 +225,16 @@ uk:
183
225
  - "Високий seq_tup_read/seq_scan = багато рядків за один scan = можливо норма."
184
226
  - "Низький seq_tup_read/seq_scan = багато scan маленьких об'ємів = можливо N+1."
185
227
 
228
+ tables_without_pk:
229
+ title: "Tables Without Primary Keys"
230
+ what: "Таблиці без первинного ключа."
231
+ how: "Перевіряє pg_index на відсутність indisprimary записів для користувацьких таблиць."
232
+ nuances:
233
+ - "Логічна реплікація потребує первинний ключ або REPLICA IDENTITY для ідентифікації рядків."
234
+ - "Без PK операції UPDATE та DELETE потребують іншого способу унікальної ідентифікації рядків, часто призводячи до повних сканів таблиці."
235
+ - "Join-таблиці (many-to-many) часто навмисно без PK — розгляньте додавання складеного PK."
236
+ - "Деякі ORM (наприклад, Rails) передбачають наявність 'id' первинного ключа — таблиці без нього можуть спричинити помилки фреймворку."
237
+
186
238
  recently_modified:
187
239
  title: "Recently Modified"
188
240
  what: "Таблиці з недавньою активністю INSERT/UPDATE/DELETE."
@@ -192,6 +244,29 @@ uk:
192
244
  - "Висока активність UPDATE створює dead tuples — слідкуйте за vacuum."
193
245
  - "HOT updates (Heap Only Tuple) ефективніші за звичайні — індекси не оновлюються."
194
246
 
247
+ update_hotspots:
248
+ title: "Гарячі точки UPDATE"
249
+ what: "Таблиці, де ті самі рядки оновлюються багаторазово, або де індексовані колонки отримують hot-update."
250
+ how: "Рахує два співвідношення з pg_stat_user_tables: updates_per_row = n_tup_upd / n_live_tup (амплифікація запису на рядок) та hot_update_pct = n_tup_hot_upd / n_tup_upd (низьке значення = індексована колонка потрапляє в SET, що ламає HOT)."
251
+ nuances:
252
+ - "updates_per_row >> 1 означає, що окремі рядки переписуються багаторазово — типово для лічильників, статусів, last_seen_at, позицій."
253
+ - "Патерни рефакторингу для гарячих рядків: винести мінливу колонку в сусідню 1:1 'state'-таблицю; перейти на event/log-таблицю з періодичними згортками; буферизувати записи у фоні; debounce на стороні застосунку."
254
+ - "Низький hot_update_pct (наприклад, <50%) = оновлюється індексована колонка. Або видаліть індекс, або винесіть колонку в менш індексовану таблицю."
255
+ - "PostgreSQL не рахує UPDATE по колонках. Щоб знайти винну колонку, парсіть pg_stat_statements: імена колонок у SET зберігаються (нормалізуються лише літерали)."
256
+ - "Гарячі рядки + fillfactor за замовчуванням (100) вбивають HOT. ALTER TABLE ... SET (fillfactor=80) залишає місце для in-place UPDATE."
257
+ - "Високий n_dead_tup корелює з гарячими рядками — autovacuum має встигати, інакше bloat зростатиме швидко."
258
+
259
+ unused_tables:
260
+ title: "Невикористовувані таблиці"
261
+ what: "Таблиці з нулем seq_scan та нулем idx_scan з моменту останнього скидання статистики — код їх не читає."
262
+ how: "Фільтрує pg_stat_user_tables за seq_scan = 0 AND idx_scan = 0. Показує db_stats_since (pg_stat_database.stats_reset), щоб було зрозуміло, на якому вікні зроблено висновок."
263
+ nuances:
264
+ - "Переконайтеся, що db_stats_since покриває репрезентативний період — мінімум тиждень, краще повний звітний/біллінговий цикл. Нещодавній stats_reset, рестарт або оновлення PG обнуляють висновки."
265
+ - "Таблиці, які читаються лише репліками, НЕ рахуються на праймарі. Перевірте репліки окремо перед видаленням."
266
+ - "Таблиці, до яких звертаються лише COPY, pg_dump або logical replication, можуть виглядати unused — ці операції не інкрементять лічильники сканів."
267
+ - "FK на цю таблицю можуть тримати її 'у роботі' через каскади без прямих запитів."
268
+ - "Безпечна послідовність: перейменувати → почекати повний цикл → видалити. Або перемістити в окрему archive-схему."
269
+
195
270
  # === CONNECTIONS ===
196
271
  active_connections:
197
272
  title: "Active Connections"
@@ -249,6 +324,46 @@ uk:
249
324
  - "Занадто багато idle = застосунок не закриває з'єднання або пул занадто великий."
250
325
  - "idle_session_timeout (PostgreSQL 14+) може автоматично закривати idle з'єднання."
251
326
 
327
+ pool_usage:
328
+ title: "Використання пулу з'єднань"
329
+ what: "Поточне використання пулу з'єднань з показниками активних, idle та доступних з'єднань по базах даних."
330
+ how: "Аналіз станів з'єднань в pg_stat_activity та порівняння з лімітом max_connections."
331
+ nuances:
332
+ - "Утилізація вище 70% означає наближення до ліміту — розгляньте масштабування."
333
+ - "Idle in transaction з'єднання витрачають ресурси та блокують VACUUM."
334
+ - "max_connections — глобальне налаштування для всієї БД, а не для кожної бази окремо."
335
+ - "Connection pooler'и (PgBouncer/pgpool) дозволяють мати більше з'єднань на рівні застосунку."
336
+
337
+ pool_wait_times:
338
+ title: "Аналіз часу очікування"
339
+ what: "Запити, що очікують ресурсів (блокування, I/O, мережа)."
340
+ how: "Аналіз wait_event та wait_event_type з pg_stat_activity для не-idle з'єднань."
341
+ nuances:
342
+ - "ClientRead очікування = повільний клієнт не встигає споживати дані."
343
+ - "Lock очікування = конкуренція між конкурентними запитами."
344
+ - "IO очікування = проблеми продуктивності диска або недостатній кеш."
345
+ - "Очікування понад 60 секунд критичні та потребують негайного розслідування."
346
+
347
+ pool_saturation:
348
+ title: "Попередження про насичення пулу"
349
+ what: "Загальні метрики здоров'я пулу з'єднань з попередженнями про насичення та рекомендаціями."
350
+ how: "Розрахунок відсотків утилізації для total, active, idle та проблемних з'єднань."
351
+ nuances:
352
+ - "Постійна утилізація вище 70% = необхідність налаштування пулу або масштабування."
353
+ - "Висока кількість idle in transaction = проблеми обробки транзакцій застосунком."
354
+ - "superuser_reserved_connections зменшують доступну ємність пулу."
355
+ - "Відстежуйте тренди — різкі стрибки можуть вказувати на витоки з'єднань."
356
+
357
+ connection_churn:
358
+ title: "Аналіз оборотності з'єднань"
359
+ what: "Аналіз патернів життєвого циклу з'єднань для виявлення надлишкового churn (часте підключення/відключення)."
360
+ how: "Вивчення віку з'єднань для ідентифікації короткоживучих з'єднань та розрахунку churn rate по застосунках."
361
+ nuances:
362
+ - "З'єднання молодші 10 секунд = короткоживучі."
363
+ - "Churn rate вище 50% = відсутність або неправильне налаштування connection pooling."
364
+ - "Багато коротких з'єднань = збільшене навантаження на CPU та автентифікацію."
365
+ - "Веб-застосунки повинні підтримувати пул з'єднань, а не створювати з'єднання на кожен запит."
366
+
252
367
  # === SYSTEM ===
253
368
  database_sizes:
254
369
  title: "Database Sizes"
@@ -286,6 +401,28 @@ uk:
286
401
  - "Корисно для швидкої оцінки стану бази."
287
402
  - "Порівнюйте з baseline для виявлення аномалій."
288
403
 
404
+ wraparound_risk:
405
+ title: "Wraparound Risk"
406
+ what: "Близькість віку Transaction ID до ліміту wraparound у 2 мільярди."
407
+ how: "Читає age(datfrozenxid) з pg_database. Коли значення наближається до 2^31 (~2.1 млрд), PostgreSQL зупиниться для запобігання пошкодження даних."
408
+ nuances:
409
+ - "autovacuum_freeze_max_age (за замовчуванням 200M) автоматично запускає агресивний anti-wraparound VACUUM."
410
+ - "Якщо age перевищує freeze_max_age, anti-wraparound VACUUM вже повинен працювати — якщо ні, розслідуйте причину."
411
+ - "Довготривалі транзакції не дають VACUUM просунути frozen XID — моніторте idle-in-transaction."
412
+ - "В екстрених випадках запустіть VACUUM FREEZE на найбільших/найстаріших таблицях першими."
413
+ - "Pct > 50% — попередження; > 75% — критично, потребує негайних дій."
414
+
415
+ checkpoint_stats:
416
+ title: "Checkpoint Stats"
417
+ what: "Частота чекпоінтів та метрики продуктивності background writer."
418
+ how: "Дані з pg_stat_bgwriter показують кількість чекпоінтів, тайминги та розподіл запису буферів."
419
+ nuances:
420
+ - "checkpoints_timed = заплановані чекпоінти (нормально); checkpoints_req = вимушені чекпоінти (під навантаженням)."
421
+ - "Високий requested_pct означає, що WAL заповнюється до checkpoint_timeout — збільште max_wal_size."
422
+ - "buffers_backend > 0 означає, що бекенди самі пишуть брудні буфери — збільште shared_buffers або активність bgwriter."
423
+ - "bgwriter_stops (maxwritten_clean) > 0 означає, що bgwriter досяг ліміту за раунд — збільште bgwriter_lru_maxpages."
424
+ - "Статистика накопичується з моменту stats_reset — порівнюйте за періоди для осмисленого аналізу."
425
+
289
426
  cache_stats:
290
427
  title: "Cache Stats"
291
428
  what: "Статистика кешування бази даних."
@@ -296,6 +433,74 @@ uk:
296
433
  - "Низький cache hit: збільште shared_buffers (до 25% RAM), або проблема в запитах."
297
434
 
298
435
  # === SCHEMA ANALYSIS ===
436
+ unused_columns:
437
+ title: "Невикористовувані колонки"
438
+ what: "Колонки, які коли-небудь зберігали лише одне унікальне значення — сильна ознака, що застосунок їх більше не оновлює."
439
+ how: "Читає pg_stats.n_distinct = 1 (одне значення за вибіркою) та джойнить pg_attrdef для відображення default. Виключає первинні ключі та колонки унікальних індексів, щоб прибрати шум."
440
+ nuances:
441
+ - "n_distinct — оцінка за вибіркою. Зробіть ANALYZE перед тим, як довіряти результату. Застаріла статистика дає хибні спрацювання."
442
+ - "Таблиці менше ~1000 рядків виключені — на маленькій вибірці одне значення статистично безглузде."
443
+ - "Хибні спрацювання: feature flags, enum-колонки з одним released-значенням, колонки з дефолтом і одним валідним варіантом."
444
+ - "Справжні знахідки: колонки, у яких видалили Ruby/ORM-аксесор, але міграцію на drop column не написали; статуси, замінені іншим механізмом, але залишені в схемі."
445
+ - "PostgreSQL не відстежує запис по колонках, це евристика. Перед видаленням grep'ніть кодову базу за іменем колонки."
446
+ - "Дивіться також `always_null_columns` — той самий діагноз з іншого ракурсу."
447
+
448
+ always_null_columns:
449
+ title: "Колонки, завжди NULL"
450
+ what: "Nullable-колонки, де 100% рядків = NULL — застосунок перестав (або ніколи не починав) їх писати."
451
+ how: "Читає pg_stats.null_frac >= 0.999 (фактично всі NULL) та виключає колонки з NOT NULL. Джойнить pg_attrdef, щоб показати залишений default."
452
+ nuances:
453
+ - "null_frac — оцінка за вибіркою. Зробіть ANALYZE, якщо сумніваєтеся. Перевірте після репрезентативного вікна навантаження."
454
+ - "Колонка може бути NULL, тому що шлях запису рідко спрацьовує (premium-only поля, opt-in фічі). Перевіряйте перед видаленням."
455
+ - "Якщо null_pct = 100 І є не-NULL default — default не використовується, видаляйте разом із колонкою."
456
+ - "Деякі ORM серіалізують порожні рядки як '' замість NULL; цей звіт їх не зловить. Якщо потрібен такий сигнал — окремий звіт."
457
+ - "Порівняйте з `unused_columns` — він ловить колонки з одним значенням, цей — все-NULL. Разом покривають основні патерни мертвих колонок."
458
+
459
+ polymorphic_without_index:
460
+ title: "Polymorphic без індексу"
461
+ what: "Поліморфні `belongs_to`-асоціації, у яких пара `(*_type, *_id)` не покрита складеним індексом."
462
+ how: "Обходить `ActiveRecord::Base.descendants`, збирає `belongs_to`-рефлексії з `polymorphic: true` та перевіряє `pg_index` на наявність індексу, що покриває обидві колонки."
463
+ nuances:
464
+ - "PostgreSQL не зможе використати два одиночні індекси так само ефективно, як один складений для `WHERE x_type = ? AND x_id = ?` — це базовий патерн завантаження поліморфної асоціації."
465
+ - "Підказка `coverage` уточнює прогалину: \"neither indexed\" / \"only id indexed\" / \"only type indexed\" / \"type and id indexed separately\". Останні два — часткові рішення, не повні."
466
+ - "На маленьких таблицях нешкідливо; на великих (Comment, Note, Activity, AuditLog) кожен запит до асоціації перетворюється на seq scan."
467
+ - "Запропонована міграція використовує порядок `(type, id)` — у type зазвичай нижча cardinality, індекс звужується швидше."
468
+ - "Перед обходом моделей виконується `eager_load!`, щоб у dev результат був повним."
469
+
470
+ counter_cache_issues:
471
+ title: "Проблеми counter_cache"
472
+ what: "`belongs_to ..., counter_cache: ...` декларації, у яких цільова колонка відсутня в батьківській таблиці."
473
+ how: "Обходить усі моделі, знаходить `belongs_to` з опцією `counter_cache`, розв'язує ім'я очікуваної колонки (`<child_table>_count` для `counter_cache: true` або явний символ/рядок) та перевіряє колонки батька."
474
+ nuances:
475
+ - "Відсутня колонка = counter мовчки зламаний: записи нікуди не йдуть, `parent.<assoc>_count` повертає nil, будь-який код, що читає cached-значення, отримує stale або нуль."
476
+ - "Зворотний напрямок (колонка `*_count` без декларації `counter_cache`) навмисно не флагується — занадто багато false positives для вручну підтримуваних лічильників."
477
+ - "Батьківський клас розв'язується через `assoc.klass`; якщо клас відсутній або не завантажується — рядок пропускається."
478
+ - "Ім'я за замовчуванням для `counter_cache: true`: на батька додається колонка з іменем за таблицею **дочірньої** моделі в множині + `_count`. Приклад: `Comment belongs_to :user, counter_cache: true` → `users.comments_count`."
479
+ - "Перед обходом моделей виконується `eager_load!`, щоб у dev результат був повним."
480
+
481
+ soft_delete_without_scope:
482
+ title: "Soft delete без scope"
483
+ what: "Таблиці з колонкою `deleted_at` / `discarded_at` / `archived_at`, у моделі яких немає scope, що фільтрує soft-deleted рядки."
484
+ how: "Для кожної таблиці перевіряється наявність канонічних імен soft-delete колонок. Якщо є — знаходиться модель і перевіряється `acts_as_paranoid` (paranoia), `discard_column` (discard) або `default_scope`, чий згенерований SQL посилається на колонку."
485
+ nuances:
486
+ - "Без default scope будь-який звичайний `Model.where(...)` повертає soft-deleted рядки. Це тихо протікає у звіти, індекси, пошук, експорти."
487
+ - "Якщо команда свідомо відмовилася від default scope (надаючи перевагу явним `kept`/`with_deleted`), звіт дасть по рядку на кожну таку модель — це intentional, можна ігнорувати."
488
+ - "Метод детекції default_scope: будує `model.all.to_sql` та шукає ім'я колонки. Ловить `default_scope { where(deleted_at: nil) }` і еквіваленти, але пропускає scope з concerns, які ще не завантажені."
489
+ - "Таблиці без моделі рапортуються окремо зі статусом `no_model` — зазвичай означає, що таблиця використовується лише raw SQL і потребує ручного рев'ю."
490
+ - "Перед обходом моделей виконується `eager_load!`, щоб у dev результат був повним."
491
+
492
+ orphan_tables:
493
+ title: "Orphan-таблиці"
494
+ what: "Таблиці БД без відповідного Rails-класу моделі."
495
+ how: "Перелічує всі таблиці з `pg_class` (за винятком `schema_migrations` та `ar_internal_metadata`) і намагається знайти модель за звичайними naming conventions. Таблиці, що не знаходять моделі, потрапляють у звіт."
496
+ nuances:
497
+ - "Три класифікації: `join_table_candidate` (рівно дві `*_id` колонки і більше нічого — імовірно, легітимна HABTM-таблиця), `join_model_without_class` (кілька FK + додаткові поля — імовірно, мала бути моделлю join'а), `legacy` (усе інше)."
498
+ - "`join_table_candidate` зазвичай intentional — Rails не вимагає моделі для HABTM. Переглянути та ігнорувати."
499
+ - "`legacy` — найцікавіші рядки: таблиці, створені до того, як додали модель; перейменовані-але-не-видалені; створені повз Rails іншим сервісом; що належать видаленій фічі."
500
+ - "Звірте з `unused_tables` (категорія Таблиці) — таблиця одночасно orphan І з нулем читань = сильний кандидат на видалення."
501
+ - "False positives: namespaced-моделі (`Admin::User` → `admin_users`), STI-підкласи на одній базовій таблиці, моделі в engines, що не eager-loadяться."
502
+ - "Row count приблизний (n_live_tup з pg_stat_user_tables) — нещодавно записані таблиці можуть показувати 0 до наступного ANALYZE."
503
+
299
504
  missing_validations:
300
505
  title: "Відсутні валідації"
301
506
  what: "Унікальні індекси в базі даних без відповідних валідацій uniqueness в Rails-моделях."
@@ -316,8 +521,410 @@ uk:
316
521
  low_cache_hit: "Низький cache hit ratio. Запит часто читає з диска замість кешу."
317
522
  high_seq_scan: "Багато sequential scan. Можливо не вистачає індексу."
318
523
  unused_index: "Індекс не використовується. Кандидат на видалення."
524
+ inefficient_index: "Індекс читає значно більше записів, ніж витягує. Ймовірно, порядок колонок складеного індексу не відповідає предикатам запиту."
525
+ fk_without_index: "FK-колонка без підтримуючого індексу. DELETE/UPDATE батьківської таблиці спричинить sequential scan."
526
+ low_correlation: "Низька фізична кореляція між індексом та порядком рядків. Range scan спричинить надлишковий random I/O."
527
+ temp_file_heavy: "Запит скидає дані у тимчасові файли на диск. Розгляньте збільшення work_mem або оптимізацію запиту."
528
+ missing_pk: "Таблиця без первинного ключа. Це ламає логічну реплікацію та може спричинити проблеми з ORM."
529
+ wraparound_risk: "Вік Transaction ID наближається до ліміту wraparound. Потрібен VACUUM FREEZE."
530
+ high_checkpoint_req: "Високий відсоток вимушених чекпоінтів. Розгляньте збільшення max_wal_size."
319
531
  high_bloat: "Високий bloat. Потрібен REINDEX або VACUUM."
320
532
  many_dead_tuples: "Багато мертвих рядків. Потрібен VACUUM."
321
533
  long_running: "Довго виконуваний запит. Може блокувати інші операції."
322
534
  blocking: "Блокує інші запити. Потребує уваги."
323
535
  idle_in_transaction: "Відкрита транзакція без активності. Блокує VACUUM та утримує локи."
536
+ high_pool_usage: "Висока утилізація пулу з'єднань. Збільште max_connections або впровадьте connection pooling."
537
+ long_wait_time: "Запит занадто довго очікує ресурсів. Перевірте конкуренцію блокувань або проблеми I/O."
538
+ pool_saturation: "Пул з'єднань насичений. Ризик вичерпання з'єднань та помилок застосунку."
539
+ high_connection_churn: "Висока оборотність з'єднань. Впровадьте connection pooling для зниження накладних витрат."
540
+ too_many_short_connections: "Занадто багато короткоживучих з'єднань. Застосунок повинен перевикористовувати з'єднання через pooling."
541
+ unused_column: "Колонка має одне значення у всіх рядках. Ймовірно, не оновлювалася з моменту створення — кандидат на видалення."
542
+ always_null_column: "Колонка на 100% NULL. Застосунок більше не пише в це поле — кандидат на видалення."
543
+ hot_rows: "Ті самі рядки оновлюються багаторазово. Розгляньте розділення гарячих/холодних колонок, батчинг запису або event-log таблицю."
544
+ low_hot_update: "Низький відсоток HOT updates — оновлюються індексовані колонки. Видаліть індекс або винесіть колонку, щоб знизити write amplification."
545
+ unused_table: "Таблиця не читалася з моменту останнього скидання статистики. Кандидат на архівацію або видалення — спочатку перевірте вікно статистики."
546
+ polymorphic_no_index: "Поліморфна асоціація без складеного індексу (type, id). Зі зростанням таблиці запити будуть seq-scan'ити."
547
+ counter_cache_missing_column: "Колонка counter_cache відсутня в батьківській таблиці. Counter мовчки зламаний — записи нікуди не йдуть."
548
+ soft_delete_unprotected: "Soft-delete колонка без scope, що фільтрує її. Звичайні запити повертають видалені рядки."
549
+ orphan_table_legacy: "Таблиця без Rails-моделі. Скоріше за все, legacy — перевірте перед видаленням."
550
+
551
+ # UI strings shown in the dashboard chrome (buttons, modals, toasts, etc.)
552
+ ui:
553
+ branding:
554
+ title: "PgReports"
555
+ subtitle: "Дашборд аналізу PostgreSQL"
556
+ page_title: "PgReports — Дашборд"
557
+ navigation:
558
+ dashboard: "Дашборд"
559
+ back: "← Назад"
560
+ actions:
561
+ cancel: "Скасувати"
562
+ retry: "Повторити"
563
+ copy: "📋 Копіювати"
564
+ copy_query: "📋 Копіювати запит"
565
+ copy_code: "📋 Копіювати код"
566
+ copy_to_clipboard_title: "Скопіювати в буфер обміну"
567
+ copied_feedback: "✓ Скопійовано!"
568
+ clear_all: "Очистити все"
569
+ run_report: "▶ Запустити звіт"
570
+ export: "⬇ Експорт"
571
+ download_text: "📄 Текст (.txt)"
572
+ download_csv: "📊 CSV (.csv)"
573
+ download_json: "📋 JSON (.json)"
574
+ download: "📥 Завантажити"
575
+ copy_ai_prompt: "Копіювати промпт"
576
+ send_telegram: "📨 Telegram"
577
+ sending: "Надсилання..."
578
+ reset_statistics: "🗑️ Скинути статистику"
579
+ resetting: "Скидання..."
580
+ confirm_reset: "Так, скинути"
581
+ create_extension: "⚡ Створити розширення"
582
+ creating: "Створення..."
583
+ ide_settings_button_title: "Налаштування IDE"
584
+ explain_analyze: "📊 EXPLAIN ANALYZE"
585
+ execute_query: "▶ Виконати запит"
586
+ create_migration_file: "📁 Створити файл і відкрити в IDE"
587
+ start_monitoring: "▶ Запустити моніторинг"
588
+ stop_monitoring: "⏹ Зупинити моніторинг"
589
+ starting: "Запуск..."
590
+ stopping: "Зупинка..."
591
+ load_history: "📜 Завантажити історію (50)"
592
+ loading: "Завантаження..."
593
+ running: "Виконання..."
594
+ save_for_comparison: "📌 Зберегти для порівняння"
595
+ saved_marker: "📌 Збережено"
596
+ status:
597
+ pg_stat_ready: "pg_stat_statements готовий"
598
+ extension_installed: "Розширення встановлено, не передзавантажено"
599
+ preloaded: "Передзавантажено, розширення не створено"
600
+ not_configured: "Не налаштовано"
601
+ monitoring_unavailable: "Live-моніторинг недоступний"
602
+ modals:
603
+ enable_pg_stat_title: "Увімкнення pg_stat_statements"
604
+ enable_pg_stat_intro: "Щоб увімкнути pg_stat_statements, виконайте такі кроки:"
605
+ edit_postgresql_conf: "Відредагуйте postgresql.conf:"
606
+ restart_postgresql: "Перезапустіть PostgreSQL:"
607
+ create_extension_step: "Створіть розширення:"
608
+ enable_button_note: "Або натисніть кнопку «Створити розширення» після перезапуску."
609
+ reset_stats_title: "⚠️ Скидання статистики"
610
+ reset_stats_confirm: "Ви впевнені, що хочете скинути статистику pg_stat_statements?"
611
+ reset_stats_warning: "Ця дія очистить усю зібрану статистику запитів і не може бути скасована."
612
+ ide_settings_title: "⚙️ Налаштування IDE"
613
+ problem_detected_title: "⚠️ Виявлено проблему"
614
+ query_analyzer_title: "📊 Аналізатор запиту"
615
+ query_label: "Запит:"
616
+ parameters_label: "Параметри:"
617
+ migration_title: "🗑️ Міграція видалення індексу"
618
+ migration_subtitle: "Згенерована міграція для видалення індексу:"
619
+ migration_warning: "Створення міграції згенерує файл міграції у вашому проєкті. Запуск цієї міграції видалить індекс із БД, що може значно вплинути на продуктивність застосунку."
620
+ migration_warning_dev_only: "Цю операцію слід виконувати лише в локальному dev-середовищі."
621
+ query_execution_disabled_title: "⚠️ Виконання запитів вимкнено"
622
+ query_execution_disabled_intro: "Щоб увімкнути цю функцію, додайте до конфігурації:"
623
+ settings:
624
+ default_ide_label: "IDE за замовчуванням для посилань на джерела:"
625
+ ide_show_menu: "Показувати меню (за замовчуванням)"
626
+ ide_vscode_wsl: "VS Code (WSL)"
627
+ ide_vscode: "VS Code"
628
+ ide_rubymine: "RubyMine"
629
+ ide_intellij: "IntelliJ IDEA"
630
+ ide_cursor_wsl: "Cursor (WSL)"
631
+ ide_cursor: "Cursor"
632
+ monitoring:
633
+ live_title: "Live-моніторинг"
634
+ update_interval: "Оновлення кожні 5с"
635
+ toggle_title: "Перемкнути live-моніторинг"
636
+ query_monitor_title: "Монітор SQL-запитів"
637
+ session_label: "Сесія:"
638
+ queries_label: "Запитів:"
639
+ feed_empty: "Натисніть «Запустити моніторинг», щоб почати захоплення SQL-запитів"
640
+ feed_no_queries: "Запитів поки не захоплено..."
641
+ unknown_source: "Джерело невідоме"
642
+ expand_collapse_title: "Розгорнути / згорнути"
643
+ metrics:
644
+ connections_label: "З'єднання"
645
+ tps_label: "TPS"
646
+ tps_unit: "тр/с"
647
+ commit_label: "commit:"
648
+ rollback_label: "rollback:"
649
+ cache_hit_label: "Cache hit"
650
+ cache_hit_detail: "блоки heap із кешу"
651
+ long_queries_label: "Довгі запити"
652
+ queries_unit: "запитів"
653
+ long_running_threshold: "> 60с виконання"
654
+ blocked_label: "Заблоковано"
655
+ processes_unit: "процесів"
656
+ waiting_for_locks: "чекають блокувань"
657
+ percent_used_suffix: "% використано"
658
+ categories:
659
+ requires_pg_stat: "🔒 Потрібен pg_stat_statements"
660
+ reports_count_suffix: "звітів"
661
+ documentation:
662
+ toggle_title: "📖 Що показує цей звіт?"
663
+ what_section: "📋 Що"
664
+ why_section: "❓ Чому це важливо"
665
+ nuances_section: "⚠️ Нюанси"
666
+ thresholds_section: "📊 Пороги"
667
+ threshold_warning_label: "⚠️ Warning:"
668
+ threshold_critical_label: "🔴 Critical:"
669
+ threshold_inverted_note: "(менше — гірше)"
670
+ filters:
671
+ title: "🔍 Параметри фільтрації"
672
+ current_value: "зараз"
673
+ saved:
674
+ title: "📌 Збережено для порівняння"
675
+ saved_at_prefix: "▸ Збережено:"
676
+ click_to_expand: "Натисніть, щоб розгорнути"
677
+ confirm_clear_all: "Видалити всі збережені записи для цього звіту?"
678
+ remove_title: "Видалити"
679
+ results:
680
+ title: "Результати"
681
+ click_run_hint: "Натисніть «Запустити звіт», щоб отримати дані"
682
+ empty_message: "Проблем не знайдено. Усе добре!"
683
+ showing_first_of_total: "Показані перші %{count} з %{total} рядків"
684
+ no_rows_returned: "Рядків немає"
685
+ rows_label: "Рядків:"
686
+ execution_time_label: "Час виконання:"
687
+ null_placeholder: "<null>"
688
+ sections:
689
+ recommendation: "💡 Рекомендація"
690
+ detected_issues: "⚠️ Виявлені проблеми"
691
+ execution_plan: "📊 План виконання"
692
+ line_label: "Рядок"
693
+ current_label: "Поточне:"
694
+ threshold_label: "Поріг:"
695
+ threshold_inverted_long: "(інверсія: менші значення — гірше)"
696
+ warning_eq: "warning"
697
+ critical_eq: "critical"
698
+ levels:
699
+ critical: "🔴 Критично"
700
+ warning: "⚠️ Увага"
701
+ errors:
702
+ error_prefix: "Помилка:"
703
+ unable_fetch_metrics: "Не вдалося отримати статистику БД."
704
+ possible_causes: "Можливі причини:"
705
+ cause_permissions: "Недостатньо прав для доступу до БД"
706
+ cause_views: "Системні представлення статистики недоступні"
707
+ cause_connection: "Проблеми зі з'єднанням"
708
+ fetch_metrics_failed: "Не вдалося отримати live-метрики"
709
+ fetch_metrics_check_perms: "Не вдалося отримати статистику БД. Перевірте права доступу."
710
+ insufficient_database_perms: "Недостатньо прав для доступу до системних представлень статистики"
711
+ network_error_prefix: "Мережева помилка:"
712
+ copy_failed: "Не вдалося скопіювати"
713
+ run_report_first: "Спочатку запустіть звіт"
714
+ run_report_failed: "Не вдалося запустити звіт"
715
+ report_not_found: "Звіт не знайдено"
716
+ send_telegram_failed: "Не вдалося надіслати в Telegram"
717
+ reset_stats_failed: "Не вдалося скинути статистику"
718
+ start_monitoring_failed: "Не вдалося запустити моніторинг"
719
+ stop_monitoring_failed: "Не вдалося зупинити моніторинг"
720
+ load_history_failed: "Не вдалося завантажити історію:"
721
+ no_query_history: "Історію запитів не знайдено"
722
+ decode_query_failed: "Не вдалося декодувати запит"
723
+ explain_analyze_failed: "Не вдалося виконати EXPLAIN ANALYZE"
724
+ execute_query_failed: "Не вдалося виконати запит"
725
+ create_migration_failed: "Не вдалося створити міграцію"
726
+ explain_disabled_toast: "⚠️ EXPLAIN ANALYZE вимкнено. Увімкніть у конфігурації: config.allow_raw_query_execution = true"
727
+ execute_disabled_toast: "⚠️ Виконання запитів вимкнено. Увімкніть у конфігурації: config.allow_raw_query_execution = true"
728
+ query_monitoring_error: "Помилка моніторингу запитів"
729
+ query_hash_required: "Потрібен хеш запиту"
730
+ query_execution_disabled: "Виконання запитів із дашборду вимкнено. Увімкніть у конфігурації: 'config.allow_raw_query_execution = true'"
731
+ query_not_found_expired: "Запит не знайдено або термін дії минув. Оновіть сторінку."
732
+ security_violation_prefix: "Порушення безпеки:"
733
+ trigger_variables_not_allowed: "Не можна виконати EXPLAIN ANALYZE для запитів із тригерними змінними (NEW, OLD). Вони доступні лише в контексті тригерних функцій."
734
+ missing_parameter_values: "Вкажіть значення для всіх плейсхолдерів параметрів ($1, $2 тощо)"
735
+ migration_dev_only: "Створення міграцій дозволено лише в development-середовищі"
736
+ filename_code_required: "Ім'я файлу та код обов'язкові"
737
+ invalid_filename_format: "Невірний формат імені файлу міграції"
738
+ migrations_dir_not_found: "Каталог міграцій не знайдено"
739
+ success:
740
+ statistics_reset: "Статистику успішно скинуто"
741
+ report_generated: "Звіт успішно згенеровано"
742
+ ai_prompt_copied: "AI-промпт скопійовано в буфер обміну"
743
+ explain_copied: "Вивід EXPLAIN скопійовано в буфер обміну"
744
+ migration_copied: "Код міграції скопійовано в буфер обміну"
745
+ migration_created: "Міграцію успішно створено"
746
+ telegram_sent: "Звіт надіслано в Telegram"
747
+ queries_loaded: "Завантажено %{count} запитів з історії"
748
+ record_saved: "Запис збережено для порівняння"
749
+ record_removed: "Запис видалено"
750
+ record_removed_saved: "Запис видалено зі збережених"
751
+ all_saved_cleared: "Усі збережені записи очищено"
752
+
753
+ # Category names (shown on the dashboard grid)
754
+ categories:
755
+ queries: "Запити"
756
+ indexes: "Індекси"
757
+ tables: "Таблиці"
758
+ connections: "З'єднання"
759
+ system: "Система"
760
+ schema_analysis: "Аналіз схеми"
761
+
762
+ # Report names and short descriptions (shown on the dashboard listing)
763
+ reports:
764
+ slow_queries:
765
+ name: "Повільні запити"
766
+ description: "Запити з високим середнім часом виконання"
767
+ heavy_queries:
768
+ name: "Часті запити"
769
+ description: "Найчастіше викликані запити"
770
+ expensive_queries:
771
+ name: "Дорогі запити"
772
+ description: "Запити з найбільшим сумарним часом"
773
+ missing_index_queries:
774
+ name: "Без індексів"
775
+ description: "Запити, яким, можливо, потрібні індекси"
776
+ low_cache_hit_queries:
777
+ name: "Низький cache hit"
778
+ description: "Запити з поганою утилізацією кешу"
779
+ temp_file_queries:
780
+ name: "Скидання на диск"
781
+ description: "Запити, що скидають дані на диск"
782
+ all_queries:
783
+ name: "Усі запити"
784
+ description: "Повна статистика запитів"
785
+ unused_indexes:
786
+ name: "Невикористовувані індекси"
787
+ description: "Індекси, що рідко або ніколи не скануються"
788
+ duplicate_indexes:
789
+ name: "Дублікати індексів"
790
+ description: "Надлишкові індекси"
791
+ invalid_indexes:
792
+ name: "Невалідні індекси"
793
+ description: "Індекси, які не вдалося побудувати"
794
+ missing_indexes:
795
+ name: "Відсутні індекси"
796
+ description: "Таблиці, яким, можливо, потрібні індекси"
797
+ inefficient_indexes:
798
+ name: "Неефективні індекси"
799
+ description: "Індекси з високим read-to-fetch ratio"
800
+ index_usage:
801
+ name: "Використання індексів"
802
+ description: "Статистика сканування індексів"
803
+ bloated_indexes:
804
+ name: "Роздуті індекси"
805
+ description: "Індекси з високим bloat"
806
+ fk_without_indexes:
807
+ name: "FK без індексів"
808
+ description: "Зовнішні ключі без підтримуючого індексу"
809
+ index_correlation:
810
+ name: "Кореляція індексів"
811
+ description: "Індекси з низькою фізичною кореляцією"
812
+ index_sizes:
813
+ name: "Розміри індексів"
814
+ description: "Використання диска індексами"
815
+ table_sizes:
816
+ name: "Розміри таблиць"
817
+ description: "Використання диска таблицями"
818
+ bloated_tables:
819
+ name: "Роздуті таблиці"
820
+ description: "Таблиці з високою часткою dead tuples"
821
+ vacuum_needed:
822
+ name: "Потрібен VACUUM"
823
+ description: "Таблиці, яким потрібен VACUUM"
824
+ row_counts:
825
+ name: "Кількість рядків"
826
+ description: "Число рядків у таблицях"
827
+ cache_hit_ratios:
828
+ name: "Cache hit таблиць"
829
+ description: "Статистика кешу по таблицях"
830
+ seq_scans:
831
+ name: "Sequential scans"
832
+ description: "Таблиці з великою кількістю sequential scan"
833
+ tables_without_pk:
834
+ name: "Без первинного ключа"
835
+ description: "Таблиці без первинного ключа"
836
+ recently_modified:
837
+ name: "Нещодавно змінені"
838
+ description: "Таблиці з нещодавньою активністю"
839
+ update_hotspots:
840
+ name: "Гарячі точки UPDATE"
841
+ description: "Ті самі рядки або індексовані колонки часто оновлюються"
842
+ unused_tables:
843
+ name: "Невикористовувані таблиці"
844
+ description: "Таблиці, які не запитуються з моменту останнього скидання статистики"
845
+ active_connections:
846
+ name: "Активні з'єднання"
847
+ description: "Поточні підключення до БД"
848
+ connection_stats:
849
+ name: "Статистика з'єднань"
850
+ description: "З'єднання за станом"
851
+ long_running_queries:
852
+ name: "Довгі запити"
853
+ description: "Запити, що виконуються довго"
854
+ blocking_queries:
855
+ name: "Блокуючі запити"
856
+ description: "Запити, що блокують інші"
857
+ locks:
858
+ name: "Блокування"
859
+ description: "Поточні блокування в БД"
860
+ idle_connections:
861
+ name: "Idle з'єднання"
862
+ description: "З'єднання, що простоюють"
863
+ pool_usage:
864
+ name: "Використання пулу"
865
+ description: "Утилізація пулу з'єднань"
866
+ pool_wait_times:
867
+ name: "Час очікування"
868
+ description: "Аналіз очікування ресурсів"
869
+ pool_saturation:
870
+ name: "Насичення пулу"
871
+ description: "Попередження про здоров'я пулу"
872
+ connection_churn:
873
+ name: "Churn з'єднань"
874
+ description: "Аналіз життєвого циклу з'єднань"
875
+ database_sizes:
876
+ name: "Розміри баз даних"
877
+ description: "Розмір усіх баз"
878
+ settings:
879
+ name: "Налаштування"
880
+ description: "Конфігурація PostgreSQL"
881
+ extensions:
882
+ name: "Розширення"
883
+ description: "Встановлені розширення"
884
+ activity_overview:
885
+ name: "Огляд активності"
886
+ description: "Зведення поточної активності"
887
+ wraparound_risk:
888
+ name: "Ризик wraparound"
889
+ description: "Близькість до ліміту Transaction ID"
890
+ checkpoint_stats:
891
+ name: "Статистика checkpoint"
892
+ description: "Метрики чекпоінтів та bgwriter"
893
+ cache_stats:
894
+ name: "Статистика кешу"
895
+ description: "Статистика кешування БД"
896
+ missing_validations:
897
+ name: "Відсутні валідації"
898
+ description: "Унікальні індекси без валідацій моделі"
899
+ unused_columns:
900
+ name: "Невикористовувані колонки"
901
+ description: "Колонки, що мають лише одне значення"
902
+ always_null_columns:
903
+ name: "Завжди NULL"
904
+ description: "Nullable-колонки, що містять лише NULL"
905
+ polymorphic_without_index:
906
+ name: "Polymorphic без індексу"
907
+ description: "Поліморфні асоціації без складеного індексу"
908
+ counter_cache_issues:
909
+ name: "Проблеми counter_cache"
910
+ description: "counter_cache декларації без цільової колонки"
911
+ soft_delete_without_scope:
912
+ name: "Soft delete без scope"
913
+ description: "Soft-delete колонки без scope, що фільтрує їх"
914
+ orphan_tables:
915
+ name: "Orphan-таблиці"
916
+ description: "Таблиці БД без відповідної Rails-моделі"
917
+
918
+ # Filter parameter labels and descriptions
919
+ parameters:
920
+ limit:
921
+ label: "Ліміт"
922
+ description: "Максимальна кількість результатів"
923
+ min_calls:
924
+ label: "Мін. викликів"
925
+ description: "Мінімальна кількість викликів запиту"
926
+ min_duration_seconds:
927
+ label: "Мін. тривалість (сек)"
928
+ description: "Мінімальна тривалість запиту в секундах"
929
+ threshold_label: "%{field} — поріг"
930
+ threshold_description: "Перевизначити поріг для %{field}"