qyu 1.0.0

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +21 -0
  8. data/README.md +90 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/server +17 -0
  12. data/bin/setup +8 -0
  13. data/examples/bin/simple +7 -0
  14. data/examples/config.rb +22 -0
  15. data/examples/simple/create_workflow.rb +18 -0
  16. data/examples/simple/enqueue_job.rb +8 -0
  17. data/examples/simple/worker.rb +32 -0
  18. data/lib/qyu.rb +74 -0
  19. data/lib/qyu/config.rb +35 -0
  20. data/lib/qyu/errors.rb +4 -0
  21. data/lib/qyu/errors/base.rb +8 -0
  22. data/lib/qyu/errors/could_not_fetch_task.rb +18 -0
  23. data/lib/qyu/errors/invalid_queue_name.rb +12 -0
  24. data/lib/qyu/errors/invalid_task_attributes.rb +12 -0
  25. data/lib/qyu/errors/job_not_found.rb +14 -0
  26. data/lib/qyu/errors/lock_already_acquired.rb +12 -0
  27. data/lib/qyu/errors/lock_not_acquired.rb +12 -0
  28. data/lib/qyu/errors/message_not_received.rb +12 -0
  29. data/lib/qyu/errors/not_implemented_error.rb +12 -0
  30. data/lib/qyu/errors/payload_validation_error.rb +12 -0
  31. data/lib/qyu/errors/task_not_found.rb +15 -0
  32. data/lib/qyu/errors/task_status_update_failed.rb +15 -0
  33. data/lib/qyu/errors/unknown_validation_option.rb +12 -0
  34. data/lib/qyu/errors/unsync_error.rb +12 -0
  35. data/lib/qyu/errors/workflow_descriptor_validation_error.rb +14 -0
  36. data/lib/qyu/errors/workflow_not_found.rb +15 -0
  37. data/lib/qyu/factory.rb +26 -0
  38. data/lib/qyu/models.rb +9 -0
  39. data/lib/qyu/models/concerns/workflow_descriptor_validator.rb +117 -0
  40. data/lib/qyu/models/enums/status.rb +44 -0
  41. data/lib/qyu/models/job.rb +174 -0
  42. data/lib/qyu/models/task.rb +218 -0
  43. data/lib/qyu/models/workflow.rb +85 -0
  44. data/lib/qyu/queue.rb +5 -0
  45. data/lib/qyu/queue/base.rb +46 -0
  46. data/lib/qyu/queue/memory/adapter.rb +90 -0
  47. data/lib/qyu/store.rb +5 -0
  48. data/lib/qyu/store/base.rb +106 -0
  49. data/lib/qyu/store/memory/adapter.rb +187 -0
  50. data/lib/qyu/ui.rb +56 -0
  51. data/lib/qyu/ui/helpers/pagination.rb +35 -0
  52. data/lib/qyu/ui/public/bootstrap.min.css +5 -0
  53. data/lib/qyu/ui/public/paper-dashboard.css +3315 -0
  54. data/lib/qyu/ui/public/script.js +28 -0
  55. data/lib/qyu/ui/public/style.css +6 -0
  56. data/lib/qyu/ui/views/footer.erb +18 -0
  57. data/lib/qyu/ui/views/helpers/pagination.erb +49 -0
  58. data/lib/qyu/ui/views/jobs.erb +58 -0
  59. data/lib/qyu/ui/views/kaminari/_first_page.html.erb +3 -0
  60. data/lib/qyu/ui/views/kaminari/_gap.html.erb +3 -0
  61. data/lib/qyu/ui/views/kaminari/_last_page.html.erb +3 -0
  62. data/lib/qyu/ui/views/kaminari/_next_page.html.erb +3 -0
  63. data/lib/qyu/ui/views/kaminari/_page.html.erb +9 -0
  64. data/lib/qyu/ui/views/kaminari/_paginator.html.erb +15 -0
  65. data/lib/qyu/ui/views/kaminari/_prev_page.html.erb +3 -0
  66. data/lib/qyu/ui/views/layout.erb +33 -0
  67. data/lib/qyu/ui/views/navbar.erb +29 -0
  68. data/lib/qyu/ui/views/pagination.erb +19 -0
  69. data/lib/qyu/ui/views/show_job.erb +55 -0
  70. data/lib/qyu/ui/views/sidebar.erb +17 -0
  71. data/lib/qyu/ui/views/task_row.erb +26 -0
  72. data/lib/qyu/utils.rb +17 -0
  73. data/lib/qyu/version.rb +3 -0
  74. data/lib/qyu/workers.rb +10 -0
  75. data/lib/qyu/workers/base.rb +126 -0
  76. data/lib/qyu/workers/concerns/callback.rb +38 -0
  77. data/lib/qyu/workers/concerns/failure_queue.rb +23 -0
  78. data/lib/qyu/workers/concerns/payload_validator.rb +124 -0
  79. data/lib/qyu/workers/sync.rb +63 -0
  80. data/qyu.gemspec +36 -0
  81. metadata +278 -0
@@ -0,0 +1,28 @@
1
+ (function(){
2
+ var collapser = function(){
3
+ var elem = this;
4
+ if ( elem.classList.contains('open') ){
5
+ var root = elem.dataset.root;
6
+ var level = elem.dataset.level;
7
+ document.querySelectorAll('tr[data-root="' + root + '"]').forEach(function(el){
8
+ var currentLevel = parseInt(el.dataset.level);
9
+ if (currentLevel > level) {
10
+ el.classList.add('collapse');
11
+ var parent = el.dataset.parent;
12
+ document.querySelector('a[data-id="' + parent + '"]').classList.remove('open');
13
+ }
14
+ });
15
+ elem.classList.remove('open');
16
+ } else {
17
+ var id = elem.dataset.id;
18
+ document.querySelectorAll('[data-parent="' + id + '"]').forEach(function(el){
19
+ el.classList.remove('collapse');
20
+ });
21
+ elem.classList.add('open');
22
+ }
23
+ }
24
+
25
+ document.querySelectorAll('.collapser').forEach(function(elem){
26
+ elem.addEventListener('click', collapser);
27
+ });
28
+ }).call(this);
@@ -0,0 +1,6 @@
1
+ .main-panel {
2
+ width: 100%;
3
+ }
4
+ .collapse {
5
+ display: none;
6
+ }
@@ -0,0 +1,18 @@
1
+ <footer class="footer">
2
+ <div class="container-fluid">
3
+ <nav class="pull-left">
4
+ <ul>
5
+ <li>
6
+ <a href="https://github.com/FindHotel/qyu">
7
+ Qyu is a distributed task execution system for complex workflows
8
+ </a>
9
+ </li>
10
+ </ul>
11
+ </nav>
12
+ <div class="copyright pull-right">
13
+ <a href="https://www.findhotel.net/">
14
+ FindHotel B.V. &copy; <%= Date.today.year %>
15
+ </a>
16
+ </div>
17
+ </div>
18
+ </footer>
@@ -0,0 +1,49 @@
1
+ <nav aria-label="Page navigation">
2
+ <ul class="pagination">
3
+ <% if collection.page > 1 %>
4
+ <li>
5
+ <a href="<%= url("/jobs?page=1") %>">
6
+ &#8672; First
7
+ </a>
8
+ </li>
9
+ <li>
10
+ <a href="<%= url("/jobs?page=#{collection.page - 1}") %>" aria-label="Previous">
11
+ <span aria-hidden="true">&laquo;</span>
12
+ </a>
13
+ </li>
14
+ <% end %>
15
+
16
+ <% previous_pages_for(collection).each do |page_num| %>
17
+ <li>
18
+ <a href="<%= url("/jobs?page=#{page_num}") %>">
19
+ <%= page_num %>
20
+ </a>
21
+ </li>
22
+ <% end %>
23
+ <li class="active">
24
+ <a href="#">
25
+ <%= collection.page %> <span class="sr-only">(current)</span>
26
+ </a>
27
+ </li>
28
+ <% next_pages_for(collection).each do |page_num| %>
29
+ <li>
30
+ <a href="<%= url("/jobs?page=#{page_num}") %>">
31
+ <%= page_num %>
32
+ </a>
33
+ </li>
34
+ <% end %>
35
+
36
+ <% if collection.total_pages > collection.page %>
37
+ <li>
38
+ <a href="<%= url("/jobs?page=#{collection.page + 1}") %>" aria-label="Next">
39
+ <span aria-hidden="true">&raquo;</span>
40
+ </a>
41
+ </li>
42
+ <li>
43
+ <a href="<%= url("/jobs?page=#{collection.total_pages}" ) %>">
44
+ Last &#8674;
45
+ </a>
46
+ </li>
47
+ <% end %>
48
+ </ul>
49
+ </nav>
@@ -0,0 +1,58 @@
1
+ <div class="row">
2
+ <div class="col-md-12">
3
+ <div class="card">
4
+ <div class="header">
5
+ <h4 class="title">Jobs</h4>
6
+ <!-- <p class="category">Last 24 Hours</p> -->
7
+ </div>
8
+ <div class="content">
9
+ <table class="table table-bordered">
10
+ <thead>
11
+ <tr>
12
+ <th>#</th>
13
+ <th>Description</th>
14
+ <th>Payload</th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ <% jobs.each do |job| %>
19
+ <tr>
20
+ <td>
21
+ <a href="<%= url("/jobs/#{job.id}") %>">
22
+ <p><%= job.id %></p>
23
+ <p>Created: <%= job.created_at %></p>
24
+ </a>
25
+ </td>
26
+ <td>
27
+ <strong>Starts:</strong>
28
+ <ul>
29
+ <% job.descriptor['starts'].each do |entry_point| %>
30
+ <li><%= entry_point %></li>
31
+ <% end %>
32
+ </ul>
33
+ <strong>Tasks:</strong>
34
+ <ul>
35
+ <% job.descriptor['tasks'].keys.each do |task| %>
36
+ <li><%= task %></li>
37
+ <% end %>
38
+ </ul>
39
+ </td>
40
+ <td>
41
+ <ul>
42
+ <% job.payload.each do |key, value| %>
43
+ <li><%= key %>: <%= value %></li>
44
+ <% end %>
45
+ </ul>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+
52
+ <nav aria-label="Page navigation" class="text-center">
53
+ <%= erb :'helpers/pagination', locals: { collection: jobs } %>
54
+ </nav>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %>
3
+ </li>
@@ -0,0 +1,3 @@
1
+ <li class='disabled'>
2
+ <%= content_tag :a, raw(t 'views.pagination.truncate') %>
3
+ </li>
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %>
3
+ </li>
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %>
3
+ </li>
@@ -0,0 +1,9 @@
1
+ <% if page.current? %>
2
+ <li class='active'>
3
+ <%= content_tag :a, page, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %>
4
+ </li>
5
+ <% else %>
6
+ <li>
7
+ <%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %>
8
+ </li>
9
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= paginator.render do -%>
2
+ <ul class="pagination">
3
+ <%= first_page_tag unless current_page.first? %>
4
+ <%= prev_page_tag unless current_page.first? %>
5
+ <% each_page do |page| -%>
6
+ <% if page.left_outer? || page.right_outer? || page.inside_window? -%>
7
+ <%= page_tag page %>
8
+ <% elsif !page.was_truncated? -%>
9
+ <%= gap_tag %>
10
+ <% end -%>
11
+ <% end -%>
12
+ <%= next_page_tag unless current_page.last? %>
13
+ <%= last_page_tag unless current_page.last? %>
14
+ </ul>
15
+ <% end -%>
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %>
3
+ </li>
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
6
+
7
+ <title>Qyu Dashboard</title>
8
+
9
+ <meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
10
+ <meta name="viewport" content="width=device-width" />
11
+
12
+ <!-- Bootstrap core CSS -->
13
+ <link rel="stylesheet" type="text/css" href="<%= url("/bootstrap.min.css")%>">
14
+ <!-- Paper Dashboard core CSS -->
15
+ <link rel="stylesheet" type="text/css" href="<%= url("/paper-dashboard.css")%>">
16
+ <!-- custom CSS -->
17
+ <link rel="stylesheet" type="text/css" href="<%= url("/style.css")%>">
18
+ </head>
19
+ <body>
20
+ <div class="wrapper">
21
+ <div class="main-panel">
22
+ <%= erb :navbar %>
23
+ <div class="content">
24
+ <div class="container-fluid">
25
+ <%= yield %>
26
+ </div>
27
+ </div>
28
+ <%= erb :footer %>
29
+ </div>
30
+ </div>
31
+ <script src="<%= url("/script.js")%>"></script>
32
+ </body>
33
+ </html>
@@ -0,0 +1,29 @@
1
+ <nav class="navbar navbar-default">
2
+ <div class="container-fluid">
3
+ <div class="navbar-header">
4
+ <button type="button" class="navbar-toggle">
5
+ <span class="sr-only">Toggle navigation</span>
6
+ <span class="icon-bar bar1"></span>
7
+ <span class="icon-bar bar2"></span>
8
+ <span class="icon-bar bar3"></span>
9
+ </button>
10
+ <a class="navbar-brand" href="/">Qyu</a>
11
+ </div>
12
+ <div class="collapse navbar-collapse">
13
+ <ul class="nav navbar-nav navbar-right">
14
+ <li class="active">
15
+ <a href="#">
16
+ <i class="ti-panel"></i>
17
+ <p>Jobs</p>
18
+ </a>
19
+ </li>
20
+ <li>
21
+ <a href="#">
22
+ <i class="ti-settings"></i>
23
+ <p>Tasks</p>
24
+ </a>
25
+ </li>
26
+ </ul>
27
+ </div>
28
+ </div>
29
+ </nav>
@@ -0,0 +1,19 @@
1
+ <nav aria-label="Page navigation" class="text-center">
2
+ <ul class="pagination">
3
+ <li>
4
+ <a href="#" aria-label="Previous">
5
+ <span aria-hidden="true">«</span>
6
+ </a>
7
+ </li>
8
+ <li><a href="#">1</a></li>
9
+ <li><a href="#">2</a></li>
10
+ <li><a href="#">3</a></li>
11
+ <li><a href="#">4</a></li>
12
+ <li><a href="#">5</a></li>
13
+ <li>
14
+ <a href="#" aria-label="Next">
15
+ <span aria-hidden="true">»</span>
16
+ </a>
17
+ </li>
18
+ </ul>
19
+ </nav>
@@ -0,0 +1,55 @@
1
+ <div class="row">
2
+ <div class="col-md-12">
3
+ <div class="card">
4
+ <div class="header">
5
+ <h4 class="title">Job #<%= job.id %></h4>
6
+ <p class="category">
7
+ Tasks list: <%= total_count %> <%= 'task'.pluralize(total_count) %>
8
+ </p>
9
+ </div>
10
+ <div class="content">
11
+ <table class="table table-stripped">
12
+ <thead>
13
+ <tr>
14
+ <th>Task Name</th>
15
+ <th>Queued</th>
16
+ <th>Working</th>
17
+ <th>Completed</th>
18
+ <th>Failed</th>
19
+ <th>Invalid Payload</th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ <% task_statuses.each do |task_name, statuses| %>
24
+ <tr>
25
+ <td><%= task_name %></td>
26
+ <td><%= statuses['queued'] %></td>
27
+ <td><%= statuses['working'] %></td>
28
+ <td><%= statuses['completed'] %></td>
29
+ <td><%= statuses['failed'] %></td>
30
+ <td><%= statuses['invalid_payload'] %></td>
31
+ </tr>
32
+ <% end %>
33
+ </tbody>
34
+ </table>
35
+ <table class="table table-stripped">
36
+ <thead>
37
+ <tr>
38
+ <th>Name</th>
39
+ <th>Queue Name</th>
40
+ <th>Status</th>
41
+ <th>Payload</th>
42
+ <!-- <th>Locked Until</th> -->
43
+ <!-- <th>Locked By</th> -->
44
+ </tr>
45
+ </thead>
46
+ <tbody>
47
+ <% (tasks[nil] || []).each do |task| %>
48
+ <%= erb :task_row, locals: { tasks: tasks, task: task, level: 0, root_id: task.id } %>
49
+ <% end %>
50
+ </tbody>
51
+ </table>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
@@ -0,0 +1,17 @@
1
+ <div class="sidebar" data-background-color="white" data-active-color="danger">
2
+ <div class="sidebar-wrapper">
3
+ <div class="logo">
4
+ <a href="/" class="simple-text">
5
+ Arc Yu
6
+ </a>
7
+ </div>
8
+
9
+ <ul class="nav">
10
+ <li class="active">
11
+ <a href="/">
12
+ <p>Jobs</p>
13
+ </a>
14
+ </li>
15
+ </ul>
16
+ </div>
17
+ </div>
@@ -0,0 +1,26 @@
1
+ <tr data-parent="<%= task.parent_task_id %>"
2
+ data-level="<%= level %>"
3
+ data-root="<%= root_id %>"
4
+ class="<%= 'collapse' if level > 0 %>">
5
+ <td>
6
+ <%= raw_html("•&nbsp;&nbsp;" * level) if level > 0 %>
7
+ <% if tasks[task.id] %>
8
+ <a href="#" class="collapser"
9
+ data-id="<%= task.id %>"
10
+ data-level="<%= level %>"
11
+ data-root="<%= root_id %>">
12
+ <%= task.name %>
13
+ </a>
14
+ <% else %>
15
+ <%= task.name %>
16
+ <% end %>
17
+ </td>
18
+ <td><%= task.queue_name %></td>
19
+ <td><%= task.status.status %></td>
20
+ <td><%= task.payload %></td>
21
+ <!-- <td><%# task.locked_until %></td> -->
22
+ <!-- <td><%# task.locked_by %></td> -->
23
+ </tr>
24
+ <% (tasks[task.id] || []).each do |t| %>
25
+ <%= erb :task_row, locals: { tasks: tasks, task: t, level: level + 1, root_id: root_id } %>
26
+ <% end %>
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qyu
4
+ module Utils
5
+ def self.seconds_after_time(seconds, start_time = Time.now)
6
+ start_time + seconds
7
+ end
8
+
9
+ def self.uuid
10
+ SecureRandom.uuid
11
+ end
12
+
13
+ def self.stringify_hash_keys(object)
14
+ object.map { |k, v| [k.to_s, v] }.to_h
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Qyu
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'qyu/workers/concerns/callback'
4
+ require 'qyu/workers/concerns/failure_queue'
5
+ require 'qyu/workers/concerns/payload_validator'
6
+ require 'qyu/workers/base'
7
+ require 'qyu/workers/sync'
8
+
9
+ Qyu::Worker = Qyu::Workers::Base
10
+ Qyu::SyncWorker = Qyu::Workers::Sync
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qyu
4
+ module Workers
5
+ class Base
6
+ include Concerns::Callback
7
+ include Concerns::PayloadValidator
8
+ include Concerns::FailureQueue
9
+
10
+ attr_reader :id
11
+ attr_accessor :processed_tasks
12
+
13
+ def initialize(&block)
14
+ @id = Qyu::Utils.uuid
15
+ @processed_tasks = 0
16
+ instance_exec(&block) if block_given?
17
+ end
18
+
19
+ def work(queue_name, blocking: true)
20
+ log(:info, "Worker started for queue '#{queue_name}'")
21
+ repeat = true
22
+
23
+ remaining_fetch_retries = 3
24
+
25
+ while repeat
26
+ run_callbacks(:execute) do
27
+ begin
28
+ fetched_task = fetch_task(queue_name)
29
+ validate_payload!(fetched_task)
30
+ log(:info, "Worker processed #{processed_tasks} tasks from queue `#{queue_name}`")
31
+ if fetched_task.acknowledgeable?
32
+ discard_completed_task(fetched_task)
33
+ elsif fetched_task.lock!
34
+ fetched_task.mark_working
35
+ begin
36
+ yield(fetched_task)
37
+ conclude_task(fetched_task)
38
+ rescue => ex
39
+ fail_task(fetched_task, ex)
40
+ end
41
+ end
42
+ rescue Qyu::Errors::UnsyncError
43
+ rescue Qyu::Errors::CouldNotFetchTask => ex
44
+ if remaining_fetch_retries <= 0
45
+ acknowledge_message_with_task_id_not_found_in_store(ex)
46
+ else
47
+ sleep(remaining_fetch_retries)
48
+ remaining_fetch_retries -= 1
49
+ retry
50
+ end
51
+ rescue Qyu::Errors::PayloadValidationError
52
+ fetched_task.mark_invalid_payload
53
+ rescue => ex
54
+ log("Worker error: #{ex.class}: #{ex.message}")
55
+ log("Backtrace: #{ex.backtrace.join("\n")}")
56
+ end
57
+ end
58
+
59
+ repeat = blocking
60
+ run_garbage_collector
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def fetch_task(queue_name)
67
+ fetched_task = Qyu::Task.fetch(queue_name)
68
+ @processed_tasks += 1
69
+ fetched_task
70
+ end
71
+
72
+ def discard_completed_task(fetched_task)
73
+ log(:debug, 'Fetched completed task. Discarding...')
74
+ fetched_task.acknowledge_message
75
+ end
76
+
77
+ def conclude_task(fetched_task)
78
+ Qyu.store.transaction do
79
+ log(:debug, 'Task finished. Creating next tasks.')
80
+ fetched_task.job.create_next_tasks(
81
+ fetched_task,
82
+ fetched_task.job.payload.merge(fetched_task.payload)
83
+ )
84
+ fetched_task.unlock!
85
+ fetched_task.mark_completed
86
+ end
87
+ fetched_task.acknowledge_message
88
+ end
89
+
90
+ def fail_task(fetched_task, exception)
91
+ unless exception.class == Qyu::Errors::UnsyncError
92
+ log("Worker error: #{exception.class}: #{exception.message}")
93
+ log("Backtrace: #{exception.backtrace.join("\n")}")
94
+ end
95
+ Qyu.store.transaction do
96
+ fetched_task.enqueue_in_failure_queue if @failure_queue
97
+ fetched_task.unlock!
98
+ fetched_task.mark_queued
99
+ end
100
+ end
101
+
102
+ def acknowledge_message_with_task_id_not_found_in_store(exception)
103
+ # If a task is not found in the Store then there is no point attempting
104
+ # to fetch the message over and over again.
105
+ log("Worker error: #{exception.class}: #{exception.message}")
106
+ log("Backtrace: #{exception.backtrace.join("\n")}")
107
+ log("Original error: #{exception.original_error.class}: #{exception.original_error.message}")
108
+ log("Backtrace: #{exception.original_error.backtrace.join("\n")}")
109
+ if exception.original_error.class == Qyu::Errors::TaskNotFound &&
110
+ exception.queue_name &&
111
+ exception.message_id
112
+ Qyu::Task.acknowledge_message(exception.queue_name, exception.message_id)
113
+ end
114
+ end
115
+
116
+ def log(level = :error, message)
117
+ Qyu.logger.public_send(level, "[#{id}] #{message}")
118
+ end
119
+
120
+ def run_garbage_collector
121
+ log(:debug, 'Running garbage collector')
122
+ GC.start
123
+ end
124
+ end
125
+ end
126
+ end