optic-rails 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ffe914a1df59184ac225cc960634735a71a437b770ae7ab9c22b4eca2fb6f46
4
- data.tar.gz: 24912fa3c973cd9d879a480a5f7797c3250b4cfa88e64b112aa9bcff54471efc
3
+ metadata.gz: b2cff9fec79805dad7c1257d4510b13a4e253959942930cf60044195f47323c9
4
+ data.tar.gz: 5d83085f13bba93a899125f0047965d4176056c7260f6772d69af895b9868d89
5
5
  SHA512:
6
- metadata.gz: 9e96164ddef86ce2cf6f2351f221eb1a0dcf8d03bcfdec9aa70d90b1b160ca6f18135376bf249bcecb9a433a17400596de7e0a47e8ac1afe40b4f29021c7a4df
7
- data.tar.gz: 9750272605b12a86c50fa356f2f7e09cf72e3889bb2cf6cea91ed34bb6ab0834409bea1611316271de6987de0441d09491fb03a7dda6d0399b0d9a484899fefc
6
+ metadata.gz: ee4a8807e8e46da376e3acf326078ef46b07ea94bb4536f7c9bf8875075d31bf80764cef33d453c7a00ac876cbe82dffb1bbce1ebaf366eaa113d0de514ddb81
7
+ data.tar.gz: d0cc8c92cd20111aa7c24719e22b87e9852006a53102cde5bbd0b256c8c746b75cf541947c8189321a06be902f257bd71b7c1d768c30a76ece5622709951389c
data/lib/optic/rails.rb CHANGED
@@ -103,57 +103,68 @@ module Optic
103
103
  }
104
104
  end
105
105
 
106
- def self.execute_sql_query(sql)
106
+ # Try to be defensive with our DB connection:
107
+ # 1) Check a connection out from the thread pool instead of using an implicit one
108
+ # 2) Make the connection read-only
109
+ # 3) Time out any queries that take more than 100ms
110
+ def self.with_connection
107
111
  ActiveRecord::Base.connection_pool.with_connection do |connection|
108
- connection.execute(sql)
112
+ connection.transaction do
113
+ connection.execute "SET TRANSACTION READ ONLY"
114
+ connection.execute "SET LOCAL statement_timeout = 100"
115
+ yield connection
116
+ end
109
117
  end
110
118
  end
111
119
 
112
120
  def self.get_metrics(pivot_name)
113
- pivot = pivot_name.constantize
114
-
115
- result = {
116
- entity_totals: [],
117
- pivot_name: pivot.name,
118
- # TODO filter columns, spread over multiple queries
119
- pivot_values: execute_sql_query(pivot.unscoped.select("*").to_sql).to_a,
120
- pivoted_totals: []
121
- # TODO also return computed "spanning" tree of objects from the pivot's POV (using the dijkstra paths from below)
122
- }
123
-
124
- graph = entity_graph
125
-
126
- # Spit out counts for each entity by the customer pivot
127
-
128
- edge_weights = lambda { |_| 1 }
129
-
130
- graph.vertices.each do |vertex|
131
- count_query = vertex.unscoped.select("COUNT(*)").to_sql
132
- result[:entity_totals] << { name: vertex.name, total: execute_sql_query(count_query).first["count"] }
121
+ with_connection do |connection|
122
+ pivot = pivot_name.constantize
123
+ pivot_values = connection.execute(pivot.unscoped.select("*").to_sql).to_a
124
+
125
+ result = {
126
+ entity_totals: [],
127
+ pivot_name: pivot.name,
128
+ # TODO filter columns, spread over multiple queries
129
+ pivot_values: pivot_values,
130
+ pivoted_totals: []
131
+ # TODO also return computed "spanning" tree of objects from the pivot's POV (using the dijkstra paths from below)
132
+ }
133
+
134
+ graph = entity_graph
135
+
136
+ # Spit out counts for each entity by the customer pivot
137
+
138
+ edge_weights = lambda { |_| 1 }
139
+
140
+ graph.vertices.each do |vertex|
141
+ count_query = vertex.unscoped.select("COUNT(*)").to_sql
142
+ result[:entity_totals] << { name: vertex.name, total: connection.execute(count_query).first["count"] }
143
+
144
+ next if vertex == pivot # Skip pivoted metrics if this is the pivot
145
+
146
+ # TODO weight edges to give preference to non-optional belongs_to (and other attributes?)
147
+ path = graph.dijkstra_shortest_path(edge_weights, vertex, pivot)
148
+ if path
149
+ # Generate a SQL query to count the number of vertex instances grouped by pivot id, with appropriate joins from the path
150
+ belongs_to_names = path.each_cons(2).map do |join_from, join_to|
151
+ # TODO we shouldn't have to look up the edge again - use a graph model that allows us to annotate the edges with the reflections
152
+ reflections = join_from.reflect_on_all_associations(:belongs_to).find_all { |reflection| !reflection.options[:polymorphic] && reflection.klass == join_to }
153
+ raise "Multiple belongs_to unsupported" unless reflections.size == 1 # TODO
154
+ reflections.first.name
155
+ end
133
156
 
134
- next if vertex == pivot # Skip pivoted metrics if this is the pivot
157
+ joins = belongs_to_names.reverse.inject { |acc, elt| { elt => acc } }
158
+ query = vertex.unscoped.joins(joins).group(qualified_primary_key(pivot)).select(qualified_primary_key(pivot), "COUNT(#{qualified_primary_key(vertex)})").to_sql
135
159
 
136
- # TODO weight edges to give preference to non-optional belongs_to (and other attributes?)
137
- path = graph.dijkstra_shortest_path(edge_weights, vertex, pivot)
138
- if path
139
- # Generate a SQL query to count the number of vertex instances grouped by pivot id, with appropriate joins from the path
140
- belongs_to_names = path.each_cons(2).map do |join_from, join_to|
141
- # TODO we shouldn't have to look up the edge again - use a graph model that allows us to annotate the edges with the reflections
142
- reflections = join_from.reflect_on_all_associations(:belongs_to).find_all { |reflection| !reflection.options[:polymorphic] && reflection.klass == join_to }
143
- raise "Multiple belongs_to unsupported" unless reflections.size == 1 # TODO
144
- reflections.first.name
160
+ result[:pivoted_totals] << { entity_name: vertex.name, totals: Hash[connection.execute(query).map { |record| [record["id"], record["count"]] }] }
161
+ else
162
+ p "WARNING: No path from #{vertex.name} to #{pivot.name}"
145
163
  end
146
-
147
- joins = belongs_to_names.reverse.inject { |acc, elt| { elt => acc } }
148
- query = vertex.unscoped.joins(joins).group(qualified_primary_key(pivot)).select(qualified_primary_key(pivot), "COUNT(#{qualified_primary_key(vertex)})").to_sql
149
-
150
- result[:pivoted_totals] << { entity_name: vertex.name, totals: Hash[execute_sql_query(query).map { |record| [record["id"], record["count"]] }] }
151
- else
152
- p "WARNING: No path from #{vertex.name} to #{pivot.name}"
153
164
  end
154
- end
155
165
 
156
- result
166
+ result
167
+ end
157
168
  end
158
169
 
159
170
  end
@@ -1,5 +1,5 @@
1
1
  module Optic
2
2
  module Rails
3
- VERSION = '0.0.6'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optic-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Vaynshtok
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-21 00:00:00.000000000 Z
11
+ date: 2018-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: action_cable_client