active-record-transactioner 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/Gemfile.lock +100 -12
- data/README.md +86 -0
- data/VERSION +1 -1
- data/active-record-transactioner.gemspec +68 -9
- data/lib/active-record-transactioner.rb +143 -106
- data/shippable.yml +16 -0
- data/spec/active-record-transactioner_spec.rb +21 -21
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +28 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.example.yml +7 -0
- data/spec/dummy/config/database.shippable.yml +7 -0
- data/spec/dummy/config/database.yml +7 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +32 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/db/migrate/20141203180942_create_users.rb +13 -0
- data/spec/dummy/db/schema.rb +27 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/models/user_spec.rb +11 -0
- data/spec/models/user_threadded_spec.rb +11 -0
- data/spec/spec_helper.rb +17 -2
- data/spec/support/basic_user_operations.rb +44 -0
- data/spec/test_classes/active-record-transactioner-test-class.rb +12 -14
- metadata +124 -22
- data/README.rdoc +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0d85c7182312c5329e6c8731c57aa2d737f9952
|
4
|
+
data.tar.gz: 42316fbb652cfef2b9b35b0c572244dde822b404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90e200215749d32941aace7319d6c05d0cd305bf237afa40740d441078542fc83a6e647385449e2e4b9908f389471c59b27c90e4034b0d9612157816452b81ce
|
7
|
+
data.tar.gz: 2ee07d9fa63ebb9bb7ec9f4d620eef2ea98166fe25464497c42ecfebb533cdbb3e392fcd0bcdd4eeb4eb586452bf707797f86b4ad5b9059392ec9943d832da21
|
data/Gemfile
CHANGED
@@ -6,9 +6,15 @@ source "http://rubygems.org"
|
|
6
6
|
# Add dependencies to develop your gem here.
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
8
8
|
group :development do
|
9
|
-
gem "
|
9
|
+
gem "rails", "~> 4.0.10"
|
10
|
+
gem "rspec-rails", "~> 3.1.0"
|
10
11
|
gem "rdoc", "~> 3.12"
|
11
12
|
gem "bundler", ">= 1.0.0"
|
12
13
|
gem "jeweler", ">= 1.8.4"
|
13
14
|
gem "builder"
|
15
|
+
gem "activerecord"
|
16
|
+
gem "mysql2"
|
17
|
+
gem "pry"
|
14
18
|
end
|
19
|
+
|
20
|
+
gem "codeclimate-test-reporter", group: :test, require: nil
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,41 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
actionmailer (4.0.12)
|
5
|
+
actionpack (= 4.0.12)
|
6
|
+
mail (~> 2.5, >= 2.5.4)
|
7
|
+
actionpack (4.0.12)
|
8
|
+
activesupport (= 4.0.12)
|
9
|
+
builder (~> 3.1.0)
|
10
|
+
erubis (~> 2.7.0)
|
11
|
+
rack (~> 1.5.2)
|
12
|
+
rack-test (~> 0.6.2)
|
13
|
+
activemodel (4.0.12)
|
14
|
+
activesupport (= 4.0.12)
|
15
|
+
builder (~> 3.1.0)
|
16
|
+
activerecord (4.0.12)
|
17
|
+
activemodel (= 4.0.12)
|
18
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
19
|
+
activesupport (= 4.0.12)
|
20
|
+
arel (~> 4.0.0)
|
21
|
+
activerecord-deprecated_finders (1.0.3)
|
22
|
+
activesupport (4.0.12)
|
23
|
+
i18n (~> 0.6, >= 0.6.9)
|
24
|
+
minitest (~> 4.2)
|
25
|
+
multi_json (~> 1.3)
|
26
|
+
thread_safe (~> 0.1)
|
27
|
+
tzinfo (~> 0.3.37)
|
4
28
|
addressable (2.3.6)
|
5
|
-
|
29
|
+
arel (4.0.2)
|
30
|
+
builder (3.1.4)
|
31
|
+
codeclimate-test-reporter (0.4.2)
|
32
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
33
|
+
coderay (1.1.0)
|
6
34
|
descendants_tracker (0.0.4)
|
7
35
|
thread_safe (~> 0.3, >= 0.3.1)
|
8
|
-
diff-lcs (1.
|
36
|
+
diff-lcs (1.2.5)
|
37
|
+
docile (1.1.5)
|
38
|
+
erubis (2.7.0)
|
9
39
|
faraday (0.9.0)
|
10
40
|
multipart-post (>= 1.2, < 3)
|
11
41
|
git (1.2.6)
|
@@ -19,6 +49,8 @@ GEM
|
|
19
49
|
oauth2
|
20
50
|
hashie (2.1.1)
|
21
51
|
highline (1.6.21)
|
52
|
+
hike (1.2.3)
|
53
|
+
i18n (0.6.11)
|
22
54
|
jeweler (2.0.1)
|
23
55
|
builder
|
24
56
|
bundler (>= 1.0)
|
@@ -31,10 +63,16 @@ GEM
|
|
31
63
|
json (1.8.1)
|
32
64
|
jwt (0.1.13)
|
33
65
|
multi_json (>= 1.5)
|
66
|
+
mail (2.6.3)
|
67
|
+
mime-types (>= 1.16, < 3)
|
68
|
+
method_source (0.8.2)
|
69
|
+
mime-types (2.4.3)
|
34
70
|
mini_portile (0.6.0)
|
71
|
+
minitest (4.7.5)
|
35
72
|
multi_json (1.10.0)
|
36
73
|
multi_xml (0.5.5)
|
37
74
|
multipart-post (2.0.0)
|
75
|
+
mysql2 (0.3.16)
|
38
76
|
nokogiri (1.6.2.1)
|
39
77
|
mini_portile (= 0.6.0)
|
40
78
|
oauth2 (0.9.3)
|
@@ -43,26 +81,76 @@ GEM
|
|
43
81
|
multi_json (~> 1.3)
|
44
82
|
multi_xml (~> 0.5)
|
45
83
|
rack (~> 1.2)
|
84
|
+
pry (0.10.1)
|
85
|
+
coderay (~> 1.1.0)
|
86
|
+
method_source (~> 0.8.1)
|
87
|
+
slop (~> 3.4)
|
46
88
|
rack (1.5.2)
|
89
|
+
rack-test (0.6.2)
|
90
|
+
rack (>= 1.0)
|
91
|
+
rails (4.0.12)
|
92
|
+
actionmailer (= 4.0.12)
|
93
|
+
actionpack (= 4.0.12)
|
94
|
+
activerecord (= 4.0.12)
|
95
|
+
activesupport (= 4.0.12)
|
96
|
+
bundler (>= 1.3.0, < 2.0)
|
97
|
+
railties (= 4.0.12)
|
98
|
+
sprockets-rails (~> 2.0)
|
99
|
+
railties (4.0.12)
|
100
|
+
actionpack (= 4.0.12)
|
101
|
+
activesupport (= 4.0.12)
|
102
|
+
rake (>= 0.8.7)
|
103
|
+
thor (>= 0.18.1, < 2.0)
|
47
104
|
rake (10.3.2)
|
48
105
|
rdoc (3.12.2)
|
49
106
|
json (~> 1.4)
|
50
|
-
rspec (
|
51
|
-
rspec-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
rspec-
|
56
|
-
|
57
|
-
rspec-
|
58
|
-
|
107
|
+
rspec-core (3.1.7)
|
108
|
+
rspec-support (~> 3.1.0)
|
109
|
+
rspec-expectations (3.1.2)
|
110
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
111
|
+
rspec-support (~> 3.1.0)
|
112
|
+
rspec-mocks (3.1.3)
|
113
|
+
rspec-support (~> 3.1.0)
|
114
|
+
rspec-rails (3.1.0)
|
115
|
+
actionpack (>= 3.0)
|
116
|
+
activesupport (>= 3.0)
|
117
|
+
railties (>= 3.0)
|
118
|
+
rspec-core (~> 3.1.0)
|
119
|
+
rspec-expectations (~> 3.1.0)
|
120
|
+
rspec-mocks (~> 3.1.0)
|
121
|
+
rspec-support (~> 3.1.0)
|
122
|
+
rspec-support (3.1.2)
|
123
|
+
simplecov (0.9.1)
|
124
|
+
docile (~> 1.1.0)
|
125
|
+
multi_json (~> 1.0)
|
126
|
+
simplecov-html (~> 0.8.0)
|
127
|
+
simplecov-html (0.8.0)
|
128
|
+
slop (3.6.0)
|
129
|
+
sprockets (2.12.3)
|
130
|
+
hike (~> 1.2)
|
131
|
+
multi_json (~> 1.0)
|
132
|
+
rack (~> 1.0)
|
133
|
+
tilt (~> 1.1, != 1.3.0)
|
134
|
+
sprockets-rails (2.2.2)
|
135
|
+
actionpack (>= 3.0)
|
136
|
+
activesupport (>= 3.0)
|
137
|
+
sprockets (>= 2.8, < 4.0)
|
138
|
+
thor (0.19.1)
|
139
|
+
thread_safe (0.3.4)
|
140
|
+
tilt (1.4.1)
|
141
|
+
tzinfo (0.3.42)
|
59
142
|
|
60
143
|
PLATFORMS
|
61
144
|
ruby
|
62
145
|
|
63
146
|
DEPENDENCIES
|
147
|
+
activerecord
|
64
148
|
builder
|
65
149
|
bundler (>= 1.0.0)
|
150
|
+
codeclimate-test-reporter
|
66
151
|
jeweler (>= 1.8.4)
|
152
|
+
mysql2
|
153
|
+
pry
|
154
|
+
rails (~> 4.0.10)
|
67
155
|
rdoc (~> 3.12)
|
68
|
-
rspec (~>
|
156
|
+
rspec-rails (~> 3.1.0)
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
[![Build Status](https://api.shippable.com/projects/540e7b993479c5ea8f9ec1f2/badge?branchName=master)](https://app.shippable.com/projects/540e7b993479c5ea8f9ec1f2/builds/latest)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/kaspernj/active-record-transactioner/badges/gpa.svg)](https://codeclimate.com/github/kaspernj/active-record-transactioner)
|
3
|
+
[![Test Coverage](https://codeclimate.com/github/kaspernj/active-record-transactioner/badges/coverage.svg)](https://codeclimate.com/github/kaspernj/active-record-transactioner)
|
4
|
+
|
5
|
+
# active-record-transactioner
|
6
|
+
|
7
|
+
Queue saving and destroying of many models into transactions through multiple threads for optimal database-performance in ActiveRecord.
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
Add to your Gemfile and bundle:
|
12
|
+
```ruby
|
13
|
+
gem 'active-record-transactioner'
|
14
|
+
```
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
### Iterate a million times - will update each 1000 records in a single transaction with `save!`.
|
19
|
+
```ruby
|
20
|
+
ActiveRecordTransactioner.new do |trans|
|
21
|
+
models.each do |model|
|
22
|
+
model.some_attribute = "some_value"
|
23
|
+
trans.save!(model)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
You can also do it a bit more complicated with some custom options.
|
29
|
+
```ruby
|
30
|
+
ActiveRecordTransactioner.new(
|
31
|
+
call_args: ["Hello world!"],
|
32
|
+
call_method: :save!,
|
33
|
+
transaction_method: :transaction,
|
34
|
+
transaction_size: 1000,
|
35
|
+
threadded: false
|
36
|
+
) do |trans|
|
37
|
+
models.each do |model|
|
38
|
+
model.some_attribute = "some_value"
|
39
|
+
trans.save!(model)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
### Destroy
|
45
|
+
```ruby
|
46
|
+
ActiveRecordTransactioner.new do |trans|
|
47
|
+
models.each do |model|
|
48
|
+
trans.destroy!(model)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
### Threadded
|
54
|
+
|
55
|
+
The "threadded" and "max_running_threads" options will start new threads to actually do the saving of the models, while continuing to queue up new models for saving in the primary thread. This way the database can utilize multiple cores, and if you use a threadded VM like JRuby or Rubinius, you will utilize even more cores.
|
56
|
+
|
57
|
+
This can help greatly speed up the processing.
|
58
|
+
|
59
|
+
Be aware that the saving of only one type of model, will be limited to only one thread, so it will make sense to try and queue up as many type of models as possible. Like users, orders and so on.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
ActiveRecordTransactioner.new(
|
63
|
+
threadded: true,
|
64
|
+
max_running_threads: 3
|
65
|
+
) do |trans|
|
66
|
+
models.each do |model|
|
67
|
+
model.some_attribute = "some_value"
|
68
|
+
trans.save!(model)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing to active-record-transactioner
|
74
|
+
|
75
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
76
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
77
|
+
* Fork the project.
|
78
|
+
* Start a feature/bugfix branch.
|
79
|
+
* Commit and push until you are happy with your contribution.
|
80
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
81
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
82
|
+
|
83
|
+
## Copyright
|
84
|
+
|
85
|
+
Copyright (c) 2013 Kasper Johansen. See LICENSE.txt for
|
86
|
+
further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
@@ -2,19 +2,21 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: active-record-transactioner 0.0.6 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "active-record-transactioner"
|
8
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.6"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Kasper Johansen"]
|
12
|
-
s.date = "2014-05
|
14
|
+
s.date = "2014-12-05"
|
13
15
|
s.description = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
|
14
16
|
s.email = "kj@gfish.com"
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"LICENSE.txt",
|
17
|
-
"README.
|
19
|
+
"README.md"
|
18
20
|
]
|
19
21
|
s.files = [
|
20
22
|
".document",
|
@@ -22,43 +24,100 @@ Gem::Specification.new do |s|
|
|
22
24
|
"Gemfile",
|
23
25
|
"Gemfile.lock",
|
24
26
|
"LICENSE.txt",
|
25
|
-
"README.
|
27
|
+
"README.md",
|
26
28
|
"Rakefile",
|
27
29
|
"VERSION",
|
28
30
|
"active-record-transactioner.gemspec",
|
29
31
|
"lib/active-record-transactioner.rb",
|
32
|
+
"shippable.yml",
|
30
33
|
"spec/active-record-transactioner_spec.rb",
|
34
|
+
"spec/dummy/README.rdoc",
|
35
|
+
"spec/dummy/Rakefile",
|
36
|
+
"spec/dummy/app/assets/images/.keep",
|
37
|
+
"spec/dummy/app/assets/javascripts/application.js",
|
38
|
+
"spec/dummy/app/assets/stylesheets/application.css",
|
39
|
+
"spec/dummy/app/controllers/application_controller.rb",
|
40
|
+
"spec/dummy/app/controllers/concerns/.keep",
|
41
|
+
"spec/dummy/app/helpers/application_helper.rb",
|
42
|
+
"spec/dummy/app/mailers/.keep",
|
43
|
+
"spec/dummy/app/models/concerns/.keep",
|
44
|
+
"spec/dummy/app/models/user.rb",
|
45
|
+
"spec/dummy/app/views/layouts/application.html.erb",
|
46
|
+
"spec/dummy/bin/bundle",
|
47
|
+
"spec/dummy/bin/rails",
|
48
|
+
"spec/dummy/bin/rake",
|
49
|
+
"spec/dummy/config.ru",
|
50
|
+
"spec/dummy/config/application.rb",
|
51
|
+
"spec/dummy/config/boot.rb",
|
52
|
+
"spec/dummy/config/database.example.yml",
|
53
|
+
"spec/dummy/config/database.shippable.yml",
|
54
|
+
"spec/dummy/config/database.yml",
|
55
|
+
"spec/dummy/config/environment.rb",
|
56
|
+
"spec/dummy/config/environments/development.rb",
|
57
|
+
"spec/dummy/config/environments/production.rb",
|
58
|
+
"spec/dummy/config/environments/test.rb",
|
59
|
+
"spec/dummy/config/initializers/backtrace_silencers.rb",
|
60
|
+
"spec/dummy/config/initializers/filter_parameter_logging.rb",
|
61
|
+
"spec/dummy/config/initializers/inflections.rb",
|
62
|
+
"spec/dummy/config/initializers/mime_types.rb",
|
63
|
+
"spec/dummy/config/initializers/secret_token.rb",
|
64
|
+
"spec/dummy/config/initializers/session_store.rb",
|
65
|
+
"spec/dummy/config/initializers/wrap_parameters.rb",
|
66
|
+
"spec/dummy/config/locales/en.yml",
|
67
|
+
"spec/dummy/config/routes.rb",
|
68
|
+
"spec/dummy/db/migrate/20141203180942_create_users.rb",
|
69
|
+
"spec/dummy/db/schema.rb",
|
70
|
+
"spec/dummy/lib/assets/.keep",
|
71
|
+
"spec/dummy/log/.keep",
|
72
|
+
"spec/dummy/public/404.html",
|
73
|
+
"spec/dummy/public/422.html",
|
74
|
+
"spec/dummy/public/500.html",
|
75
|
+
"spec/dummy/public/favicon.ico",
|
76
|
+
"spec/models/user_spec.rb",
|
77
|
+
"spec/models/user_threadded_spec.rb",
|
31
78
|
"spec/spec_helper.rb",
|
79
|
+
"spec/support/basic_user_operations.rb",
|
32
80
|
"spec/test_classes/active-record-transactioner-test-class.rb"
|
33
81
|
]
|
34
82
|
s.homepage = "http://github.com/kaspernj/active-record-transactioner"
|
35
83
|
s.licenses = ["MIT"]
|
36
|
-
s.
|
37
|
-
s.rubygems_version = "2.0.7"
|
84
|
+
s.rubygems_version = "2.4.0"
|
38
85
|
s.summary = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
|
39
86
|
|
40
87
|
if s.respond_to? :specification_version then
|
41
88
|
s.specification_version = 4
|
42
89
|
|
43
90
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
44
|
-
s.add_development_dependency(%q<
|
91
|
+
s.add_development_dependency(%q<rails>, ["~> 4.0.10"])
|
92
|
+
s.add_development_dependency(%q<rspec-rails>, ["~> 3.1.0"])
|
45
93
|
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
46
94
|
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
47
95
|
s.add_development_dependency(%q<jeweler>, [">= 1.8.4"])
|
48
96
|
s.add_development_dependency(%q<builder>, [">= 0"])
|
97
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
98
|
+
s.add_development_dependency(%q<mysql2>, [">= 0"])
|
99
|
+
s.add_development_dependency(%q<pry>, [">= 0"])
|
49
100
|
else
|
50
|
-
s.add_dependency(%q<
|
101
|
+
s.add_dependency(%q<rails>, ["~> 4.0.10"])
|
102
|
+
s.add_dependency(%q<rspec-rails>, ["~> 3.1.0"])
|
51
103
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
52
104
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
53
105
|
s.add_dependency(%q<jeweler>, [">= 1.8.4"])
|
54
106
|
s.add_dependency(%q<builder>, [">= 0"])
|
107
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
108
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
109
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
55
110
|
end
|
56
111
|
else
|
57
|
-
s.add_dependency(%q<
|
112
|
+
s.add_dependency(%q<rails>, ["~> 4.0.10"])
|
113
|
+
s.add_dependency(%q<rspec-rails>, ["~> 3.1.0"])
|
58
114
|
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
59
115
|
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
60
116
|
s.add_dependency(%q<jeweler>, [">= 1.8.4"])
|
61
117
|
s.add_dependency(%q<builder>, [">= 0"])
|
118
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
119
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
120
|
+
s.add_dependency(%q<pry>, [">= 0"])
|
62
121
|
end
|
63
122
|
end
|
64
123
|
|
@@ -2,147 +2,184 @@ require "monitor"
|
|
2
2
|
|
3
3
|
class ActiveRecordTransactioner
|
4
4
|
DEFAULT_ARGS = {
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
5
|
+
call_args: [],
|
6
|
+
call_method: :save!,
|
7
|
+
transaction_method: :transaction,
|
8
|
+
transaction_size: 1000,
|
9
|
+
threadded: false,
|
10
|
+
max_running_threads: 2,
|
11
|
+
debug: false
|
11
12
|
}
|
12
|
-
|
13
|
+
|
13
14
|
ALLOWED_ARGS = DEFAULT_ARGS.keys
|
14
|
-
|
15
|
+
|
15
16
|
def initialize(args = {})
|
16
|
-
args.each
|
17
|
-
|
18
|
-
end
|
19
|
-
|
17
|
+
args.each { |key, val| raise "Invalid key: '#{key}'." unless ALLOWED_ARGS.include?(key) }
|
18
|
+
|
20
19
|
@args = DEFAULT_ARGS.merge(args)
|
21
|
-
|
22
|
-
|
23
|
-
@count = 0
|
24
|
-
@lock = Monitor.new
|
25
|
-
@lock_threads = Monitor.new
|
26
|
-
@lock_models = {}
|
27
|
-
@debug = @args[:debug]
|
28
|
-
|
20
|
+
parse_and_set_args
|
21
|
+
|
29
22
|
if block_given?
|
30
23
|
begin
|
31
24
|
yield self
|
32
25
|
ensure
|
33
26
|
flush
|
34
|
-
join
|
27
|
+
join if threadded?
|
35
28
|
end
|
36
29
|
end
|
37
30
|
end
|
38
|
-
|
39
|
-
#Adds another model to the queue and calls 'flush' if it is over the limit.
|
40
|
-
def
|
31
|
+
|
32
|
+
# Adds another model to the queue and calls 'flush' if it is over the limit.
|
33
|
+
def save!(model)
|
34
|
+
raise ActiveRecord::RecordInvalid, model unless model.valid?
|
35
|
+
queue(model, type: :save!, validate: false)
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy!(model)
|
39
|
+
queue(model, type: :destroy!)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds another model to the queue and calls 'flush' if it is over the limit.
|
43
|
+
def queue(model, args = {})
|
44
|
+
args[:type] ||= :save!
|
45
|
+
|
41
46
|
@lock.synchronize do
|
42
47
|
klass = model.class
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@
|
48
|
+
|
49
|
+
validate = args.key?(:validate) ? args[:validate] : true
|
50
|
+
|
51
|
+
@lock_models[klass] ||= Monitor.new
|
52
|
+
@models[klass] ||= []
|
53
|
+
@models[klass] << {model: model, type: args[:type], validate: validate}
|
47
54
|
@count += 1
|
48
55
|
end
|
49
|
-
|
50
|
-
flush if
|
56
|
+
|
57
|
+
flush if should_flush?
|
51
58
|
end
|
52
|
-
|
53
|
-
#Flushes the specified method on all the queued models in a thread for each type of model.
|
59
|
+
|
60
|
+
# Flushes the specified method on all the queued models in a thread for each type of model.
|
54
61
|
def flush
|
55
|
-
|
56
|
-
|
57
|
-
|
62
|
+
wait_for_threads if threadded?
|
63
|
+
|
58
64
|
@lock.synchronize do
|
59
|
-
@models.each do |klass,
|
60
|
-
next if
|
61
|
-
|
62
|
-
models = val
|
65
|
+
@models.each do |klass, models|
|
66
|
+
next if models.empty?
|
67
|
+
|
63
68
|
@models[klass] = []
|
64
69
|
@count -= models.length
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@lock_models[klass].synchronize do
|
71
|
-
debug "Opening new transaction by using '#{@args[:transaction_method]}'."
|
72
|
-
klass.__send__(@args[:transaction_method]) do
|
73
|
-
models.each do |model|
|
74
|
-
# debug "Saving #{model.class.name}(#{model.id}) with method #{@args[:call_method]}"
|
75
|
-
model.__send__(@args[:call_method], *@args[:call_args])
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
rescue => e
|
80
|
-
puts e.inspect
|
81
|
-
puts e.backtrace
|
82
|
-
|
83
|
-
if e.is_a?(NoMethodError) and e.message.to_s.include?("`reverse' for nil:NilClass")
|
84
|
-
puts "Warning: Known Rails reverse error when using transaction - retrying in 2 sec."
|
85
|
-
sleep 2
|
86
|
-
puts "Retrying"
|
87
|
-
puts
|
88
|
-
retry
|
89
|
-
end
|
90
|
-
|
91
|
-
raise e
|
92
|
-
ensure
|
93
|
-
debug "Removing thread #{Thread.current.__id__}"
|
94
|
-
@threads.delete(Thread.current)
|
95
|
-
|
96
|
-
@lock.synchronize do
|
97
|
-
ActiveRecord::Base.connection.close if ActiveRecord::Base.connection
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
@lock_threads.synchronize do
|
104
|
-
threads << thread
|
105
|
-
@threads << thread
|
70
|
+
|
71
|
+
if threadded?
|
72
|
+
work_threadded(klass, models)
|
73
|
+
else
|
74
|
+
work_models_through_transaction(klass, models)
|
106
75
|
end
|
107
76
|
end
|
108
77
|
end
|
109
|
-
|
110
|
-
return {
|
111
|
-
:threads => threads
|
112
|
-
}
|
113
78
|
end
|
114
|
-
|
115
|
-
#Waits for any remaining running threads.
|
79
|
+
|
80
|
+
# Waits for any remaining running threads.
|
116
81
|
def join
|
117
|
-
@lock_threads.synchronize
|
118
|
-
|
119
|
-
|
120
|
-
|
82
|
+
threads_to_join = @lock_threads.synchronize { @threads.clone }
|
83
|
+
|
84
|
+
debug "Threads to join: #{threads_to_join}" if @debug
|
85
|
+
threads_to_join.each do |thread|
|
86
|
+
thread.join
|
121
87
|
end
|
122
88
|
end
|
123
|
-
|
89
|
+
|
90
|
+
def threadded?
|
91
|
+
@args[:threadded]
|
92
|
+
end
|
93
|
+
|
124
94
|
private
|
125
|
-
|
95
|
+
|
96
|
+
def parse_and_set_args
|
97
|
+
@models = {}
|
98
|
+
@threads = []
|
99
|
+
@count = 0
|
100
|
+
@lock = Monitor.new
|
101
|
+
@lock_threads = Monitor.new
|
102
|
+
@lock_models = {}
|
103
|
+
@max_running_threads = @args[:max_running_threads].to_i
|
104
|
+
@transaction_size = @args[:transaction_size].to_i
|
105
|
+
@debug = @args[:debug]
|
106
|
+
end
|
107
|
+
|
126
108
|
def debug(str)
|
127
|
-
|
109
|
+
print "{ActiveRecordTransactioner}: #{str}\n" if @debug
|
128
110
|
end
|
129
|
-
|
111
|
+
|
130
112
|
def wait_for_threads
|
131
113
|
break_loop = false
|
132
114
|
while !break_loop
|
133
|
-
debug "
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
else
|
139
|
-
debug "Waiting for threads #{@threads.length} / #{@args[:max_running_threads]}" if @debug
|
140
|
-
end
|
115
|
+
debug "Running threads: #{@threads.length} / #{@max_running_threads}" if @debug
|
116
|
+
if allowed_to_start_new_thread?
|
117
|
+
break_loop = true
|
118
|
+
else
|
119
|
+
debug "Waiting for threads #{@threads.length} / #{@max_running_threads}" if @debug
|
141
120
|
end
|
142
|
-
|
143
|
-
sleep 0.2
|
121
|
+
|
122
|
+
sleep 0.2 unless break_loop
|
144
123
|
end
|
145
|
-
|
124
|
+
|
146
125
|
debug "Done waiting." if @debug
|
147
126
|
end
|
148
|
-
|
127
|
+
|
128
|
+
def work_models_through_transaction(klass, models)
|
129
|
+
debug "Synchronizing model: #{klass.name}"
|
130
|
+
|
131
|
+
@lock_models[klass].synchronize do
|
132
|
+
debug "Opening new transaction by using '#{@args[:transaction_method]}'." if @debug
|
133
|
+
|
134
|
+
klass.__send__(@args[:transaction_method]) do
|
135
|
+
debug "Going through models." if @debug
|
136
|
+
models.each do |work|
|
137
|
+
debug work if @debug
|
138
|
+
|
139
|
+
if work[:type] == :save!
|
140
|
+
validate = work.key?(:validate) ? work[:validate] : true
|
141
|
+
work[:model].save! validate: validate
|
142
|
+
elsif work[:type] == :destroy!
|
143
|
+
work[:model].destroy!
|
144
|
+
else
|
145
|
+
raise "Invalid type: '#{work[:type]}'."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
debug "Done working with models." if @debug
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def work_threadded(klass, models)
|
155
|
+
@lock_threads.synchronize do
|
156
|
+
@threads << Thread.new do
|
157
|
+
begin
|
158
|
+
ActiveRecord::Base.connection_pool.with_connection do
|
159
|
+
work_models_through_transaction(klass, models)
|
160
|
+
end
|
161
|
+
rescue => e
|
162
|
+
pute e.inspect
|
163
|
+
puts e.backtrace
|
164
|
+
|
165
|
+
raise e
|
166
|
+
ensure
|
167
|
+
debug "Removing thread #{Thread.current.__id__}" if @debug
|
168
|
+
@lock_threads.synchronize { @threads.delete(Thread.current) }
|
169
|
+
|
170
|
+
debug "Threads count after remove: #{@threads.length}" if @debug
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
debug "Threads-count after started to work: #{@threads.length}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def should_flush?
|
179
|
+
@count >= @transaction_size
|
180
|
+
end
|
181
|
+
|
182
|
+
def allowed_to_start_new_thread?
|
183
|
+
@lock_threads.synchronize { return @threads.length < @max_running_threads }
|
184
|
+
end
|
185
|
+
end
|