q4m 0.0.6 → 0.0.7

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.
Files changed (4) hide show
  1. data/README.rdoc +248 -26
  2. data/VERSION +1 -1
  3. data/lib/q4m.rb +11 -1
  4. metadata +4 -4
data/README.rdoc CHANGED
@@ -2,32 +2,83 @@
2
2
 
3
3
  Interface to pragmatically setup a queue job for Q4M!
4
4
 
5
+ = So... What's Q4M
6
+
7
+ Q4M is a open-sourced MySQL Engine. that attempts to take away some
8
+ logic from your application by encapsulating it inside of mysql.
9
+
10
+ The documentation is kinda bad, but the magic that it does in the
11
+ background is sweeeeeet!
12
+
13
+ You basically store all of your background jobs inside a MySQL table.
14
+ that table's engine should be a *queue* (q4m) engine!
15
+
16
+ Exampe:
17
+
18
+ drop table if exists shell_jobs, ruby_jobs;
19
+ create table shell_jobs (command text) engine=queue;
20
+ create table ruby_jobs (command text) engine=queue;
21
+
22
+ That engine will work under some kind of transaction. When you connect
23
+ to the database the engine will give you a list of all the pending
24
+ jobs. When you start a transaction you'll receive *ONE* job to work
25
+ with. In the mean time the whole table gets locked..
26
+ And if you do a 'select *' all results will return emtpy!
27
+
28
+ Don't panic!
29
+
30
+ This is great, you can never access two jobs at the same time.
31
+ if another worker/machine connects they'll get a list of jobs except
32
+ the one is wrapped in the transaction!
33
+
34
+ So that means that the locking only happens on the connection that is
35
+ perfoming the transaction but any other workers will get the full list
36
+ of jobs except the ones that are being processed by other workers.
37
+
38
+ When the first transaction gets terminated the engine will
39
+ automatically unlock the table and delete the record.
40
+
41
+ This is great, because you can have any number of workers working
42
+ concurrently without making a mess!
43
+
44
+ Meaning that a job can never be called in twice by anyone, and that
45
+ you can grow horizontally, the limit is the sky!
46
+
47
+ What's next?
48
+
49
+ The job is done here!
50
+
51
+ Go do something more interesting like clustering! ;)
52
+
53
+ Seek & destroy, Seek & destroy!
54
+
55
+ = So what exactly does this gem do?
56
+
57
+ Gives you an interface to quickly setup how specificly a job is
58
+ supposed to run!
59
+
60
+ Basically, you define a class create an method called execute, which
61
+ receives a *job* argument. truly a MySQL record in the form of an
62
+ array, what you do from there is up to you!
63
+
64
+ if something goes wrong just call the *queue_abort* method and
65
+ everything gets rolled back!
66
+
5
67
  = Installation
6
68
 
7
69
  kazu@utopia:~$ gem install q4m
8
70
 
9
- = Usage
71
+ = Simple example
10
72
 
11
73
  require 'rubygems'
12
74
  require 'q4m'
13
75
 
14
- class ShellJob < Q4m::Queue::Job
76
+ # Specify how shell jobs will run
15
77
 
16
- # *job* is a mysql record that is retrived from *table_name*
17
- def execute job
18
- # let the log know if the job was executed correctly!
19
- job = job.first
20
- return @log.info("Executed: #{job.inspect}") if system job
21
-
22
- # If failed then log the job that failed and rollback the job so
23
- # it stays in the queue!
24
- @log.error "Aborted job: #{job.inspect}"
25
- queue_abort
26
- end
78
+ class ShellJob < ::Q4m::Queue::Job
27
79
 
28
- # Database q4m table (name) where jobs are stored
29
- def table_name
30
- 'my_queue'
80
+ def execute job
81
+ queue_abort unless system(job.first)
31
82
  end
32
83
 
33
84
  end
@@ -37,23 +88,194 @@ Interface to pragmatically setup a queue job for Q4M!
37
88
  pass = 'secret'
38
89
  database = 'my_db'
39
90
 
91
+ # Instantiate and run a job!
40
92
  mysql = Mysql.new host, user, pass, database
41
93
  job = ShellJob.new mysql, Logger.new('q4m.log')
42
94
 
43
95
  job.run
44
96
 
97
+ = Quick tip
98
+
99
+ Note that: The *ShellJob* class will try to connect to the
100
+ 'shell_jobs' q4m table by default, if you want to use a different table
101
+ override the *table_name* method and specify one of your liking!
102
+
103
+ class ShellJob < ::Q4m::Queue::Job
104
+
105
+ def execute job
106
+ queue_abort unless system(job.first)
107
+ end
108
+
109
+ def table_name
110
+ 'my_cool_table'
111
+ end
112
+
113
+ end
114
+
115
+ = Run on a specific environment like rails?
116
+
117
+ Would you like to have an entire rails-app as your worker?
118
+ but dont't want to load the whole env with rake tasks everytime?
119
+
120
+ That would be sweet right? and you can even reuse/test that code.
121
+
122
+ No problem!
123
+
124
+ You can leave only-one instance of rails running
125
+ and tell it to pull jobs from the queue whenever it can?
126
+
127
+ Yes!!
128
+
129
+ But let's keep it flexibe so let's run two kinds of jobs, :ruby & :shell
130
+
131
+ module Background
132
+
133
+ # Specify a shell job. isn't this short & sweet?
134
+ class ShellJob < ::Q4m::Queue::Job
135
+
136
+ def execute job
137
+ queue_abort unless system(job.first)
138
+ end
139
+
140
+ end
141
+
142
+ # Now do the same thing for ruby
143
+ # and let's encapsulate the whole thing, and say that all ruby
144
+ # code that goes into the queue will be methods and they must
145
+ # always return booleans, if true complete the job successfully!
146
+ # rollback the transaction otherwise!
147
+ class RubyJob < ::Q4m::Queue::Job
148
+
149
+ def execute job
150
+ queue_abort unless (eval(job.first) == true)
151
+ end
152
+
153
+ end
154
+
155
+ # This hack will mimic a very simple deamon that will run in the
156
+ # background stay alert for any new jobs that get appended to
157
+ # the queue
158
+ class Deamon
159
+
160
+ def self.run type = :ruby
161
+ log = Rails.root + 'log' + 'q4m.log'
162
+ path = Rails.root + 'config' + 'database.yml'
163
+ conf = YAML::load_file(path)['queues']
164
+ mysql = Mysql.new conf['host'], conf['username'], conf['password'], conf['database']
165
+ job = if type == :ruby
166
+ Background::RubyJob.new mysql, Logger.new(log)
167
+ else
168
+ Background::ShellJob.new mysql, Logger.new(log)
169
+ end
170
+ if File.exist? '/tmp/run_queue.txt'
171
+ job.run
172
+ sleep 1
173
+ print '.'
174
+ mysql.close
175
+ run
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+
183
+ # Now let's create a model that will help us create jobs for ruby
184
+ class RubyJob < ActiveRecord::Base
185
+
186
+ establish_connection :queues
187
+
188
+ def self.append command
189
+ create! :command => command
190
+ end
191
+
192
+ end
193
+
194
+ # And for shell
195
+ # this can be encapsulated into a parent class but whatever!
196
+ class ShellJob < ActiveRecord::Base
197
+
198
+ establish_connection :queues
199
+
200
+ def self.append command
201
+ create! :command => command
202
+ end
203
+
204
+ end
205
+
206
+ # take it one step farther and build a helper, after all we are
207
+ # going to be working with strings!
208
+ class String
209
+
210
+ def jobify type = :ruby
211
+ return RubyJob.append self if type == :ruby
212
+ return ShellJob.append self if type == :shell
213
+ nil
214
+ end
215
+
216
+ end
217
+
218
+ = How to actually run it (on rails)!
219
+
220
+ # With the code above you can do something like this!
221
+
222
+ $ touch /tmp/run_queue.txt
223
+
224
+ # Run the deamon in a rails console!
225
+ >> Background::Deamon.run
226
+
227
+ # Let's create some background jobs on another console..
228
+
229
+ # Create a shell job, send it to the queue for later processing!
230
+ 'RAILS_ENV=production rake -T'.jobify :shell
231
+
232
+ # Do the same but with ruby!
233
+ # NOTE: method inside should return boolean!!
234
+ 'User.create_thumbnails(1)'.jobify
235
+
236
+ # Things should start running on the background, look at your logs!
237
+
238
+ # To stop the deamon cleanly just delete the file '/tmp/run_queue.txt'
239
+
240
+ = This is great!
241
+
242
+ Basically any machine running rails with this code, can pull jobs and
243
+ do some hard work for you! even if they are not part of your cluster!
244
+
245
+ The only requirement is that they can access the mysql-database.
246
+
247
+ and no, you don't need to install that engine locally!
248
+
249
+ = Extensibility!
250
+
251
+ Go farther just create a new job class and run whatever you like!
252
+
253
+ = Q4M lib?
254
+
255
+ This is how I installed it.
256
+
257
+ wget http://q4m.kazuhooku.com/dist/old/mysql-5.1.41-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.9.tar.gz
258
+ unzip *.gz
259
+ tar -xvf mysql-5.1.41-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.9.tar
260
+ cd q4m-0.8.9-linux-x86_64
261
+ sudo cp libqueue_engine.so /usr/lib/mysql/plugin
262
+ sudo chmod 777 /usr/lib/mysql/plugin/libqueue_engine.so
263
+ mysql -u root -p -f mysql < support-files/install.sql
264
+
265
+ You might wanna go to:
266
+
267
+ http://q4m.github.com/
45
268
 
269
+ And see tha latest on how to get it installed.
46
270
 
47
- == Note on Patches/Pull Requests
271
+ just thought of posting it, so you'll get an idea.
48
272
 
49
- * Fork the project.
50
- * Make your feature addition or bug fix.
51
- * Add tests for it. This is important so I don't break it in a
52
- future version unintentionally.
53
- * Commit, do not mess with rakefile, version, or history.
54
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
55
- * Send me a pull request. Bonus points for topic branches.
273
+ Yeah, but what's your distro?
56
274
 
57
- == Copyright
275
+ it's right here!
58
276
 
59
- Copyright (c) 2010 kazuyoshi tlacaelel. See LICENSE for details.
277
+ deploy@staging> cat /etc/*-release
278
+ DISTRIB_ID=Ubuntu
279
+ DISTRIB_RELEASE=10.04
280
+ DISTRIB_CODENAME=lucid
281
+ DISTRIB_DESCRIPTION="Ubuntu 10.04 LTS"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.7
data/lib/q4m.rb CHANGED
@@ -24,12 +24,22 @@ module Q4m
24
24
  # - Executes the latest job.
25
25
  # - Restores the queue so other worker can execute a process.
26
26
  #
27
- def run
27
+ def run terminate = false
28
28
  return unless @has_jobs
29
29
  queue_wait
30
30
  @log.info("Executing: #{latest_job.inspect}")
31
31
  execute latest_job
32
32
  queue_end
33
+ shutdown if terminate
34
+ nil
35
+ end
36
+
37
+ # Shuts down the worker
38
+ #
39
+ def shutdown
40
+ @mysql.close
41
+ @log.close
42
+ nil
33
43
  end
34
44
 
35
45
  private
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: q4m
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - kazuyoshi tlacaelel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-09 00:00:00 -06:00
18
+ date: 2010-11-12 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency