mat_views 0.1.2

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 (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +121 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/stylesheets/mat_views/application.css +15 -0
  6. data/app/jobs/mat_views/application_job.rb +39 -0
  7. data/app/jobs/mat_views/create_view_job.rb +188 -0
  8. data/app/jobs/mat_views/delete_view_job.rb +196 -0
  9. data/app/jobs/mat_views/refresh_view_job.rb +215 -0
  10. data/app/models/mat_views/application_record.rb +34 -0
  11. data/app/models/mat_views/mat_view_definition.rb +98 -0
  12. data/app/models/mat_views/mat_view_run.rb +89 -0
  13. data/config/routes.rb +12 -0
  14. data/lib/generators/mat_views/install/install_generator.rb +86 -0
  15. data/lib/generators/mat_views/install/templates/create_mat_view_definitions.rb +29 -0
  16. data/lib/generators/mat_views/install/templates/create_mat_view_runs.rb +32 -0
  17. data/lib/generators/mat_views/install/templates/mat_views_initializer.rb +23 -0
  18. data/lib/mat_views/configuration.rb +49 -0
  19. data/lib/mat_views/engine.rb +34 -0
  20. data/lib/mat_views/jobs/adapter.rb +78 -0
  21. data/lib/mat_views/service_response.rb +60 -0
  22. data/lib/mat_views/services/base_service.rb +308 -0
  23. data/lib/mat_views/services/concurrent_refresh.rb +177 -0
  24. data/lib/mat_views/services/create_view.rb +156 -0
  25. data/lib/mat_views/services/delete_view.rb +160 -0
  26. data/lib/mat_views/services/regular_refresh.rb +146 -0
  27. data/lib/mat_views/services/swap_refresh.rb +221 -0
  28. data/lib/mat_views/version.rb +21 -0
  29. data/lib/mat_views.rb +57 -0
  30. data/lib/tasks/helpers.rb +185 -0
  31. data/lib/tasks/mat_views_tasks.rake +145 -0
  32. metadata +95 -0
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ ##
9
+ # Top-level namespace for the mat_views engine.
10
+ module MatViews
11
+ ##
12
+ # ActiveJob that handles `REFRESH MATERIALIZED VIEW` for a given
13
+ # {MatViews::MatViewDefinition}.
14
+ #
15
+ # The job mirrors {MatViews::CreateViewJob}'s lifecycle:
16
+ # it measures duration and persists state in {MatViews::MatViewRun}.
17
+ #
18
+ # The actual refresh implementation is delegated based on
19
+ # `definition.refresh_strategy`:
20
+ #
21
+ # - `"concurrent"` → {MatViews::Services::ConcurrentRefresh}
22
+ # - `"swap"` → {MatViews::Services::SwapRefresh}
23
+ # - otherwise → {MatViews::Services::RegularRefresh}
24
+ #
25
+ # Row count reporting can be controlled via `row_count_strategy`:
26
+ # - `:estimated` (default) — fast, approximate via reltuples
27
+ # - `:exact` — accurate `COUNT(*)`
28
+ # - `nil` — skip counting
29
+ #
30
+ # @see MatViews::MatViewDefinition
31
+ # @see MatViews::MatViewRun
32
+ # @see MatViews::Services::RegularRefresh
33
+ # @see MatViews::Services::ConcurrentRefresh
34
+ # @see MatViews::Services::SwapRefresh
35
+ #
36
+ # @example Enqueue a refresh with exact row count
37
+ # MatViews::RefreshViewJob.perform_later(definition.id, :exact)
38
+ #
39
+ # @example Enqueue using keyword-hash form
40
+ # MatViews::RefreshViewJob.perform_later(definition.id, row_count_strategy: :estimated)
41
+ #
42
+ class RefreshViewJob < ::ActiveJob::Base
43
+ ##
44
+ # Queue name for the job.
45
+ #
46
+ # Uses `MatViews.configuration.job_queue` when configured, otherwise `:default`.
47
+ #
48
+ queue_as { MatViews.configuration.job_queue || :default }
49
+
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`.
55
+ #
56
+ # @api public
57
+ #
58
+ # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
59
+ # @param strategy_arg [Symbol, String, Hash, nil] Row count strategy override.
60
+ # When a Hash, looks for `:row_count_strategy` / `"row_count_strategy"`.
61
+ #
62
+ # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
63
+ # - `:status` [Symbol]
64
+ # - `:payload` [Hash]
65
+ # - `:error` [String, nil]
66
+ # - `:duration_ms` [Integer]
67
+ # - `:meta` [Hash]
68
+ #
69
+ # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
70
+ #
71
+ def perform(definition_id, strategy_arg = nil)
72
+ row_count_strategy = normalize_strategy(strategy_arg)
73
+ definition = MatViews::MatViewDefinition.find(definition_id)
74
+ run = start_run(definition)
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
102
+ end
103
+ end
104
+
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
119
+
120
+ ##
121
+ # Select the refresh service class based on the definition's strategy.
122
+ #
123
+ # @api private
124
+ #
125
+ # @param definition [MatViews::MatViewDefinition]
126
+ # @return [Class] One of the refresh service classes.
127
+ #
128
+ def service(definition)
129
+ case definition.refresh_strategy
130
+ when 'concurrent'
131
+ MatViews::Services::ConcurrentRefresh
132
+ when 'swap'
133
+ MatViews::Services::SwapRefresh
134
+ else
135
+ MatViews::Services::RegularRefresh
136
+ end
137
+ 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
+ end
215
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ ##
9
+ # Top-level namespace for the mat_views engine.
10
+ module MatViews
11
+ ##
12
+ # Base model class for all ActiveRecord models in the mat_views engine.
13
+ #
14
+ # Inherits from {ActiveRecord::Base} and marks itself as an abstract class.
15
+ # Other engine models should subclass this rather than inheriting directly
16
+ # from {ActiveRecord::Base}, so that shared behavior or configuration can be
17
+ # applied in one place.
18
+ #
19
+ # @abstract
20
+ #
21
+ # @example Define a new model under mat_views
22
+ # class MatViews::MatViewDefinition < MatViews::ApplicationRecord
23
+ # self.table_name = "mat_view_definitions"
24
+ # end
25
+ #
26
+ class ApplicationRecord < ActiveRecord::Base
27
+ ##
28
+ # Marks this record class as abstract, so it won’t be persisted to a table.
29
+ #
30
+ # @return [void]
31
+ #
32
+ self.abstract_class = true
33
+ end
34
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ ##
9
+ # Top-level namespace for the mat_views engine.
10
+ module MatViews
11
+ ##
12
+ # Represents a **materialized view definition** managed by the engine.
13
+ #
14
+ # A definition stores the canonical name and SQL for a materialized view and
15
+ # drives lifecycle operations (create, refresh, delete) via background jobs
16
+ # and services. It also tracks operational history through associated
17
+ # run models.
18
+ #
19
+ # Validations ensure a sane PostgreSQL identifier for `name` and that `sql`
20
+ # begins with `SELECT` (case-insensitive).
21
+ #
22
+ # @see MatViews::CreateViewJob
23
+ # @see MatViews::RefreshViewJob
24
+ # @see MatViews::DeleteViewJob
25
+ # @see MatViews::Services::CreateView
26
+ # @see MatViews::Services::RegularRefresh
27
+ # @see MatViews::Services::ConcurrentRefresh
28
+ # @see MatViews::Services::SwapRefresh
29
+ #
30
+ # @example Creating a definition
31
+ # defn = MatViews::MatViewDefinition.create!(
32
+ # name: "mv_user_accounts",
33
+ # sql: "SELECT users.id, accounts.id AS account_id FROM users JOIN accounts ON ..."
34
+ # )
35
+ #
36
+ # @example Enqueue a refresh
37
+ # MatViews::RefreshViewJob.perform_later(defn.id, :estimated)
38
+ #
39
+ class MatViewDefinition < ApplicationRecord
40
+ ##
41
+ # Underlying database table name.
42
+ self.table_name = 'mat_view_definitions'
43
+
44
+ # ────────────────────────────────────────────────────────────────
45
+ # Associations
46
+ # ────────────────────────────────────────────────────────────────
47
+
48
+ ##
49
+ # Historical create runs linked to this definition.
50
+ #
51
+ # @return [ActiveRecord::Relation<MatViews::MatViewRun>]
52
+ #
53
+ has_many :mat_view_runs,
54
+ dependent: :destroy,
55
+ class_name: 'MatViews::MatViewRun'
56
+
57
+ # ────────────────────────────────────────────────────────────────
58
+ # Validations
59
+ # ────────────────────────────────────────────────────────────────
60
+
61
+ ##
62
+ # @!attribute name
63
+ # @return [String] PostgreSQL identifier for the materialized view.
64
+ #
65
+ validates :name,
66
+ presence: true,
67
+ uniqueness: true,
68
+ format: { with: /\A[a-zA-Z_][a-zA-Z0-9_]*\z/ }
69
+
70
+ ##
71
+ # @!attribute sql
72
+ # @return [String] SELECT statement used to materialize the view.
73
+ #
74
+ validates :sql,
75
+ presence: true,
76
+ format: { with: /\A\s*SELECT/i, message: 'must begin with a SELECT' }
77
+
78
+ # ────────────────────────────────────────────────────────────────
79
+ # Enums / configuration
80
+ # ────────────────────────────────────────────────────────────────
81
+
82
+ ##
83
+ # Refresh strategy that governs which service is used by {RefreshViewJob}.
84
+ #
85
+ # - `:regular` → {MatViews::Services::RegularRefresh}
86
+ # - `:concurrent` → {MatViews::Services::ConcurrentRefresh}
87
+ # - `:swap` → {MatViews::Services::SwapRefresh}
88
+ #
89
+ # @!attribute [rw] refresh_strategy
90
+ # @return [String] one of `"regular"`, `"concurrent"`, `"swap"`
91
+ #
92
+ enum :refresh_strategy, { regular: 0, concurrent: 1, swap: 2 }
93
+
94
+ def last_run
95
+ mat_view_runs.order(created_at: :desc).first
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ ##
9
+ # Top-level namespace for the mat_views engine.
10
+ module MatViews
11
+ ##
12
+ # ActiveRecord model that tracks the lifecycle of *runs* for
13
+ # materialized views.
14
+ #
15
+ # Each record corresponds to a single attempt to mutate a materialized view
16
+ # from a {MatViews::MatViewDefinition}, storing its status, timing, and
17
+ # any associated error or metadata.
18
+ #
19
+ # This model provides an auditable history of view provisioning across
20
+ # environments, useful for telemetry, dashboards, and debugging.
21
+ #
22
+ # @see MatViews::MatViewDefinition
23
+ # @see MatViews::CreateViewJob
24
+ #
25
+ # @example Query recent successful runs
26
+ # MatViews::MatViewRun.status_success.order(created_at: :desc).limit(10)
27
+ #
28
+ # @example Check if a definition has any failed runs
29
+ # definition.mat_view_runs.status_failed.any?
30
+ #
31
+ class MatViewRun < ApplicationRecord
32
+ ##
33
+ # Underlying database table name.
34
+ self.table_name = 'mat_view_runs'
35
+
36
+ ##
37
+ # The definition this run belongs to.
38
+ #
39
+ # @return [MatViews::MatViewDefinition]
40
+ #
41
+ belongs_to :mat_view_definition, class_name: 'MatViews::MatViewDefinition'
42
+
43
+ ##
44
+ # Status of the create run.
45
+ #
46
+ # @!attribute [r] status
47
+ # @return [Symbol] One of:
48
+ # - `:pending` — queued but not yet started
49
+ # - `:running` — currently executing
50
+ # - `:success` — completed successfully
51
+ # - `:failed` — encountered an error
52
+ #
53
+ enum :status, {
54
+ pending: 0,
55
+ running: 1,
56
+ success: 2,
57
+ failed: 3
58
+ }, prefix: :status
59
+
60
+ # Operation type of the run.
61
+ #
62
+ # @!attribute [r] operation
63
+ # @return [Symbol] One of:
64
+ # - `:create` — initial creation of the materialized view
65
+ # - `:refresh` — refreshing an existing view
66
+ # - `:drop` — dropping the materialized view
67
+ enum :operation, {
68
+ create: 0,
69
+ refresh: 1,
70
+ drop: 2
71
+ }, prefix: :operation
72
+
73
+ ##
74
+ # Validations
75
+ #
76
+ # Ensures that a status is always present.
77
+ validates :status, presence: true
78
+
79
+ ##
80
+ # Scopes
81
+ scope :create_runs, -> { where(operation: :create) }
82
+ scope :refresh_runs, -> { where(operation: :refresh) }
83
+ scope :drop_runs, -> { where(operation: :drop) }
84
+
85
+ def row_count
86
+ meta['row_count']
87
+ end
88
+ end
89
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ MatViews::Engine.routes.draw do
9
+ # Routes for MatViews can be defined here.
10
+ # For example, you can add a root route or other resources as needed.
11
+ # root 'mat_views#home'
12
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ require 'rails/generators'
9
+ require 'rails/generators/migration'
10
+ require 'rails/generators/active_record'
11
+
12
+ ##
13
+ # Top-level namespace for the mat_views engine.
14
+ module MatViews
15
+ ##
16
+ # Namespace for Rails generators shipped with the mat_views engine.
17
+ module Generators
18
+ ##
19
+ # Rails generator that installs MatViews into a host application by:
20
+ #
21
+ # 1. Copying migrations for definitions and run-tracking tables.
22
+ # 2. Creating an initializer at `config/initializers/mat_views.rb`.
23
+ # 3. Printing a success message with next steps.
24
+ #
25
+ # @example Run the installer
26
+ # bin/rails g mat_views:install
27
+ #
28
+ # @see MatViews::MatViewDefinition
29
+ # @see MatViews::MatViewRun
30
+ #
31
+ class InstallGenerator < Rails::Generators::Base
32
+ include Rails::Generators::Migration
33
+
34
+ ##
35
+ # Directory containing template files for the generator.
36
+ #
37
+ # @return [String] absolute path to templates dir
38
+ #
39
+ source_root File.expand_path('templates', __dir__)
40
+
41
+ ##
42
+ # Short description shown in `rails g --help`.
43
+ desc 'Installs MatViews: copies migrations and initializer.'
44
+
45
+ ##
46
+ # Copies all required migrations into the host app.
47
+ #
48
+ # @return [void]
49
+ #
50
+ def copy_migrations
51
+ migration_template 'create_mat_view_definitions.rb', 'db/migrate/create_mat_view_definitions.rb'
52
+ migration_template 'create_mat_view_runs.rb', 'db/migrate/create_mat_view_runs.rb'
53
+ end
54
+
55
+ ##
56
+ # Creates the engine initializer in the host app.
57
+ #
58
+ # @return [void]
59
+ #
60
+ def create_initializer
61
+ copy_file 'mat_views_initializer.rb', 'config/initializers/mat_views.rb'
62
+ end
63
+
64
+ ##
65
+ # Prints a success message after installation.
66
+ #
67
+ # @return [void]
68
+ #
69
+ def show_success_message
70
+ say "\n✅ MatViews installed! Don't forget to run: rails db:migrate\n", :green
71
+ end
72
+
73
+ ##
74
+ # Computes the next migration number for copied migrations.
75
+ #
76
+ # Required by Rails to generate timestamped migration filenames.
77
+ #
78
+ # @param path [String] destination path for migrations
79
+ # @return [String] the next migration number (timestamp)
80
+ #
81
+ def self.next_migration_number(path)
82
+ ActiveRecord::Generators::Base.next_migration_number(path)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ # This migration creates the mat_view_definitions table, which stores definitions for materialized views.
9
+ # It includes fields for the view name, SQL definition, refresh strategy, schedule, unique index columns,
10
+ # dependencies, last refreshed timestamp, and timestamps for creation and updates.
11
+ class CreateMatViewDefinitions < ActiveRecord::Migration[7.1]
12
+ def change
13
+ create_table :mat_view_definitions do |t|
14
+ t.string :name, null: false, comment: 'The name of the materialized view'
15
+ t.text :sql, null: false, comment: 'The SQL query defining the materialized view'
16
+ # refresh_strategy can be
17
+ # regular: 0 - Default strategy, in-place refresh.
18
+ # concurrent: 1 - Concurrent refresh, requires at least one unique index.
19
+ # swap: 2 - Swap the materialized view with a new one, uses more memory.
20
+ t.integer :refresh_strategy, default: 0, null: false,
21
+ comment: 'Strategy for refreshing the materialized view. Options: regular, concurrent, swap'
22
+ t.string :schedule_cron, comment: 'Cron schedule for automatic refresh of the materialized view'
23
+ t.jsonb :unique_index_columns, default: [], comment: 'Columns used for unique indexing, if any'
24
+ t.jsonb :dependencies, default: [],
25
+ comment: 'Dependencies of the materialized view, such as other views or tables'
26
+ t.timestamps
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ # This migration creates the mat_view_runs table, which tracks the mutation runs(create,refresh,drop) of materialized views.
9
+ # It includes references to the materialized view definition, status, operation type, timestamps,
10
+ # duration, error messages, and additional metadata.
11
+ class CreateMatViewRuns < ActiveRecord::Migration[7.1]
12
+ def change
13
+ create_table :mat_view_runs do |t|
14
+ t.references :mat_view_definition,
15
+ null: false,
16
+ foreign_key: true,
17
+ comment: 'Reference to the materialized view definition'
18
+
19
+ # 0=pending, 1=running, 2=success, 3=failed
20
+ t.integer :status, null: false, default: 0, comment: '0=pending,1=running,2=success,3=failed'
21
+ # 0=create, 1=refresh, 2=drop
22
+ t.integer :operation, null: false, default: 0, comment: '0=create,1=refresh,2=drop'
23
+ t.datetime :started_at, comment: 'Timestamp when the operation started'
24
+ t.datetime :finished_at, comment: 'Timestamp when the operation finished'
25
+ t.integer :duration_ms, comment: 'Duration of the operation in milliseconds'
26
+ t.text :error, comment: 'Error message if the operation failed'
27
+ t.jsonb :meta, null: false, default: {}, comment: 'Additional metadata about the run, such as job ID, row count or parameters'
28
+
29
+ t.timestamps
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ MatViews.configure do |config|
9
+ ### Job Adapter Configuration
10
+ # Set job adapter for MatViews
11
+ # This can be set to :active_job, :sidekiq, and :resque,
12
+ # defaults to :active_job.
13
+ #
14
+ # Depending on your application's job processing setup.
15
+ # Uncomment the line below to set the job adapter.
16
+ # config.job_adapter = :active_job
17
+
18
+ # job_queue is the queue name for the job adapter.
19
+ # Default is :default.
20
+ # This is used to specify the queue where MatViews jobs will be enqueued.
21
+ # Uncomment the line below to set the job queue.
22
+ # config.job_queue = :default
23
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Codevedas Inc. 2025-present
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ module MatViews
9
+ ##
10
+ # Configuration for the MatViews engine.
11
+ #
12
+ # This class provides customization points for how MatViews integrates
13
+ # with background job systems and controls default behavior across
14
+ # the engine.
15
+ #
16
+ # @example Configure in an initializer
17
+ # MatViews.configure do |config|
18
+ # config.job_adapter = :sidekiq
19
+ # config.job_queue = :low_priority
20
+ # end
21
+ #
22
+ # Supported job adapters:
23
+ # - `:active_job` (default)
24
+ # - `:sidekiq`
25
+ # - `:resque`
26
+ #
27
+ class Configuration
28
+ ##
29
+ # The job adapter to use for enqueuing jobs.
30
+ #
31
+ # @return [Symbol] :active_job, :sidekiq, or :resque
32
+ attr_accessor :job_adapter
33
+
34
+ ##
35
+ # The default queue name to use for jobs.
36
+ #
37
+ # @return [Symbol, String]
38
+ attr_accessor :job_queue
39
+
40
+ ##
41
+ # Initialize with defaults.
42
+ #
43
+ # @return [void]
44
+ def initialize
45
+ @job_adapter = :active_job
46
+ @job_queue = :default
47
+ end
48
+ end
49
+ end