mongo-resque 1.17.1
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.
- data/HISTORY.md +278 -0
- data/LICENSE +20 -0
- data/README.markdown +76 -0
- data/bin/resque +67 -0
- data/bin/resque-web +23 -0
- data/docs/HOOKS.md +132 -0
- data/docs/PLUGINS.md +93 -0
- data/lib/resque/errors.rb +13 -0
- data/lib/resque/failure/base.rb +64 -0
- data/lib/resque/failure/hoptoad.rb +133 -0
- data/lib/resque/failure/mongo.rb +45 -0
- data/lib/resque/failure/multiple.rb +54 -0
- data/lib/resque/failure/redis.rb +46 -0
- data/lib/resque/failure.rb +70 -0
- data/lib/resque/helpers.rb +75 -0
- data/lib/resque/job.rb +215 -0
- data/lib/resque/plugin.rb +51 -0
- data/lib/resque/server/public/favicon.ico +0 -0
- data/lib/resque/server/public/idle.png +0 -0
- data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
- data/lib/resque/server/public/jquery.relatize_date.js +95 -0
- data/lib/resque/server/public/poll.png +0 -0
- data/lib/resque/server/public/ranger.js +73 -0
- data/lib/resque/server/public/reset.css +48 -0
- data/lib/resque/server/public/style.css +92 -0
- data/lib/resque/server/public/working.png +0 -0
- data/lib/resque/server/test_helper.rb +19 -0
- data/lib/resque/server/views/error.erb +1 -0
- data/lib/resque/server/views/failed.erb +65 -0
- data/lib/resque/server/views/key_sets.erb +19 -0
- data/lib/resque/server/views/key_string.erb +11 -0
- data/lib/resque/server/views/layout.erb +42 -0
- data/lib/resque/server/views/next_more.erb +10 -0
- data/lib/resque/server/views/overview.erb +4 -0
- data/lib/resque/server/views/queues.erb +69 -0
- data/lib/resque/server/views/stats.erb +73 -0
- data/lib/resque/server/views/workers.erb +109 -0
- data/lib/resque/server/views/working.erb +72 -0
- data/lib/resque/server.rb +268 -0
- data/lib/resque/stat.rb +54 -0
- data/lib/resque/tasks.rb +42 -0
- data/lib/resque/version.rb +3 -0
- data/lib/resque/worker.rb +497 -0
- data/lib/resque.rb +417 -0
- data/lib/tasks/resque.rake +2 -0
- metadata +148 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>Resque.</title>
|
6
|
+
<link href="<%=u 'reset.css' %>" media="screen" rel="stylesheet" type="text/css">
|
7
|
+
<link href="<%=u 'style.css' %>" media="screen" rel="stylesheet" type="text/css">
|
8
|
+
<script src="<%=u 'jquery-1.3.2.min.js' %>" type="text/javascript"></script>
|
9
|
+
<script src="<%=u 'jquery.relatize_date.js' %>" type="text/javascript"></script>
|
10
|
+
<script src="<%=u 'ranger.js' %>" type="text/javascript"></script>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<div class="header">
|
14
|
+
<ul class='nav'>
|
15
|
+
<% tabs.each do |tab_name| %>
|
16
|
+
<%= tab tab_name %>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
<abbr class="namespace" title="Mongo Connection">
|
20
|
+
<%= Resque.to_s %>
|
21
|
+
</abbr>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<% if @subtabs %>
|
25
|
+
<ul class='subnav'>
|
26
|
+
<% for subtab in @subtabs %>
|
27
|
+
<li <%= class_if_current "#{current_section}/#{subtab}" %>><a href="<%= current_section %>/<%= subtab %>"><span><%= subtab %></span></a></li>
|
28
|
+
<% end %>
|
29
|
+
</ul>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<div id="main">
|
33
|
+
<%= yield %>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div id="footer">
|
37
|
+
<p>Powered by <a href="https://github.com/dbackeus/mongo-resque">Mongo-Resque</a> v<%=Resque::Version%></p>
|
38
|
+
<p>Connected to Mongo at <%= Resque.to_s %></p>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
</body>
|
42
|
+
</html>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%if start - 20 >= 0 || start + 20 <= size%>
|
2
|
+
<p class='pagination'>
|
3
|
+
<% if start - 20 >= 0 %>
|
4
|
+
<a href="<%= current_page %>?start=<%= start - 20 %>" class='less'>« less</a>
|
5
|
+
<% end %>
|
6
|
+
<% if start + 20 <= size %>
|
7
|
+
<a href="<%= current_page %>?start=<%= start + 20 %>" class='more'>more »</a>
|
8
|
+
<% end %>
|
9
|
+
</p>
|
10
|
+
<%end%>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
<% @subtabs = resque.queues unless partial? || params[:id].nil? %>
|
2
|
+
|
3
|
+
<% if queue = params[:id] %>
|
4
|
+
<h1>Jobs on <span class='hl'><%= queue %></span></h1>
|
5
|
+
<form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
|
6
|
+
<input type='submit' name='' value='Remove Queue' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
|
7
|
+
</form>
|
8
|
+
<% if !resque.queue_allows_delayed queue.to_sym %>
|
9
|
+
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue.to_sym)%></b> jobs</p><table class='jobs'>
|
10
|
+
<tr>
|
11
|
+
<th>Class</th>
|
12
|
+
<th>Args</th>
|
13
|
+
<th>Enqueued At</th>
|
14
|
+
</tr>
|
15
|
+
<% for job in (jobs = resque.peek(queue.to_sym, start, 20)) %>
|
16
|
+
<tr>
|
17
|
+
<td class='class'><%= job['class'] %></td>
|
18
|
+
<td class='args'><%=h job['args'].inspect %></td>
|
19
|
+
<td class='enqueuedat'><%=h enqueued_at(job['resque_enqueue_timestamp']) %></td>
|
20
|
+
</tr>
|
21
|
+
<% end %>
|
22
|
+
<% else %>
|
23
|
+
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue.to_sym)%></b> jobs</p><table class='delays'>
|
24
|
+
<tr>
|
25
|
+
<th>Class</th>
|
26
|
+
<th>Args</th>
|
27
|
+
<th>Enqueued At</th>
|
28
|
+
<th>Processes In</th>
|
29
|
+
</tr>
|
30
|
+
<% for job in (jobs = resque.peek(queue.to_sym, start, 20, :all_sorted)) %>
|
31
|
+
<tr>
|
32
|
+
<td class='class'><%= job['class'] %></td>
|
33
|
+
<td class='args'><%=h job['args'].inspect %></td>
|
34
|
+
<td class='enqueuedat'><%=h enqueued_at(job['resque_enqueue_timestamp']) %></td>
|
35
|
+
<td class='delay'><%=h processes_in(job['delay_until']) %></td>
|
36
|
+
</tr>
|
37
|
+
<% end %>
|
38
|
+
<% end %>
|
39
|
+
<% if jobs.empty? %>
|
40
|
+
<tr>
|
41
|
+
<td class='no-data' colspan='2'>There are no pending jobs in this queue</td>
|
42
|
+
</tr>
|
43
|
+
<% end %>
|
44
|
+
</table>
|
45
|
+
<%= partial :next_more, :start => start, :size => size %>
|
46
|
+
<% else %>
|
47
|
+
|
48
|
+
<h1 class='wi'>Queues</h1>
|
49
|
+
<p class='intro'>The list below contains all the registered queues with the number of jobs currently in the queue. Select a queue from above to view all jobs currently pending on the queue.</p>
|
50
|
+
<table class='queues'>
|
51
|
+
<tr>
|
52
|
+
<th>Name</th>
|
53
|
+
<th>Ready Jobs</th>
|
54
|
+
<th>Delayed Jobs</th>
|
55
|
+
</tr>
|
56
|
+
<% for queue in resque.queues.sort_by { |q| q.to_s } %>
|
57
|
+
<tr>
|
58
|
+
<td class='queue'><a class="queue" href="<%= u "queues/#{queue}" %>"><%= queue %></a></td>
|
59
|
+
<td class='size'><%= resque.ready_size queue.to_sym %></td>
|
60
|
+
<td class='size'><%= resque.delayed_size(queue.to_sym) if resque.queue_allows_delayed(queue.to_sym) %></td>
|
61
|
+
</tr>
|
62
|
+
<% end %>
|
63
|
+
<tr class="<%= Resque::Failure.count.zero? ? "failed" : "failure" %>">
|
64
|
+
<td class='queue failed'><a class="queue" href="<%= u :failed %>">failed</a></td>
|
65
|
+
<td class='size'><%= Resque::Failure.count %></td>
|
66
|
+
</tr>
|
67
|
+
</table>
|
68
|
+
|
69
|
+
<% end %>
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<% @subtabs = %w( resque mongo keys ) %>
|
2
|
+
|
3
|
+
<% if params[:key] %>
|
4
|
+
|
5
|
+
<%= partial :key_sets %>
|
6
|
+
|
7
|
+
<% elsif params[:id] == "resque" %>
|
8
|
+
|
9
|
+
<h1><%= resque %></h1>
|
10
|
+
<table class='stats'>
|
11
|
+
<% for key, value in resque.info.to_a.sort_by { |i| i[0].to_s } %>
|
12
|
+
<tr>
|
13
|
+
<th>
|
14
|
+
<%= key %>
|
15
|
+
</th>
|
16
|
+
<td>
|
17
|
+
<%= value %>
|
18
|
+
</td>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
</table>
|
22
|
+
|
23
|
+
<% elsif params[:id] == 'mongo' %>
|
24
|
+
|
25
|
+
<h1><%= resque.to_s %></h1>
|
26
|
+
<table class='stats'>
|
27
|
+
<% resque.mongo_stats.find.to_a.each do |hash| %>
|
28
|
+
<tr>
|
29
|
+
<th>
|
30
|
+
<%= hash['stat'] %>
|
31
|
+
</th>
|
32
|
+
<td>
|
33
|
+
<%= hash['value'].inspect %>
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
</table>
|
38
|
+
|
39
|
+
<h1>Mongo Database Health</h1>
|
40
|
+
<table class='stats'>
|
41
|
+
<% resque.mongo.stats.each_pair do |key, value| %>
|
42
|
+
<tr>
|
43
|
+
<th>
|
44
|
+
<%= key %>
|
45
|
+
</th>
|
46
|
+
<td>
|
47
|
+
<%= value %>
|
48
|
+
</td>
|
49
|
+
</tr>
|
50
|
+
<% end %>
|
51
|
+
</table>
|
52
|
+
|
53
|
+
<% elsif params[:id] == 'keys' %>
|
54
|
+
|
55
|
+
<h1>Collections in <%= resque.to_s %></h1>
|
56
|
+
<table class='stats'>
|
57
|
+
<tr>
|
58
|
+
<th>collection</th>
|
59
|
+
<th>size</th>
|
60
|
+
</tr>
|
61
|
+
<% for key in resque.keys.sort %>
|
62
|
+
<tr>
|
63
|
+
<th>
|
64
|
+
<a href="<%=u "/stats/keys/#{key}" %>"><%= key %></a>
|
65
|
+
</th>
|
66
|
+
<td><%= resque.mongo[key].count %></td>
|
67
|
+
</tr>
|
68
|
+
<% end %>
|
69
|
+
</table>
|
70
|
+
|
71
|
+
<% else %>
|
72
|
+
|
73
|
+
<% end %>
|
@@ -0,0 +1,109 @@
|
|
1
|
+
<% @subtabs = worker_hosts.keys.sort unless worker_hosts.size == 1 %>
|
2
|
+
|
3
|
+
<% if params[:id] && worker = Resque::Worker.find(params[:id]) %>
|
4
|
+
|
5
|
+
<h1>Worker <%= worker %></h1>
|
6
|
+
<table class='workers'>
|
7
|
+
<tr>
|
8
|
+
<th> </th>
|
9
|
+
<th>Host</th>
|
10
|
+
<th>Pid</th>
|
11
|
+
<th>Started</th>
|
12
|
+
<th>Queues</th>
|
13
|
+
<th>Processed</th>
|
14
|
+
<th>Failed</th>
|
15
|
+
<th>Processing</th>
|
16
|
+
</tr>
|
17
|
+
<tr>
|
18
|
+
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
19
|
+
|
20
|
+
<% host, pid, queues = worker.to_s.split(':') %>
|
21
|
+
<td><%= host %></td>
|
22
|
+
<td><%= pid %></td>
|
23
|
+
<td><span class="time"><%= worker.started %></span></td>
|
24
|
+
<td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
|
25
|
+
<td><%= worker.processed %></td>
|
26
|
+
<td><%= worker.failed %></td>
|
27
|
+
<td class='process'>
|
28
|
+
<% data = worker.processing || {} %>
|
29
|
+
<% if data['queue'] %>
|
30
|
+
<code><%= data['payload']['class'] %></code>
|
31
|
+
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
|
32
|
+
<% else %>
|
33
|
+
<span class='waiting'>Waiting for a job...</span>
|
34
|
+
<% end %>
|
35
|
+
</td>
|
36
|
+
</tr>
|
37
|
+
</table>
|
38
|
+
|
39
|
+
<% elsif params[:id] && !worker_hosts.keys.include?(params[:id]) && params[:id] != 'all' %>
|
40
|
+
|
41
|
+
<h1>Worker doesn't exist</h1>
|
42
|
+
|
43
|
+
<% elsif worker_hosts.size == 1 || params[:id] %>
|
44
|
+
|
45
|
+
<% if worker_hosts.size == 1 || params[:id] == 'all' %>
|
46
|
+
<% workers = Resque.workers %>
|
47
|
+
<% else %>
|
48
|
+
<% workers = worker_hosts[params[:id]].map { |id| Resque::Worker.find(id) } %>
|
49
|
+
<% end %>
|
50
|
+
|
51
|
+
<h1 class='wi'><%= workers.size %> Workers</h1>
|
52
|
+
<p class='intro'>The workers listed below are all registered as active on your system.</p>
|
53
|
+
<table class='workers'>
|
54
|
+
<tr>
|
55
|
+
<th> </th>
|
56
|
+
<th>Where</th>
|
57
|
+
<th>Queues</th>
|
58
|
+
<th>Processing</th>
|
59
|
+
</tr>
|
60
|
+
<% for worker in (workers = workers.sort_by { |w| w.to_s }) %>
|
61
|
+
<tr class="<%=state = worker.state%>">
|
62
|
+
<td class='icon'><img src="<%=u state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
63
|
+
|
64
|
+
<% host, pid, queues = worker.to_s.split(':') %>
|
65
|
+
<td class='where'><a href="<%=u "workers/#{worker}"%>"><%= host %>:<%= pid %></a></td>
|
66
|
+
<td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
|
67
|
+
|
68
|
+
<td class='process'>
|
69
|
+
<% data = worker.processing || {} %>
|
70
|
+
<% if data['queue'] %>
|
71
|
+
<code><%= data['payload']['class'] %></code>
|
72
|
+
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
|
73
|
+
<% else %>
|
74
|
+
<span class='waiting'>Waiting for a job...</span>
|
75
|
+
<% end %>
|
76
|
+
</td>
|
77
|
+
</tr>
|
78
|
+
<% end %>
|
79
|
+
<% if workers.empty? %>
|
80
|
+
<tr>
|
81
|
+
<td colspan='4' class='no-data'>There are no registered workers</td>
|
82
|
+
</tr>
|
83
|
+
<% end %>
|
84
|
+
</table>
|
85
|
+
<%=poll%>
|
86
|
+
|
87
|
+
<% else %>
|
88
|
+
<% @subtabs = [] %>
|
89
|
+
<h1 class='wi'>Workers</h1>
|
90
|
+
<p class='intro'>The hostnames below all have registered workers. Select a hostname to view its workers, or "all" to see all workers.</p>
|
91
|
+
<table class='queues'>
|
92
|
+
<tr>
|
93
|
+
<th>Hostname</th>
|
94
|
+
<th>Workers</th>
|
95
|
+
</tr>
|
96
|
+
<% for hostname, workers in worker_hosts.sort_by { |h,w| h } %>
|
97
|
+
<tr>
|
98
|
+
<td class='queue'><a class="queue" href="<%= u "workers/#{hostname}" %>"><%= hostname %></a></td>
|
99
|
+
<td class='size'><%= workers.size %></td>
|
100
|
+
</tr>
|
101
|
+
<% end %>
|
102
|
+
<tr class="failed">
|
103
|
+
<td class='queue failed'><a class="queue" href="<%= u "workers/all" %>">all workers</a></td>
|
104
|
+
<td class='size'><%= Resque.workers.size %></td>
|
105
|
+
</tr>
|
106
|
+
</table>
|
107
|
+
|
108
|
+
|
109
|
+
<% end %>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<% if params[:id] && (worker = Resque::Worker.find(params[:id])) && worker.job %>
|
2
|
+
<h1><%= worker %>'s job</h1>
|
3
|
+
|
4
|
+
<table>
|
5
|
+
<tr>
|
6
|
+
<th> </th>
|
7
|
+
<th>Where</th>
|
8
|
+
<th>Queue</th>
|
9
|
+
<th>Started</th>
|
10
|
+
<th>Class</th>
|
11
|
+
<th>Args</th>
|
12
|
+
</tr>
|
13
|
+
<tr>
|
14
|
+
<td><img src="<%=u 'working.png' %>" alt="working" title="working"></td>
|
15
|
+
<% host, pid, _ = worker.to_s.split(':') %>
|
16
|
+
<td><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
|
17
|
+
<% data = worker.job %>
|
18
|
+
<% queue = data['queue'] %>
|
19
|
+
<td><a class="queue" href="<%=u "/queues/#{queue}" %>"><%= queue %></a></td>
|
20
|
+
<td><span class="time"><%= data['run_at'] %></span></td>
|
21
|
+
<td>
|
22
|
+
<code><%= data['payload']['class'] %></code>
|
23
|
+
</td>
|
24
|
+
<td><%=h data['payload']['args'].inspect %></td>
|
25
|
+
</tr>
|
26
|
+
</table>
|
27
|
+
|
28
|
+
<% else %>
|
29
|
+
|
30
|
+
<%
|
31
|
+
workers = resque.working
|
32
|
+
jobs = workers.collect {|w| w.job }
|
33
|
+
worker_jobs = workers.zip(jobs)
|
34
|
+
worker_jobs = worker_jobs.reject { |w, j| w.idle? }
|
35
|
+
%>
|
36
|
+
|
37
|
+
<h1 class='wi'><%= worker_jobs.size %> of <%= resque.workers.size %> Workers Working</h1>
|
38
|
+
<p class='intro'>The list below contains all workers which are currently running a job.</p>
|
39
|
+
<table class='workers'>
|
40
|
+
<tr>
|
41
|
+
<th> </th>
|
42
|
+
<th>Where</th>
|
43
|
+
<th>Queue</th>
|
44
|
+
<th>Processing</th>
|
45
|
+
</tr>
|
46
|
+
<% if worker_jobs.empty? %>
|
47
|
+
<tr>
|
48
|
+
<td colspan="4" class='no-data'>Nothing is happening right now...</td>
|
49
|
+
</tr>
|
50
|
+
<% end %>
|
51
|
+
|
52
|
+
<% worker_jobs.sort_by {|w, j| j['run_at'] ? j['run_at'] : '' }.each do |worker, job| %>
|
53
|
+
<tr>
|
54
|
+
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
55
|
+
<% host, pid, queues = worker.to_s.split(':') %>
|
56
|
+
<td class='where'><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
|
57
|
+
<td class='queues queue'>
|
58
|
+
<a class="queue-tag" href="<%=u "/queues/#{job['queue']}" %>"><%= job['queue'] %></a>
|
59
|
+
</td>
|
60
|
+
<td class='process'>
|
61
|
+
<% if job['queue'] %>
|
62
|
+
<code><%= job['payload']['class'] %></code>
|
63
|
+
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= job['run_at'] %></a></small>
|
64
|
+
<% else %>
|
65
|
+
<span class='waiting'>Waiting for a job...</span>
|
66
|
+
<% end %>
|
67
|
+
</td>
|
68
|
+
</tr>
|
69
|
+
<% end %>
|
70
|
+
</table>
|
71
|
+
|
72
|
+
<% end %>
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'erb'
|
3
|
+
require 'resque'
|
4
|
+
require 'resque/version'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Resque
|
8
|
+
class Server < Sinatra::Base
|
9
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
10
|
+
|
11
|
+
set :views, "#{dir}/server/views"
|
12
|
+
set :public, "#{dir}/server/public"
|
13
|
+
set :static, true
|
14
|
+
|
15
|
+
helpers do
|
16
|
+
include Rack::Utils
|
17
|
+
alias_method :h, :escape_html
|
18
|
+
|
19
|
+
def current_section
|
20
|
+
url_path request.path_info.sub('/','').split('/')[0].downcase
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_page
|
24
|
+
url_path request.path_info.sub('/','')
|
25
|
+
end
|
26
|
+
|
27
|
+
def url_path(*path_parts)
|
28
|
+
[ path_prefix, path_parts ].join("/").squeeze('/')
|
29
|
+
end
|
30
|
+
alias_method :u, :url_path
|
31
|
+
|
32
|
+
def path_prefix
|
33
|
+
request.env['SCRIPT_NAME']
|
34
|
+
end
|
35
|
+
|
36
|
+
def class_if_current(path = '')
|
37
|
+
'class="current"' if current_page[0, path.size] == path
|
38
|
+
end
|
39
|
+
|
40
|
+
def tab(name)
|
41
|
+
dname = name.to_s.downcase
|
42
|
+
path = url_path(dname)
|
43
|
+
"<li #{class_if_current(path)}><a href='#{path}'>#{name}</a></li>"
|
44
|
+
end
|
45
|
+
|
46
|
+
def tabs
|
47
|
+
Resque::Server.tabs
|
48
|
+
end
|
49
|
+
|
50
|
+
def mongo_get_size(key)
|
51
|
+
Resque.mongo[key].count
|
52
|
+
end
|
53
|
+
|
54
|
+
def mongo_get_value_as_array(key, start=0)
|
55
|
+
Resque.mongo[key].find({ }, { :skip => start, :limit => 20}).to_a
|
56
|
+
end
|
57
|
+
|
58
|
+
def show_args(args)
|
59
|
+
Array(args).map { |a| a.inspect }.join("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
def worker_hosts
|
63
|
+
@worker_hosts ||= worker_hosts!
|
64
|
+
end
|
65
|
+
|
66
|
+
def worker_hosts!
|
67
|
+
hosts = Hash.new { [] }
|
68
|
+
|
69
|
+
Resque.workers.each do |worker|
|
70
|
+
host, _ = worker.to_s.split(':')
|
71
|
+
hosts[host] += [worker.to_s]
|
72
|
+
end
|
73
|
+
|
74
|
+
hosts
|
75
|
+
end
|
76
|
+
|
77
|
+
def partial?
|
78
|
+
@partial
|
79
|
+
end
|
80
|
+
|
81
|
+
def partial(template, local_vars = {})
|
82
|
+
@partial = true
|
83
|
+
erb(template.to_sym, {:layout => false}, local_vars)
|
84
|
+
ensure
|
85
|
+
@partial = false
|
86
|
+
end
|
87
|
+
|
88
|
+
def poll
|
89
|
+
if @polling
|
90
|
+
text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
|
91
|
+
else
|
92
|
+
text = "<a href='#{u(request.path_info)}.poll' rel='poll'>Live Poll</a>"
|
93
|
+
end
|
94
|
+
"<p class='poll'>#{text}</p>"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def show(page, layout = true)
|
100
|
+
begin
|
101
|
+
erb page.to_sym, {:layout => layout}, :resque => Resque
|
102
|
+
rescue Errno::ECONNREFUSED
|
103
|
+
erb :error, {:layout => false}, :error => "Can't connect to Redis! (#{Resque.redis_id})"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def show_for_polling(page)
|
108
|
+
content_type "text/html"
|
109
|
+
@polling = true
|
110
|
+
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
111
|
+
end
|
112
|
+
|
113
|
+
def processes_in(delay_until)
|
114
|
+
return 'Immediately' if delay_until.nil?
|
115
|
+
now = Time.now
|
116
|
+
time = distance_of_time_in_words(now, delay_until)
|
117
|
+
return "Immediately (#{time})" if now > delay_until
|
118
|
+
return time
|
119
|
+
end
|
120
|
+
|
121
|
+
def enqueued_at(resque_enqueue_timestamp)
|
122
|
+
return 'Unknown' if resque_enqueue_timestamp.nil?
|
123
|
+
now = Time.now
|
124
|
+
time = distance_of_time_in_words(now, resque_enqueue_timestamp)
|
125
|
+
return time
|
126
|
+
end
|
127
|
+
|
128
|
+
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = true, options = {})
|
129
|
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
|
130
|
+
to_time = to_time.to_time if to_time.respond_to?(:to_time)
|
131
|
+
distance_in_minutes = (((to_time - from_time).abs)/60).round
|
132
|
+
distance_in_seconds = ((to_time - from_time).abs).round
|
133
|
+
|
134
|
+
ago = from_time > to_time ? ' ago' : ''
|
135
|
+
|
136
|
+
|
137
|
+
case distance_in_minutes
|
138
|
+
when 0..1
|
139
|
+
return distance_in_minutes == 0 ?
|
140
|
+
"less than 1 minute" + ago :
|
141
|
+
"#{distance_in_minutes} minutes" + ago unless include_seconds
|
142
|
+
|
143
|
+
case distance_in_seconds
|
144
|
+
when 0..4 then "less than 5 seconds" + ago
|
145
|
+
when 5..9 then "less than 10 seconds" + ago
|
146
|
+
when 10..19 then "less than 20 seconds" + ago
|
147
|
+
when 20..39 then "half a minute" + ago
|
148
|
+
when 40..59 then "less than 1 minute" + ago
|
149
|
+
else "1 minute" + ago
|
150
|
+
end
|
151
|
+
|
152
|
+
when 2..44 then "#{distance_in_minutes} minutes" + ago
|
153
|
+
when 45..89 then "about 1 hour" + ago
|
154
|
+
when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours" + ago
|
155
|
+
when 1440..2529 then "about 1 day" + ago
|
156
|
+
when 2530..43199 then "about #{(distance_in_minutes.to_f / 1440.0).round} days" + ago
|
157
|
+
when 43200..86399 then "about 1 month" + ago
|
158
|
+
when 86400..525599 then "about #{(distance_in_minutes.to_f / 43200.0).round} months" + ago
|
159
|
+
else
|
160
|
+
distance_in_years = distance_in_minutes / 525600
|
161
|
+
minute_offset_for_leap_year = (distance_in_years / 4) * 1440
|
162
|
+
remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
|
163
|
+
if remainder < 131400
|
164
|
+
"about #{distance_in_years} years" + ago
|
165
|
+
elsif remainder < 394200
|
166
|
+
"over #{distance_in_years} years" + ago
|
167
|
+
else
|
168
|
+
"almost #{distance_in_years} years" + ago
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# to make things easier on ourselves
|
174
|
+
get "/?" do
|
175
|
+
redirect url_path(:overview)
|
176
|
+
end
|
177
|
+
|
178
|
+
%w( overview workers ).each do |page|
|
179
|
+
get "/#{page}.poll" do
|
180
|
+
show_for_polling(page)
|
181
|
+
end
|
182
|
+
|
183
|
+
get "/#{page}/:id.poll" do
|
184
|
+
show_for_polling(page)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
%w( overview queues working workers key ).each do |page|
|
189
|
+
get "/#{page}" do
|
190
|
+
show page
|
191
|
+
end
|
192
|
+
|
193
|
+
get "/#{page}/:id" do
|
194
|
+
show page
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
post "/queues/:id/remove" do
|
199
|
+
Resque.remove_queue(params[:id])
|
200
|
+
redirect u('queues')
|
201
|
+
end
|
202
|
+
|
203
|
+
get "/failed" do
|
204
|
+
if Resque::Failure.url
|
205
|
+
redirect Resque::Failure.url
|
206
|
+
else
|
207
|
+
show :failed
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
post "/failed/clear" do
|
212
|
+
Resque::Failure.clear
|
213
|
+
redirect u('failed')
|
214
|
+
end
|
215
|
+
|
216
|
+
get "/failed/requeue/:index" do
|
217
|
+
Resque::Failure.requeue(params[:index])
|
218
|
+
if request.xhr?
|
219
|
+
return Resque::Failure.all(params[:index])['retried_at']
|
220
|
+
else
|
221
|
+
redirect u('failed')
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
get "/failed/remove/:index" do
|
226
|
+
Resque::Failure.remove(params[:index])
|
227
|
+
redirect u('failed')
|
228
|
+
end
|
229
|
+
|
230
|
+
get "/stats" do
|
231
|
+
redirect url_path("/stats/resque")
|
232
|
+
end
|
233
|
+
|
234
|
+
get "/stats/:id" do
|
235
|
+
show :stats
|
236
|
+
end
|
237
|
+
|
238
|
+
get "/stats/keys/:key" do
|
239
|
+
show :stats
|
240
|
+
end
|
241
|
+
|
242
|
+
get "/stats.txt" do
|
243
|
+
info = Resque.info
|
244
|
+
|
245
|
+
stats = []
|
246
|
+
stats << "resque.pending=#{info[:pending]}"
|
247
|
+
stats << "resque.processed+=#{info[:processed]}"
|
248
|
+
stats << "resque.failed+=#{info[:failed]}"
|
249
|
+
stats << "resque.workers=#{info[:workers]}"
|
250
|
+
stats << "resque.working=#{info[:working]}"
|
251
|
+
|
252
|
+
Resque.queues.each do |queue|
|
253
|
+
stats << "queues.#{queue}=#{Resque.size(queue)}"
|
254
|
+
end
|
255
|
+
|
256
|
+
content_type 'text/plain'
|
257
|
+
stats.join "\n"
|
258
|
+
end
|
259
|
+
|
260
|
+
def resque
|
261
|
+
Resque
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.tabs
|
265
|
+
@tabs ||= ["Overview", "Working", "Failed", "Queues", "Workers", "Stats"]
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|