mat_views 0.1.2 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +10 -10
- data/app/assets/images/mat_views/android-chrome-192x192.png +0 -0
- data/app/assets/images/mat_views/android-chrome-512x512.png +0 -0
- data/app/assets/images/mat_views/apple-touch-icon.png +0 -0
- data/app/assets/images/mat_views/favicon-16x16.png +0 -0
- data/app/assets/images/mat_views/favicon-32x32.png +0 -0
- data/app/assets/images/mat_views/favicon-48x48.png +0 -0
- data/app/assets/images/mat_views/favicon.ico +0 -0
- data/app/assets/images/mat_views/favicon.svg +18 -0
- data/app/assets/images/mat_views/logo.svg +18 -0
- data/app/assets/images/mat_views/mask-icon.svg +5 -0
- data/app/assets/stylesheets/mat_views/application.css +323 -12
- data/app/controllers/mat_views/admin/application_controller.rb +135 -0
- data/app/controllers/mat_views/admin/dashboard_controller.rb +32 -0
- data/app/controllers/mat_views/admin/mat_view_definitions_controller.rb +248 -0
- data/app/controllers/mat_views/admin/preferences_controller.rb +91 -0
- data/app/controllers/mat_views/admin/runs_controller.rb +74 -0
- data/app/helpers/mat_views/admin/ui_helper.rb +385 -0
- data/app/javascript/mat_views/application.js +8 -0
- data/app/javascript/mat_views/controllers/application.js +10 -0
- data/app/javascript/mat_views/controllers/details_controller.js +122 -0
- data/app/javascript/mat_views/controllers/drawer_controller.js +252 -0
- data/app/javascript/mat_views/controllers/filter_controller.js +90 -0
- data/app/javascript/mat_views/controllers/flash_controller.js +13 -0
- data/app/javascript/mat_views/controllers/index.js +10 -0
- data/app/javascript/mat_views/controllers/mv_confirm_controller.js +281 -0
- data/app/javascript/mat_views/controllers/submitter_controller.js +15 -0
- data/app/javascript/mat_views/controllers/tabs_controller.js +67 -0
- data/app/javascript/mat_views/controllers/timezone_controller.js +16 -0
- data/app/javascript/mat_views/controllers/tooltip_controller.js +328 -0
- data/app/javascript/mat_views/controllers/turbo_frame_lifecycle_controller.js +49 -0
- data/app/jobs/mat_views/application_job.rb +107 -2
- data/app/jobs/mat_views/create_view_job.rb +21 -122
- data/app/jobs/mat_views/delete_view_job.rb +22 -129
- data/app/jobs/mat_views/refresh_view_job.rb +12 -133
- data/app/models/concerns/mat_views_i18n.rb +139 -0
- data/app/models/mat_views/application_record.rb +1 -0
- data/app/models/mat_views/mat_view_definition.rb +12 -7
- data/app/models/mat_views/mat_view_run.rb +34 -16
- data/app/views/layouts/mat_views/_footer.html.erb +41 -0
- data/app/views/layouts/mat_views/_header.html.erb +25 -0
- data/app/views/layouts/mat_views/admin.html.erb +47 -0
- data/app/views/layouts/mat_views/turbo_frame.html.erb +3 -0
- data/app/views/mat_views/admin/dashboard/index.html.erb +33 -0
- data/app/views/mat_views/admin/mat_view_definitions/_definition_actions.html.erb +94 -0
- data/app/views/mat_views/admin/mat_view_definitions/_table.html.erb +48 -0
- data/app/views/mat_views/admin/mat_view_definitions/empty.html.erb +1 -0
- data/app/views/mat_views/admin/mat_view_definitions/form.html.erb +79 -0
- data/app/views/mat_views/admin/mat_view_definitions/index.html.erb +10 -0
- data/app/views/mat_views/admin/mat_view_definitions/show.html.erb +40 -0
- data/app/views/mat_views/admin/preferences/show.html.erb +50 -0
- data/app/views/mat_views/admin/runs/_table.html.erb +61 -0
- data/app/views/mat_views/admin/runs/index.html.erb +38 -0
- data/app/views/mat_views/admin/runs/show.html.erb +64 -0
- data/app/views/mat_views/admin/ui/_card.html.erb +15 -0
- data/app/views/mat_views/admin/ui/_details.html.erb +10 -0
- data/app/views/mat_views/admin/ui/_flash.html.erb +6 -0
- data/app/views/mat_views/admin/ui/_table.html.erb +8 -0
- data/config/importmap.rb +9 -0
- data/config/locales/en-AU-ocker.yml +187 -0
- data/config/locales/en-AU.yml +187 -0
- data/config/locales/en-BB.yml +187 -0
- data/config/locales/en-BD.yml +187 -0
- data/config/locales/en-BE.yml +187 -0
- data/config/locales/en-BORK.yml +187 -0
- data/config/locales/en-BS.yml +187 -0
- data/config/locales/en-BZ.yml +187 -0
- data/config/locales/en-CA.yml +187 -0
- data/config/locales/en-CM.yml +187 -0
- data/config/locales/en-CY.yml +187 -0
- data/config/locales/en-EG.yml +187 -0
- data/config/locales/en-FJ.yml +187 -0
- data/config/locales/en-GB.yml +187 -0
- data/config/locales/en-GH.yml +187 -0
- data/config/locales/en-GI.yml +187 -0
- data/config/locales/en-GM.yml +187 -0
- data/config/locales/en-GY.yml +187 -0
- data/config/locales/en-HK.yml +187 -0
- data/config/locales/en-IE.yml +187 -0
- data/config/locales/en-IN.yml +187 -0
- data/config/locales/en-JM.yml +187 -0
- data/config/locales/en-KE.yml +187 -0
- data/config/locales/en-LK.yml +187 -0
- data/config/locales/en-LOL.yml +187 -0
- data/config/locales/en-LR.yml +187 -0
- data/config/locales/en-MS.yml +187 -0
- data/config/locales/en-MT.yml +187 -0
- data/config/locales/en-MW.yml +187 -0
- data/config/locales/en-MY.yml +187 -0
- data/config/locales/en-NG.yml +187 -0
- data/config/locales/en-NP.yml +187 -0
- data/config/locales/en-NZ.yml +187 -0
- data/config/locales/en-PG.yml +187 -0
- data/config/locales/en-PH.yml +187 -0
- data/config/locales/en-PK.yml +187 -0
- data/config/locales/en-RW.yml +187 -0
- data/config/locales/en-SCOT.yml +187 -0
- data/config/locales/en-SG.yml +187 -0
- data/config/locales/en-SHAKESPEARE.yml +187 -0
- data/config/locales/en-SL.yml +187 -0
- data/config/locales/en-SS.yml +187 -0
- data/config/locales/en-TH.yml +187 -0
- data/config/locales/en-TT.yml +187 -0
- data/config/locales/en-TZ.yml +187 -0
- data/config/locales/en-UG.yml +187 -0
- data/config/locales/en-US-pirate.yml +187 -0
- data/config/locales/en-US.yml +187 -0
- data/config/locales/en-YODA.yml +187 -0
- data/config/locales/en-ZA.yml +187 -0
- data/config/locales/en-ZW.yml +187 -0
- data/config/locales/en.yml +187 -0
- data/config/routes.rb +27 -3
- data/lib/ext/exception.rb +20 -0
- data/lib/generators/mat_views/install/templates/create_mat_view_definitions.rb +7 -7
- data/lib/generators/mat_views/install/templates/create_mat_view_runs.rb +7 -7
- data/lib/mat_views/admin/auth_bridge.rb +93 -0
- data/lib/mat_views/admin/default_auth.rb +61 -0
- data/lib/mat_views/configuration.rb +9 -0
- data/lib/mat_views/engine.rb +50 -2
- data/lib/mat_views/helpers/ui_test_ids.rb +43 -0
- data/lib/mat_views/jobs/adapter.rb +8 -5
- data/lib/mat_views/service_response.rb +30 -15
- data/lib/mat_views/services/base_service.rb +204 -41
- data/lib/mat_views/services/check_matview_exists.rb +76 -0
- data/lib/mat_views/services/concurrent_refresh.rb +38 -121
- data/lib/mat_views/services/create_view.rb +72 -55
- data/lib/mat_views/services/delete_view.rb +46 -95
- data/lib/mat_views/services/regular_refresh.rb +38 -94
- data/lib/mat_views/services/swap_refresh.rb +83 -123
- data/lib/mat_views/version.rb +1 -1
- data/lib/mat_views.rb +13 -6
- data/lib/tasks/helpers.rb +27 -27
- data/lib/tasks/mat_views_tasks.rake +48 -42
- metadata +131 -5
@@ -9,7 +9,7 @@
|
|
9
9
|
# Top-level namespace for the mat_views engine.
|
10
10
|
module MatViews
|
11
11
|
##
|
12
|
-
# ActiveJob that handles *creation* of PostgreSQL
|
12
|
+
# ActiveJob that handles *creation* of PostgreSQL materialised views for a
|
13
13
|
# given {MatViews::MatViewDefinition}.
|
14
14
|
#
|
15
15
|
# The job:
|
@@ -17,7 +17,7 @@ module MatViews
|
|
17
17
|
# 2. Looks up the target {MatViews::MatViewDefinition}.
|
18
18
|
# 3. Starts a {MatViews::MatViewRun} row to track lifecycle/timing, with `operation: :create`.
|
19
19
|
# 4. Executes {MatViews::Services::CreateView}.
|
20
|
-
# 5. Finalizes the run with success/failure, duration, and
|
20
|
+
# 5. Finalizes the run with success/failure, duration, and meta.
|
21
21
|
#
|
22
22
|
# @see MatViews::Services::CreateView
|
23
23
|
# @see MatViews::MatViewDefinition
|
@@ -29,7 +29,7 @@ module MatViews
|
|
29
29
|
# @example Inline run (test/dev)
|
30
30
|
# MatViews::CreateViewJob.new.perform(definition.id, false)
|
31
31
|
#
|
32
|
-
class CreateViewJob <
|
32
|
+
class CreateViewJob < ApplicationJob
|
33
33
|
##
|
34
34
|
# Queue name for the job.
|
35
35
|
#
|
@@ -40,37 +40,30 @@ module MatViews
|
|
40
40
|
queue_as { MatViews.configuration.job_queue || :default }
|
41
41
|
|
42
42
|
##
|
43
|
-
# Perform the job for the given
|
43
|
+
# Perform the create job for the given materialised view definition.
|
44
44
|
#
|
45
45
|
# @api public
|
46
46
|
#
|
47
|
-
# @param
|
47
|
+
# @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
|
48
48
|
# @param force_arg [Boolean, Hash, nil] Optional flag or hash (`{ force: true }`)
|
49
|
-
#
|
49
|
+
# @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
|
50
50
|
#
|
51
|
-
# @return [Hash]
|
52
|
-
# - `:status` [Symbol]
|
53
|
-
# - `:
|
54
|
-
# - `:
|
55
|
-
# - `:duration_ms` [Integer, nil]
|
51
|
+
# @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
|
52
|
+
# - `:status` [Symbol]
|
53
|
+
# - `:error` [String, nil]
|
54
|
+
# - `:duration_ms` [Integer]
|
56
55
|
# - `:meta` [Hash]
|
57
56
|
#
|
58
57
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
59
58
|
#
|
60
|
-
|
61
|
-
|
62
|
-
def perform(definition_id, force_arg = nil)
|
63
|
-
force = normalize_force(force_arg)
|
64
|
-
|
65
|
-
definition = MatViews::MatViewDefinition.find(definition_id)
|
66
|
-
run = start_run(definition)
|
59
|
+
def perform(mat_view_definition_id, force_arg = nil, row_count_strategy_arg = nil)
|
60
|
+
definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
|
67
61
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
raise e
|
62
|
+
record_run(definition, :create) do
|
63
|
+
MatViews::Services::CreateView.new(definition,
|
64
|
+
force: force?(force_arg),
|
65
|
+
row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
|
66
|
+
end
|
74
67
|
end
|
75
68
|
|
76
69
|
private
|
@@ -82,107 +75,13 @@ module MatViews
|
|
82
75
|
#
|
83
76
|
# @api private
|
84
77
|
#
|
85
|
-
# @param arg [Object] Raw argument; commonly `true/false`, `nil
|
78
|
+
# @param arg [Object] Raw argument; commonly `true/false`, `nil`
|
86
79
|
# @return [Boolean] Coerced force flag.
|
87
80
|
#
|
88
|
-
def
|
89
|
-
|
90
|
-
when Hash
|
91
|
-
arg[:force] || arg['force'] || false
|
92
|
-
else
|
93
|
-
!!arg
|
94
|
-
end
|
95
|
-
end
|
81
|
+
def force?(arg)
|
82
|
+
return false if arg.nil?
|
96
83
|
|
97
|
-
|
98
|
-
# Execute the create service and measure duration.
|
99
|
-
#
|
100
|
-
# @api private
|
101
|
-
#
|
102
|
-
# @param definition [MatViews::MatViewDefinition]
|
103
|
-
# @param force [Boolean]
|
104
|
-
# @return [Array(MatViews::ServiceResponse, Integer)] response and elapsed ms.
|
105
|
-
#
|
106
|
-
def execute(definition, force:)
|
107
|
-
started = monotime
|
108
|
-
response = MatViews::Services::CreateView.new(definition, force: force).run
|
109
|
-
[response, elapsed_ms(started)]
|
84
|
+
!!arg
|
110
85
|
end
|
111
|
-
|
112
|
-
##
|
113
|
-
# Begin a {MatViews::MatViewRun} row for lifecycle tracking.
|
114
|
-
#
|
115
|
-
# @api private
|
116
|
-
#
|
117
|
-
# @param definition [MatViews::MatViewDefinition]
|
118
|
-
# @return [MatViews::MatViewRun] newly created run with `status: :running`
|
119
|
-
#
|
120
|
-
def start_run(definition)
|
121
|
-
MatViews::MatViewRun.create!(
|
122
|
-
mat_view_definition: definition,
|
123
|
-
status: :running,
|
124
|
-
started_at: Time.current,
|
125
|
-
operation: :create
|
126
|
-
)
|
127
|
-
end
|
128
|
-
|
129
|
-
##
|
130
|
-
# Finalize the run with success/failure, timing, and meta from the response payload.
|
131
|
-
#
|
132
|
-
# @api private
|
133
|
-
#
|
134
|
-
# @param run [MatViews::MatViewRun]
|
135
|
-
# @param response [MatViews::ServiceResponse]
|
136
|
-
# @param duration_ms [Integer]
|
137
|
-
# @return [void]
|
138
|
-
#
|
139
|
-
def finalize_run!(run, response, duration_ms)
|
140
|
-
base_attrs = {
|
141
|
-
finished_at: Time.current,
|
142
|
-
duration_ms: duration_ms,
|
143
|
-
meta: response.payload || {}
|
144
|
-
}
|
145
|
-
|
146
|
-
if response.success?
|
147
|
-
run.update!(base_attrs.merge(status: :success, error: nil))
|
148
|
-
else
|
149
|
-
run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
##
|
154
|
-
# Mark the run failed due to an exception.
|
155
|
-
#
|
156
|
-
# @api private
|
157
|
-
#
|
158
|
-
# @param run [MatViews::MatViewRun]
|
159
|
-
# @param exception [Exception]
|
160
|
-
# @return [void]
|
161
|
-
#
|
162
|
-
def fail_run!(run, exception)
|
163
|
-
run.update!(
|
164
|
-
finished_at: Time.current,
|
165
|
-
duration_ms: run.duration_ms || 0,
|
166
|
-
error: "#{exception.class}: #{exception.message}",
|
167
|
-
status: :failed
|
168
|
-
)
|
169
|
-
end
|
170
|
-
|
171
|
-
##
|
172
|
-
# Monotonic clock getter (for elapsed-time measurement).
|
173
|
-
#
|
174
|
-
# @api private
|
175
|
-
# @return [Float] seconds from a monotonic source.
|
176
|
-
#
|
177
|
-
def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
178
|
-
|
179
|
-
##
|
180
|
-
# Convert a monotonic start time to elapsed milliseconds.
|
181
|
-
#
|
182
|
-
# @api private
|
183
|
-
# @param start [Float] monotonic seconds.
|
184
|
-
# @return [Integer] elapsed milliseconds.
|
185
|
-
#
|
186
|
-
def elapsed_ms(start) = ((monotime - start) * 1000).round
|
187
86
|
end
|
188
87
|
end
|
@@ -9,7 +9,7 @@
|
|
9
9
|
# Top-level namespace for the mat_views engine.
|
10
10
|
module MatViews
|
11
11
|
##
|
12
|
-
# ActiveJob that handles *deletion* of PostgreSQL
|
12
|
+
# ActiveJob that handles *deletion* of PostgreSQL materialised views via
|
13
13
|
# {MatViews::Services::DeleteView}.
|
14
14
|
#
|
15
15
|
# This job mirrors {MatViews::CreateViewJob} and {MatViews::RefreshViewJob}:
|
@@ -25,7 +25,12 @@ module MatViews
|
|
25
25
|
# @example Inline run (test/dev)
|
26
26
|
# MatViews::DeleteViewJob.new.perform(definition.id, false)
|
27
27
|
#
|
28
|
-
class DeleteViewJob <
|
28
|
+
class DeleteViewJob < ApplicationJob
|
29
|
+
###
|
30
|
+
# cascade flag for the service call
|
31
|
+
# @return [Boolean]
|
32
|
+
attr_reader :cascade
|
33
|
+
|
29
34
|
##
|
30
35
|
# Queue name for the job.
|
31
36
|
#
|
@@ -34,66 +39,41 @@ module MatViews
|
|
34
39
|
queue_as { MatViews.configuration.job_queue || :default }
|
35
40
|
|
36
41
|
##
|
37
|
-
# Perform the job for the given
|
42
|
+
# Perform the job for the given materialised view definition.
|
38
43
|
#
|
39
44
|
# @api public
|
40
45
|
#
|
41
|
-
# @param
|
46
|
+
# @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
|
42
47
|
# @param cascade_arg [Boolean, String, Integer, Hash, nil] Cascade option.
|
43
|
-
#
|
44
|
-
# - `true/false`
|
45
|
-
# - `1` (treated as true)
|
46
|
-
# - `"true"`, `"1"`, `"yes"` (case-insensitive)
|
47
|
-
# - `{ cascade: true }` or `{ "cascade" => true }`
|
48
|
+
# @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
|
48
49
|
#
|
49
|
-
# @return [Hash]
|
50
|
-
# - `:status` [Symbol]
|
51
|
-
# - `:payload` [Hash] response payload (also stored in run.meta)
|
50
|
+
# @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
|
51
|
+
# - `:status` [Symbol]
|
52
52
|
# - `:error` [String, nil]
|
53
53
|
# - `:duration_ms` [Integer]
|
54
54
|
# - `:meta` [Hash]
|
55
55
|
#
|
56
56
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
57
57
|
#
|
58
|
-
def perform(
|
59
|
-
|
60
|
-
definition
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
response.to_h
|
66
|
-
rescue StandardError => e
|
67
|
-
fail_run!(run, e) if run
|
68
|
-
raise e
|
58
|
+
def perform(mat_view_definition_id, cascade_arg = nil, row_count_strategy_arg = nil)
|
59
|
+
definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
|
60
|
+
record_run(definition, :drop) do
|
61
|
+
MatViews::Services::DeleteView.new(definition,
|
62
|
+
cascade: cascade?(cascade_arg),
|
63
|
+
row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
|
64
|
+
end
|
69
65
|
end
|
70
66
|
|
71
67
|
private
|
72
68
|
|
73
69
|
##
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# @api private
|
77
|
-
# @param arg [Object] Raw cascade argument.
|
78
|
-
# @return [Boolean] Whether to cascade drop.
|
79
|
-
#
|
80
|
-
def normalize_cascade?(arg)
|
81
|
-
value = if arg.is_a?(Hash)
|
82
|
-
arg[:cascade] || arg['cascade']
|
83
|
-
else
|
84
|
-
arg
|
85
|
-
end
|
86
|
-
cascade_value_trueish?(value)
|
87
|
-
end
|
88
|
-
|
89
|
-
##
|
90
|
-
# Evaluate if a value is "truthy" for cascade purposes.
|
70
|
+
# Evaluate if a value is "truthy" for cascade.
|
91
71
|
#
|
92
72
|
# @api private
|
93
|
-
# @param value [Object]
|
73
|
+
# @param value [TrueClass, FalseClass, String, Integer, nil, Object]
|
94
74
|
# @return [Boolean]
|
95
75
|
#
|
96
|
-
def
|
76
|
+
def cascade?(value)
|
97
77
|
case value
|
98
78
|
when true
|
99
79
|
true
|
@@ -105,92 +85,5 @@ module MatViews
|
|
105
85
|
false
|
106
86
|
end
|
107
87
|
end
|
108
|
-
|
109
|
-
##
|
110
|
-
# Execute the delete service and measure duration.
|
111
|
-
#
|
112
|
-
# @api private
|
113
|
-
# @param definition [MatViews::MatViewDefinition]
|
114
|
-
# @param cascade [Boolean]
|
115
|
-
# @return [Array(MatViews::ServiceResponse, Integer)]
|
116
|
-
#
|
117
|
-
def execute(definition, cascade:)
|
118
|
-
started = monotime
|
119
|
-
response = MatViews::Services::DeleteView.new(definition, cascade: cascade, if_exists: true).run
|
120
|
-
[response, elapsed_ms(started)]
|
121
|
-
end
|
122
|
-
|
123
|
-
##
|
124
|
-
# Begin a {MatViews::MatViewRun} row for lifecycle tracking.
|
125
|
-
#
|
126
|
-
# @api private
|
127
|
-
# @param definition [MatViews::MatViewDefinition]
|
128
|
-
# @return [MatViews::MatViewRun]
|
129
|
-
#
|
130
|
-
def start_run(definition)
|
131
|
-
MatViews::MatViewRun.create!(
|
132
|
-
mat_view_definition: definition,
|
133
|
-
status: :running,
|
134
|
-
started_at: Time.current,
|
135
|
-
operation: :drop
|
136
|
-
)
|
137
|
-
end
|
138
|
-
|
139
|
-
##
|
140
|
-
# Finalize the run with success/failure, timing, and meta from the response.
|
141
|
-
#
|
142
|
-
# @api private
|
143
|
-
# @param run [MatViews::MatViewRun]
|
144
|
-
# @param response [MatViews::ServiceResponse]
|
145
|
-
# @param duration_ms [Integer]
|
146
|
-
# @return [void]
|
147
|
-
#
|
148
|
-
def finalize_run!(run, response, duration_ms)
|
149
|
-
base_attrs = {
|
150
|
-
finished_at: Time.current,
|
151
|
-
duration_ms: duration_ms,
|
152
|
-
meta: response.payload || {}
|
153
|
-
}
|
154
|
-
|
155
|
-
if response.success?
|
156
|
-
run.update!(base_attrs.merge(status: :success, error: nil))
|
157
|
-
else
|
158
|
-
run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
##
|
163
|
-
# Mark the run failed due to an exception.
|
164
|
-
#
|
165
|
-
# @api private
|
166
|
-
# @param run [MatViews::MatViewRun]
|
167
|
-
# @param exception [Exception]
|
168
|
-
# @return [void]
|
169
|
-
#
|
170
|
-
def fail_run!(run, exception)
|
171
|
-
run.update!(
|
172
|
-
finished_at: Time.current,
|
173
|
-
duration_ms: run.duration_ms || 0,
|
174
|
-
error: "#{exception.class}: #{exception.message}",
|
175
|
-
status: :failed
|
176
|
-
)
|
177
|
-
end
|
178
|
-
|
179
|
-
##
|
180
|
-
# Monotonic clock getter (for elapsed-time measurement).
|
181
|
-
#
|
182
|
-
# @api private
|
183
|
-
# @return [Float] seconds
|
184
|
-
#
|
185
|
-
def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
186
|
-
|
187
|
-
##
|
188
|
-
# Convert monotonic start time to elapsed milliseconds.
|
189
|
-
#
|
190
|
-
# @api private
|
191
|
-
# @param start [Float]
|
192
|
-
# @return [Integer] elapsed ms
|
193
|
-
#
|
194
|
-
def elapsed_ms(start) = ((monotime - start) * 1000).round
|
195
88
|
end
|
196
89
|
end
|
@@ -23,9 +23,9 @@ module MatViews
|
|
23
23
|
# - otherwise → {MatViews::Services::RegularRefresh}
|
24
24
|
#
|
25
25
|
# Row count reporting can be controlled via `row_count_strategy`:
|
26
|
-
# - `:estimated` (default)
|
27
|
-
# - `:exact`
|
28
|
-
# - `nil`
|
26
|
+
# - `:estimated` (default) - fast, approximate via reltuples
|
27
|
+
# - `:exact` - accurate `COUNT(*)`
|
28
|
+
# - `nil` - skip counting
|
29
29
|
#
|
30
30
|
# @see MatViews::MatViewDefinition
|
31
31
|
# @see MatViews::MatViewRun
|
@@ -39,7 +39,7 @@ module MatViews
|
|
39
39
|
# @example Enqueue using keyword-hash form
|
40
40
|
# MatViews::RefreshViewJob.perform_later(definition.id, row_count_strategy: :estimated)
|
41
41
|
#
|
42
|
-
class RefreshViewJob <
|
42
|
+
class RefreshViewJob < ApplicationJob
|
43
43
|
##
|
44
44
|
# Queue name for the job.
|
45
45
|
#
|
@@ -48,74 +48,29 @@ module MatViews
|
|
48
48
|
queue_as { MatViews.configuration.job_queue || :default }
|
49
49
|
|
50
50
|
##
|
51
|
-
# Perform the job for the given
|
52
|
-
#
|
53
|
-
# Accepts either a symbol/string (`:estimated`, `:exact`) or a hash
|
54
|
-
# (`{ row_count_strategy: :exact }`) for `strategy_arg`.
|
51
|
+
# Perform the refresh job for the given materialised view definition.
|
55
52
|
#
|
56
53
|
# @api public
|
57
54
|
#
|
58
|
-
# @param
|
59
|
-
# @param
|
60
|
-
# When a Hash, looks for `:row_count_strategy` / `"row_count_strategy"`.
|
55
|
+
# @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
|
56
|
+
# @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
|
61
57
|
#
|
62
58
|
# @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
|
63
59
|
# - `:status` [Symbol]
|
64
|
-
# - `:payload` [Hash]
|
65
60
|
# - `:error` [String, nil]
|
66
61
|
# - `:duration_ms` [Integer]
|
67
62
|
# - `:meta` [Hash]
|
68
63
|
#
|
69
64
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
70
65
|
#
|
71
|
-
def perform(
|
72
|
-
|
73
|
-
definition
|
74
|
-
|
75
|
-
|
76
|
-
response, duration_ms = execute(definition, row_count_strategy: row_count_strategy)
|
77
|
-
finalize_run!(run, response, duration_ms)
|
78
|
-
response.to_h
|
79
|
-
rescue StandardError => e
|
80
|
-
fail_run!(run, e) if run
|
81
|
-
raise e
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
##
|
87
|
-
# Normalize the strategy argument into a symbol or default.
|
88
|
-
#
|
89
|
-
# @api private
|
90
|
-
#
|
91
|
-
# @param arg [Symbol, String, Hash, nil]
|
92
|
-
# @return [Symbol] One of `:estimated`, `:exact`, or `:estimated` by default.
|
93
|
-
#
|
94
|
-
def normalize_strategy(arg)
|
95
|
-
case arg
|
96
|
-
when Hash
|
97
|
-
(arg[:row_count_strategy] || arg['row_count_strategy'] || :estimated).to_sym
|
98
|
-
when String, Symbol
|
99
|
-
arg.to_sym
|
100
|
-
else
|
101
|
-
:estimated
|
66
|
+
def perform(mat_view_definition_id, row_count_strategy_arg = nil)
|
67
|
+
definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
|
68
|
+
record_run(definition, :refresh) do
|
69
|
+
service(definition).new(definition, row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
|
102
70
|
end
|
103
71
|
end
|
104
72
|
|
105
|
-
|
106
|
-
# Execute the appropriate refresh service and measure duration.
|
107
|
-
#
|
108
|
-
# @api private
|
109
|
-
#
|
110
|
-
# @param definition [MatViews::MatViewDefinition]
|
111
|
-
# @param row_count_strategy [Symbol, nil]
|
112
|
-
# @return [Array(MatViews::ServiceResponse, Integer)] response and elapsed ms.
|
113
|
-
#
|
114
|
-
def execute(definition, row_count_strategy:)
|
115
|
-
started = monotime
|
116
|
-
response = service(definition).new(definition, row_count_strategy: row_count_strategy).run
|
117
|
-
[response, elapsed_ms(started)]
|
118
|
-
end
|
73
|
+
private
|
119
74
|
|
120
75
|
##
|
121
76
|
# Select the refresh service class based on the definition's strategy.
|
@@ -135,81 +90,5 @@ module MatViews
|
|
135
90
|
MatViews::Services::RegularRefresh
|
136
91
|
end
|
137
92
|
end
|
138
|
-
|
139
|
-
##
|
140
|
-
# Begin a {MatViews::MatViewRun} row for lifecycle tracking.
|
141
|
-
#
|
142
|
-
# @api private
|
143
|
-
#
|
144
|
-
# @param definition [MatViews::MatViewDefinition]
|
145
|
-
# @return [MatViews::MatViewRun]
|
146
|
-
#
|
147
|
-
def start_run(definition)
|
148
|
-
MatViews::MatViewRun.create!(
|
149
|
-
mat_view_definition: definition,
|
150
|
-
status: :running,
|
151
|
-
started_at: Time.current,
|
152
|
-
operation: :refresh
|
153
|
-
)
|
154
|
-
end
|
155
|
-
|
156
|
-
##
|
157
|
-
# Finalize the run with success/failure, timing, and meta from the response.
|
158
|
-
#
|
159
|
-
# @api private
|
160
|
-
#
|
161
|
-
# @param run [MatViews::MatViewRun]
|
162
|
-
# @param response [MatViews::ServiceResponse]
|
163
|
-
# @param duration_ms [Integer]
|
164
|
-
# @return [void]
|
165
|
-
#
|
166
|
-
def finalize_run!(run, response, duration_ms)
|
167
|
-
base_attrs = {
|
168
|
-
finished_at: Time.current,
|
169
|
-
duration_ms: duration_ms,
|
170
|
-
meta: response.payload || {}
|
171
|
-
}
|
172
|
-
|
173
|
-
if response.success?
|
174
|
-
run.update!(base_attrs.merge(status: :success, error: nil))
|
175
|
-
else
|
176
|
-
run.update!(base_attrs.merge(status: :failed, error: response.error.to_s.presence))
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
##
|
181
|
-
# Mark the run failed due to an exception.
|
182
|
-
#
|
183
|
-
# @api private
|
184
|
-
#
|
185
|
-
# @param run [MatViews::MatViewRun]
|
186
|
-
# @param exception [Exception]
|
187
|
-
# @return [void]
|
188
|
-
#
|
189
|
-
def fail_run!(run, exception)
|
190
|
-
run.update!(
|
191
|
-
finished_at: Time.current,
|
192
|
-
duration_ms: run.duration_ms || 0,
|
193
|
-
error: "#{exception.class}: #{exception.message}",
|
194
|
-
status: :failed
|
195
|
-
)
|
196
|
-
end
|
197
|
-
|
198
|
-
##
|
199
|
-
# Monotonic clock getter (for elapsed-time measurement).
|
200
|
-
#
|
201
|
-
# @api private
|
202
|
-
# @return [Float] seconds
|
203
|
-
#
|
204
|
-
def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
205
|
-
|
206
|
-
##
|
207
|
-
# Convert monotonic start time to elapsed milliseconds.
|
208
|
-
#
|
209
|
-
# @api private
|
210
|
-
# @param start [Float]
|
211
|
-
# @return [Integer] elapsed ms
|
212
|
-
#
|
213
|
-
def elapsed_ms(start) = ((monotime - start) * 1000).round
|
214
93
|
end
|
215
94
|
end
|