workhorse 0.0.1 → 0.0.2

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: 76af1f05b3bf26fa8ecf78fdef9bd16d651f4c21
4
- data.tar.gz: 71b70edd2c90872a13f5c27c9f512a9b61808b63
3
+ metadata.gz: cf7da416379ae17a4defae96289beca3c4dd1d58
4
+ data.tar.gz: 5b4e9cb886632f4a78982506747e1bcbdef1a7f3
5
5
  SHA512:
6
- metadata.gz: 9f29aaab73b1c7e52adcbb1ab224a8c13c2223760e52d95ee458166a8cb7d252db9d294dfeab2487c481b40be4bd4fd449beef475ad70293385501f9432dc8c5
7
- data.tar.gz: e1c5c223f41f677c65d3e932719833c2771f394c2132c406d2f10621b1f45edbf729a13e46690f16c51ebde996d364855291fe1755e901f25f2b94e181f8835a
6
+ metadata.gz: c7f8b1730ada8f2613b63ee87bf570acae14fe057bfd2650ba1ea4daa479f641a99f6d77e74b2f9484cd62aedc2899a2fd5fed8b138b09b52a2191855439d884
7
+ data.tar.gz: ee19f3e7a11eafda737333a7c41b6a83444e927c37033d8230adb26987c8bbd60c17346da5e4abfa61521500991b9856dc5fba91f5f37368fc72941a8fce60d6
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  *.rbc
3
+ *.log
3
4
  /.config
4
5
  /coverage/
5
6
  /InstalledFiles
@@ -42,7 +43,7 @@ build-iPhoneSimulator/
42
43
 
43
44
  # for a library or gem, you might want to ignore these files since the code is
44
45
  # intended to run in multiple environments; otherwise, check them in:
45
- # Gemfile.lock
46
+ Gemfile.lock
46
47
  # .ruby-version
47
48
  # .ruby-gemset
48
49
 
data/.releaser_config ADDED
@@ -0,0 +1,3 @@
1
+ version_file: VERSION
2
+ always_from_master: true
3
+ gem_style: github
data/.rubocop.yml ADDED
@@ -0,0 +1,84 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+
4
+ Exclude:
5
+ - 'vendor/**/*'
6
+ - 'tmp/**/*'
7
+ - 'log/**/*'
8
+ - '*.gemspec'
9
+
10
+ DisplayCopNames: true
11
+
12
+ Style/FrozenStringLiteralComment:
13
+ Enabled: false
14
+
15
+ Style/DoubleNegation:
16
+ Enabled: false
17
+
18
+ Style/SignalException:
19
+ EnforcedStyle: only_fail
20
+
21
+ Lint/RescueWithoutErrorClass:
22
+ Enabled: False
23
+
24
+ Style/ConditionalAssignment:
25
+ Enabled: false
26
+
27
+ Layout/IndentArray:
28
+ EnforcedStyle: consistent
29
+
30
+ Metrics/MethodLength:
31
+ Enabled: false
32
+
33
+ Metrics/ClassLength:
34
+ Enabled: false
35
+
36
+ Metrics/ModuleLength:
37
+ Enabled: false
38
+
39
+ Metrics/ParameterLists:
40
+ Max: 5
41
+ CountKeywordArgs: false
42
+
43
+ Metrics/AbcSize:
44
+ Enabled: False
45
+
46
+ Metrics/CyclomaticComplexity:
47
+ Enabled: False
48
+
49
+ Metrics/PerceivedComplexity:
50
+ Enabled: False
51
+
52
+ Metrics/LineLength:
53
+ Max: 160
54
+
55
+ Metrics/BlockNesting:
56
+ Enabled: false
57
+
58
+ Metrics/BlockLength:
59
+ Enabled: false
60
+
61
+ Style/IfUnlessModifier:
62
+ Enabled: false
63
+
64
+ Style/Documentation:
65
+ Enabled: false
66
+
67
+ Style/RedundantReturn:
68
+ Enabled: false
69
+
70
+ Style/AsciiComments:
71
+ Enabled: false
72
+
73
+ Style/GuardClause:
74
+ Enabled: false
75
+
76
+ Style/ClassAndModuleChildren:
77
+ Enabled: false
78
+ EnforcedStyle: compact
79
+ SupportedStyles:
80
+ - nested
81
+ - compact
82
+
83
+ Style/NumericPredicate:
84
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ services:
5
+ - mysql
6
+ before_install:
7
+ - mysql -e 'CREATE DATABASE workhorse;'
8
+ script:
9
+ - bundle install
10
+ - bundle exec rake test
11
+ - bundle exec rubocop
data/README.md CHANGED
@@ -1,20 +1,172 @@
1
1
  [![Build Status](https://travis-ci.org/sitrox/workhorse.svg?branch=master)](https://travis-ci.org/sitrox/workhorse)
2
2
  [![Gem Version](https://badge.fury.io/rb/workhorse.svg)](https://badge.fury.io/rb/workhorse)
3
3
 
4
- **This gem is considered work in progress. Do not use this in production yet.**
5
-
6
4
  # Workhorse
7
5
 
6
+ **This Gem is still in an early stage of development. Please not use this in production yet.**
7
+
8
8
  Multi-threaded job backend with database queuing for ruby.
9
9
 
10
+ ## Introduction
11
+
12
+ What it is:
13
+
14
+ * Jobs are instances of classes that support the `perform` method.
15
+ * Jobs are persisted in the database using ActiveRecord.
16
+ * You can start one or more worker processes.
17
+ * Each worker is configurable as to which queue(s) it processes.
18
+ * Each worker polls the database and spawns a number of threads to execute jobs
19
+ of different queues simultaneously.
20
+
21
+ What it isn't:
22
+
23
+ * It cannot spawn new processes. Jobs are run in separate threads but not in
24
+ separate processes (unless you start multiple worker processes manually).
25
+ * It does not support retries.
26
+
10
27
  ## Installation
11
28
 
12
- To install this gem using bundler, add it to your `Gemfile`:
29
+ ### Requirements
30
+
31
+ * A database and table handler that properly supports row-level locking (such as
32
+ MySQL with InnoDB, PostgreSQL or Oracle).
33
+ * An operating system and file system that supports file locking.
34
+
35
+ ### Installing under Rails
36
+
37
+ 1. Add `workhorse` to your `Gemfile`:
38
+
39
+ ```ruby
40
+ gem 'workhorse'
41
+ ```
42
+
43
+ 2. Run the install generator:
44
+
45
+ ```bash
46
+ bin/rails generate workhorse:install
47
+ ```
48
+
49
+ This generates:
50
+
51
+ * A database migration for creating the `jobs` table
52
+ * An initializer `config/initializers/workhorse.rb` for global configuration
53
+ * A daemon worker script under `bin/workhorse.rb`
54
+
55
+ Please customize the configuration files to your liking.
56
+
57
+ ## Queuing jobs
58
+
59
+ ### Basic jobs
60
+
61
+ Workhorse can handle any jobs that support the `perform` method and are
62
+ serializable. To queue a basic job, use the static method `Workhorse.enqueue`:
63
+
64
+ ```ruby
65
+ class MyJob
66
+ def initialize(name)
67
+ @name = name
68
+ end
69
+
70
+ def perform
71
+ puts "Hello #{@name}"
72
+ end
73
+ end
74
+
75
+ Workhorse.enqueue MyJob.new('John'), queue: :test
76
+ ```
77
+
78
+ In the above example, we also specify a queue named `:test`. This means that
79
+ this job will never run simoultaneously with other jobs in the same queue. If no
80
+ queue is given, the job can always be executed simoultaneously with any other
81
+ job.
82
+
83
+ ### RailsOps operations
84
+
85
+ Workhorse allows you to easily queue
86
+ [RailsOps](https://github.com/sitrox/rails_ops) operations using the static
87
+ method `Workhorse.enqueue_op`:
13
88
 
14
89
  ```ruby
15
- gem 'workhorse'
90
+ Workhorse.enqueue Operations::Jobs::CleanUpDatabase, quiet: true
16
91
  ```
17
92
 
93
+ Params passed using the second argument will be used for operation instantiation
94
+ at job execution.
95
+
96
+ You can also specify a queue:
97
+
98
+ ```ruby
99
+ Workhorse.enqueue Operations::Jobs::CleanUpDatabase, { quiet: true }, queue: :maintenance
100
+ ```
101
+
102
+ ## Configuring and starting workers
103
+
104
+ Workers poll the database for new jobs and execute them in one or more threads.
105
+ Typically, one worker is started per process. While you can start workers
106
+ manually, either in your main application process(es) or in a separate one,
107
+ workhorse also provides you with a convenient way of starting one or multiple
108
+ worker processes as a daemon.
109
+
110
+ ### Start workers manually
111
+
112
+ Workers are created by instantiatating, configuring and starting a new
113
+ `Workhorse::Worker` instance.
114
+
115
+ ```ruby
116
+ Workhorse::Worker.start_and_wait(
117
+ pool_size: 5, # Processes 5 jobs concurrently
118
+ quiet: false, # Logs to STDOUT
119
+ logger: Rails.logger # Logs to Rails log. You can also
120
+ # provide any custom logger.
121
+ )
122
+ ```
123
+
124
+ See [code
125
+ documentation](http://www.rubydoc.info/github/sitrox/workhorse/Workhorse%2FWorker:initialize)
126
+ for more information on the arguments.
127
+
128
+ ### Start workers using a daemon script
129
+
130
+ Using `Workhorse::Daemon` (`Workhorse::Daemon::ShellHandler`), you can spawn one
131
+ or multiple worker processes automatically. This is useful for cases where you
132
+ want the workers to exist in separate processes as opposed to in your main
133
+ application process(es).
134
+
135
+ For this case, the workhorse install routine automatically creates a file called
136
+ `bin/workhorse.rb` which can be used to start one or more worker processes.
137
+
138
+ To start the daemon:
139
+
140
+ ```bash
141
+ bin/workhorse.rb start[|stop|status|watch|restart|usage]
142
+ ```
143
+
144
+ #### Background and customization
145
+
146
+ The daemon-part allows you to run arbitrary code as a daemon:
147
+
148
+ ```ruby
149
+ Workhorse::Daemon::ShellHandler.run count: 5 do
150
+ # This runs as a daemon and will be started 5 times
151
+ end
152
+ ```
153
+
154
+ Within this shell handler, you can now instantiate, configure and start a worker
155
+ as described under *Start workers manually*:
156
+
157
+ ```ruby
158
+ Workhorse::Daemon::ShellHandler.run count: 5 do
159
+ # This will be run 5 times, each time in a separate process. Per process, it
160
+ # will be able to process 3 jobs concurrently.
161
+ Workhorse::Worker.start_and_wait(pool_size: 3, logger: Rails.logger)
162
+ end
163
+ ```
164
+
165
+ ## Roadmap
166
+
167
+ * [ ] ActiveJob integration for Rails
168
+ * [ ] Job timeouts
169
+
18
170
  ## Copyright
19
171
 
20
172
  Copyright (c) 2017 Sitrox. See `LICENSE` for further details.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ task :gemspec do
2
+ gemspec = Gem::Specification.new do |spec|
3
+ spec.name = 'workhorse'
4
+ spec.version = IO.read('VERSION').chomp
5
+ spec.authors = ['Sitrox']
6
+ spec.summary = %(
7
+ Multi-threaded job backend with database queuing for ruby.
8
+ )
9
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
10
+ spec.executables = []
11
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
12
+ spec.require_paths = ['lib']
13
+
14
+ spec.add_development_dependency 'bundler', '~> 1.3'
15
+ spec.add_development_dependency 'rake'
16
+ spec.add_development_dependency 'rubocop', '0.51.0'
17
+ spec.add_development_dependency 'minitest'
18
+ spec.add_development_dependency 'mysql2', '~> 0.3.13'
19
+ spec.add_development_dependency 'benchmark-ips'
20
+ spec.add_dependency 'activesupport'
21
+ spec.add_dependency 'activerecord'
22
+ spec.add_dependency 'schemacop', '~> 2.0'
23
+ spec.add_dependency 'concurrent-ruby'
24
+ end
25
+
26
+ File.open('workhorse.gemspec', 'w') { |f| f.write(gemspec.to_ruby.strip) }
27
+ end
28
+
29
+ require 'rake/testtask'
30
+
31
+ Rake::TestTask.new do |t|
32
+ t.pattern = 'test/workhorse/**/*_test.rb'
33
+ t.verbose = false
34
+ t.libs << 'test/lib'
35
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/bin/rubocop ADDED
@@ -0,0 +1 @@
1
+ bundle exec rubocop "$@"
@@ -0,0 +1,24 @@
1
+ module Workhorse
2
+ class InstallGenerator < Rails::Generators::Base
3
+ include Rails::Generators::Migration
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def self.next_migration_number(_dir)
8
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
9
+ end
10
+
11
+ def install_migration
12
+ migration_template 'create_table_jobs.rb', 'db/migrate/create_table_jobs.rb'
13
+ end
14
+
15
+ def install_daemon_script
16
+ template 'bin/workhorse.rb'
17
+ chmod 'bin/workhorse.rb', 0o755
18
+ end
19
+
20
+ def install_initializer
21
+ template 'config/initializers/workhorse.rb'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require './config/environment'
4
+
5
+ Workhorse::Daemon::ShellHandler.run do
6
+ Workhorse::Worker.start_and_wait(pool_size: 5, logger: Rails.logger)
7
+ end
@@ -0,0 +1,11 @@
1
+ Workhorse.setup do |config|
2
+ # Set this to false in order to prevent jobs from being automatically
3
+ # wrapped into a transaction. The built-in workhorse logic will still run
4
+ # in transactions.
5
+ config.perform_jobs_in_tx = true
6
+
7
+ # Enable this to specify an alternative callback for handling transactions.
8
+ # config.tx_callback = proc do |&block|
9
+ # ActiveRecord::Base.transaction&(&block)
10
+ # end
11
+ end
@@ -0,0 +1,23 @@
1
+ class CreateTableJobs < ActiveRecord::Migration
2
+ def change
3
+ create_table :jobs, force: true do |t|
4
+ t.string :state, null: false, default: 'waiting'
5
+ t.string :queue, null: true
6
+ t.text :handler, null: false, limit: 4_294_967_295
7
+
8
+ t.string :locked_by
9
+ t.datetime :locked_at
10
+
11
+ t.datetime :started_at
12
+
13
+ t.datetime :succeeded_at
14
+ t.datetime :failed_at
15
+ t.text :last_error, limit: 4_294_967_295
16
+
17
+ t.timestamps null: false
18
+ end
19
+
20
+ add_index :jobs, :queue
21
+ add_index :jobs, :state
22
+ end
23
+ end
data/lib/workhorse.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'socket'
2
+ require 'active_support/all'
3
+ require 'active_record'
4
+
5
+ module Workhorse
6
+ @set_up = false
7
+
8
+ # Returns the performer currently performing the active job. This can only be
9
+ # called from within a job and the same thread.
10
+ def self.performer
11
+ Thread.current[:workhorse_current_performer]\
12
+ || fail('No performer is associated with the current thread. This method must always be called inside of a job.')
13
+ end
14
+
15
+ cattr_accessor :tx_callback
16
+ self.tx_callback = proc do |&block|
17
+ ActiveRecord::Base.transaction(&block)
18
+ end
19
+
20
+ cattr_accessor :perform_jobs_in_tx
21
+ self.perform_jobs_in_tx = true
22
+
23
+ def self.setup
24
+ fail 'Workhorse is already set up.' if @set_up
25
+ yield self
26
+ @set_up = true
27
+ end
28
+ end
29
+
30
+ require 'workhorse/db_job'
31
+ require 'workhorse/enqueuer'
32
+ require 'workhorse/performer'
33
+ require 'workhorse/poller'
34
+ require 'workhorse/pool'
35
+ require 'workhorse/worker'
36
+ require 'workhorse/jobs/run_rails_op'
37
+
38
+ # Daemon functionality is not available on java platforms
39
+ if RUBY_PLATFORM != 'java'
40
+ require 'workhorse/daemon'
41
+ require 'workhorse/daemon/shell_handler'
42
+ end