railwatch 0.9.3 → 1.0.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 (454) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +66 -0
  4. data/README.md +51 -60
  5. data/config/routes.rb +0 -59
  6. data/docs/ai-and-mcp.md +9 -8
  7. data/docs/configuration.md +57 -71
  8. data/docs/faq.md +17 -25
  9. data/docs/getting-started.md +105 -117
  10. data/docs/replacing-nightwatch.md +8 -7
  11. data/docs/replacing-sentry.md +12 -16
  12. data/docs/security.md +14 -20
  13. data/docs/self-hosting.md +24 -22
  14. data/docs/testing.md +6 -14
  15. data/docs/troubleshooting.md +12 -46
  16. data/lib/generators/railwatch/install/install_generator.rb +12 -240
  17. data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
  18. data/lib/generators/railwatch/install/templates/post-deploy +0 -7
  19. data/lib/railwatch/authentication.rb +6 -5
  20. data/lib/railwatch/buffer.rb +9 -0
  21. data/lib/railwatch/configuration.rb +21 -151
  22. data/lib/railwatch/engine.rb +9 -118
  23. data/lib/railwatch/patches.rb +0 -11
  24. data/lib/railwatch/reporter.rb +92 -21
  25. data/lib/railwatch/sampler.rb +10 -2
  26. data/lib/railwatch/subscribers/process_info.rb +1 -2
  27. data/lib/railwatch/version.rb +1 -1
  28. data/lib/railwatch.rb +8 -64
  29. data/lib/tasks/railwatch_tasks.rake +3 -325
  30. data/llms.txt +23 -25
  31. metadata +3 -453
  32. data/app/channels/railwatch/environment_channel.rb +0 -62
  33. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  34. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  35. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  36. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  37. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  38. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  39. data/app/controllers/railwatch/commands_controller.rb +0 -16
  40. data/app/controllers/railwatch/comments_controller.rb +0 -11
  41. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  42. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  43. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  44. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  45. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  46. data/app/controllers/railwatch/executions_controller.rb +0 -13
  47. data/app/controllers/railwatch/issues_controller.rb +0 -258
  48. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  49. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  50. data/app/controllers/railwatch/logs_controller.rb +0 -40
  51. data/app/controllers/railwatch/mails_controller.rb +0 -19
  52. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  53. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  54. data/app/controllers/railwatch/overview_controller.rb +0 -38
  55. data/app/controllers/railwatch/people_controller.rb +0 -25
  56. data/app/controllers/railwatch/processes_controller.rb +0 -33
  57. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  58. data/app/controllers/railwatch/queries_controller.rb +0 -59
  59. data/app/controllers/railwatch/releases_controller.rb +0 -75
  60. data/app/controllers/railwatch/requests_controller.rb +0 -37
  61. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  62. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  63. data/app/controllers/railwatch/spans_controller.rb +0 -47
  64. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  65. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  66. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  67. data/app/controllers/railwatch/traces_controller.rb +0 -72
  68. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  69. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  70. data/app/controllers/railwatch/visits_controller.rb +0 -53
  71. data/app/frontend/components/app-content.tsx +0 -26
  72. data/app/frontend/components/app-logo-icon.tsx +0 -23
  73. data/app/frontend/components/app-logo.tsx +0 -14
  74. data/app/frontend/components/app-shell.tsx +0 -38
  75. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  76. data/app/frontend/components/app-sidebar.tsx +0 -94
  77. data/app/frontend/components/app-wordmark.tsx +0 -29
  78. data/app/frontend/components/breadcrumbs.tsx +0 -46
  79. data/app/frontend/components/icon.tsx +0 -16
  80. data/app/frontend/components/input-error.tsx +0 -18
  81. data/app/frontend/components/nav-footer.tsx +0 -46
  82. data/app/frontend/components/nav-main.tsx +0 -41
  83. data/app/frontend/components/nav-user.tsx +0 -50
  84. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  85. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  86. data/app/frontend/components/railwatch/code.tsx +0 -28
  87. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  88. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  89. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  90. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  91. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  92. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  93. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  94. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  95. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  96. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  97. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  98. data/app/frontend/components/railwatch/frames.tsx +0 -295
  99. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  100. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  101. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  102. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  103. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  104. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  105. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  106. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  107. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  108. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  109. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  110. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  111. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  112. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  113. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  114. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  115. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  116. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  117. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  118. data/app/frontend/components/railwatch/stat.tsx +0 -185
  119. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  120. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  121. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  122. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  123. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  124. data/app/frontend/components/ui/avatar.tsx +0 -51
  125. data/app/frontend/components/ui/badge.tsx +0 -46
  126. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  127. data/app/frontend/components/ui/button.tsx +0 -60
  128. data/app/frontend/components/ui/card.tsx +0 -92
  129. data/app/frontend/components/ui/chart.tsx +0 -374
  130. data/app/frontend/components/ui/checkbox.tsx +0 -32
  131. data/app/frontend/components/ui/collapsible.tsx +0 -31
  132. data/app/frontend/components/ui/command.tsx +0 -182
  133. data/app/frontend/components/ui/dialog.tsx +0 -158
  134. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  135. data/app/frontend/components/ui/hover-card.tsx +0 -42
  136. data/app/frontend/components/ui/input.tsx +0 -21
  137. data/app/frontend/components/ui/label.tsx +0 -24
  138. data/app/frontend/components/ui/popover.tsx +0 -87
  139. data/app/frontend/components/ui/select.tsx +0 -185
  140. data/app/frontend/components/ui/separator.tsx +0 -28
  141. data/app/frontend/components/ui/sheet.tsx +0 -137
  142. data/app/frontend/components/ui/sidebar.tsx +0 -726
  143. data/app/frontend/components/ui/skeleton.tsx +0 -13
  144. data/app/frontend/components/ui/sonner.tsx +0 -41
  145. data/app/frontend/components/ui/table.tsx +0 -114
  146. data/app/frontend/components/ui/tabs.tsx +0 -91
  147. data/app/frontend/components/ui/textarea.tsx +0 -18
  148. data/app/frontend/components/ui/tooltip.tsx +0 -61
  149. data/app/frontend/components/user-info.tsx +0 -32
  150. data/app/frontend/components/user-menu-content.tsx +0 -94
  151. data/app/frontend/entrypoints/application.css +0 -244
  152. data/app/frontend/entrypoints/inertia.tsx +0 -92
  153. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  154. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  157. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  158. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  159. data/app/frontend/hooks/use-appearance.tsx +0 -67
  160. data/app/frontend/hooks/use-clipboard.ts +0 -32
  161. data/app/frontend/hooks/use-flash.tsx +0 -23
  162. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  163. data/app/frontend/hooks/use-initials.tsx +0 -15
  164. data/app/frontend/hooks/use-live.ts +0 -205
  165. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  166. data/app/frontend/hooks/use-mobile.ts +0 -21
  167. data/app/frontend/hooks/use-percentile.ts +0 -24
  168. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  169. data/app/frontend/hooks/use-row-nav.ts +0 -106
  170. data/app/frontend/hooks/use-step.ts +0 -27
  171. data/app/frontend/hooks/use-ticker.ts +0 -12
  172. data/app/frontend/hooks/use-window.ts +0 -58
  173. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  174. data/app/frontend/layouts/app-layout.tsx +0 -21
  175. data/app/frontend/layouts/env-layout.tsx +0 -53
  176. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  177. data/app/frontend/lib/cable.ts +0 -10
  178. data/app/frontend/lib/env-pages.ts +0 -236
  179. data/app/frontend/lib/execution-path.ts +0 -55
  180. data/app/frontend/lib/explain.ts +0 -50
  181. data/app/frontend/lib/filter.ts +0 -25
  182. data/app/frontend/lib/flamegraph.ts +0 -155
  183. data/app/frontend/lib/format.ts +0 -71
  184. data/app/frontend/lib/keyboard.ts +0 -18
  185. data/app/frontend/lib/mount.ts +0 -23
  186. data/app/frontend/lib/source-link.ts +0 -217
  187. data/app/frontend/lib/utils.ts +0 -6
  188. data/app/frontend/pages/alerts/index.tsx +0 -171
  189. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  190. data/app/frontend/pages/cache_events/index.tsx +0 -127
  191. data/app/frontend/pages/commands/index.tsx +0 -143
  192. data/app/frontend/pages/deploys/index.tsx +0 -94
  193. data/app/frontend/pages/deploys/show.tsx +0 -309
  194. data/app/frontend/pages/deprecations/index.tsx +0 -176
  195. data/app/frontend/pages/exceptions/index.tsx +0 -271
  196. data/app/frontend/pages/executions/show.tsx +0 -821
  197. data/app/frontend/pages/issues/index.tsx +0 -399
  198. data/app/frontend/pages/issues/show.tsx +0 -1367
  199. data/app/frontend/pages/jobs/index.tsx +0 -381
  200. data/app/frontend/pages/jobs/klass.tsx +0 -215
  201. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  202. data/app/frontend/pages/logs/index.tsx +0 -303
  203. data/app/frontend/pages/mails/index.tsx +0 -218
  204. data/app/frontend/pages/notifications/index.tsx +0 -161
  205. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  206. data/app/frontend/pages/overview/show.tsx +0 -333
  207. data/app/frontend/pages/people/index.tsx +0 -102
  208. data/app/frontend/pages/people/show.tsx +0 -149
  209. data/app/frontend/pages/processes/index.tsx +0 -335
  210. data/app/frontend/pages/profiles/index.tsx +0 -110
  211. data/app/frontend/pages/profiles/show.tsx +0 -105
  212. data/app/frontend/pages/queries/index.tsx +0 -492
  213. data/app/frontend/pages/queries/show.tsx +0 -276
  214. data/app/frontend/pages/releases/index.tsx +0 -190
  215. data/app/frontend/pages/releases/release-health.tsx +0 -68
  216. data/app/frontend/pages/releases/release-path.ts +0 -25
  217. data/app/frontend/pages/releases/show.tsx +0 -262
  218. data/app/frontend/pages/requests/index.tsx +0 -235
  219. data/app/frontend/pages/requests/route.tsx +0 -159
  220. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  221. data/app/frontend/pages/spans/index.tsx +0 -229
  222. data/app/frontend/pages/spans/show.tsx +0 -220
  223. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  224. data/app/frontend/pages/tenants/index.tsx +0 -236
  225. data/app/frontend/pages/tenants/show.tsx +0 -477
  226. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  227. data/app/frontend/pages/thresholds/index.tsx +0 -414
  228. data/app/frontend/pages/traces/show.tsx +0 -208
  229. data/app/frontend/pages/transactions/index.tsx +0 -220
  230. data/app/frontend/pages/view_renders/index.tsx +0 -241
  231. data/app/frontend/pages/visits/index.tsx +0 -321
  232. data/app/frontend/public/icon.png +0 -0
  233. data/app/frontend/public/icon.svg +0 -5
  234. data/app/frontend/routes/index.d.ts +0 -2136
  235. data/app/frontend/routes/index.js +0 -2112
  236. data/app/frontend/types/globals.d.ts +0 -9
  237. data/app/frontend/types/index.ts +0 -288
  238. data/app/frontend/types/vite-env.d.ts +0 -9
  239. data/app/helpers/railwatch/assets_helper.rb +0 -52
  240. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  241. data/app/jobs/railwatch/application_job.rb +0 -7
  242. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  243. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  244. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  245. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  246. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  247. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  248. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  249. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  250. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  251. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  252. data/app/jobs/railwatch/rollup_job.rb +0 -132
  253. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  254. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  255. data/app/models/railwatch/alert.rb +0 -229
  256. data/app/models/railwatch/alert_rule.rb +0 -121
  257. data/app/models/railwatch/anomaly_rule.rb +0 -51
  258. data/app/models/railwatch/application.rb +0 -44
  259. data/app/models/railwatch/application_record.rb +0 -33
  260. data/app/models/railwatch/comment.rb +0 -36
  261. data/app/models/railwatch/deploy.rb +0 -67
  262. data/app/models/railwatch/environment.rb +0 -68
  263. data/app/models/railwatch/execution_presenter.rb +0 -185
  264. data/app/models/railwatch/filter_query.rb +0 -184
  265. data/app/models/railwatch/followup_receipt.rb +0 -27
  266. data/app/models/railwatch/ingest/batch.rb +0 -355
  267. data/app/models/railwatch/ingest/mapper.rb +0 -585
  268. data/app/models/railwatch/ingest/payload.rb +0 -96
  269. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  270. data/app/models/railwatch/ingest/writer.rb +0 -137
  271. data/app/models/railwatch/issue.rb +0 -277
  272. data/app/models/railwatch/issue_activity.rb +0 -23
  273. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  274. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  275. data/app/models/railwatch/maintenance_task.rb +0 -53
  276. data/app/models/railwatch/saved_view.rb +0 -63
  277. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  278. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  279. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  280. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  281. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  282. data/app/models/railwatch/telemetry/child.rb +0 -53
  283. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  284. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  285. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  286. data/app/models/railwatch/telemetry/exception.rb +0 -28
  287. data/app/models/railwatch/telemetry/execution.rb +0 -152
  288. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  289. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  290. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  291. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  292. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  293. data/app/models/railwatch/telemetry/log.rb +0 -91
  294. data/app/models/railwatch/telemetry/mail.rb +0 -13
  295. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  296. data/app/models/railwatch/telemetry/notification.rb +0 -13
  297. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  298. data/app/models/railwatch/telemetry/person.rb +0 -50
  299. data/app/models/railwatch/telemetry/process.rb +0 -10
  300. data/app/models/railwatch/telemetry/profile.rb +0 -37
  301. data/app/models/railwatch/telemetry/query.rb +0 -57
  302. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  303. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  304. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  305. data/app/models/railwatch/telemetry/session.rb +0 -24
  306. data/app/models/railwatch/telemetry/span.rb +0 -19
  307. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  308. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  309. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  310. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  311. data/app/models/railwatch/telemetry/visit.rb +0 -24
  312. data/app/models/railwatch/telemetry_record.rb +0 -107
  313. data/app/models/railwatch/threshold.rb +0 -66
  314. data/app/models/railwatch/user.rb +0 -47
  315. data/app/models/railwatch/viewer.rb +0 -13
  316. data/app/models/railwatch/window.rb +0 -66
  317. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  318. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  319. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  320. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  321. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  322. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  323. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  324. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  325. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  327. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  328. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  329. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  330. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  331. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  332. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  333. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  334. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  335. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  336. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  337. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  338. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  340. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  341. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  342. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  343. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  344. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  345. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  347. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  348. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  349. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  350. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  351. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  352. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  353. data/docs/embedded.md +0 -503
  354. data/lib/puma/plugin/railwatch.rb +0 -215
  355. data/lib/railwatch/dashboard_assets.rb +0 -45
  356. data/lib/railwatch/embedded.rb +0 -50
  357. data/lib/railwatch/export/client.rb +0 -124
  358. data/lib/railwatch/export/lease.rb +0 -56
  359. data/lib/railwatch/export/outbox.rb +0 -361
  360. data/lib/railwatch/export/policy.rb +0 -79
  361. data/lib/railwatch/export/sender.rb +0 -130
  362. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  363. data/lib/railwatch/maintenance.rb +0 -194
  364. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  365. data/lib/railwatch/transport/local.rb +0 -78
  366. data/lib/railwatch/transport/socket.rb +0 -183
  367. data/lib/railwatch/writer.rb +0 -370
  368. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  369. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  372. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  373. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  374. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  375. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  376. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  377. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  378. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  379. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  380. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  381. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  382. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  383. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  384. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  385. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  386. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  387. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  388. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  389. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  390. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  391. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  392. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  393. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  394. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  395. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  396. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  397. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  398. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  399. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  400. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  401. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  402. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  403. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  404. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  405. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  406. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  407. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  408. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  409. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  410. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  411. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  412. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  413. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  414. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  415. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  416. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  417. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  418. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  419. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  420. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  421. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  422. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  423. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  424. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  425. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  426. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  427. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  428. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  429. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  430. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  431. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  432. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  433. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  434. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  435. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  436. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  437. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  438. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  439. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  440. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  441. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  442. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  443. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  444. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  445. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  446. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  447. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  448. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  449. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  450. data/public/railwatch/icon.png +0 -0
  451. data/public/railwatch/icon.svg +0 -5
  452. data/public/railwatch/manifest.json +0 -1245
  453. data/public/railwatch/rails-vite.json +0 -1
  454. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Sampled CPU/wall profiles for an execution, attachments (files an app
4
- # attaches to an execution or exception), and opt-in captured bodies.
5
- class AddProfilesAndAttachments < ActiveRecord::Migration[8.1]
6
- def change
7
- create_table :profiles do |t|
8
- # The record envelope as it was when this migration shipped; inlined so
9
- # the history never calls a model that has since changed.
10
- t.datetime :occurred_at, null: false, precision: 6
11
- t.string :deploy, limit: 128
12
- t.string :server, limit: 255
13
- t.string :group_hash, limit: 32
14
- t.string :trace_id, limit: 36
15
- t.string :execution_source, limit: 20
16
- t.string :execution_id, limit: 36
17
- t.string :execution_preview, limit: 255
18
- t.string :execution_stage, limit: 32
19
- t.string :user_ref, limit: 255
20
- t.string :app_tenant, limit: 255
21
- t.string :profiler, null: false, limit: 16 # vernier, stackprof
22
- t.string :mode, limit: 8 # wall, cpu
23
- t.integer :interval # microseconds between samples
24
- t.integer :duration, null: false # microseconds profiled
25
- t.integer :samples, null: false # total samples collected
26
- t.binary :stacks, null: false # gzip of collapsed stacks: "frame;frame;frame count\n"
27
- t.integer :stacks_bytes # uncompressed size
28
- end
29
- add_index :profiles, [ :execution_id ]
30
- add_index :profiles, [ :group_hash, :occurred_at ]
31
- add_index :profiles, [ :occurred_at ]
32
-
33
- create_table :attachments do |t|
34
- # The record envelope as it was when this migration shipped; inlined so
35
- # the history never calls a model that has since changed.
36
- t.datetime :occurred_at, null: false, precision: 6
37
- t.string :deploy, limit: 128
38
- t.string :server, limit: 255
39
- t.string :group_hash, limit: 32
40
- t.string :trace_id, limit: 36
41
- t.string :execution_source, limit: 20
42
- t.string :execution_id, limit: 36
43
- t.string :execution_preview, limit: 255
44
- t.string :execution_stage, limit: 32
45
- t.string :user_ref, limit: 255
46
- t.string :app_tenant, limit: 255
47
- t.string :name, null: false, limit: 255
48
- t.string :content_type, limit: 128
49
- t.integer :bytes, null: false
50
- t.binary :data, null: false # gzip
51
- t.string :exception_group_hash, limit: 32 # set when attached to an exception
52
- end
53
- add_index :attachments, [ :execution_id ]
54
- add_index :attachments, [ :exception_group_hash, :occurred_at ]
55
- add_index :attachments, [ :occurred_at ]
56
-
57
- add_column :outgoing_requests, :response_body, :text # captured on error only, truncated
58
- add_column :executions, :profile_id, :integer # denormalised: the execution has a profile
59
- end
60
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The gem cuts an attachment off at its size limit and flags the wire
4
- # record; without this column the UI cannot tell a whole file from a stub.
5
- class AddTruncatedToAttachments < ActiveRecord::Migration[8.1]
6
- def change
7
- add_column :attachments, :truncated, :boolean, default: false, null: false
8
- end
9
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Release health. `sessions` are the raw records the gem ships -- one per
4
- # browser tab and per authenticated/cookied server session, repeated every
5
- # flush interval, so a session id appears many times and the rollup keeps the
6
- # worst status it saw. `release_health` is the hourly aggregate per deploy
7
- # (the release) that the crash-free rates are read from.
8
- class CreateSessionsAndReleaseHealth < ActiveRecord::Migration[8.1]
9
- def change
10
- create_table :sessions do |t|
11
- # The record envelope as it was when this migration shipped; inlined so
12
- # the history never calls a model that has since changed.
13
- t.datetime :occurred_at, null: false, precision: 6
14
- t.string :deploy, limit: 128
15
- t.string :server, limit: 255
16
- t.string :group_hash, limit: 32
17
- t.string :trace_id, limit: 36
18
- t.string :execution_source, limit: 20
19
- t.string :execution_id, limit: 36
20
- t.string :execution_preview, limit: 255
21
- t.string :execution_stage, limit: 32
22
- t.string :user_ref, limit: 255
23
- t.string :app_tenant, limit: 255
24
- t.string :session_id, null: false, limit: 64
25
- t.string :source, limit: 16 # browser, server
26
- t.string :status, limit: 16 # started, ok, errored, crashed
27
- t.datetime :started_at, precision: 6
28
- t.integer :duration # microseconds, null until the session has one
29
- t.integer :requests, default: 0
30
- t.integer :visits, default: 0
31
- t.integer :error_count, default: 0 # `errors` is a reserved Active Record name
32
- t.boolean :ended, default: false
33
- end
34
- add_index :sessions, [ :session_id, :occurred_at ]
35
- add_index :sessions, [ :deploy, :occurred_at ]
36
- add_index :sessions, [ :occurred_at ]
37
-
38
- create_table :release_health do |t|
39
- t.string :deploy, null: false, limit: 128
40
- t.datetime :bucket, null: false, precision: 6
41
- t.integer :sessions, default: 0
42
- t.integer :sessions_errored, default: 0
43
- t.integer :sessions_crashed, default: 0
44
- t.integer :users, default: 0
45
- t.integer :users_crashed, default: 0
46
- t.bigint :duration_sum, default: 0
47
- t.integer :duration_count, default: 0
48
- end
49
- add_index :release_health, [ :deploy, :bucket ], unique: true
50
- end
51
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The parts the gem hashed this exception into an issue on, and where they
4
- # came from (its own default, a resolver, a per-call fingerprint, or the
5
- # error class itself) -- so an issue can show why it grouped the way it did.
6
- class AddFingerprintToExceptions < ActiveRecord::Migration[8.1]
7
- def change
8
- add_column :exceptions, :fingerprint, :json, default: [], null: false
9
- add_column :exceptions, :fingerprint_source, :string, limit: 16
10
- end
11
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Whether a channel action raised. The gem reports the exception itself
4
- # (source "application.action_cable"); this flag lets the Broadcasts page mark
5
- # the failed action alongside its siblings, the way jobs and mail already do.
6
- class AddFailedToBroadcasts < ActiveRecord::Migration[8.1]
7
- def change
8
- add_column :broadcasts, :failed, :boolean, default: false, null: false
9
- end
10
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Keyset pagination orders every telemetry list by (sort column, id), so an
4
- # index that stops at the sort column still leaves SQLite sorting ties in a
5
- # temp B-tree. Each index below ends in id for that reason, and exists only
6
- # where the leading filter column is selective enough that seeking beats
7
- # scanning the ordering index and filtering.
8
- #
9
- # Deliberately NOT indexed, even though the FilterBar exposes them:
10
- # exceptions.handled, queries.connection, queries.role, and executions.queue
11
- # all carry a handful of distinct values in practice, so the existing ordering
12
- # index plus a row filter is as good as a seek and costs nothing on write.
13
- class AddFilterCursorIndexes < ActiveRecord::Migration[8.1]
14
- def change
15
- # Jobs page, filtered by tenant. index_executions_on_app_tenant_and_occurred_at
16
- # cannot serve it: the page is already restricted to kind = "job".
17
- add_index :executions, [ :kind, :app_tenant, :occurred_at, :id ], name: "idx_executions_kind_tenant_cursor"
18
- # "Failed jobs" is the most-used view on that page and failures are rare,
19
- # so the two-valued outcome column is highly selective here.
20
- add_index :executions, [ :kind, :outcome, :occurred_at, :id ], name: "idx_executions_kind_outcome_cursor"
21
-
22
- # exceptions has only (occurred_at); both of these filters are per-tenant
23
- # or per-class needles in a large table.
24
- add_index :exceptions, [ :app_tenant, :occurred_at, :id ], name: "idx_exceptions_tenant_cursor"
25
- add_index :exceptions, [ :class_name, :occurred_at, :id ], name: "idx_exceptions_class_cursor"
26
-
27
- add_index :logs, [ :app_tenant, :occurred_at, :id ], name: "idx_logs_tenant_cursor"
28
- # Replaced, not added: the same index with the keyset tiebreaker appended.
29
- remove_index :logs, [ :level, :occurred_at ], name: "index_logs_on_level_and_occurred_at"
30
- add_index :logs, [ :level, :occurred_at, :id ], name: "idx_logs_level_cursor"
31
-
32
- # The queries page sorts by duration, which index_queries_on_occurred_at_and_duration
33
- # cannot order by. The first index serves the unfiltered page.
34
- add_index :queries, [ :duration, :id ], name: "idx_queries_duration_cursor"
35
- add_index :queries, [ :app_tenant, :duration, :id ], name: "idx_queries_tenant_duration_cursor"
36
- end
37
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # n_plus_ones had no execution_id index, alone among the child tables, so
4
- # every trace page and every get_route call that asked "which N+1s did
5
- # these requests hit" scanned the table: 1.6 s at 70k rows on the
6
- # platform's own tenant, and it only grows.
7
- class AddNPlusOnesExecutionIdIndex < ActiveRecord::Migration[8.1]
8
- def change
9
- add_index :n_plus_ones, :execution_id, name: "index_n_plus_ones_on_execution_id"
10
- end
11
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A query group's normalized statement, stored once; rows that carry
4
- # exactly it store "" instead (docs/architecture.md). queries.sql keeps
5
- # its NOT NULL: relaxing it would rewrite the table on every tenant, which
6
- # SQLite cannot do in place and the deploy's health window cannot afford.
7
- class CreateQueryShapes < ActiveRecord::Migration[8.1]
8
- def change
9
- create_table :query_shapes, id: false do |t|
10
- t.string :group_hash, null: false, limit: 32
11
- t.text :sql, null: false
12
- end
13
- add_index :query_shapes, :group_hash, unique: true
14
- end
15
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The gem reports when load forces it to sample every execution kind, so the
4
- # platform can distinguish quiet traffic from reduced client-side reporting.
5
- class AddBackpressureFactorToIngestBatches < ActiveRecord::Migration[8.1]
6
- def change
7
- add_column :ingest_batches, :backpressure_factor, :float, null: false, default: 1.0
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The gem version a process reports. Column renamed with the platform; the
4
- # ingest mapper accepts both wire spellings while environments still run the
5
- # lantern gem.
6
- class RenameLanternVersionOnProcesses < ActiveRecord::Migration[8.1]
7
- def change
8
- rename_column :processes, :lantern_version, :nightrail_version
9
- end
10
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The gem version a process reports. Column renamed with the platform; the
4
- # ingest mapper still accepts the pinned lantern gem's wire spelling.
5
- class RenameNightrailVersionOnProcesses < ActiveRecord::Migration[8.1]
6
- def change
7
- rename_column :processes, :nightrail_version, :railwatch_version
8
- end
9
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Telemetry half of the primary migration of the same name: #84's
4
- # rollup_cursors and source_maps, and #60's release_health_finalizations,
5
- # stayed behind in every tenant after their code was reverted or replaced.
6
- class DropOrphanDurableIngestTables < ActiveRecord::Migration[8.1]
7
- ORPHAN_TABLES = %w[release_health_finalizations rollup_cursors source_maps].freeze
8
- ORPHAN_VERSIONS = %w[20260904000013 20260905000100 20260905000200 20260905000400].freeze
9
-
10
- def up
11
- ORPHAN_TABLES.each { |table| drop_table table, if_exists: true }
12
- execute "DELETE FROM schema_migrations WHERE version IN (#{ORPHAN_VERSIONS.map { |v| "'#{v}'" }.join(", ")})"
13
- end
14
-
15
- def down
16
- raise ActiveRecord::IrreversibleMigration, "the dropped tables belonged to reverted code; there is nothing to restore"
17
- end
18
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The column half of the #84 cleanup (see 20260914000000): its migrations
4
- # also added delivery-state columns to ingest_batches and resolved-frame
5
- # columns to exceptions in every tenant before the code was reverted in
6
- # 5521379. Nothing reads them, and the weekly restore drill fails the
7
- # column comparison on every restored tenant. Indexes go first: SQLite
8
- # refuses to drop an indexed column.
9
- class DropOrphanDurableIngestColumns < ActiveRecord::Migration[8.1]
10
- def up
11
- remove_index :ingest_batches, :followups_completed_at, if_exists: true
12
- remove_index :ingest_batches, :delivery_key, if_exists: true
13
- remove_index :ingest_batches, :batch_id, if_exists: true
14
- %i[batch_id delivery_key followups followups_completed_at].each do |column|
15
- remove_column :ingest_batches, column if column_exists?(:ingest_batches, column)
16
- end
17
- %i[resolved_frames resolved_frames_key].each do |column|
18
- remove_column :exceptions, column if column_exists?(:exceptions, column)
19
- end
20
- end
21
-
22
- def down
23
- raise ActiveRecord::IrreversibleMigration, "the dropped columns belonged to reverted code; there is nothing to restore"
24
- end
25
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # One row per RubyLLM call: a model call of any operation, or a tool
4
- # invocation (operation "tool", which carries a name and a duration but no
5
- # tokens or cost). Token counts keep RubyLLM's normalized buckets rather
6
- # than a provider's own names, and cost is nanodollars so a month of rollup
7
- # sums stays exact -- nil there means unpriced, never free.
8
- class CreateLlmCalls < ActiveRecord::Migration[8.1]
9
- def change
10
- create_table :llm_calls do |t|
11
- # The record envelope as it was when this migration shipped; inlined so
12
- # the history never calls a model that has since changed.
13
- t.datetime :occurred_at, null: false, precision: 6
14
- t.string :deploy, limit: 128
15
- t.string :server, limit: 255
16
- t.string :group_hash, limit: 32
17
- t.string :trace_id, limit: 36
18
- t.string :execution_source, limit: 20
19
- t.string :execution_id, limit: 36
20
- t.string :execution_preview, limit: 255
21
- t.string :execution_stage, limit: 32
22
- t.string :user_ref, limit: 255
23
- t.string :app_tenant, limit: 255
24
- t.string :operation, null: false, limit: 20
25
- t.string :provider, limit: 64
26
- t.string :model, limit: 255
27
- t.string :response_model, limit: 255
28
- t.string :tool_name, limit: 255
29
- t.integer :duration, null: false
30
- t.string :status, limit: 16
31
- t.string :error, limit: 255
32
- t.boolean :streaming
33
- t.integer :message_count
34
- t.integer :tool_count
35
- t.integer :input_tokens
36
- t.integer :output_tokens
37
- t.integer :cache_read_tokens
38
- t.integer :cache_write_tokens
39
- t.integer :thinking_tokens
40
- t.bigint :cost_nanos
41
- # Present only for calls made inside RubyLLM.workflow (2.0 and up).
42
- # The parent step id is what reconstructs a nested agent run.
43
- t.string :workflow_id, limit: 64
44
- t.string :workflow_name, limit: 255
45
- t.string :workflow_step_id, limit: 64
46
- t.string :workflow_step_name, limit: 255
47
- t.string :workflow_step_parent_id, limit: 64
48
- t.text :prompt
49
- t.text :completion
50
- end
51
- add_index :llm_calls, [ :group_hash, :occurred_at ]
52
- add_index :llm_calls, [ :execution_id ]
53
- add_index :llm_calls, [ :workflow_id, :occurred_at ]
54
- end
55
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # What an LLM call carried and how it was configured, alongside what it
4
- # cost. finish_reason is the one that changes what you can see: max_tokens
5
- # means the answer was cut off, and a truncated extraction otherwise reads
6
- # exactly like a complete one.
7
- class AddDetailToLlmCalls < ActiveRecord::Migration[8.1]
8
- def change
9
- change_table :llm_calls, bulk: true do |t|
10
- t.string :finish_reason, limit: 32
11
- t.string :provider_request_id, limit: 128
12
- t.string :tools, limit: 1024
13
- t.boolean :cost_reported
14
- t.integer :attachments
15
- t.string :attachment_types, limit: 128
16
- t.string :attachment_names, limit: 1024
17
- t.string :tool_call_id, limit: 128
18
- t.json :params
19
- end
20
- end
21
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The queries page asks "which of these 200 shapes have a captured plan in
4
- # the window?" -- WHERE occurred_at BETWEEN ? AND ? AND group_hash IN (200
5
- # values) AND explain IS NOT NULL. The planner walks the (group_hash,
6
- # occurred_at) index for every one of the 200 shapes and then checks
7
- # `explain` on each row, which is 840 ms at one day of one environment's
8
- # queries (1M rows), and that whole time is a GVL hold: sqlite3-ruby does
9
- # not release the GVL inside sqlite3_step. Plans are only captured for slow
10
- # queries, so a partial index over just the explained rows is tiny and
11
- # answers the question in 4 ms.
12
- class AddExplainedIndexToQueries < ActiveRecord::Migration[8.1]
13
- def change
14
- add_index :queries, [ :group_hash, :occurred_at ], name: "idx_queries_explained", where: "explain IS NOT NULL"
15
- end
16
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The queries page's "slowest in window" list orders the window by duration
4
- # and takes a page. Every existing index leads with occurred_at, so the
5
- # planner scans the whole window and sorts it: 48 ms for a day of one
6
- # environment's queries (1M rows) -- and sqlite3-ruby holds the GVL for the
7
- # whole statement, so every other request thread waits too. An index that
8
- # leads with (duration DESC, id DESC) matches the page's ORDER BY exactly, so
9
- # SQLite walks it from the slowest row and stops at the page (microseconds,
10
- # and it cannot get worse as the table grows); occurred_at rides along so
11
- # the window filter is answered from the index too. The planner still
12
- # prefers the occurred_at index on its own cost model, so CursorPage forces
13
- # this one.
14
- class AddSlowestIndexToQueries < ActiveRecord::Migration[8.1]
15
- def change
16
- add_index :queries, [ :duration, :id, :occurred_at ], name: "idx_queries_slowest", order: { duration: :desc, id: :desc }
17
- end
18
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Delivery ledger for in-process ingest. The reporter gives every batch a
4
- # stable id before the first attempt; recording it on the ingest_batches row
5
- # inside the batch transaction lets a retry ask "did this commit?" instead of
6
- # guessing, and lets the follow-up work the batch still owes (exception
7
- # grouping, which writes the other database) be resumed after a crash.
8
- # `followups` is the small outbox: nil once everything has run.
9
- class AddBatchLedgerToIngestBatches < ActiveRecord::Migration[8.1]
10
- def change
11
- add_column :ingest_batches, :batch_id, :string, limit: 36
12
- add_column :ingest_batches, :followups, :json
13
- add_index :ingest_batches, :batch_id, unique: true
14
- add_index :ingest_batches, :received_at, where: "followups IS NOT NULL", name: "index_ingest_batches_pending_followups"
15
- end
16
- end
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The durable queue an embedded install uses to mirror its telemetry to a
4
- # remote receiver.
5
- #
6
- # Why the bodies are stored rather than re-derived from the rows next door:
7
- # the rows are not a reversible copy of what was sent. Mapping renames and
8
- # drops fields, caps strings, swaps a query's SQL for a shape reference and
9
- # coalesces people; and ingest rejects records the receiver may well accept,
10
- # so sending only what we kept would not be the same telemetry. A delivery
11
- # therefore keeps the exact bytes it will send, and frees them the moment it
12
- # reaches a terminal state -- the cost tracks what is unacknowledged, not what
13
- # is stored.
14
- #
15
- # Nothing here is written unless export is explicitly enabled.
16
- #
17
- # Every statement is if_not_exists, because this migration has run before
18
- # under another number. Through 0.5.0 it was 20260919000000; 0.5.1 moved it
19
- # here so it could not collide with a host's own telemetry migration at that
20
- # number (Railwatch Cloud has one). An install upgrading from 0.5.0 or older
21
- # therefore already holds every table, index and column below, recorded
22
- # under the old version, and sees this one as pending: a plain create_table
23
- # raised "table export_destinations already exists" in db:prepare, which a
24
- # deploy runs before the app boots. The old run is detected by what it
25
- # built, not by its version, because on a host like Railwatch Cloud the old
26
- # number belongs to a different migration.
27
- #
28
- # Rolling back has the mirror problem: on a database that came through the
29
- # old number, these tables and columns -- and anything queued in them -- are
30
- # the old version's, which stays recorded as applied. So down leaves them
31
- # alone there, and reverses the migration everywhere else.
32
- class CreateExportQueue < ActiveRecord::Migration[8.1]
33
- OLD_VERSION = 20260919000000
34
-
35
- def up
36
- build_queue
37
- end
38
-
39
- def down
40
- if built_under_old_number?
41
- say("left in place: #{OLD_VERSION} built this queue under its pre-0.5.1 number and is still recorded as applied")
42
- else
43
- revert { build_queue }
44
- end
45
- end
46
-
47
- private
48
- # The old version is recorded, and no migration file this database knows
49
- # of claims it: only the gem's own pre-0.5.1 CreateExportQueue did.
50
- def built_under_old_number?
51
- context = connection.pool.migration_context
52
- context.get_all_versions.include?(OLD_VERSION) && context.migrations.none? { |m| m.version == OLD_VERSION }
53
- end
54
-
55
- def build_queue
56
- # One row per destination this database has ever been pointed at.
57
- create_table :export_destinations, if_not_exists: true do |t|
58
- t.string :url, null: false
59
- t.string :url_sha256, limit: 64, null: false
60
- # Permanent for this database's lineage: it is how the receiver tells
61
- # our deliveries from another installation's.
62
- t.string :producer_id, limit: 36, null: false
63
- # A fingerprint, never the token. If the token changes, queued bytes
64
- # must not follow it to whatever tenant the new one belongs to.
65
- t.string :credential_sha256, limit: 64, null: false
66
-
67
- t.string :state, limit: 16, null: false, default: "ready"
68
- t.datetime :retry_at, precision: 6
69
- t.string :reason, limit: 64
70
-
71
- t.string :lease_owner, limit: 36
72
- t.bigint :lease_generation, null: false, default: 0
73
- t.datetime :lease_expires_at, precision: 6
74
-
75
- # Live totals, maintained in the same transaction as the rows they
76
- # describe, so admission can be decided without counting the table.
77
- t.bigint :queued_bytes, null: false, default: 0
78
- t.bigint :queued_deliveries, null: false, default: 0
79
- # Lifetime accounting: acked, rejected, expired, discarded, shed.
80
- t.json :counters, null: false, default: {}
81
-
82
- t.timestamps
83
- end
84
-
85
- add_index :export_destinations, :url_sha256, unique: true, if_not_exists: true
86
- add_index :export_destinations, :producer_id, unique: true, if_not_exists: true
87
-
88
- create_table :export_deliveries, if_not_exists: true do |t|
89
- t.references :export_destination, null: false, foreign_key: true
90
- # A UUIDv7: its embedded time is how the receiver ages it out, so a
91
- # delivery cannot be made young again by relabelling it.
92
- t.string :delivery_id, limit: 36, null: false
93
- # What this delivery is a delivery OF. One per source batch today; a
94
- # future policy that selects across batches supplies its own key.
95
- t.string :selection_key, limit: 160, null: false
96
-
97
- t.binary :body
98
- t.string :body_sha256, limit: 64, null: false
99
- t.string :metadata_sha256, limit: 64, null: false
100
- t.bigint :body_bytes, null: false
101
- t.bigint :ndjson_bytes, null: false
102
- t.integer :record_count, null: false
103
- # Everything about the delivery that is not its body: version, drop
104
- # counts, backpressure. Digested into metadata_sha256 so the same id
105
- # arriving with different counts is a conflict, not an update.
106
- t.json :wire_metadata, null: false, default: {}
107
-
108
- t.string :state, limit: 8, null: false, default: "pending"
109
- # Set only when done: acked, rejected, expired, discarded.
110
- t.string :disposition, limit: 16
111
- t.datetime :enqueued_at, null: false, precision: 6
112
- t.datetime :expires_at, null: false, precision: 6
113
- t.datetime :next_attempt_at, null: false, precision: 6
114
- t.bigint :attempts, null: false, default: 0
115
-
116
- t.string :claim_token, limit: 36
117
- t.bigint :claim_generation
118
- t.datetime :claim_expires_at, precision: 6
119
-
120
- t.integer :last_status
121
- t.string :last_reason, limit: 64
122
- t.json :ack
123
- t.datetime :finished_at, precision: 6
124
-
125
- t.timestamps
126
- end
127
-
128
- add_index :export_deliveries, %i[export_destination_id delivery_id], unique: true,
129
- name: "index_export_deliveries_on_destination_and_delivery", if_not_exists: true
130
- # One delivery per selection: a batch replayed into the same transaction
131
- # cannot enqueue itself twice.
132
- add_index :export_deliveries, %i[export_destination_id selection_key], unique: true,
133
- name: "index_export_deliveries_on_destination_and_selection", if_not_exists: true
134
- add_index :export_deliveries, %i[export_destination_id id], where: "state <> 'done'",
135
- name: "index_export_deliveries_live", if_not_exists: true
136
- add_index :export_deliveries, :expires_at, where: "body IS NOT NULL",
137
- name: "index_export_deliveries_expiring", if_not_exists: true
138
- add_index :export_deliveries, :finished_at, where: "state = 'done'",
139
- name: "index_export_deliveries_finished", if_not_exists: true
140
-
141
- # Why a batch did or did not enqueue anything. Null on every row an
142
- # install without export ever writes.
143
- add_column :ingest_batches, :export_disposition, :string, limit: 24, if_not_exists: true
144
- add_column :ingest_batches, :export_record_count, :bigint, if_not_exists: true
145
- end
146
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Two dashboard aggregates read every row in the window and only a few
4
- # narrow columns of each: the Jobs page's per-queue breakdown and the
5
- # Processes page's health series. Through (kind, occurred_at) and
6
- # (sampled_at) SQLite finds the rows in order but then fetches each one
7
- # from the table, and on a telemetry file much larger than memory those
8
- # rows are scattered pages on disk. On the platform's own 30 GB tenant,
9
- # over a week: 38 s for the queue breakdown (491,000 job attempts) and
10
- # 19 s for the health series (263,000 samples), with the GVL held the
11
- # whole time, so every other request in the process waited too.
12
- #
13
- # Both indexes carry every column their query reads, so the whole answer
14
- # comes from one contiguous index range: 335 ms and 370 ms on a copy of
15
- # the same file. They cost 151 MB and 29 MB there, and took 13 s and 2 s
16
- # to build.
17
- #
18
- # broadcasts is the one windowed table with no index led by occurred_at,
19
- # so the Broadcasts page's "recent" list sorted the whole window to show
20
- # 100 rows. Every other raw table has had this index since it was created.
21
- #
22
- # Free-text search over jobs and requests also matches the exception
23
- # preview, which only a failed execution carries (15 of 491,000 job
24
- # attempts in that same week). A partial index over just those rows lets
25
- # Execution.named_like seek them instead of reading the window.
26
- class AddCoveringIndexesForDashboardAggregates < ActiveRecord::Migration[8.1]
27
- def change
28
- add_index :executions, [ :kind, :occurred_at, :queue, :outcome, :queue_latency ], name: "idx_executions_queue_stats", if_not_exists: true
29
- add_index :health_samples, [ :sampled_at, :threads_max, :threads_busy, :backlog, :queue_depth, :queue_latency ],
30
- name: "idx_health_samples_series", if_not_exists: true
31
- add_index :broadcasts, :occurred_at, if_not_exists: true
32
- add_index :executions, [ :kind, :occurred_at ], name: "idx_executions_with_preview", where: "exception_preview IS NOT NULL", if_not_exists: true
33
- end
34
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The People page groups the window's signed-in requests by user_ref.
4
- # Through (user_ref, occurred_at) SQLite found those rows but fetched each
5
- # one from the table for kind and status: 4 s over 30 days on the
6
- # platform's own tenant, where 18,000 of 2.8 million executions carry a
7
- # user. This index holds every column the query reads, and only for rows
8
- # that have a user, so it answers in 18 ms from under a megabyte. It took
9
- # 10 s to build on a copy of that 30 GB file.
10
- class AddPeopleIndex < ActiveRecord::Migration[8.1]
11
- def change
12
- add_index :executions, [ :user_ref, :kind, :occurred_at, :status ], name: "idx_executions_people",
13
- where: "user_ref IS NOT NULL", if_not_exists: true
14
- end
15
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The Tenants page and MCP's list_tenants sum every tenant-tagged request
4
- # and job in the window per tenant. Through (app_tenant, occurred_at)
5
- # SQLite finds the rows but fetches each from the table for kind, status,
6
- # outcome, duration and user_ref: 8.1 s over 30 days on rebulk-system
7
- # (358,000 tagged requests), and 24 s once in production, which held the
8
- # web process -- and every ingest request queued behind it -- the whole
9
- # time. This index carries every column those aggregates read, only for
10
- # tagged rows: 131 ms for the same query, 41 MB, 1.6 s to build on a copy
11
- # of that 16 GB file. An untagged app gets an empty index.
12
- class AddTenantSummaryIndex < ActiveRecord::Migration[8.1]
13
- def change
14
- add_index :executions, [ :kind, :app_tenant, :occurred_at, :status, :outcome, :duration, :user_ref ],
15
- name: "idx_executions_tenant_summary", where: "app_tenant IS NOT NULL", if_not_exists: true
16
- end
17
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # An outgoing HTTP call made while the execution held a database transaction
4
- # open: the transaction (and on SQLite with BEGIN IMMEDIATE, the writer lock)
5
- # stays held for the whole round trip. Same meaning as queries.in_transaction.
6
- class AddInTransactionToOutgoingRequests < ActiveRecord::Migration[8.1]
7
- def change
8
- add_column :outgoing_requests, :in_transaction, :boolean, default: false
9
- end
10
- end