-
Notifications
You must be signed in to change notification settings - Fork 0
/
model.rb
105 lines (91 loc) · 3.18 KB
/
model.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
require "scenic"
require "ext/scenic/adapters/postgres"
module SearchCraft::Model
# Maintain a list of classes that include this module
@included_classes = []
# Class method to add a class to the list of included classes
def self.included(base)
if base.is_a?(Class)
base.extend ClassMethods
if base.is_a?(ClassMethods) && base.respond_to?(:table_name=)
base.table_name = base.name.to_s.tableize.tr("/", "_")
@included_classes << base unless @included_classes.include?(base)
end
end
super
end
# Runs .refresh! on all classes that include SearchCraft::Model
def self.refresh_all!
if SearchCraft.config.explicit_model_class_names.present?
SearchCraft.config.explicit_model_class_names.each do |model_class_name|
model_class_name.constantize.refresh!
end
else
included_classes.each do |klass|
if klass.is_a?(ClassMethods)
klass.refresh!
end
end
end
end
def self.refresh_any_unpopulated!
included_classes.each do |klass|
klass.refresh! unless klass.populated?
end
end
def self.included_classes
@included_classes | if SearchCraft.config.explicit_model_class_names
SearchCraft.config.explicit_model_class_names.map(&:constantize)
else
[]
end
end
module ClassMethods
def refresh!
refresh_concurrently = @refresh_concurrently && populated?
unless Rails.env.test?
if refresh_concurrently
warn "Refreshing materialized view concurrently #{table_name}..."
else
warn "Refreshing materialized view #{table_name}..."
end
end
Scenic.database.refresh_materialized_view(table_name, concurrently: refresh_concurrently, cascade: false)
rescue ActiveRecord::StatementInvalid
# If populated? lies and returns true; then might get error:
# PG::FeatureNotSupported: ERROR: CONCURRENTLY cannot be used when the materialized view is not populated (ActiveRecord::StatementInvalid)
Scenic.database.refresh_materialized_view(table_name, concurrently: false, cascade: false)
end
def populated?
Scenic.database.populated?(table_name)
end
def refresh_concurrently=(value)
@refresh_concurrently = value
end
# Checks the database server to see if the materialized view is currently being refreshed
def currently_refreshing?
# quoted_table_name is table_name, but with double quotes around each chunk
# e.g. "schema"."table" or "table"
quoted_table_name = Scenic.database.quote_table_name(table_name)
dbname = ActiveRecord::Base.connection_db_config.database
sql = <<~SQL
SELECT EXISTS (
SELECT 1
FROM pg_stat_activity
WHERE datname = '#{dbname}'
AND query LIKE '%REFRESH MATERIALIZED VIEW #{quoted_table_name}%'
AND pid <> pg_backend_pid()
) AS is_refresh_running;
SQL
warn "Checking if #{table_name} is currently being refreshed..." if SearchCraft.debug?
if (result = ActiveRecord::Base.connection.execute(sql))
result.first["is_refresh_running"]
else
false
end
end
end
def read_only?
true
end
end