Аналіз спільної Mongo-бази
stack-golden → GET /golden/debug/db-analysis (src/golden/debug/db-analysis.ts) — знімок здоров’я спільної бази stack, яку юзають усі бек-сервіси.
Що це. Усі бек-сервіси (
stack,golden,prime,udate,chathouse,academy,ai) пишуть в одну Mongo-базуstack. Read-only debug-ендпоінт (@ApiRoles([director])) знімає метрики колекцій/індексів. Назви колекцій — як у БД; призначення кожної тут не розшифровуємо (див. реєстр сутностей сервісів).
Дві природи даних — звідки що валідне
| Дані | Валідний запуск | Чому |
|---|---|---|
невикористані індекси (ops/since), фрагментація (reusableBytes) | прод ?docScan=off | runtime-лічильники у пам’яті mongod, у дамп не потрапляють |
| розміри, кількість, дублікати/надлишкові індекси, max doc size | локально на свіжому дампі ?docScan=full | повний скан безпечний; точніше |
⚠️
$indexStatsрахує з моменту рестарту mongod. У прод-знімку аптайм ~9.85 дня (since≈ 2026-06-03), тож «0 ops» — надійний сигнал «не юзається». На свіжому дампі лічильники порожні — там «unused» це шум (підтверджено: локальний прогін дав ~200 «unused», прод — лише ~90).
Знімки
| Прод | Локальний дамп | |
|---|---|---|
| Дата | 2026-06-13 | 2026-06-13 |
| Mongo | 7.0.12 | 8.2.3 |
| Аптайм | ~9.85 дня | — (свіжий) |
| Колекцій / індексів | 225 / 479 | 226 / 481 |
| dataSize | 23.5 GB | 23.1 GB |
| storageSize | 9.80 GB | 6.19 GB |
| indexSize | 3.18 GB | 1.63 GB |
| totalSize | 12.98 GB | 7.82 GB |
| documents | 45.5 млн | 43.8 млн |
Одиниці — десяткові MB/GB (bytes ÷ 10⁶), як показує Compass. Не MiB.
Дані майже однакові (23.5 vs 23.1 GB), а займане місце відрізняється на ~5.2 GB. Уся ця різниця — фрагментація на проді (storage +3.6 GB, index +1.55 GB). На свіжому дампі вона зникає → це верхня оцінка того, що поверне
compact/resync.
Якщо звіряєш із Compass: локальний Compass (
Docker > stack) = колонка «Локальний дамп». Прод-топи фрагментації (нижче) там НЕ видно — на дампі ці колекції крихітні (напр.prime_task_completed_or_canceledsу Compass 28.67 kB, на проді 924 MB). Саме ця різниця — суть аналізу.
🔴 №1 проблема: фрагментація прода (~5 GB реюзабельні)
reusableBytes = місце, виділене WiredTiger, але порожнє (наслідок масових delete/update). Це прод-онлі; у локальному Compass цих роздутих storage нема — там та сама колекція займає стільки, скільки даних. Топ за обсягом сміття (прод, MB):
| Колекція | storage прод | дані | реюзабельно | % | storage на дампі |
|---|---|---|---|---|---|
academy_answers | 1038.57 MB | 68.35 MB | 966.19 MB | 93% | 33.74 MB |
prime_task_completed_or_canceleds | 923.91 MB | 34.83 KB | 922.78 MB | 99.9% | 28.67 KB |
golden_history_sending_mails | 766.88 MB | 650.48 MB | 573.92 MB | 75% | 182.52 MB |
golden_history_sending_messages | 210.22 MB | 429.30 MB | 117.78 MB | 56% | 32.29 MB |
statusworkeronudateladyonoperators | 116.04 MB | 41.42 MB | 96.81 MB | 83% | 10.96 MB |
golden_electron_logs | 107.00 MB | 39.80 MB | 94.72 MB | 89% | 10.92 MB |
golden_man_profiles | 98.76 MB | 20.92 MB | 89.21 MB | 90% | 6.60 MB |
golden_tasks | 21.79 MB | 425 KB | 21.30 MB | 98% | 110 KB |
Колонка «на дампі» показує реальний розмір після стиснення — різниця і є те, що поверне compact.
Плюс роздуті індекси: golden_history_sending_mails має 919.68 MB індексів на проді проти 221.09 MB на дампі (×4). Тобто ця одна колекція — ~1.7 GB на проді, реально потрібно ~400 MB.
Що з цим (перевірити):
academy_answersіprime_task_completed_or_canceleds— величезний churn (записи створюються й видаляються).TODO: перевірити чи є сенс у TTL / архівації, і запустити compact.prime_task_completed_or_canceleds— окремо показовий: 365 живих доків, 34 KB даних, але 881 MB storage + 69 MB індексів, усі 5 вторинних індексів з 0 ops. Кандидат №1: дроп невикористаних індексів + compact ≈ зекономить ~950 MB.
🟡 Невикористані індекси (ПРОД, 0 ops за ~10 днів)
~90 індексів з 0 операцій. Сортовано за розміром (топ — найбільша економія місця + пришвидшення запису). TODO-нюанс: перед дропом ще раз звірити since з аптаймом після можливих рестартів.
| Колекція | Індекс | Розмір | ops |
|---|---|---|---|
golden_history_sending_messages | ladyId_api_1_manId_api_1_timestamp_1_initiatorType_1 | 135.86 MB | 0 |
prime_favorite_actions | ladyId_1_manId_1_messageId_1 | 29.42 MB | 0 |
golden_history_sending_messages | timestamp_1 | 28.41 MB | 0 |
golden_statistics_relations | date_1_operatorId_1_ladyId_1_final_1 | 26.86 MB | 0 |
prime_task_completed_or_canceleds | idDialog_1 | 18.99 MB | 0 |
golden_history_sending_messages | ladyId_api_1 | 16.76 MB | 0 |
prime_task_completed_or_canceleds | canceled.status_1 | 9.21 MB | 0 |
golden_operators_activities | startActive_1_stopActive_1 | 5.55 MB | 0 |
chathouse_action_operators | tasks.ladyDbId_1_tasks.config.type_1 | 3.50 MB | 0 |
udate_counter_messages_men | manUlid_1 | 1.34 MB | 0 |
prime_favorite_news | stackTopManagerId_1 | 1.21 MB | 0 |
Решта — десятки індексів по 20–800 KB (email_messages 13 невикористаних, *_admins.email_1, *-supervisors.email_1, user-*-families.* тощо). email_messages має 15 індексів, з них ~13 з 0 ops (many-indexes + has-unused-index).
ai_notes(порожня) має 3 індекси сумарно ~2.4 MiB — реліктові, колекція без даних.
Надлишкові префіксні індекси (структурні)
Індекс є строгим префіксом іншого складеного → запити покриває довший, короткий зайвий. ~20 на проді:
| Колекція | Зайвий | Покритий |
|---|---|---|
golden_action_operators | yearMonthDay_1 | yearMonthDay_1_tasks.ladyId_api_1 |
chathouse_action_operators | operatorFamilyId_1 | operatorFamilyId_1_supervisorFamilyId_1_yearMonthDay_1 |
golden_online_times | operatorFamilyId_1 | operatorFamilyId_1_date_1 |
golden_history_sending_messages | operatorFamilyId_1, ladyId_api_1 | *_timestamp_1 варіанти |
golden_statistics_relations | date_1, date_1_operatorId_1 | date_1_operatorId_1_ladyId_1_final_1 |
golden_favorites, golden_send_mail_histories | ladyId_api_1 | ladyId_api_1_manId_api_1 |
golden_lady_online, golden_stream_times | operatorFamilyId_1 | operatorFamilyId_1_date_1 |
udate_operators_activities | operator_1 | operator_1_stopActive_1_autocreated |
monitoring_logs | vpsId_1 | vpsId_1_nameMicroservice_1_message_1_level_1_createdAt_-1 |
email_messages | ladyStackId_1, manEmail_1, dialogId_1, emailAccountId_1, threadId_1 | відповідні складені |
lady_emails | stackLadyId_1 | stackLadyId_1_email_1 |
ai_notes | ladyId_1 | ladyId_1_manId_1 |
Нюанс: деякі з них юзаються (ops>0, напр.
golden_online_times.operatorFamilyId_1— 1238 ops), але складений покриває ті ж запити. Дроп можливий, але звірити плани запитів.duplicateIndexes— порожньо (точних дублікатів нема).
⚠️ Великі документи (локальний docScan=full)
Ризик: ліміт документа Mongo — 16 MB. *_sender_lady_histories ростуть і вже близько:
| Колекція | max doc | доків >1 MB | >8 MB |
|---|---|---|---|
prime_sender_lady_histories | 9.51 MB | 157 | 4 |
chathouse_sender_lady_histories | 8.40 MB | 345 | 1 |
udate_sender_lady_histories | 5.98 MB | 384 | 0 |
udate_favorites | 5.57 MB | 18 | 0 |
udate_action_operators | 1.80 MB | 6 | 0 |
chathouse_action_operators | 1.32 MB | 1 | 0 |
udate_saveddataforladies | 1.16 MB | 3 | 0 |
*_sender_lady_histories— десятки/сотні KB–MB на документ (avg 0.5–2 MiB).*_action_operators— avg 60–140 KiB/doc. Схоже на доки з масивом, що росте без межі.TODO: перевірити структуру; 4 доки в prime вже >8MB — при 16MB запис впаде.
Найбільші колекції — за обсягом даних
Сортовано за data size (реальний обсяг, майже однаковий прод/локал — на відміну від storage, який на проді роздутий). Збігається з Compass, відсортованим за «Data size». avg/doc = dataSize / count.
| Колекція | дані | docs | avg/doc |
|---|---|---|---|
prime_action_operators | 2.92 GB | 20.6K | 141.6 KB |
golden_action_operators | 2.42 GB | 24.6K | 98.1 KB |
udate_action_operators | 1.95 GB | 17.9K | 109.2 KB |
chathouse_sender_lady_histories | 1.37 GB | 875 | 1.56 MB |
chathouse_action_operators | 1.15 GB | 18.8K | 61.2 KB |
udate_sender_lady_histories | 933.41 MB | 1.3K | 733 KB |
golden_online_users | 922.53 MB | 100K | 9.2 KB |
golden_online_times | 911.90 MB | 3.8M | 240 B |
log_ladies | 892.49 MB | 2.7M | 330 B |
golden_statistics | 838.96 MB | 3.5M | 241 B |
prime_favorite_actions | 743.15 MB | 4.5M | 165 B |
prime_log_ladies | 671.54 MB | 1.6M | 414 B |
golden_history_sending_mails | 650.48 MB | 6.8M | 95 B |
chathouse_statistics | 639.07 MB | 2.4M | 270 B |
За кількістю документів (прод)
| Колекція | docs | дані |
|---|---|---|
golden_history_sending_mails | 6.8M | 650.48 MB |
prime_favorite_actions | 4.5M | 743.15 MB |
golden_online_times | 3.8M | 911.90 MB |
golden_statistics | 3.5M | 838.96 MB |
log_ladies | 2.7M | 892.49 MB |
prime_deepl_usings | 2.5M | 246.11 MB |
chathouse_statistics | 2.4M | 639.07 MB |
golden_history_sending_messages | 2.0M | 429.30 MB |
udate_sendicebreakers | 1.7M | 290.11 MB |
prime_log_ladies | 1.6M | 671.54 MB |
Інші флаги
index-heavier-than-data(~52 колекції): індекси важчі за дані. Переважно дрібні службові (*_admins,*-supervisors,settings,user-*-families) + churn-колекції (prime_task_completed_or_canceleds,golden_history_sending_mails).- Порожні колекції (10):
ai_notes,golden_man_notes,golden_send_history,chathouse_lady_event_logs,chathouse_not_enougth_ices_ladies,udate_hidden_mail_histories,udate_history_profit_ladies,udate_operator_actions,udate_statistics_request_queues,udate_temporary_statistics.TODO: перевірити чи дроп. - Підозра на сміття:
teststoragetokenforusers_udates(206 MiB / 263 тис. доків, назва «test…»),test-user-operator2(1 док, 4 індекси).TODO: підтвердити.
Підсумок: пріоритети чистки (перевірити)
- Compact / resync прода — поверне ~5 GB. Найбільші:
academy_answers,prime_task_completed_or_canceleds,golden_history_sending_mails. - Дроп невикористаних індексів (прод-список вище) — ~300 MB + швидший запис. Почати з
golden_history_sending_messages(135.86 MB) і всіх вторинних уprime_task_completed_or_canceleds. - Перевірити безмежний ріст
*_sender_lady_histories— 16MB-ризик. - Прибрати надлишкові префіксні індекси (звіривши плани запитів).
Дроп/compact — рішення людини, не з цього аналізу. Ендпоінт лише read-only діагностика.
Як оновлювати
- Прод (golden на проді):
GET /golden/debug/db-analysis?docScan=off→ невикористані індекси, фрагментація, живі розміри. - Локально (golden на свіжому дампі):
?docScan=full→ точні max doc size, структурний аналіз. - Оновити дати/аптайм у шапці. Дельта
prod.totalSize − dump.totalSize= поточний обсяг фрагментації.
TODO: додати ноду на architecture/Canvas.canvas(за конвенцією кожен файл = нода).