railwatch 0.1.4 → 0.2.0

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 (302) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -0
  3. data/README.md +10 -0
  4. data/app/channels/railwatch/environment_channel.rb +28 -0
  5. data/app/controllers/concerns/railwatch/telemetry_identity.rb +25 -0
  6. data/app/controllers/railwatch/alerts_controller.rb +48 -0
  7. data/app/controllers/railwatch/anomaly_rules_controller.rb +31 -0
  8. data/app/controllers/railwatch/attachments_controller.rb +22 -0
  9. data/app/controllers/railwatch/beacon_controller.rb +67 -7
  10. data/app/controllers/railwatch/broadcasts_controller.rb +15 -0
  11. data/app/controllers/railwatch/cache_events_controller.rb +30 -0
  12. data/app/controllers/railwatch/commands_controller.rb +16 -0
  13. data/app/controllers/railwatch/comments_controller.rb +11 -0
  14. data/app/controllers/railwatch/dashboard_controller.rb +69 -0
  15. data/app/controllers/railwatch/deploys_controller.rb +34 -0
  16. data/app/controllers/railwatch/deprecations_controller.rb +54 -0
  17. data/app/controllers/railwatch/environment_scoped.rb +99 -0
  18. data/app/controllers/railwatch/exceptions_controller.rb +53 -0
  19. data/app/controllers/railwatch/executions_controller.rb +13 -0
  20. data/app/controllers/railwatch/issues_controller.rb +258 -0
  21. data/app/controllers/railwatch/jobs_controller.rb +63 -0
  22. data/app/controllers/railwatch/llm_calls_controller.rb +124 -0
  23. data/app/controllers/railwatch/logs_controller.rb +40 -0
  24. data/app/controllers/railwatch/mails_controller.rb +19 -0
  25. data/app/controllers/railwatch/notifications_controller.rb +11 -0
  26. data/app/controllers/railwatch/outgoing_requests_controller.rb +19 -0
  27. data/app/controllers/railwatch/overview_controller.rb +38 -0
  28. data/app/controllers/railwatch/people_controller.rb +25 -0
  29. data/app/controllers/railwatch/processes_controller.rb +33 -0
  30. data/app/controllers/railwatch/profiles_controller.rb +50 -0
  31. data/app/controllers/railwatch/queries_controller.rb +59 -0
  32. data/app/controllers/railwatch/releases_controller.rb +75 -0
  33. data/app/controllers/railwatch/requests_controller.rb +55 -0
  34. data/app/controllers/railwatch/saved_views_controller.rb +53 -0
  35. data/app/controllers/railwatch/scheduled_tasks_controller.rb +45 -0
  36. data/app/controllers/railwatch/spans_controller.rb +47 -0
  37. data/app/controllers/railwatch/storage_ops_controller.rb +15 -0
  38. data/app/controllers/railwatch/tenants_controller.rb +44 -0
  39. data/app/controllers/railwatch/thresholds_controller.rb +40 -0
  40. data/app/controllers/railwatch/traces_controller.rb +72 -0
  41. data/app/controllers/railwatch/transactions_controller.rb +21 -0
  42. data/app/controllers/railwatch/view_renders_controller.rb +28 -0
  43. data/app/controllers/railwatch/visits_controller.rb +53 -0
  44. data/app/helpers/railwatch/assets_helper.rb +52 -0
  45. data/app/jobs/railwatch/anomaly_scan_job.rb +11 -0
  46. data/app/jobs/railwatch/application_job.rb +7 -0
  47. data/app/jobs/railwatch/auto_resolve_issues_job.rb +19 -0
  48. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +113 -0
  49. data/app/jobs/railwatch/detect_anomalies_job.rb +171 -0
  50. data/app/jobs/railwatch/detect_performance_issues_job.rb +85 -0
  51. data/app/jobs/railwatch/group_exceptions_job.rb +91 -0
  52. data/app/jobs/railwatch/optimize_telemetry_job.rb +23 -0
  53. data/app/jobs/railwatch/performance_scan_job.rb +11 -0
  54. data/app/jobs/railwatch/prune_telemetry_job.rb +110 -0
  55. data/app/jobs/railwatch/release_health_rollup_job.rb +64 -0
  56. data/app/jobs/railwatch/rollup_catchup_job.rb +21 -0
  57. data/app/jobs/railwatch/rollup_job.rb +130 -0
  58. data/app/jobs/railwatch/scheduled_task_scan_job.rb +11 -0
  59. data/app/models/concerns/railwatch/detection_snapshotting.rb +20 -0
  60. data/app/models/railwatch/alert.rb +229 -0
  61. data/app/models/railwatch/alert_rule.rb +121 -0
  62. data/app/models/railwatch/anomaly_rule.rb +33 -0
  63. data/app/models/railwatch/application.rb +44 -0
  64. data/app/models/railwatch/application_record.rb +33 -0
  65. data/app/models/railwatch/comment.rb +36 -0
  66. data/app/models/railwatch/deploy.rb +67 -0
  67. data/app/models/railwatch/environment.rb +54 -0
  68. data/app/models/railwatch/execution_presenter.rb +185 -0
  69. data/app/models/railwatch/filter_query.rb +143 -0
  70. data/app/models/railwatch/followup_receipt.rb +27 -0
  71. data/app/models/railwatch/ingest/batch.rb +305 -0
  72. data/app/models/railwatch/ingest/mapper.rb +575 -0
  73. data/app/models/railwatch/ingest/payload.rb +96 -0
  74. data/app/models/railwatch/ingest/rollup_absorber.rb +170 -0
  75. data/app/models/railwatch/ingest/writer.rb +137 -0
  76. data/app/models/railwatch/issue.rb +277 -0
  77. data/app/models/railwatch/issue_activity.rb +23 -0
  78. data/app/models/railwatch/issue_detection_presenter.rb +309 -0
  79. data/app/models/railwatch/issue_detection_snapshot.rb +77 -0
  80. data/app/models/railwatch/maintenance_task.rb +53 -0
  81. data/app/models/railwatch/saved_view.rb +63 -0
  82. data/app/models/railwatch/telemetry/aggregations.rb +116 -0
  83. data/app/models/railwatch/telemetry/attachment.rb +31 -0
  84. data/app/models/railwatch/telemetry/bounded_gzip.rb +101 -0
  85. data/app/models/railwatch/telemetry/broadcast.rb +13 -0
  86. data/app/models/railwatch/telemetry/cache_event.rb +16 -0
  87. data/app/models/railwatch/telemetry/child.rb +53 -0
  88. data/app/models/railwatch/telemetry/cursor_page.rb +99 -0
  89. data/app/models/railwatch/telemetry/deprecation.rb +13 -0
  90. data/app/models/railwatch/telemetry/enqueued_job.rb +13 -0
  91. data/app/models/railwatch/telemetry/exception.rb +21 -0
  92. data/app/models/railwatch/telemetry/execution.rb +71 -0
  93. data/app/models/railwatch/telemetry/health_sample.rb +72 -0
  94. data/app/models/railwatch/telemetry/ingest_batch.rb +31 -0
  95. data/app/models/railwatch/telemetry/llm_call.rb +37 -0
  96. data/app/models/railwatch/telemetry/log.rb +85 -0
  97. data/app/models/railwatch/telemetry/mail.rb +13 -0
  98. data/app/models/railwatch/telemetry/n_plus_one.rb +92 -0
  99. data/app/models/railwatch/telemetry/notification.rb +13 -0
  100. data/app/models/railwatch/telemetry/outgoing_request.rb +13 -0
  101. data/app/models/railwatch/telemetry/person.rb +50 -0
  102. data/app/models/railwatch/telemetry/process.rb +10 -0
  103. data/app/models/railwatch/telemetry/profile.rb +37 -0
  104. data/app/models/railwatch/telemetry/query.rb +50 -0
  105. data/app/models/railwatch/telemetry/query_shape.rb +45 -0
  106. data/app/models/railwatch/telemetry/release_health.rb +63 -0
  107. data/app/models/railwatch/telemetry/rollup.rb +78 -0
  108. data/app/models/railwatch/telemetry/session.rb +24 -0
  109. data/app/models/railwatch/telemetry/span.rb +19 -0
  110. data/app/models/railwatch/telemetry/storage_op.rb +13 -0
  111. data/app/models/railwatch/telemetry/tenant.rb +221 -0
  112. data/app/models/railwatch/telemetry/transaction.rb +13 -0
  113. data/app/models/railwatch/telemetry/view_render.rb +13 -0
  114. data/app/models/railwatch/telemetry/visit.rb +24 -0
  115. data/app/models/railwatch/telemetry_record.rb +57 -0
  116. data/app/models/railwatch/threshold.rb +29 -0
  117. data/app/models/railwatch/user.rb +47 -0
  118. data/app/models/railwatch/viewer.rb +13 -0
  119. data/app/views/layouts/railwatch/dashboard.html.erb +25 -0
  120. data/config/routes.rb +59 -0
  121. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +151 -0
  122. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +18 -0
  123. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +20 -0
  124. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +71 -0
  125. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +481 -0
  126. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +14 -0
  127. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +9 -0
  128. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +10 -0
  129. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +9 -0
  130. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +82 -0
  131. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +10 -0
  132. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +60 -0
  133. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +9 -0
  134. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +51 -0
  135. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +11 -0
  136. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +10 -0
  137. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +37 -0
  138. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +11 -0
  139. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +15 -0
  140. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +9 -0
  141. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +10 -0
  142. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +9 -0
  143. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +18 -0
  144. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +25 -0
  145. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +55 -0
  146. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +21 -0
  147. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +16 -0
  148. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +18 -0
  149. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +16 -0
  150. data/docs/configuration.md +26 -0
  151. data/docs/embedded.md +352 -0
  152. data/docs/getting-started.md +5 -0
  153. data/lib/generators/railwatch/install/install_generator.rb +222 -1
  154. data/lib/generators/railwatch/install/templates/{initializer.rb → initializer.rb.tt} +39 -0
  155. data/lib/generators/railwatch/install/templates/post-deploy +8 -0
  156. data/lib/puma/plugin/railwatch.rb +170 -0
  157. data/lib/railwatch/authentication.rb +83 -0
  158. data/lib/railwatch/configuration.rb +134 -3
  159. data/lib/railwatch/dashboard_assets.rb +45 -0
  160. data/lib/railwatch/embedded.rb +54 -0
  161. data/lib/railwatch/engine.rb +89 -1
  162. data/lib/railwatch/ingest_request_body_limit.rb +10 -0
  163. data/lib/railwatch/json_compat.rb +60 -0
  164. data/lib/railwatch/maintenance.rb +183 -0
  165. data/lib/railwatch/patches/runner_command.rb +21 -1
  166. data/lib/railwatch/record.rb +33 -8
  167. data/lib/railwatch/reporter.rb +16 -0
  168. data/lib/railwatch/subscribers/process_info.rb +2 -1
  169. data/lib/railwatch/transport/local.rb +78 -0
  170. data/lib/railwatch/transport/socket.rb +183 -0
  171. data/lib/railwatch/version.rb +1 -1
  172. data/lib/railwatch/writer.rb +370 -0
  173. data/lib/railwatch.rb +38 -1
  174. data/lib/tasks/railwatch_tasks.rake +128 -0
  175. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  176. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  177. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  178. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  179. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  180. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  181. data/public/railwatch/assets/app-layout-yh-sPWgK.js +1 -0
  182. data/public/railwatch/assets/app-wordmark-nbjkzxwQ.js +1 -0
  183. data/public/railwatch/assets/appearance-CDuRvQTB.js +1 -0
  184. data/public/railwatch/assets/application-C_kpBdnf.css +1 -0
  185. data/public/railwatch/assets/arrow-up-DVtOGdVA.js +1 -0
  186. data/public/railwatch/assets/auth-layout-TCwPpS1K.js +1 -0
  187. data/public/railwatch/assets/badge-Daw4Hvr8.js +1 -0
  188. data/public/railwatch/assets/braces-DhFbHPsz.js +1 -0
  189. data/public/railwatch/assets/card-BX_3HXcJ.js +1 -0
  190. data/public/railwatch/assets/chart-B14-N9g7.js +39 -0
  191. data/public/railwatch/assets/chart-hover-Vy52H4uD.js +1 -0
  192. data/public/railwatch/assets/chart-panel-Cb4S_ej_.js +1 -0
  193. data/public/railwatch/assets/checkbox-D3aRSsBj.js +1 -0
  194. data/public/railwatch/assets/code-KvW8k7Jr.js +1 -0
  195. data/public/railwatch/assets/copy-DaQMJWoT.js +1 -0
  196. data/public/railwatch/assets/copy-block-BKFGd11J.js +1 -0
  197. data/public/railwatch/assets/copy-id-Djks1fXB.js +1 -0
  198. data/public/railwatch/assets/cursor-load-more-BXV0f1D_.js +1 -0
  199. data/public/railwatch/assets/data-table-iv1bdF6u.js +1 -0
  200. data/public/railwatch/assets/edit-BZc_Iawe.js +1 -0
  201. data/public/railwatch/assets/edit-DMKUF8Zi.js +8 -0
  202. data/public/railwatch/assets/edit-__9yJlO3.js +1 -0
  203. data/public/railwatch/assets/empty-state-6j_0AaQQ.js +1 -0
  204. data/public/railwatch/assets/env-layout-DrT8rO6P.js +1 -0
  205. data/public/railwatch/assets/execution-path-CzgBUi5e.js +1 -0
  206. data/public/railwatch/assets/filter-bar-9SU5NrzX.js +1 -0
  207. data/public/railwatch/assets/flamegraph-qcekju8V.js +2 -0
  208. data/public/railwatch/assets/format-B9SDkrWj.js +1 -0
  209. data/public/railwatch/assets/frames-Cyu7KMxZ.js +1 -0
  210. data/public/railwatch/assets/google-sign-in-button-DsTSfmzY.js +1 -0
  211. data/public/railwatch/assets/index-1ol1-QWI.js +1 -0
  212. data/public/railwatch/assets/index-5jI4aFzC.js +1 -0
  213. data/public/railwatch/assets/index-9KTrVnrc.js +1 -0
  214. data/public/railwatch/assets/index-B0-8lcTp.js +1 -0
  215. data/public/railwatch/assets/index-B7jjfNfO.js +1 -0
  216. data/public/railwatch/assets/index-BBchRy0M.js +1 -0
  217. data/public/railwatch/assets/index-BRiq3SNR.js +1 -0
  218. data/public/railwatch/assets/index-BgKj9xhr.js +1 -0
  219. data/public/railwatch/assets/index-BkTZqqOu.js +1 -0
  220. data/public/railwatch/assets/index-BprKx8QO.js +1 -0
  221. data/public/railwatch/assets/index-C3jzvPs3.js +1 -0
  222. data/public/railwatch/assets/index-Cbs6gGyQ.js +1 -0
  223. data/public/railwatch/assets/index-CdRZ6AWF.js +1 -0
  224. data/public/railwatch/assets/index-CeYKnapu.js +1 -0
  225. data/public/railwatch/assets/index-Cmlwy1-V.js +1 -0
  226. data/public/railwatch/assets/index-Cwx6058d.js +1 -0
  227. data/public/railwatch/assets/index-D4CSdbHv.js +1 -0
  228. data/public/railwatch/assets/index-DEFMSkdG.js +1 -0
  229. data/public/railwatch/assets/index-DFiHEBSh.js +1 -0
  230. data/public/railwatch/assets/index-DL4vWdWJ.js +1 -0
  231. data/public/railwatch/assets/index-DU9F5b5d.js +1 -0
  232. data/public/railwatch/assets/index-DaXgPcGL.js +1 -0
  233. data/public/railwatch/assets/index-DbtaU-EE.js +1 -0
  234. data/public/railwatch/assets/index-DeOe83F4.js +1 -0
  235. data/public/railwatch/assets/index-DiucHN4B.js +1 -0
  236. data/public/railwatch/assets/index-DlnR_l9o.js +1 -0
  237. data/public/railwatch/assets/index-DlumCsWY.js +2 -0
  238. data/public/railwatch/assets/index-DmRd7aIG.js +1 -0
  239. data/public/railwatch/assets/index-DxSh2UpM.js +1 -0
  240. data/public/railwatch/assets/index-MIMGuFNt.js +1 -0
  241. data/public/railwatch/assets/index-OqI59zPb.js +1 -0
  242. data/public/railwatch/assets/index-P4rC7IlX.js +1 -0
  243. data/public/railwatch/assets/index-gpPOcFWq.js +1 -0
  244. data/public/railwatch/assets/index-oVkururr.js +1 -0
  245. data/public/railwatch/assets/index-p9puqVge.js +1 -0
  246. data/public/railwatch/assets/inertia-TViv6kNv.js +97 -0
  247. data/public/railwatch/assets/input-error-LxImUkxv.js +1 -0
  248. data/public/railwatch/assets/json-viewer-Ar4cjPDW.js +1 -0
  249. data/public/railwatch/assets/klass-CJ-J4INB.js +1 -0
  250. data/public/railwatch/assets/label-COUKWqE_.js +1 -0
  251. data/public/railwatch/assets/layout-DNSLAkw_.js +1 -0
  252. data/public/railwatch/assets/live-dot-ChfUtY3p.js +41 -0
  253. data/public/railwatch/assets/nav-CNnDqPlm.js +1 -0
  254. data/public/railwatch/assets/new-84S8ZJq9.js +1 -0
  255. data/public/railwatch/assets/new-Be55nmt9.js +1 -0
  256. data/public/railwatch/assets/new-Bi_xQiIb.js +1 -0
  257. data/public/railwatch/assets/new-BvCT8TMg.js +1 -0
  258. data/public/railwatch/assets/new-D05SajFR.js +1 -0
  259. data/public/railwatch/assets/new-D4uewYC8.js +1 -0
  260. data/public/railwatch/assets/onboarding-CYZi5Cqc.js +1 -0
  261. data/public/railwatch/assets/origin-identity-6q1-CBts.js +1 -0
  262. data/public/railwatch/assets/percentile-picker-gFZCXtdb.js +1 -0
  263. data/public/railwatch/assets/relative-time-IOOgl5n2.js +1 -0
  264. data/public/railwatch/assets/release-health-DC8oc7uw.js +1 -0
  265. data/public/railwatch/assets/route-Dv6LAWvT.js +1 -0
  266. data/public/railwatch/assets/segmented-h1VdDTqE.js +1 -0
  267. data/public/railwatch/assets/select-_AJsUa7X.js +1 -0
  268. data/public/railwatch/assets/separator-BwwTYtCF.js +1 -0
  269. data/public/railwatch/assets/series-chart-DaFPefku.js +1 -0
  270. data/public/railwatch/assets/show-B7NCgkEo.js +1 -0
  271. data/public/railwatch/assets/show-BKqyKjBK.js +1 -0
  272. data/public/railwatch/assets/show-BM6X2Mpo.js +1 -0
  273. data/public/railwatch/assets/show-BNw4tN5q.js +1 -0
  274. data/public/railwatch/assets/show-BO3bnG5h.js +1 -0
  275. data/public/railwatch/assets/show-BhrAVAEA.js +1 -0
  276. data/public/railwatch/assets/show-C4Ltf5i9.js +2 -0
  277. data/public/railwatch/assets/show-C8sHalnw.js +1 -0
  278. data/public/railwatch/assets/show-CeTL4B37.js +2 -0
  279. data/public/railwatch/assets/show-CpfgV1jP.js +1 -0
  280. data/public/railwatch/assets/show-DACku6AD.js +3 -0
  281. data/public/railwatch/assets/show-DIOSGcXV.js +6 -0
  282. data/public/railwatch/assets/show-DQp_1n-B.js +1 -0
  283. data/public/railwatch/assets/show-DVNz46RI.js +1 -0
  284. data/public/railwatch/assets/show-DYteoYWW.js +1 -0
  285. data/public/railwatch/assets/show-DgSIoRvA.js +1 -0
  286. data/public/railwatch/assets/show-JxFtB4eK.js +2 -0
  287. data/public/railwatch/assets/sort-header-DpFzXblu.js +1 -0
  288. data/public/railwatch/assets/source-link-B2183i2-.js +1 -0
  289. data/public/railwatch/assets/sparkline-cell-C3-5vFkP.js +1 -0
  290. data/public/railwatch/assets/stat-s4RpOS9w.js +1 -0
  291. data/public/railwatch/assets/status-badge-8jVV-LA4.js +1 -0
  292. data/public/railwatch/assets/tenant-path-G-6u9A-o.js +1 -0
  293. data/public/railwatch/assets/text-link-DfsiaCcP.js +1 -0
  294. data/public/railwatch/assets/textarea-Dye72uP7.js +1 -0
  295. data/public/railwatch/assets/timeline-CD7WHnbo.js +1 -0
  296. data/public/railwatch/assets/transition-B_AW8rMK.js +5 -0
  297. data/public/railwatch/assets/use-clipboard-ColgLyQ2.js +1 -0
  298. data/public/railwatch/icon.png +0 -0
  299. data/public/railwatch/icon.svg +5 -0
  300. data/public/railwatch/manifest.json +2171 -0
  301. data/public/railwatch/rails-vite.json +1 -0
  302. metadata +314 -4
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The embedded install's maintenance lease table: one row per task in
4
+ # Railwatch::Maintenance::TASKS, recording when it last ran and which process
5
+ # holds it now. Lives in the railwatch database with the other permanent,
6
+ # operator-facing state rather than in the pruned telemetry file.
7
+ class CreateRailwatchMaintenanceTasks < ActiveRecord::Migration[8.1]
8
+ def change
9
+ create_table :railwatch_maintenance_tasks do |t|
10
+ t.string :name, null: false
11
+ t.datetime :last_run_at
12
+ t.string :lease_owner
13
+ t.datetime :lease_expires_at
14
+ t.timestamps
15
+ end
16
+ add_index :railwatch_maintenance_tasks, :name, unique: true
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # What makes replaying a batch's follow-ups safe. Exception grouping bumps an
4
+ # issue's occurrence count in this database, but the "done" mark lives on the
5
+ # batch's ledger row in the OTHER database, so a crash between the two (or
6
+ # the inline drain racing the maintenance drain) would count the same
7
+ # exceptions twice. A receipt per (batch, group) is inserted in the same
8
+ # transaction as the issue update; the unique index turns a second attempt
9
+ # into a no-op.
10
+ class CreateRailwatchFollowupReceipts < ActiveRecord::Migration[8.1]
11
+ def change
12
+ create_table :railwatch_followup_receipts do |t|
13
+ t.string :batch_id, limit: 36, null: false
14
+ t.string :group_hash, limit: 32, null: false
15
+ t.datetime :created_at, null: false
16
+ end
17
+ add_index :railwatch_followup_receipts, [ :batch_id, :group_hash ], unique: true
18
+ add_index :railwatch_followup_receipts, :created_at
19
+ end
20
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The ids on comments, issue activity, saved views and issue assignment are
4
+ # whatever the host's `dashboard_user` resolver returned. They were integer
5
+ # columns, which quietly assumed every application numbers its users. An app
6
+ # with UUID primary keys could not store a comment at all, and one with ids
7
+ # past the signed range could not either.
8
+ #
9
+ # Nothing joins on these columns and nothing indexes them; they are an opaque
10
+ # handle the host gave us and hands back. A string says that, and costs
11
+ # nothing in a database that is a few thousand rows of authored content.
12
+ #
13
+ # Existing values convert cleanly: SQLite rewrites 7 as "7", and the models
14
+ # compare with to_s on both sides, so a row written before this migration
15
+ # still matches the same viewer after it.
16
+ class WidenHostUserIds < ActiveRecord::Migration[8.1]
17
+ COLUMNS = {
18
+ railwatch_issues: :assignee_id,
19
+ railwatch_comments: :viewer_id,
20
+ railwatch_issue_activities: :viewer_id,
21
+ railwatch_saved_views: :viewer_id
22
+ }.freeze
23
+
24
+ # SQLite rebuilds a table to change a column type, and a rebuild inside a
25
+ # transaction ignores `PRAGMA foreign_keys = OFF`. This database declares no
26
+ # foreign keys, so there is nothing to cascade, but the rebuild is kept out
27
+ # of the migrator's transaction anyway: it is the cheap half of a habit
28
+ # whose expensive half is deleted rows.
29
+ disable_ddl_transaction!
30
+
31
+ def up
32
+ COLUMNS.each do |table, column|
33
+ null = column == :viewer_id && table == :railwatch_saved_views ? false : true
34
+ change_column table, column, :string, limit: 255, null: null
35
+ end
36
+ end
37
+
38
+ # Reversible only while every stored id still looks like a number. Once a
39
+ # host with UUIDs (or emails, or anything else) has written one, there is
40
+ # no integer to go back to: the column would either refuse the value or
41
+ # quietly coerce it to something that is no longer that person. Say so and
42
+ # stop, rather than losing the identity on the way down. These tables are
43
+ # authored content, a few thousand rows at most, so reading them is cheap.
44
+ INTEGERISH = /\A-?\d+\z/
45
+ MAX_REPORTED = 3
46
+
47
+ def down
48
+ blocking = COLUMNS.filter_map do |table, column|
49
+ next unless connection.table_exists?(table)
50
+
51
+ values = connection.select_values(
52
+ "SELECT DISTINCT #{connection.quote_column_name(column)} FROM #{connection.quote_table_name(table)} " \
53
+ "WHERE #{connection.quote_column_name(column)} IS NOT NULL"
54
+ )
55
+ offenders = values.reject { |value| INTEGERISH.match?(value.to_s) }
56
+ "#{table}.#{column} (#{offenders.first(MAX_REPORTED).join(', ')}#{"..." if offenders.size > MAX_REPORTED})" if offenders.any?
57
+ end
58
+
59
+ if blocking.any?
60
+ raise ActiveRecord::IrreversibleMigration,
61
+ "cannot narrow host user ids back to integers: #{blocking.join('; ')}. " \
62
+ "Those ids came from this application's own dashboard_user resolver and have no integer form; " \
63
+ "rolling back would discard them."
64
+ end
65
+
66
+ COLUMNS.each do |table, column|
67
+ null = column == :viewer_id && table == :railwatch_saved_views ? false : true
68
+ change_column table, column, :integer, null: null
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,481 @@
1
+ # frozen_string_literal: true
2
+
3
+ # One table per record type, matching the gem's wire types one to one.
4
+ # Every table starts with the shared envelope. JSON columns hold the
5
+ # variable parts; hot filter columns are real columns with indexes.
6
+ class CreateTelemetry < ActiveRecord::Migration[8.1]
7
+ def change
8
+ create_table :executions do |t|
9
+ # The record envelope as it was when this migration shipped; inlined so
10
+ # the history never calls a model that has since changed.
11
+ t.datetime :occurred_at, null: false, precision: 6
12
+ t.string :deploy, limit: 128
13
+ t.string :server, limit: 255
14
+ t.string :group_hash, limit: 32
15
+ t.string :trace_id, limit: 36
16
+ t.string :execution_source, limit: 20
17
+ t.string :execution_id, limit: 36
18
+ t.string :execution_preview, limit: 255
19
+ t.string :execution_stage, limit: 32
20
+ t.string :user_ref, limit: 255
21
+ t.string :tenant, limit: 255
22
+ t.string :kind, null: false, limit: 20 # request, job_attempt, scheduled_task, command
23
+ t.string :name, null: false, limit: 255 # "GET /widgets/:id", "WidgetJob", "rake db:migrate"
24
+ t.integer :duration, null: false # microseconds
25
+ t.integer :status # http status, or exit code
26
+ t.string :outcome, limit: 20 # processed, failed, aborted
27
+ t.string :method, limit: 10
28
+ t.string :route, limit: 255
29
+ t.string :controller, limit: 255
30
+ t.string :action, limit: 128
31
+ t.string :queue, limit: 128
32
+ t.integer :attempt
33
+ t.integer :queue_latency
34
+ t.string :job_id, limit: 36
35
+ t.string :task_key, limit: 128
36
+ t.string :inertia_component, limit: 255
37
+ t.boolean :inertia_partial, default: false
38
+ t.integer :allocations
39
+ t.bigint :peak_memory
40
+ t.string :exception_preview, limit: 255
41
+ t.json :stages, null: false, default: {}
42
+ t.json :counters, null: false, default: {}
43
+ t.json :detail, null: false, default: {} # headers, payload, url, ip, user agent, context, extras
44
+ end
45
+ add_index :executions, [ :kind, :occurred_at ]
46
+ add_index :executions, [ :group_hash, :occurred_at ]
47
+ add_index :executions, :execution_id, unique: true
48
+ add_index :executions, :trace_id
49
+ add_index :executions, [ :user_ref, :occurred_at ]
50
+ add_index :executions, [ :deploy, :occurred_at ]
51
+ add_index :executions, [ :tenant, :occurred_at ]
52
+
53
+ create_table :queries do |t|
54
+ # The record envelope as it was when this migration shipped; inlined so
55
+ # the history never calls a model that has since changed.
56
+ t.datetime :occurred_at, null: false, precision: 6
57
+ t.string :deploy, limit: 128
58
+ t.string :server, limit: 255
59
+ t.string :group_hash, limit: 32
60
+ t.string :trace_id, limit: 36
61
+ t.string :execution_source, limit: 20
62
+ t.string :execution_id, limit: 36
63
+ t.string :execution_preview, limit: 255
64
+ t.string :execution_stage, limit: 32
65
+ t.string :user_ref, limit: 255
66
+ t.string :tenant, limit: 255
67
+ t.text :sql, null: false
68
+ t.string :name, limit: 255
69
+ t.integer :duration, null: false
70
+ t.string :connection, limit: 64
71
+ t.string :adapter, limit: 32
72
+ t.boolean :async, default: false
73
+ t.boolean :in_transaction, default: false
74
+ t.integer :row_count
75
+ t.string :source, limit: 255
76
+ t.integer :allocations
77
+ end
78
+ add_index :queries, [ :group_hash, :occurred_at ]
79
+ add_index :queries, [ :execution_id, :occurred_at ]
80
+ add_index :queries, [ :occurred_at, :duration ]
81
+
82
+ create_table :exceptions do |t|
83
+ # The record envelope as it was when this migration shipped; inlined so
84
+ # the history never calls a model that has since changed.
85
+ t.datetime :occurred_at, null: false, precision: 6
86
+ t.string :deploy, limit: 128
87
+ t.string :server, limit: 255
88
+ t.string :group_hash, limit: 32
89
+ t.string :trace_id, limit: 36
90
+ t.string :execution_source, limit: 20
91
+ t.string :execution_id, limit: 36
92
+ t.string :execution_preview, limit: 255
93
+ t.string :execution_stage, limit: 32
94
+ t.string :user_ref, limit: 255
95
+ t.string :tenant, limit: 255
96
+ t.string :class_name, null: false, limit: 255
97
+ t.text :message, null: false
98
+ t.boolean :handled, null: false, default: false
99
+ t.string :severity, limit: 16
100
+ t.string :source, limit: 128
101
+ t.string :file, limit: 255
102
+ t.integer :line
103
+ t.json :frames, null: false, default: []
104
+ t.json :cause
105
+ t.text :context
106
+ t.string :ruby_version, limit: 16
107
+ t.string :rails_version, limit: 16
108
+ end
109
+ add_index :exceptions, [ :group_hash, :occurred_at ]
110
+ add_index :exceptions, [ :execution_id ]
111
+ add_index :exceptions, [ :occurred_at ]
112
+
113
+ create_table :cache_events do |t|
114
+ # The record envelope as it was when this migration shipped; inlined so
115
+ # the history never calls a model that has since changed.
116
+ t.datetime :occurred_at, null: false, precision: 6
117
+ t.string :deploy, limit: 128
118
+ t.string :server, limit: 255
119
+ t.string :group_hash, limit: 32
120
+ t.string :trace_id, limit: 36
121
+ t.string :execution_source, limit: 20
122
+ t.string :execution_id, limit: 36
123
+ t.string :execution_preview, limit: 255
124
+ t.string :execution_stage, limit: 32
125
+ t.string :user_ref, limit: 255
126
+ t.string :tenant, limit: 255
127
+ t.string :store, limit: 64
128
+ t.string :key, limit: 255
129
+ t.string :type, null: false, limit: 20
130
+ t.integer :duration, null: false
131
+ t.integer :ttl
132
+ t.integer :hits
133
+ end
134
+ add_index :cache_events, [ :group_hash, :occurred_at ]
135
+ add_index :cache_events, [ :execution_id ]
136
+
137
+ create_table :mails do |t|
138
+ # The record envelope as it was when this migration shipped; inlined so
139
+ # the history never calls a model that has since changed.
140
+ t.datetime :occurred_at, null: false, precision: 6
141
+ t.string :deploy, limit: 128
142
+ t.string :server, limit: 255
143
+ t.string :group_hash, limit: 32
144
+ t.string :trace_id, limit: 36
145
+ t.string :execution_source, limit: 20
146
+ t.string :execution_id, limit: 36
147
+ t.string :execution_preview, limit: 255
148
+ t.string :execution_stage, limit: 32
149
+ t.string :user_ref, limit: 255
150
+ t.string :tenant, limit: 255
151
+ t.string :mailer, null: false, limit: 255
152
+ t.string :subject, limit: 255
153
+ t.integer :to, default: 0
154
+ t.integer :cc, default: 0
155
+ t.integer :bcc, default: 0
156
+ t.integer :attachments, default: 0
157
+ t.string :delivery_method, limit: 64
158
+ t.boolean :perform_deliveries, default: true
159
+ t.integer :duration, null: false
160
+ t.boolean :failed, default: false
161
+ t.string :message_id, limit: 255
162
+ end
163
+ add_index :mails, [ :group_hash, :occurred_at ]
164
+ add_index :mails, [ :execution_id ]
165
+
166
+ create_table :broadcasts do |t|
167
+ # The record envelope as it was when this migration shipped; inlined so
168
+ # the history never calls a model that has since changed.
169
+ t.datetime :occurred_at, null: false, precision: 6
170
+ t.string :deploy, limit: 128
171
+ t.string :server, limit: 255
172
+ t.string :group_hash, limit: 32
173
+ t.string :trace_id, limit: 36
174
+ t.string :execution_source, limit: 20
175
+ t.string :execution_id, limit: 36
176
+ t.string :execution_preview, limit: 255
177
+ t.string :execution_stage, limit: 32
178
+ t.string :user_ref, limit: 255
179
+ t.string :tenant, limit: 255
180
+ t.string :kind, null: false, limit: 20
181
+ t.string :stream, limit: 255
182
+ t.string :channel, limit: 255
183
+ t.string :action, limit: 128
184
+ t.integer :bytes
185
+ t.integer :duration, null: false
186
+ end
187
+ add_index :broadcasts, [ :group_hash, :occurred_at ]
188
+ add_index :broadcasts, [ :execution_id ]
189
+
190
+ create_table :notifications do |t|
191
+ # The record envelope as it was when this migration shipped; inlined so
192
+ # the history never calls a model that has since changed.
193
+ t.datetime :occurred_at, null: false, precision: 6
194
+ t.string :deploy, limit: 128
195
+ t.string :server, limit: 255
196
+ t.string :group_hash, limit: 32
197
+ t.string :trace_id, limit: 36
198
+ t.string :execution_source, limit: 20
199
+ t.string :execution_id, limit: 36
200
+ t.string :execution_preview, limit: 255
201
+ t.string :execution_stage, limit: 32
202
+ t.string :user_ref, limit: 255
203
+ t.string :tenant, limit: 255
204
+ t.string :notifier, limit: 255
205
+ t.string :delivery_method, limit: 128
206
+ t.integer :duration, null: false
207
+ t.boolean :failed, default: false
208
+ end
209
+ add_index :notifications, [ :group_hash, :occurred_at ]
210
+
211
+ create_table :outgoing_requests do |t|
212
+ # The record envelope as it was when this migration shipped; inlined so
213
+ # the history never calls a model that has since changed.
214
+ t.datetime :occurred_at, null: false, precision: 6
215
+ t.string :deploy, limit: 128
216
+ t.string :server, limit: 255
217
+ t.string :group_hash, limit: 32
218
+ t.string :trace_id, limit: 36
219
+ t.string :execution_source, limit: 20
220
+ t.string :execution_id, limit: 36
221
+ t.string :execution_preview, limit: 255
222
+ t.string :execution_stage, limit: 32
223
+ t.string :user_ref, limit: 255
224
+ t.string :tenant, limit: 255
225
+ t.string :host, null: false, limit: 255
226
+ t.string :method, null: false, limit: 10
227
+ t.string :url, limit: 2048
228
+ t.integer :duration, null: false
229
+ t.integer :status_code
230
+ t.integer :request_size
231
+ t.integer :response_size
232
+ t.string :error, limit: 255
233
+ t.string :source, limit: 255
234
+ end
235
+ add_index :outgoing_requests, [ :group_hash, :occurred_at ]
236
+ add_index :outgoing_requests, [ :execution_id ]
237
+
238
+ create_table :storage_ops do |t|
239
+ # The record envelope as it was when this migration shipped; inlined so
240
+ # the history never calls a model that has since changed.
241
+ t.datetime :occurred_at, null: false, precision: 6
242
+ t.string :deploy, limit: 128
243
+ t.string :server, limit: 255
244
+ t.string :group_hash, limit: 32
245
+ t.string :trace_id, limit: 36
246
+ t.string :execution_source, limit: 20
247
+ t.string :execution_id, limit: 36
248
+ t.string :execution_preview, limit: 255
249
+ t.string :execution_stage, limit: 32
250
+ t.string :user_ref, limit: 255
251
+ t.string :tenant, limit: 255
252
+ t.string :service, limit: 64
253
+ t.string :op, null: false, limit: 32
254
+ t.string :key, limit: 255
255
+ t.integer :duration, null: false
256
+ end
257
+ add_index :storage_ops, [ :group_hash, :occurred_at ]
258
+
259
+ create_table :view_renders do |t|
260
+ # The record envelope as it was when this migration shipped; inlined so
261
+ # the history never calls a model that has since changed.
262
+ t.datetime :occurred_at, null: false, precision: 6
263
+ t.string :deploy, limit: 128
264
+ t.string :server, limit: 255
265
+ t.string :group_hash, limit: 32
266
+ t.string :trace_id, limit: 36
267
+ t.string :execution_source, limit: 20
268
+ t.string :execution_id, limit: 36
269
+ t.string :execution_preview, limit: 255
270
+ t.string :execution_stage, limit: 32
271
+ t.string :user_ref, limit: 255
272
+ t.string :tenant, limit: 255
273
+ t.string :identifier, null: false, limit: 255
274
+ t.string :kind, limit: 20
275
+ t.string :layout, limit: 255
276
+ t.integer :count
277
+ t.integer :cache_hits
278
+ t.integer :duration, null: false
279
+ end
280
+ add_index :view_renders, [ :group_hash, :occurred_at ]
281
+ add_index :view_renders, [ :execution_id ]
282
+
283
+ create_table :logs do |t|
284
+ # The record envelope as it was when this migration shipped; inlined so
285
+ # the history never calls a model that has since changed.
286
+ t.datetime :occurred_at, null: false, precision: 6
287
+ t.string :deploy, limit: 128
288
+ t.string :server, limit: 255
289
+ t.string :group_hash, limit: 32
290
+ t.string :trace_id, limit: 36
291
+ t.string :execution_source, limit: 20
292
+ t.string :execution_id, limit: 36
293
+ t.string :execution_preview, limit: 255
294
+ t.string :execution_stage, limit: 32
295
+ t.string :user_ref, limit: 255
296
+ t.string :tenant, limit: 255
297
+ t.string :level, null: false, limit: 10
298
+ t.text :message, null: false
299
+ t.json :tags, null: false, default: []
300
+ t.text :context
301
+ t.string :source, limit: 255
302
+ end
303
+ add_index :logs, [ :level, :occurred_at ]
304
+ add_index :logs, [ :execution_id ]
305
+ add_index :logs, [ :occurred_at ]
306
+
307
+ create_table :enqueued_jobs do |t|
308
+ # The record envelope as it was when this migration shipped; inlined so
309
+ # the history never calls a model that has since changed.
310
+ t.datetime :occurred_at, null: false, precision: 6
311
+ t.string :deploy, limit: 128
312
+ t.string :server, limit: 255
313
+ t.string :group_hash, limit: 32
314
+ t.string :trace_id, limit: 36
315
+ t.string :execution_source, limit: 20
316
+ t.string :execution_id, limit: 36
317
+ t.string :execution_preview, limit: 255
318
+ t.string :execution_stage, limit: 32
319
+ t.string :user_ref, limit: 255
320
+ t.string :tenant, limit: 255
321
+ t.string :job_id, limit: 36
322
+ t.string :name, null: false, limit: 255
323
+ t.string :queue, limit: 128
324
+ t.string :adapter, limit: 64
325
+ t.integer :priority
326
+ t.datetime :scheduled_at, precision: 6
327
+ t.integer :duration, null: false
328
+ t.boolean :failed, default: false
329
+ end
330
+ add_index :enqueued_jobs, [ :group_hash, :occurred_at ]
331
+ add_index :enqueued_jobs, [ :execution_id ]
332
+ add_index :enqueued_jobs, :job_id
333
+
334
+ create_table :transactions do |t|
335
+ # The record envelope as it was when this migration shipped; inlined so
336
+ # the history never calls a model that has since changed.
337
+ t.datetime :occurred_at, null: false, precision: 6
338
+ t.string :deploy, limit: 128
339
+ t.string :server, limit: 255
340
+ t.string :group_hash, limit: 32
341
+ t.string :trace_id, limit: 36
342
+ t.string :execution_source, limit: 20
343
+ t.string :execution_id, limit: 36
344
+ t.string :execution_preview, limit: 255
345
+ t.string :execution_stage, limit: 32
346
+ t.string :user_ref, limit: 255
347
+ t.string :tenant, limit: 255
348
+ t.integer :duration, null: false
349
+ t.string :outcome, limit: 20
350
+ t.string :connection, limit: 64
351
+ end
352
+ add_index :transactions, [ :execution_id ]
353
+
354
+ create_table :n_plus_ones do |t|
355
+ # The record envelope as it was when this migration shipped; inlined so
356
+ # the history never calls a model that has since changed.
357
+ t.datetime :occurred_at, null: false, precision: 6
358
+ t.string :deploy, limit: 128
359
+ t.string :server, limit: 255
360
+ t.string :group_hash, limit: 32
361
+ t.string :trace_id, limit: 36
362
+ t.string :execution_source, limit: 20
363
+ t.string :execution_id, limit: 36
364
+ t.string :execution_preview, limit: 255
365
+ t.string :execution_stage, limit: 32
366
+ t.string :user_ref, limit: 255
367
+ t.string :tenant, limit: 255
368
+ t.text :sql, null: false
369
+ t.integer :count, null: false
370
+ t.string :source, limit: 255
371
+ end
372
+ add_index :n_plus_ones, [ :group_hash, :occurred_at ]
373
+
374
+ create_table :deprecations do |t|
375
+ # The record envelope as it was when this migration shipped; inlined so
376
+ # the history never calls a model that has since changed.
377
+ t.datetime :occurred_at, null: false, precision: 6
378
+ t.string :deploy, limit: 128
379
+ t.string :server, limit: 255
380
+ t.string :group_hash, limit: 32
381
+ t.string :trace_id, limit: 36
382
+ t.string :execution_source, limit: 20
383
+ t.string :execution_id, limit: 36
384
+ t.string :execution_preview, limit: 255
385
+ t.string :execution_stage, limit: 32
386
+ t.string :user_ref, limit: 255
387
+ t.string :tenant, limit: 255
388
+ t.text :message, null: false
389
+ t.string :gem_name, limit: 128
390
+ t.string :horizon, limit: 32
391
+ t.string :source, limit: 255
392
+ end
393
+ add_index :deprecations, [ :group_hash, :occurred_at ]
394
+
395
+ create_table :visits do |t|
396
+ # The record envelope as it was when this migration shipped; inlined so
397
+ # the history never calls a model that has since changed.
398
+ t.datetime :occurred_at, null: false, precision: 6
399
+ t.string :deploy, limit: 128
400
+ t.string :server, limit: 255
401
+ t.string :group_hash, limit: 32
402
+ t.string :trace_id, limit: 36
403
+ t.string :execution_source, limit: 20
404
+ t.string :execution_id, limit: 36
405
+ t.string :execution_preview, limit: 255
406
+ t.string :execution_stage, limit: 32
407
+ t.string :user_ref, limit: 255
408
+ t.string :tenant, limit: 255
409
+ t.string :component, limit: 255
410
+ t.string :url, limit: 2048
411
+ t.string :method, limit: 10
412
+ t.integer :duration, null: false
413
+ t.string :status, limit: 20
414
+ t.boolean :partial, default: false
415
+ t.json :only, null: false, default: []
416
+ t.integer :props_bytes
417
+ t.string :user_agent, limit: 255
418
+ end
419
+ add_index :visits, [ :group_hash, :occurred_at ]
420
+
421
+ create_table :people do |t|
422
+ t.string :ref, null: false, limit: 255 # tenant:id or id
423
+ t.string :name, limit: 255
424
+ t.string :email, limit: 255
425
+ t.string :tenant, limit: 255
426
+ t.datetime :first_seen_at, precision: 6
427
+ t.datetime :last_seen_at, precision: 6
428
+ t.integer :requests_count, null: false, default: 0
429
+ t.integer :exceptions_count, null: false, default: 0
430
+ end
431
+ add_index :people, :ref, unique: true
432
+ add_index :people, :last_seen_at
433
+
434
+ create_table :processes do |t|
435
+ t.datetime :booted_at, null: false, precision: 6
436
+ t.integer :pid
437
+ t.string :role, limit: 20
438
+ t.string :server, limit: 255
439
+ t.string :deploy, limit: 128
440
+ t.string :ruby_version, limit: 16
441
+ t.string :rails_version, limit: 16
442
+ t.string :lantern_version, limit: 16
443
+ t.float :boot_seconds
444
+ t.json :detail, null: false, default: {}
445
+ end
446
+ add_index :processes, :booted_at
447
+
448
+ # Hourly aggregates per record type and group. Percentiles come from a
449
+ # t-digest blob so they merge across hours without the raw rows.
450
+ create_table :rollups do |t|
451
+ t.datetime :bucket, null: false # start of the hour, UTC
452
+ t.string :record_type, null: false, limit: 32 # request, job_attempt, query, ...
453
+ t.string :group_hash, null: false, limit: 32
454
+ t.string :name, null: false, limit: 255
455
+ t.bigint :count, null: false, default: 0
456
+ t.bigint :error_count, null: false, default: 0 # 5xx, failed, exceptions
457
+ t.bigint :client_error_count, null: false, default: 0
458
+ t.bigint :duration_sum, null: false, default: 0
459
+ t.integer :duration_max, null: false, default: 0
460
+ t.integer :p50, null: false, default: 0
461
+ t.integer :p95, null: false, default: 0
462
+ t.integer :p99, null: false, default: 0
463
+ t.binary :digest
464
+ t.json :extra, null: false, default: {}
465
+ end
466
+ add_index :rollups, [ :record_type, :group_hash, :bucket ], unique: true
467
+ add_index :rollups, [ :record_type, :bucket ]
468
+
469
+ create_table :ingest_batches do |t|
470
+ t.datetime :received_at, null: false, precision: 6
471
+ t.integer :accepted, null: false, default: 0
472
+ t.integer :rejected, null: false, default: 0
473
+ t.integer :dropped_by_client, null: false, default: 0
474
+ t.integer :bytes, null: false, default: 0
475
+ t.string :gem_version, limit: 16
476
+ t.json :counts_by_type, null: false, default: {}
477
+ t.json :rejections, null: false, default: []
478
+ end
479
+ add_index :ingest_batches, :received_at
480
+ end
481
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # activerecord-tenanted defines `#tenant` on every tenanted model (the DB the
4
+ # row lives in), which shadowed the wire record's own tenant column. The
5
+ # monitored app's tenant is a different thing, so it gets its own name.
6
+ class RenameTenantToAppTenant < ActiveRecord::Migration[8.1]
7
+ TABLES = %i[executions queries exceptions cache_events mails broadcasts notifications outgoing_requests
8
+ storage_ops view_renders logs enqueued_jobs transactions n_plus_ones deprecations visits].freeze
9
+
10
+ def change
11
+ TABLES.each { |t| rename_column t, :tenant, :app_tenant }
12
+ rename_column :people, :tenant, :app_tenant
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The gem reports how many statements ran inside each transaction; the
4
+ # ingest mapper dropped it because the column never existed.
5
+ class AddStatementCountToTransactions < ActiveRecord::Migration[8.1]
6
+ def change
7
+ add_column :transactions, :statement_count, :integer
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Nightwatch parity: a query's multi-DB role (writing/reading) and a
4
+ # notification's channel (email/slack/...) alongside the delivery class.
5
+ class AddRoleAndChannel < ActiveRecord::Migration[8.1]
6
+ def change
7
+ add_column :queries, :role, :string, limit: 16
8
+ add_column :notifications, :channel, :string, limit: 64
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Redacted local variables at the raise site, when the gem is configured to
4
+ # capture them (NIGHTRAIL_CAPTURE_EXCEPTION_LOCALS).
5
+ class AddLocalsToExceptions < ActiveRecord::Migration[8.1]
6
+ def change
7
+ add_column :exceptions, :locals, :json
8
+ end
9
+ end