optic-rails 0.0.6 → 0.1.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 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