mat_views 0.1.2 → 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.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/app/jobs/mat_views/application_job.rb +105 -0
- data/app/jobs/mat_views/create_view_job.rb +16 -118
- data/app/jobs/mat_views/delete_view_job.rb +18 -125
- data/app/jobs/mat_views/refresh_view_job.rb +7 -127
- data/app/models/mat_views/mat_view_run.rb +23 -3
- data/lib/ext/exception.rb +20 -0
- data/lib/generators/mat_views/install/templates/create_mat_view_runs.rb +2 -2
- 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 +179 -24
- data/lib/mat_views/services/concurrent_refresh.rb +35 -121
- data/lib/mat_views/services/create_view.rb +64 -47
- data/lib/mat_views/services/delete_view.rb +41 -87
- data/lib/mat_views/services/regular_refresh.rb +35 -92
- data/lib/mat_views/services/swap_refresh.rb +75 -117
- data/lib/mat_views/version.rb +1 -1
- data/lib/mat_views.rb +3 -2
- data/lib/tasks/helpers.rb +19 -19
- data/lib/tasks/mat_views_tasks.rake +35 -29
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7120f5d0821cb752001586381ab084a7366aeaa9616b86ef6e648be2b52d152
|
4
|
+
data.tar.gz: ab541f3705c974e8272cc07db3c9612add6311cb159467e04055eb980fa648b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3d260f3fcaf3029af874c943ae72b8786e2e86232dbc352a955ae9c4e1add587a0df88507cb86fe5463b308df1ff354b64ebfed00ebe2bcd124924984121797
|
7
|
+
data.tar.gz: 235208e771305d1085eeea02f5bd762bfb91942901e2b9d7d127281d8e7d04da878fcbf5eb8df4cdec9434c1e5db243fb90b20c79d77ede4136006449cbe2df8
|
data/README.md
CHANGED
@@ -96,9 +96,9 @@ MatViews::Jobs::Adapter.enqueue(job_class, queue: :default, args: [...])
|
|
96
96
|
|
97
97
|
```bash
|
98
98
|
# Create
|
99
|
-
bundle exec rake mat_views:create_by_name\[VIEW_NAME,force,--yes]
|
100
|
-
bundle exec rake mat_views:create_by_id\[ID,force,--yes]
|
101
|
-
bundle exec rake mat_views:create_all\[force,--yes]
|
99
|
+
bundle exec rake mat_views:create_by_name\[VIEW_NAME,force,row_count_strategy,--yes]
|
100
|
+
bundle exec rake mat_views:create_by_id\[ID,force,row_count_strategy,--yes]
|
101
|
+
bundle exec rake mat_views:create_all\[force,row_count_strategy,--yes]
|
102
102
|
|
103
103
|
# Refresh
|
104
104
|
bundle exec rake mat_views:refresh_by_name\[VIEW_NAME,row_count_strategy,--yes]
|
@@ -106,9 +106,9 @@ bundle exec rake mat_views:refresh_by_id\[ID,row_count_strategy,--yes]
|
|
106
106
|
bundle exec rake mat_views:refresh_all\[row_count_strategy,--yes]
|
107
107
|
|
108
108
|
# Delete
|
109
|
-
bundle exec rake mat_views:delete_by_name\[VIEW_NAME,cascade,--yes]
|
110
|
-
bundle exec rake mat_views:delete_by_id\[ID,cascade,--yes]
|
111
|
-
bundle exec rake mat_views:delete_all\[cascade,--yes]
|
109
|
+
bundle exec rake mat_views:delete_by_name\[VIEW_NAME,cascade,row_count_strategy,--yes]
|
110
|
+
bundle exec rake mat_views:delete_by_id\[ID,cascade,row_count_strategy,--yes]
|
111
|
+
bundle exec rake mat_views:delete_all\[cascade,row_count_strategy,--yes]
|
112
112
|
```
|
113
113
|
|
114
114
|
---
|
@@ -35,5 +35,110 @@ module MatViews
|
|
35
35
|
# end
|
36
36
|
#
|
37
37
|
class ApplicationJob < ActiveJob::Base
|
38
|
+
private
|
39
|
+
|
40
|
+
def record_run(definition, operation, &)
|
41
|
+
start = monotime
|
42
|
+
run = start_run(definition, operation)
|
43
|
+
response = yield
|
44
|
+
finalize_run(run, response, elapsed_ms(start))
|
45
|
+
response.to_h
|
46
|
+
rescue StandardError => e
|
47
|
+
fail_run(run, e, elapsed_ms(start))
|
48
|
+
raise e
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Begin a {MatViews::MatViewRun} row for lifecycle tracking.
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
#
|
56
|
+
# @return [MatViews::MatViewRun]
|
57
|
+
#
|
58
|
+
def start_run(definition, operation)
|
59
|
+
MatViews::MatViewRun.create!(
|
60
|
+
mat_view_definition: definition,
|
61
|
+
status: :running,
|
62
|
+
started_at: Time.current,
|
63
|
+
operation: operation
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Finalize the run with success/failure, timing, and meta from the response.
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
# @param run [MatViews::MatViewRun]
|
73
|
+
# @param response [MatViews::ServiceResponse, nil] may be nil if exception raised
|
74
|
+
# @param duration_ms [Integer]
|
75
|
+
# @return [void]
|
76
|
+
#
|
77
|
+
def finalize_run(run, response, duration_ms)
|
78
|
+
base_attrs = {
|
79
|
+
finished_at: Time.current,
|
80
|
+
duration_ms: duration_ms,
|
81
|
+
meta: { request: response.request, response: response.response }.compact
|
82
|
+
}
|
83
|
+
|
84
|
+
if response.success?
|
85
|
+
run.update!(base_attrs.merge(status: :success, error: nil))
|
86
|
+
else
|
87
|
+
run.update!(base_attrs.merge(status: :failed, error: response.error))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Mark the run failed due to an exception.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
#
|
96
|
+
# @param run [MatViews::MatViewRun]
|
97
|
+
# @param exception [Exception]
|
98
|
+
# @param duration_ms [Integer]
|
99
|
+
# @return [void]
|
100
|
+
#
|
101
|
+
def fail_run(run, exception, duration_ms)
|
102
|
+
run&.update!(
|
103
|
+
error: exception.mv_serialize_error,
|
104
|
+
finished_at: Time.current,
|
105
|
+
duration_ms: duration_ms,
|
106
|
+
status: :failed
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Monotonic clock getter (for elapsed-time measurement).
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
# @return [Float] seconds
|
115
|
+
#
|
116
|
+
def monotime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
117
|
+
|
118
|
+
##
|
119
|
+
# Convert monotonic start time to elapsed milliseconds.
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
# @param start [Float]
|
123
|
+
# @return [Integer] elapsed ms
|
124
|
+
#
|
125
|
+
def elapsed_ms(start) = ((monotime - start) * 1000).round
|
126
|
+
|
127
|
+
##
|
128
|
+
# Normalize the strategy argument into a symbol or default.
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
#
|
132
|
+
# @param arg [Symbol, String, nil]
|
133
|
+
# @return [Symbol] One of `:estimated`, `:exact`, or `:none` by default.
|
134
|
+
#
|
135
|
+
def normalize_strategy(arg)
|
136
|
+
case arg
|
137
|
+
when String, Symbol
|
138
|
+
arg.to_sym
|
139
|
+
else
|
140
|
+
:none
|
141
|
+
end
|
142
|
+
end
|
38
143
|
end
|
39
144
|
end
|
@@ -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,29 @@ module MatViews
|
|
40
40
|
queue_as { MatViews.configuration.job_queue || :default }
|
41
41
|
|
42
42
|
##
|
43
|
-
# Perform the job for the given materialized view definition.
|
43
|
+
# Perform the create job for the given materialized view definition.
|
44
44
|
#
|
45
45
|
# @api public
|
46
46
|
#
|
47
47
|
# @param 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
|
-
# - `:payload` [Hash]
|
54
|
-
# - `:error` [String, nil]
|
55
|
-
# - `:duration_ms` [Integer
|
51
|
+
# @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
|
52
|
+
# - `:status` [Symbol]
|
53
|
+
# - `:payload` [Hash]
|
54
|
+
# - `:error` [String, nil]
|
55
|
+
# - `:duration_ms` [Integer]
|
56
56
|
# - `:meta` [Hash]
|
57
57
|
#
|
58
58
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
59
59
|
#
|
60
|
-
|
61
|
-
#
|
62
|
-
def perform(definition_id, force_arg = nil)
|
63
|
-
force = normalize_force(force_arg)
|
64
|
-
|
60
|
+
def perform(definition_id, force_arg = nil, row_count_strategy_arg = nil)
|
65
61
|
definition = MatViews::MatViewDefinition.find(definition_id)
|
66
|
-
run = start_run(definition)
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
rescue StandardError => e
|
72
|
-
fail_run!(run, e) if run
|
73
|
-
raise e
|
63
|
+
record_run(definition, :create) do
|
64
|
+
MatViews::Services::CreateView.new(definition, force: force?(force_arg), row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
|
65
|
+
end
|
74
66
|
end
|
75
67
|
|
76
68
|
private
|
@@ -82,107 +74,13 @@ module MatViews
|
|
82
74
|
#
|
83
75
|
# @api private
|
84
76
|
#
|
85
|
-
# @param arg [Object] Raw argument; commonly `true/false`, `nil
|
77
|
+
# @param arg [Object] Raw argument; commonly `true/false`, `nil`
|
86
78
|
# @return [Boolean] Coerced force flag.
|
87
79
|
#
|
88
|
-
def
|
89
|
-
|
90
|
-
when Hash
|
91
|
-
arg[:force] || arg['force'] || false
|
92
|
-
else
|
93
|
-
!!arg
|
94
|
-
end
|
95
|
-
end
|
80
|
+
def force?(arg)
|
81
|
+
return false if arg.nil?
|
96
82
|
|
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)]
|
83
|
+
!!arg
|
110
84
|
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
85
|
end
|
188
86
|
end
|
@@ -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
|
#
|
@@ -40,60 +45,35 @@ module MatViews
|
|
40
45
|
#
|
41
46
|
# @param 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]
|
50
|
+
# @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
|
51
|
+
# - `:status` [Symbol]
|
52
|
+
# - `:payload` [Hash]
|
52
53
|
# - `:error` [String, nil]
|
53
54
|
# - `:duration_ms` [Integer]
|
54
55
|
# - `:meta` [Hash]
|
55
56
|
#
|
56
57
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
57
58
|
#
|
58
|
-
def perform(definition_id, cascade_arg = nil)
|
59
|
-
cascade = normalize_cascade?(cascade_arg)
|
59
|
+
def perform(definition_id, cascade_arg = nil, row_count_strategy_arg = nil)
|
60
60
|
definition = MatViews::MatViewDefinition.find(definition_id)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
response.to_h
|
66
|
-
rescue StandardError => e
|
67
|
-
fail_run!(run, e) if run
|
68
|
-
raise e
|
61
|
+
record_run(definition, :drop) do
|
62
|
+
MatViews::Services::DeleteView.new(definition, cascade: cascade?(cascade_arg),
|
63
|
+
row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
|
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
|
@@ -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,16 +48,12 @@ module MatViews
|
|
48
48
|
queue_as { MatViews.configuration.job_queue || :default }
|
49
49
|
|
50
50
|
##
|
51
|
-
# Perform the job for the given materialized view definition.
|
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 materialized view definition.
|
55
52
|
#
|
56
53
|
# @api public
|
57
54
|
#
|
58
55
|
# @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
|
59
|
-
# @param
|
60
|
-
# When a Hash, looks for `:row_count_strategy` / `"row_count_strategy"`.
|
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]
|
@@ -68,54 +64,14 @@ module MatViews
|
|
68
64
|
#
|
69
65
|
# @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
|
70
66
|
#
|
71
|
-
def perform(definition_id,
|
72
|
-
row_count_strategy = normalize_strategy(strategy_arg)
|
67
|
+
def perform(definition_id, row_count_strategy_arg = nil)
|
73
68
|
definition = MatViews::MatViewDefinition.find(definition_id)
|
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
|
69
|
+
record_run(definition, :refresh) do
|
70
|
+
service(definition).new(definition, row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
|
102
71
|
end
|
103
72
|
end
|
104
73
|
|
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
|
74
|
+
private
|
119
75
|
|
120
76
|
##
|
121
77
|
# Select the refresh service class based on the definition's strategy.
|
@@ -135,81 +91,5 @@ module MatViews
|
|
135
91
|
MatViews::Services::RegularRefresh
|
136
92
|
end
|
137
93
|
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
94
|
end
|
215
95
|
end
|
@@ -77,13 +77,33 @@ module MatViews
|
|
77
77
|
validates :status, presence: true
|
78
78
|
|
79
79
|
##
|
80
|
-
#
|
80
|
+
# Scope create runs
|
81
|
+
# All runs with `operation: :create`.
|
82
|
+
# @return [ActiveRecord::Relation<MatViews::MatViewRun>]
|
81
83
|
scope :create_runs, -> { where(operation: :create) }
|
84
|
+
|
85
|
+
##
|
86
|
+
# Scope refresh runs
|
87
|
+
# All runs with `operation: :refresh`.
|
88
|
+
# @return [ActiveRecord::Relation<MatViews::MatViewRun>]
|
82
89
|
scope :refresh_runs, -> { where(operation: :refresh) }
|
90
|
+
|
91
|
+
##
|
92
|
+
# Scope drop runs
|
93
|
+
# All runs with `operation: :drop`.
|
94
|
+
# @return [ActiveRecord::Relation<MatViews::MatViewRun>]
|
83
95
|
scope :drop_runs, -> { where(operation: :drop) }
|
84
96
|
|
85
|
-
|
86
|
-
|
97
|
+
# row count before the operation, if applicable
|
98
|
+
# @return [Integer, nil]
|
99
|
+
def row_count_before
|
100
|
+
meta.dig('response', 'row_count_before')
|
101
|
+
end
|
102
|
+
|
103
|
+
# row count after the operation, if applicable
|
104
|
+
# @return [Integer, nil]
|
105
|
+
def row_count_after
|
106
|
+
meta.dig('response', 'row_count_after')
|
87
107
|
end
|
88
108
|
end
|
89
109
|
end
|