rq-ruby1.8 3.4.3

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 (67) hide show
  1. data/Gemfile +22 -0
  2. data/Gemfile.lock +22 -0
  3. data/INSTALL +166 -0
  4. data/LICENSE +10 -0
  5. data/Makefile +6 -0
  6. data/README +1183 -0
  7. data/Rakefile +37 -0
  8. data/TODO +24 -0
  9. data/TUTORIAL +230 -0
  10. data/VERSION +1 -0
  11. data/bin/rq +902 -0
  12. data/bin/rqmailer +865 -0
  13. data/example/a.rb +7 -0
  14. data/extconf.rb +198 -0
  15. data/gemspec.rb +40 -0
  16. data/install.rb +210 -0
  17. data/lib/rq.rb +155 -0
  18. data/lib/rq/arrayfields.rb +371 -0
  19. data/lib/rq/backer.rb +31 -0
  20. data/lib/rq/configfile.rb +82 -0
  21. data/lib/rq/configurator.rb +40 -0
  22. data/lib/rq/creator.rb +54 -0
  23. data/lib/rq/cron.rb +144 -0
  24. data/lib/rq/defaultconfig.txt +5 -0
  25. data/lib/rq/deleter.rb +51 -0
  26. data/lib/rq/executor.rb +40 -0
  27. data/lib/rq/feeder.rb +527 -0
  28. data/lib/rq/ioviewer.rb +48 -0
  29. data/lib/rq/job.rb +51 -0
  30. data/lib/rq/jobqueue.rb +947 -0
  31. data/lib/rq/jobrunner.rb +110 -0
  32. data/lib/rq/jobrunnerdaemon.rb +193 -0
  33. data/lib/rq/lister.rb +47 -0
  34. data/lib/rq/locker.rb +43 -0
  35. data/lib/rq/lockfile.rb +564 -0
  36. data/lib/rq/logging.rb +124 -0
  37. data/lib/rq/mainhelper.rb +189 -0
  38. data/lib/rq/orderedautohash.rb +39 -0
  39. data/lib/rq/orderedhash.rb +240 -0
  40. data/lib/rq/qdb.rb +733 -0
  41. data/lib/rq/querier.rb +98 -0
  42. data/lib/rq/rails.rb +80 -0
  43. data/lib/rq/recoverer.rb +28 -0
  44. data/lib/rq/refresher.rb +80 -0
  45. data/lib/rq/relayer.rb +283 -0
  46. data/lib/rq/resource.rb +22 -0
  47. data/lib/rq/resourcemanager.rb +40 -0
  48. data/lib/rq/resubmitter.rb +100 -0
  49. data/lib/rq/rotater.rb +98 -0
  50. data/lib/rq/sleepcycle.rb +46 -0
  51. data/lib/rq/snapshotter.rb +40 -0
  52. data/lib/rq/sqlite.rb +286 -0
  53. data/lib/rq/statuslister.rb +48 -0
  54. data/lib/rq/submitter.rb +113 -0
  55. data/lib/rq/toucher.rb +182 -0
  56. data/lib/rq/updater.rb +94 -0
  57. data/lib/rq/usage.rb +1222 -0
  58. data/lib/rq/util.rb +304 -0
  59. data/rdoc.sh +17 -0
  60. data/rq-ruby1.8.gemspec +120 -0
  61. data/test/.gitignore +1 -0
  62. data/test/test_rq.rb +145 -0
  63. data/white_box/crontab +2 -0
  64. data/white_box/joblist +8 -0
  65. data/white_box/killrq +18 -0
  66. data/white_box/rq_killer +27 -0
  67. metadata +208 -0
@@ -0,0 +1,733 @@
1
+ unless defined? $__rq_qdb__
2
+ module RQ
3
+ #--{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require 'arrayfields'
8
+
9
+ require LIBDIR + 'util'
10
+ require LIBDIR + 'logging'
11
+ require LIBDIR + 'sleepcycle'
12
+ require LIBDIR + 'refresher'
13
+
14
+ #
15
+ # the QDB class is the low level access point to the actual sqlite database.
16
+ # the primary function if performs is to serialize access to the queue db
17
+ # via the locking protocol
18
+ #
19
+ class QDB
20
+ #--{{{
21
+ include Util
22
+ include Logging
23
+
24
+ class RollbackTransactionError < StandardError; end
25
+ class AbortedTransactionError < StandardError; end
26
+
27
+ FIELDS =
28
+ #--{{{
29
+ %w(
30
+ jid priority state
31
+ submitted started finished elapsed
32
+ submitter runner
33
+ stdin stdout stderr data
34
+ pid exit_status
35
+ tag restartable command
36
+ )
37
+ #--}}}
38
+
39
+ PRAGMAS =
40
+ #--{{{
41
+ <<-sql
42
+ PRAGMA default_synchronous = FULL;
43
+ sql
44
+ #--}}}
45
+
46
+ SCHEMA =
47
+ #--{{{
48
+ <<-sql
49
+ create table jobs
50
+ (
51
+ jid integer primary key,
52
+ #{ FIELDS[1..-1].join ",\n " }
53
+ );
54
+ create table attributes
55
+ (
56
+ key,
57
+ value,
58
+ primary key (key)
59
+ );
60
+ sql
61
+ #--}}}
62
+
63
+ DEFAULT_LOGGER = Logger::new(STDERR)
64
+ DEFAULT_SQL_DEBUG = false
65
+ DEFAULT_TRANSACTION_RETRIES = 4
66
+ DEFAULT_AQUIRE_LOCK_SC = SleepCycle::new(2, 16, 2)
67
+ DEFAULT_TRANSACTION_RETRIES_SC = SleepCycle::new(8, 24, 8)
68
+ DEFAULT_ATTEMPT_LOCKD_RECOVERY = true
69
+ DEFAULT_LOCKD_RECOVER_WAIT = 3600 # 1 hr
70
+ DEFAULT_AQUIRE_LOCK_LOCKFILE_STALE_AGE = 21600 # 6 hrs
71
+ DEFAULT_AQUIRE_LOCK_REFRESH_RATE = 30
72
+
73
+ class << self
74
+ #--{{{
75
+ attr :sql_debug, true
76
+ attr :transaction_retries, true
77
+ attr :aquire_lock_sc, true
78
+ attr :transaction_retries_sc, true
79
+ attr :attempt_lockd_recovery, true
80
+ attr :lockd_recover_wait, true
81
+ attr :aquire_lock_lockfile_stale_age, true
82
+ attr :aquire_lock_refresh_rate, true
83
+
84
+ def fields
85
+ #--{{{
86
+ FIELDS
87
+ #--}}}
88
+ end
89
+ def integrity_check dbpath
90
+ #--{{{
91
+ ret = false
92
+ tuple = nil
93
+ begin
94
+ db =
95
+ begin
96
+ SQLite::Database::new dbpath, 0
97
+ rescue
98
+ SQLite::Database::new dbpath
99
+ end
100
+ opened = true
101
+ db.use_array = true rescue nil
102
+ tuple = db.execute 'PRAGMA integrity_check;'
103
+ ret = (tuple and tuple.first and (tuple.first["integrity_check"] =~ /^\s*ok\s*$/io))
104
+ ensure
105
+ db.close if opened
106
+ db = nil
107
+ end
108
+ ret
109
+ #--}}}
110
+ end
111
+ def t2h tuple
112
+ #--{{{
113
+ h = {}
114
+ FIELDS.each_with_index{|f,i| h[f] = tuple[i]}
115
+ h
116
+ #--}}}
117
+ end
118
+ def h2t h
119
+ #--{{{
120
+ t = tuple
121
+ FIELDS.each{|f| t[f] = h[f]}
122
+ t
123
+ #--}}}
124
+ end
125
+ def tuple
126
+ #--{{{
127
+ t = Array::new FIELDS.size
128
+ t.fields = FIELDS
129
+ t
130
+ #--}}}
131
+ end
132
+ def q tuple
133
+ #--{{{
134
+ [ tuple ].flatten.map do |f|
135
+ if f and not f.to_s.empty?
136
+ "'" << Util.escape(f,"'","'") << "'"
137
+ else
138
+ 'NULL'
139
+ end
140
+ end
141
+ #--}}}
142
+ end
143
+ def create path, opts = {}
144
+ #--{{{
145
+ qdb = new path, opts
146
+ FileUtils::touch qdb.lockfile
147
+ create_schema qdb.schema
148
+ qdb.transaction do
149
+ qdb.execute PRAGMAS
150
+ qdb.execute SCHEMA
151
+ end
152
+ qdb
153
+ #--}}}
154
+ end
155
+ def create_schema path
156
+ #--{{{
157
+ tmp = "#{ path }.tmp"
158
+ open(tmp,'w') do |f|
159
+ f.puts PRAGMAS
160
+ f.puts SCHEMA
161
+ end
162
+ FileUtils::mv tmp, path
163
+ #--}}}
164
+ end
165
+ #--}}}
166
+ end
167
+
168
+ attr :path
169
+ attr :opts
170
+ attr :dirname
171
+ attr :schema
172
+ attr :fields
173
+ attr :mutex
174
+ attr :lockfile
175
+ attr :sql_debug, true
176
+ attr :transaction_retries, true
177
+ attr :aquire_lock_sc, true
178
+ attr :transaction_retries_sc, true
179
+ attr :attempt_lockd_recovery, true
180
+ attr :lockd_recover_wait, true
181
+ attr :aquire_lock_lockfile_stale_age, true
182
+ attr :aquire_lock_refresh_rate, true
183
+
184
+
185
+ def initialize path, opts = {}
186
+ #--{{{
187
+ @path = path
188
+ @opts = opts
189
+
190
+ @logger =
191
+ Util::getopt('logger', @opts) ||
192
+ klass.logger ||
193
+ DEFAULT_LOGGER
194
+
195
+ @sql_debug =
196
+ Util::getopt('sql_debug', @opts) ||
197
+ klass.sql_debug ||
198
+ ENV['RQ_SQL_DEBUG'] ||
199
+ DEFAULT_SQL_DEBUG
200
+
201
+ @transaction_retries =
202
+ Util::getopt('transaction_retries', @opts) ||
203
+ klass.transaction_retries ||
204
+ DEFAULT_TRANSACTION_RETRIES
205
+
206
+ @aquire_lock_sc =
207
+ Util::getopt('aquire_lock_sc', @opts) ||
208
+ klass.aquire_lock_sc ||
209
+ DEFAULT_AQUIRE_LOCK_SC
210
+
211
+ @transaction_retries_sc =
212
+ Util::getopt('transaction_retries_sc', @opts) ||
213
+ klass.transaction_retries_sc ||
214
+ DEFAULT_TRANSACTION_RETRIES_SC
215
+
216
+ @attempt_lockd_recovery =
217
+ Util::getopt('attempt_lockd_recovery', @opts) ||
218
+ klass.attempt_lockd_recovery ||
219
+ DEFAULT_ATTEMPT_LOCKD_RECOVERY
220
+
221
+ @lockd_recover_wait =
222
+ Util::getopt('lockd_recover_wait', @opts) ||
223
+ klass.lockd_recover_wait ||
224
+ DEFAULT_LOCKD_RECOVER_WAIT
225
+
226
+ @aquire_lock_lockfile_stale_age =
227
+ Util::getopt('aquire_lock_lockfile_stale_age', @opts) ||
228
+ klass.aquire_lock_lockfile_stale_age ||
229
+ DEFAULT_AQUIRE_LOCK_LOCKFILE_STALE_AGE
230
+
231
+ @aquire_lock_refresh_rate =
232
+ Util::getopt('aquire_lock_refresh_rate', @opts) ||
233
+ klass.aquire_lock_refresh_rate ||
234
+ DEFAULT_AQUIRE_LOCK_REFRESH_RATE
235
+
236
+
237
+ @schema = "#{ @path }.schema"
238
+ @dirname = File::dirname(path).gsub(%r|/+\s*$|,'')
239
+ @basename = File::basename(path)
240
+ @waiting_w = File::join(@dirname, "#{ Util::hostname }.#{ $$ }.waiting.w")
241
+ @waiting_r = File::join(@dirname, "#{ Util::hostname }.#{ $$ }.waiting.r")
242
+ @lock_w = File::join(@dirname, "#{ Util::hostname }.#{ $$ }.lock.w")
243
+ @lock_r = File::join(@dirname, "#{ Util::hostname }.#{ $$ }.lock.r")
244
+ @lockfile = File::join(@dirname, 'lock')
245
+ @lockf = Lockfile::new("#{ @path }.lock")
246
+ @fields = FIELDS
247
+ @in_transaction = false
248
+ @in_ro_transaction = false
249
+ @db = nil
250
+
251
+ @lockd_recover = "#{ @dirname }.lockd_recover"
252
+ @lockd_recover_lockf = Lockfile::new "#{ @lockd_recover }.lock"
253
+ @lockd_recovered = false
254
+ #--}}}
255
+ end
256
+ def ro_transaction(opts = {}, &block)
257
+ #--{{{
258
+ opts['read_only'] = true
259
+ transaction(opts, &block)
260
+ #--}}}
261
+ end
262
+ def transaction opts = {}
263
+ #--{{{
264
+ raise 'nested transaction' if @in_transaction
265
+ ro = Util::getopt 'read_only', opts
266
+ ret = nil
267
+ begin
268
+ @in_transaction = true
269
+ lockd_recover_wrap(opts) do
270
+ transaction_wrap(opts) do
271
+ aquire_lock(opts) do
272
+ #sillyclean(opts) do
273
+ connect do
274
+ execute 'begin' unless ro
275
+ ret = yield
276
+ execute 'commit' unless ro
277
+ end
278
+ #end
279
+ end
280
+ end
281
+ end
282
+ ensure
283
+ @in_transaction = false
284
+ end
285
+ ret
286
+ #--}}}
287
+ end
288
+ if false
289
+ def ro_transaction(opts = {}, &block)
290
+ #--{{{
291
+ opts['read_only'] = true
292
+ transaction(opts, &block)
293
+ #--}}}
294
+ end
295
+ def transaction opts = {}
296
+ #--{{{
297
+ ro = Util::getopt 'read_only', opts
298
+ ret = nil
299
+ if @in_transaction
300
+ STDERR.puts 'continuing transaction...'
301
+ ret = yield
302
+ else
303
+ begin
304
+ STDERR.puts 'starting transaction...'
305
+ @in_transaction = true
306
+ lockd_recover_wrap(opts) do
307
+ transaction_wrap(opts) do
308
+ aquire_lock(opts) do
309
+ #sillyclean(opts) do
310
+ connect do
311
+ execute 'begin' unless ro
312
+ ret = yield
313
+ execute 'commit' unless ro
314
+ end
315
+ #end
316
+ end
317
+ end
318
+ end
319
+ ensure
320
+ @in_transaction = false
321
+ end
322
+ end
323
+ ret
324
+ #--}}}
325
+ end
326
+ end
327
+ def lockd_recover_wrap opts = {}
328
+ #--{{{
329
+ ret = nil
330
+ try_again = false
331
+ begin
332
+ begin
333
+ @lockd_recovered = false
334
+ old_mtime =
335
+ begin
336
+ Util::uncache @lockd_recover rescue nil
337
+ File::stat(@lockd_recover).mtime
338
+ rescue
339
+ Time::now
340
+ end
341
+ ret = yield
342
+ ensure
343
+ new_mtime =
344
+ begin
345
+ Util::uncache @lockd_recover rescue nil
346
+ File::stat(@lockd_recover).mtime
347
+ rescue
348
+ old_mtime
349
+ end
350
+
351
+ if new_mtime and old_mtime and new_mtime > old_mtime and not @lockd_recovered
352
+ try_again = true
353
+ end
354
+ end
355
+ rescue
356
+ if try_again
357
+ warn{ "a remote lockd recovery has invalidated this transaction!" }
358
+ warn{ "retrying..."}
359
+ sleep 120
360
+ retry
361
+ else
362
+ raise
363
+ end
364
+ end
365
+ ret
366
+ #--}}}
367
+ end
368
+ #
369
+ # TODO - perhaps should not retry on SQLException?? yet errors seem to map to
370
+ # this exception even when the sql is fine... safest (and most anoying) is to
371
+ # simply retry.
372
+ #
373
+ def transaction_wrap opts = {}
374
+ #--{{{
375
+ ro = Util::getopt 'read_only', opts
376
+ ret = nil
377
+ if ro
378
+ ret = yield
379
+ else
380
+ errors = []
381
+ @transaction_retries_sc.reset
382
+ begin
383
+ ret = yield
384
+ rescue => e
385
+ #rescue SQLite::DatabaseException, SQLite::SQLException, SystemCallError => e
386
+ case e
387
+ when AbortedTransactionError
388
+ raise
389
+ when RollbackTransactionError
390
+ raise
391
+ else
392
+ if @transaction_retries == 0
393
+ raise
394
+ elsif errors.size >= @transaction_retries
395
+ error{ "MAXIMUM TRANSACTION RETRIES SURPASSED" }
396
+ raise
397
+ else
398
+ warn{ e } if(errors.empty? or not Util::erreq(errors[-1], e))
399
+ errors << e
400
+ warn{ "retry <#{ errors.size }>..." }
401
+ end
402
+ sleep @transaction_retries_sc.next
403
+ retry
404
+ end
405
+ end
406
+ end
407
+ ret
408
+ #--}}}
409
+ end
410
+ def abort_transaction(*a)
411
+ #--{{{
412
+ raise AbortedTransactionError, *a
413
+ #--}}}
414
+ end
415
+ def rollback_transaction(*a)
416
+ #--{{{
417
+ raise RollbackTransactionError, *a
418
+ #--}}}
419
+ end
420
+ def sillyclean opts = {}
421
+ #--{{{
422
+ ro = Util::getopt 'read_only', opts
423
+ ret = nil
424
+ if ro
425
+ ret = yield
426
+ else
427
+ glob = File::join @dirname,'.nfs*'
428
+ orgsilly = Dir[glob]
429
+ ret = yield
430
+ newsilly = Dir[glob]
431
+ silly = newsilly - orgsilly
432
+ silly.each{|path| FileUtils::rm_rf path}
433
+ end
434
+ ret
435
+ #--}}}
436
+ end
437
+ def aquire_lock opts = {}
438
+ #--{{{
439
+ ro = Util::getopt 'read_only', opts
440
+ ret = nil
441
+
442
+ @aquire_lock_sc.reset
443
+
444
+ waiting, ltype, lfile =
445
+ if ro
446
+ [@waiting_r, File::LOCK_SH | File::LOCK_NB, @lock_r]
447
+ else
448
+ [@waiting_w, File::LOCK_EX | File::LOCK_NB, @lock_w]
449
+ end
450
+
451
+ ltype_s = (ltype == File::LOCK_EX ? 'write' : 'read')
452
+ ltype ||= File::LOCK_NB
453
+
454
+ aquired = false
455
+
456
+ until aquired
457
+ begin
458
+ debug{ "aquiring lock" }
459
+ #@lockf.lock unless ro
460
+
461
+ open(@lockfile, 'a+') do |lf|
462
+
463
+ locked = false
464
+ refresher = nil
465
+ sc = nil
466
+
467
+ begin
468
+ FileUtils::touch waiting
469
+ # poll
470
+ 42.times do
471
+ locked = lf.posixlock(ltype | File::LOCK_NB)
472
+ break if locked
473
+ sleep rand
474
+ end
475
+
476
+ if locked
477
+ aquired = true
478
+ refresher = Refresher::new @lockfile, @aquire_lock_refresh_rate
479
+ debug{ "refresher pid <#{ refresher.pid }> refresh_rate <#{ @aquire_lock_refresh_rate }>" }
480
+ FileUtils::rm_f waiting rescue nil
481
+ FileUtils::touch lfile rescue nil
482
+ debug{ "aquired lock" }
483
+ ret = yield
484
+ debug{ "released lock" }
485
+ else
486
+ aquired = false
487
+ stat = File::stat @lockfile
488
+ mtime = stat.mtime
489
+ stale = mtime < (Time::now - @aquire_lock_lockfile_stale_age)
490
+ if stale
491
+ Util::uncache @lockfile rescue nil
492
+ stat = File::stat @lockfile
493
+ mtime = stat.mtime
494
+ stale = mtime < (Time::now - @aquire_lock_lockfile_stale_age)
495
+ if stale
496
+ warn{ "detected stale lockfile of mtime <#{ mtime }>" }
497
+ lockd_recover if @attempt_lockd_recovery
498
+ end
499
+ end
500
+ sc = @aquire_lock_sc.next
501
+ debug{ "failed to aquire lock - sleep(#{ sc })" }
502
+ sleep sc
503
+ end
504
+
505
+ ensure
506
+ if locked
507
+ unlocked = false
508
+ begin
509
+ 42.times do
510
+ unlocked = lf.posixlock(File::LOCK_UN | File::LOCK_NB)
511
+ break if unlocked
512
+ sleep rand
513
+ end
514
+ ensure
515
+ lf.posixlock File::LOCK_UN unless unlocked
516
+ end
517
+ end
518
+ refresher.kill if refresher
519
+ FileUtils::rm_f waiting rescue nil
520
+ FileUtils::rm_f lfile rescue nil
521
+ end
522
+ end
523
+ ensure
524
+ #@lockf.unlock rescue nil unless read_only
525
+ end
526
+ end
527
+ ret
528
+ #--}}}
529
+ end
530
+ def connect
531
+ #--{{{
532
+ ret = nil
533
+ opened = nil
534
+ begin
535
+ raise 'db has no schema' unless test ?e, @schema
536
+ debug{"connecting to db <#{ @path }>..."}
537
+ $db = @db =
538
+ begin
539
+ SQLite::Database::new(@path, 0)
540
+ rescue
541
+ SQLite::Database::new(@path)
542
+ end
543
+ debug{"connected."}
544
+ opened = true
545
+ @db.use_array = true rescue nil
546
+ ret = yield @db
547
+ ensure
548
+ @db.close if opened
549
+ $db = @db = nil
550
+ debug{"disconnected from db <#{ @path }>"}
551
+ end
552
+ ret
553
+ #--}}}
554
+ end
555
+ def execute sql, &block
556
+ #--{{{
557
+ raise 'not in transaction' unless @in_transaction
558
+ if @sql_debug
559
+ logger << "SQL:\n#{ sql }\n"
560
+ end
561
+ #ret = retry_if_locked{ @db.execute sql, &block }
562
+ ret = @db.execute sql, &block
563
+ if @sql_debug and ret and Array === ret and ret.first
564
+ logger << "RESULT:\n#{ ret.first.inspect }\n...\n"
565
+ end
566
+ ret
567
+ #--}}}
568
+ end
569
+ #
570
+ # TODO - add sleep cycle if this ends up getting used
571
+ #
572
+ def retry_if_locked
573
+ #--{{{
574
+ ret = nil
575
+ begin
576
+ ret = yield
577
+ rescue SQLite::BusyException
578
+ warn{ "database locked - waiting(1.0) and retrying" }
579
+ sleep 1.0
580
+ retry
581
+ end
582
+ ret
583
+ #--}}}
584
+ end
585
+ def vacuum
586
+ #--{{{
587
+ raise 'nested transaction' if @in_transaction
588
+ begin
589
+ @in_transaction = true
590
+ connect{ execute 'vacuum' }
591
+ ensure
592
+ @in_transaction = false
593
+ end
594
+ self
595
+ #--}}}
596
+ end
597
+ def recover!
598
+ #--{{{
599
+ raise 'nested transaction' if @in_transaction
600
+ begin
601
+ @in_transaction = true
602
+ connect{ execute 'vacuum' }
603
+ require 'timeout'
604
+ Timeout::timeout(60){ system "sqlite #{ @path } .tables >/dev/null 2>&1" }
605
+ ensure
606
+ @in_transaction = false
607
+ end
608
+ integrity_check
609
+ #--}}}
610
+ end
611
+ def lockd_recover
612
+ #--{{{
613
+ return nil unless @attempt_lockd_recovery
614
+ warn{ "attempting lockd recovery" }
615
+ time = Time::now
616
+ ret = nil
617
+
618
+ @lockd_recover_lockf.lock do
619
+ Util::uncache @dirname rescue nil
620
+ Util::uncache @path rescue nil
621
+ Util::uncache @lockfile rescue nil
622
+ Util::uncache @lockd_recover rescue nil
623
+ mtime = File::stat(@lockd_recover).mtime rescue time
624
+
625
+ if mtime > time
626
+ warn{ "skipping lockd recovery (another node has already recovered)" }
627
+ ret = true
628
+ else
629
+ moved = false
630
+ begin
631
+ FileUtils::touch @lockd_recover
632
+ @lockd_recovered = false
633
+
634
+ begin
635
+ report = <<-msg
636
+ hostname : #{ Util::hostname }
637
+ pid : #{ Process.pid }
638
+ time : #{ Time::now }
639
+ q :
640
+ path : #{ @dirname }
641
+ stat : #{ File::stat(@dirname).inspect }
642
+ db :
643
+ path : #{ @path }
644
+ stat : #{ File::stat(@path).inspect }
645
+ lockfile :
646
+ path : #{ @lockfile }
647
+ stat : #{ File::stat(@lockfile).inspect }
648
+ msg
649
+ info{ "LOCKD RECOVERY REPORT" }
650
+ logger << report
651
+ cmd = "mail -s LOCKD_RECOVERY ara.t.howard@noaa.gov <<eof\n#{ report }\neof"
652
+ Util::system cmd
653
+ rescue
654
+ nil
655
+ end
656
+
657
+ warn{ "sleeping #{ @lockd_recover_wait }s before continuing..." }
658
+ sleep @lockd_recover_wait
659
+
660
+ tmp = "#{ @dirname }.tmp"
661
+ FileUtils::rm_rf tmp
662
+ FileUtils::mv @dirname, tmp
663
+ moved = true
664
+
665
+ rfiles = [@path, @lockfile].map{|f| File::join(tmp,File::basename(f))}
666
+ rfiles.each do |f|
667
+ ftmp = "#{ f }.tmp"
668
+ FileUtils::rm_rf ftmp
669
+ FileUtils::cp f, ftmp
670
+ FileUtils::rm f
671
+ FileUtils::mv ftmp, f
672
+ end
673
+
674
+ dbtmp = File::join(tmp,File::basename(@path))
675
+
676
+ if integrity_check(dbtmp)
677
+ FileUtils::mv tmp, @dirname
678
+ FileUtils::cp @lockd_recover_lockf.path, @lockd_recover
679
+ @lockd_recovered = true
680
+ Util::uncache @dirname rescue nil
681
+ Util::uncache @path rescue nil
682
+ Util::uncache @lockfile rescue nil
683
+ Util::uncache @lockd_recover rescue nil
684
+ warn{ "lockd recovery complete" }
685
+ else
686
+ FileUtils::mv tmp, @dirname
687
+ @lockd_recovered = false
688
+ error{ "lockd recovery failed" }
689
+ end
690
+
691
+ ret = @lockd_recovered
692
+ ensure
693
+ if moved and not @lockd_recovered and tmp and test(?d, tmp)
694
+ FileUtils::mv tmp, @dirname
695
+ end
696
+ end
697
+ end
698
+ end
699
+ ret
700
+ #--}}}
701
+ end
702
+ def integrity_check path = @path
703
+ #--{{{
704
+ debug{ "running integrity_check on <#{ path }>" }
705
+ klass.integrity_check(path)
706
+ #--}}}
707
+ end
708
+ def lock opts = {}
709
+ #--{{{
710
+ ret = nil
711
+ lockd_recover_wrap do
712
+ aquire_lock(opts) do
713
+ ret = yield
714
+ end
715
+ end
716
+ ret
717
+ #--}}}
718
+ end
719
+ alias write_lock lock
720
+ alias wlock write_lock
721
+ def read_lock(opts = {}, &block)
722
+ #--{{{
723
+ opts['read_only'] = true
724
+ lock opts, &block
725
+ #--}}}
726
+ end
727
+ alias rlock read_lock
728
+ #--}}}
729
+ end # class QDB
730
+ #--}}}
731
+ end # module RQ
732
+ $__rq_qdb__ = __FILE__
733
+ end