querrel 1.2.0 → 1.3.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
  SHA1:
3
- metadata.gz: 8a221606ea4c6afe6ec28d674c8a3991bb221d15
4
- data.tar.gz: 3c1bb8daff36108b3addd087d3769cbe3b5cefab
3
+ metadata.gz: ac00fa1731878f9644eeef4cba1df54454b70d3c
4
+ data.tar.gz: 3333ac82975cc0ab9a9359ea254db41c97330ded
5
5
  SHA512:
6
- metadata.gz: 971c487ef162a42b1874616ea1f267a13836d0671766fe12f83dc359fef3df071d0b4b9c6cf8e39d9cd85cf896184b806f3bfb6881c4f9fb5d102942c7c9e744
7
- data.tar.gz: d2aa19d3e6cb412bee5210107b616607847870baf61bc5d4c8948a13d756c771c278610aa578f5f5e4f1f0c6a225cb2af61a3b384a30a85751e00e52602681cd
6
+ metadata.gz: fa8d8b12c96c966a0b5e755749a175902f4ba5b1808f94d3cdb70179250d6f624eba73d69f754a9e0e642bc10c02960455ac1bafb25fccb69dd44e8980daaef3
7
+ data.tar.gz: a65135e0097d7ccdad1d7f276ab77adc0cc48f1787db0a413c12afa93920795558157ccda86737eb51a85e589ab86c6f2ed7910b771cb0155a89063e567f41e6
data/README.md CHANGED
@@ -18,6 +18,8 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
+ ### Basic
22
+
21
23
  You can use with Querrel directly via it's top level namespace, or by creating an instance:
22
24
 
23
25
  ```ruby
@@ -28,6 +30,35 @@ all_brands = Querrel.new(['db1', 'db2', 'db3']).query(Brand.all)
28
30
 
29
31
  Either of the above will give you an array of all the Brand objects from all the given databases. The records will be marked as readonly.
30
32
 
33
+ ### Advanced
34
+
35
+ `query` will yield a block with the passed in `ActiveRecord::Relation`, this allows you to do additional operations on the results before they are merged, for example you could use pluck:
36
+
37
+ ```ruby
38
+ all_brand_names = q.query(Brand.all) do |s|
39
+ s.pluck(:name)
40
+ end
41
+ ```
42
+
43
+ There is also a `run` method which instead of running a preprescribed scope and merging, will yield the `ConnectedModelFactory` class which allows you to wrap any ActiveRecord class so that you can query it in the thread, for instance:
44
+
45
+ ```ruby
46
+ require 'thread'
47
+ all_brands = Brand.all
48
+ b_s = Mutex.new
49
+ all_products = Product.all
50
+ p_s = Mutex.new
51
+
52
+ Querrel.run(on: dbs) do |q|
53
+ b_s.synchronize { all_brands += q[Brand].all.to_a }
54
+ p_s.synchronize { all_products += q[Product].all.to_a }
55
+ end
56
+ ```
57
+
58
+ At the moment, any queries using `run` and the ConnectedModelFactory won't return objects of the class passed in, but an anonymous class instead. However, `query` can marshall the results back into the original class.
59
+
60
+ ### Databases
61
+
31
62
  There are three ways in which you can instruct Querrel which databases to use:
32
63
 
33
64
  1. Pass in an array of environments, e.g. `Querrel.new([:customer1, :customer2])`
@@ -47,6 +78,14 @@ There are three ways in which you can instruct Querrel which databases to use:
47
78
  })
48
79
  ```
49
80
 
81
+ ### Configuration
82
+
83
+ By default Querrel will use a maximum of 20 threads, but you can adjust this using the `:threads` option:
84
+
85
+ ```ruby
86
+ q = Querrel.new(dbs, threads: 50)
87
+ ```
88
+
50
89
  ## Contributing
51
90
 
52
91
  1. Fork it ( https://github.com/meritec/querrel/fork )
@@ -6,6 +6,8 @@ module Querrel
6
6
  Class.new(model).tap do |m|
7
7
  m.send(:define_singleton_method, :name) { dynamic_class_name }
8
8
  m.establish_connection(con_spec.config)
9
+
10
+ Thread.current[:querrel_connected_models] << m
9
11
  end
10
12
  end
11
13
  end
@@ -6,6 +6,7 @@ module Querrel
6
6
  end
7
7
 
8
8
  def map(scope, options = {}, &blk)
9
+ options = @options.merge(options)
9
10
  if options.key?(:on)
10
11
  resolver = ConnectionResolver.new(options[:on], !!options[:db_names])
11
12
  dbs = resolver.configurations.keys
@@ -16,29 +17,38 @@ module Querrel
16
17
 
17
18
  query_model = scope.model
18
19
  results = {}
20
+ results_semaphore = Mutex.new
19
21
 
20
- threads = []
22
+ pool = StaticPool.new(options[:threads] || 20)
21
23
  dbs.each do |db|
22
- threads << Thread.new do
24
+ pool.enqueue do
25
+ Thread.current[:querrel_connected_models] = []
23
26
  con_spec = retrieve_connection_spec(db, resolver)
24
27
  Thread.current[:querrel_con_spec] = con_spec
25
28
  dynamic_class = ConnectedModelFactory[query_model, con_spec]
26
29
 
27
30
  begin
28
31
  local_scope = dynamic_class.all.merge(scope)
29
- results[db] = if block_given?
30
- res = yield(local_scope)
32
+ local_results = if block_given?
33
+ res = yield(local_scope, ConnectedModelFactory)
31
34
  res.to_a.each(&:readonly!) if res.is_a?(ActiveRecord::Relation)
32
35
  res
33
36
  else
34
- local_scope.to_a.each(&:readonly!)
37
+ local_scope.to_a.map do |r|
38
+ query_model.instantiate(r.attributes, {}).tap(&:readonly!)
39
+ end
35
40
  end
41
+
42
+ results_semaphore.synchronize { results[db] = local_results }
36
43
  ensure
37
- dynamic_class.connection_pool.release_connection
44
+ Thread.current[:querrel_connected_models].each do |m|
45
+ m.connection_pool.release_connection
46
+ end
47
+ Thread.current[:querrel_connected_models] = nil
38
48
  end
39
49
  end
40
50
  end
41
- threads.each(&:join)
51
+ pool.do_your_thang!
42
52
 
43
53
  results
44
54
  end
@@ -1,5 +1,6 @@
1
1
  require 'querrel/connected_model_factory'
2
2
  require 'querrel/connection_resolver'
3
+ require 'querrel/static_pool'
3
4
  require 'querrel/map_reduce'
4
5
 
5
6
  module Querrel
@@ -8,10 +9,12 @@ module Querrel
8
9
  attr_accessor :connection_resolver
9
10
 
10
11
  def initialize(dbs, options = {})
11
- @connection_resolver = ConnectionResolver.new(dbs, options[:db_names])
12
+ @connection_resolver = ConnectionResolver.new(dbs, options.delete(:db_names))
13
+ @options = options
12
14
  end
13
15
 
14
16
  def run(options = {}, &blk)
17
+ options = @options.merge(options)
15
18
  if options.key?(:on)
16
19
  resolver = ConnectionResolver.new(options[:on], !!options[:db_names])
17
20
  dbs = resolver.configurations.keys
@@ -20,15 +23,23 @@ module Querrel
20
23
  dbs = @connection_resolver.configurations.keys
21
24
  end
22
25
 
23
- threads = []
26
+ pool = StaticPool.new(options[:threads] || 20)
24
27
  dbs.each do |db|
25
- threads << Thread.new do
26
- con_spec = retrieve_connection_spec(db, resolver)
27
- Thread.current[:querrel_con_spec] = con_spec
28
- yield(ConnectedModelFactory)
28
+ pool.enqueue do
29
+ begin
30
+ Thread.current[:querrel_connected_models] = []
31
+ con_spec = retrieve_connection_spec(db, resolver)
32
+ Thread.current[:querrel_con_spec] = con_spec
33
+ yield(ConnectedModelFactory)
34
+ ensure
35
+ Thread.current[:querrel_connected_models].each do |m|
36
+ m.connection_pool.release_connection
37
+ end
38
+ Thread.current[:querrel_connected_models] = nil
39
+ end
29
40
  end
30
41
  end
31
- threads.each(&:join)
42
+ pool.do_your_thang!
32
43
  end
33
44
 
34
45
  def retrieve_connection_spec(db, resolver)
@@ -0,0 +1,26 @@
1
+ require 'thread'
2
+
3
+ module Querrel
4
+ class StaticPool
5
+ def initialize(size)
6
+ @size = size
7
+ @jobs = Queue.new
8
+ end
9
+
10
+ def enqueue(&job)
11
+ @jobs.push(job)
12
+ end
13
+
14
+ def do_your_thang!
15
+ threads = Array.new(@size) do
16
+ Thread.new do
17
+ while job = @jobs.pop(true) rescue nil
18
+ job.call
19
+ end
20
+ end
21
+ end
22
+
23
+ threads.each(&:join)
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Querrel
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -14,6 +14,7 @@ class NonInstanceTest < Querrel::Test
14
14
 
15
15
  assert_equal num_brands * @dbs.length, res.length,
16
16
  "Not returning the correct number of results"
17
+ assert res.all?{ |r| r.is_a?(Brand) }
17
18
  end
18
19
 
19
20
  def test_map
@@ -0,0 +1,23 @@
1
+ require 'thread'
2
+ require_relative 'setup/test_helper'
3
+
4
+ class StaticPoolTest < Querrel::Test
5
+ def test_never_more_than_max_threads
6
+ max_threads = 10
7
+ p = Querrel::StaticPool.new(max_threads)
8
+
9
+ thread_counts = []
10
+ thread_semaphore = Mutex.new
11
+
12
+ 50.times do
13
+ p.enqueue do
14
+ thread_semaphore.synchronize do
15
+ thread_counts << Thread.list.count{ |t| t.status == "run" }
16
+ end
17
+ end
18
+ end
19
+ p.do_your_thang!
20
+
21
+ assert thread_counts.all?{ |c| c <= max_threads }
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: querrel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Campbell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-21 00:00:00.000000000 Z
11
+ date: 2015-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -97,6 +97,7 @@ files:
97
97
  - lib/querrel/connection_resolver.rb
98
98
  - lib/querrel/map_reduce.rb
99
99
  - lib/querrel/querreller.rb
100
+ - lib/querrel/static_pool.rb
100
101
  - lib/querrel/version.rb
101
102
  - querrel.gemspec
102
103
  - test/instance_test.rb
@@ -110,6 +111,7 @@ files:
110
111
  - test/setup/querrel_test_class.rb
111
112
  - test/setup/schema.rb
112
113
  - test/setup/test_helper.rb
114
+ - test/static_pool_test.rb
113
115
  - test/test_test.rb
114
116
  homepage: https://github.com/meritec/querrel
115
117
  licenses:
@@ -147,4 +149,5 @@ test_files:
147
149
  - test/setup/querrel_test_class.rb
148
150
  - test/setup/schema.rb
149
151
  - test/setup/test_helper.rb
152
+ - test/static_pool_test.rb
150
153
  - test/test_test.rb