joblin 0.1.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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1 -0
  3. data/app/models/joblin/background_task/api_access.rb +148 -0
  4. data/app/models/joblin/background_task/attachments.rb +47 -0
  5. data/app/models/joblin/background_task/executor.rb +63 -0
  6. data/app/models/joblin/background_task/options.rb +75 -0
  7. data/app/models/joblin/background_task/retention_policy.rb +28 -0
  8. data/app/models/joblin/background_task.rb +72 -0
  9. data/app/models/joblin/concerns/job_working_dirs.rb +21 -0
  10. data/db/migrate/20250903184852_create_background_tasks.rb +12 -0
  11. data/joblin.gemspec +35 -0
  12. data/lib/joblin/batching/batch.rb +537 -0
  13. data/lib/joblin/batching/callback.rb +135 -0
  14. data/lib/joblin/batching/chain_builder.rb +247 -0
  15. data/lib/joblin/batching/compat/active_job.rb +108 -0
  16. data/lib/joblin/batching/compat/sidekiq/web/batches_assets/css/styles.less +182 -0
  17. data/lib/joblin/batching/compat/sidekiq/web/batches_assets/js/batch_tree.js +108 -0
  18. data/lib/joblin/batching/compat/sidekiq/web/batches_assets/js/util.js +2 -0
  19. data/lib/joblin/batching/compat/sidekiq/web/helpers.rb +41 -0
  20. data/lib/joblin/batching/compat/sidekiq/web/views/_batch_tree.erb +6 -0
  21. data/lib/joblin/batching/compat/sidekiq/web/views/_batches_table.erb +44 -0
  22. data/lib/joblin/batching/compat/sidekiq/web/views/_common.erb +13 -0
  23. data/lib/joblin/batching/compat/sidekiq/web/views/_jobs_table.erb +21 -0
  24. data/lib/joblin/batching/compat/sidekiq/web/views/_pagination.erb +26 -0
  25. data/lib/joblin/batching/compat/sidekiq/web/views/batch.erb +81 -0
  26. data/lib/joblin/batching/compat/sidekiq/web/views/batches.erb +23 -0
  27. data/lib/joblin/batching/compat/sidekiq/web/views/pool.erb +137 -0
  28. data/lib/joblin/batching/compat/sidekiq/web/views/pools.erb +47 -0
  29. data/lib/joblin/batching/compat/sidekiq/web.rb +218 -0
  30. data/lib/joblin/batching/compat/sidekiq.rb +149 -0
  31. data/lib/joblin/batching/compat.rb +20 -0
  32. data/lib/joblin/batching/context_hash.rb +157 -0
  33. data/lib/joblin/batching/hier_batch_ids.lua +25 -0
  34. data/lib/joblin/batching/jobs/base_job.rb +7 -0
  35. data/lib/joblin/batching/jobs/concurrent_batch_job.rb +20 -0
  36. data/lib/joblin/batching/jobs/managed_batch_job.rb +175 -0
  37. data/lib/joblin/batching/jobs/serial_batch_job.rb +20 -0
  38. data/lib/joblin/batching/pool.rb +254 -0
  39. data/lib/joblin/batching/pool_refill.lua +47 -0
  40. data/lib/joblin/batching/schedule_callback.lua +14 -0
  41. data/lib/joblin/batching/status.rb +89 -0
  42. data/lib/joblin/engine.rb +15 -0
  43. data/lib/joblin/lazy_access.rb +72 -0
  44. data/lib/joblin/uniqueness/compat/active_job.rb +75 -0
  45. data/lib/joblin/uniqueness/compat/sidekiq.rb +135 -0
  46. data/lib/joblin/uniqueness/compat.rb +20 -0
  47. data/lib/joblin/uniqueness/configuration.rb +25 -0
  48. data/lib/joblin/uniqueness/job_uniqueness.rb +49 -0
  49. data/lib/joblin/uniqueness/lock_context.rb +199 -0
  50. data/lib/joblin/uniqueness/locksmith.rb +92 -0
  51. data/lib/joblin/uniqueness/on_conflict/base.rb +32 -0
  52. data/lib/joblin/uniqueness/on_conflict/log.rb +13 -0
  53. data/lib/joblin/uniqueness/on_conflict/null_strategy.rb +9 -0
  54. data/lib/joblin/uniqueness/on_conflict/raise.rb +11 -0
  55. data/lib/joblin/uniqueness/on_conflict/reject.rb +21 -0
  56. data/lib/joblin/uniqueness/on_conflict/reschedule.rb +20 -0
  57. data/lib/joblin/uniqueness/on_conflict.rb +62 -0
  58. data/lib/joblin/uniqueness/strategy/base.rb +107 -0
  59. data/lib/joblin/uniqueness/strategy/until_and_while_executing.rb +35 -0
  60. data/lib/joblin/uniqueness/strategy/until_executed.rb +20 -0
  61. data/lib/joblin/uniqueness/strategy/until_executing.rb +20 -0
  62. data/lib/joblin/uniqueness/strategy/until_expired.rb +16 -0
  63. data/lib/joblin/uniqueness/strategy/while_executing.rb +26 -0
  64. data/lib/joblin/uniqueness/strategy.rb +27 -0
  65. data/lib/joblin/uniqueness/unique_job_common.rb +79 -0
  66. data/lib/joblin/version.rb +3 -0
  67. data/lib/joblin.rb +37 -0
  68. data/spec/batching/batch_spec.rb +493 -0
  69. data/spec/batching/callback_spec.rb +38 -0
  70. data/spec/batching/compat/active_job_spec.rb +107 -0
  71. data/spec/batching/compat/sidekiq_spec.rb +127 -0
  72. data/spec/batching/context_hash_spec.rb +54 -0
  73. data/spec/batching/flow_spec.rb +82 -0
  74. data/spec/batching/integration/fail_then_succeed.rb +42 -0
  75. data/spec/batching/integration/integration.rb +57 -0
  76. data/spec/batching/integration/nested.rb +88 -0
  77. data/spec/batching/integration/simple.rb +47 -0
  78. data/spec/batching/integration/workflow.rb +134 -0
  79. data/spec/batching/integration_helper.rb +50 -0
  80. data/spec/batching/pool_spec.rb +161 -0
  81. data/spec/batching/status_spec.rb +76 -0
  82. data/spec/batching/support/base_job.rb +19 -0
  83. data/spec/batching/support/sample_callback.rb +2 -0
  84. data/spec/internal/config/database.yml +5 -0
  85. data/spec/internal/config/routes.rb +5 -0
  86. data/spec/internal/config/storage.yml +3 -0
  87. data/spec/internal/db/combustion_test.sqlite +0 -0
  88. data/spec/internal/db/schema.rb +6 -0
  89. data/spec/internal/log/test.log +48200 -0
  90. data/spec/internal/public/favicon.ico +0 -0
  91. data/spec/models/background_task_spec.rb +41 -0
  92. data/spec/spec_helper.rb +29 -0
  93. data/spec/uniqueness/compat/active_job_spec.rb +49 -0
  94. data/spec/uniqueness/compat/sidekiq_spec.rb +68 -0
  95. data/spec/uniqueness/lock_context_spec.rb +106 -0
  96. data/spec/uniqueness/on_conflict/log_spec.rb +11 -0
  97. data/spec/uniqueness/on_conflict/raise_spec.rb +10 -0
  98. data/spec/uniqueness/on_conflict/reschedule_spec.rb +63 -0
  99. data/spec/uniqueness/on_conflict_spec.rb +16 -0
  100. data/spec/uniqueness/spec_helper.rb +19 -0
  101. data/spec/uniqueness/strategy/base_spec.rb +100 -0
  102. data/spec/uniqueness/strategy/until_and_while_executing_spec.rb +48 -0
  103. data/spec/uniqueness/strategy/until_executed_spec.rb +23 -0
  104. data/spec/uniqueness/strategy/until_executing_spec.rb +23 -0
  105. data/spec/uniqueness/strategy/until_expired_spec.rb +23 -0
  106. data/spec/uniqueness/strategy/while_executing_spec.rb +33 -0
  107. data/spec/uniqueness/support/lock_strategy.rb +28 -0
  108. data/spec/uniqueness/support/on_conflict.rb +24 -0
  109. data/spec/uniqueness/support/test_worker.rb +19 -0
  110. data/spec/uniqueness/unique_job_common_spec.rb +45 -0
  111. metadata +308 -0
@@ -0,0 +1,44 @@
1
+ <table class="table table-striped table-bordered table-hover">
2
+ <thead>
3
+ <tr>
4
+ <th rowspan="2"><%= t('Started') %></th>
5
+ <th rowspan="2"><%= t('BID') %></th>
6
+ <th rowspan="2"><%= t('Description') %></th>
7
+
8
+ <th colspan="5"><%= t('Jobs') %></th>
9
+ <th colspan="4"><%= t('Sub-Batches') %></th>
10
+ </tr>
11
+ <tr>
12
+ <th><%= t('Pending') %></th>
13
+ <th><%= t('Failed') %></th>
14
+ <th><%= t('Dead') %></th>
15
+ <th><%= t('Complete') %></th>
16
+ <th><%= t('Total') %></th>
17
+
18
+ <th><%= t('Pending') %></th>
19
+ <th><%= t('Failed') %></th>
20
+ <th><%= t('Success') %></th>
21
+ <th><%= t('Total') %></th>
22
+ </tr>
23
+ </thead>
24
+
25
+ <% batches.each do |batch| %>
26
+ <% status = Joblin::Batching::Batch::Status.new(batch) %>
27
+ <tr>
28
+ <td><%= safe_relative_time(batch.created_at.to_f) %></th>
29
+ <td><a href="<%= root_path %>batches/<%= batch.bid %>"><%= batch.bid %></a></td>
30
+ <td><%= batch.description %></th>
31
+
32
+ <td><%= status.pending %></th>
33
+ <td><%= status.failures %></th>
34
+ <td><%= status.dead %></th>
35
+ <td><%= status.completed_count %></th>
36
+ <td><%= status.job_count %></th>
37
+
38
+ <td><%= status.child_count - status.successful_children_count %></th>
39
+ <td><%= status.failed_children_count %></th>
40
+ <td><%= status.successful_children_count %></th>
41
+ <td><%= status.child_count %></th>
42
+ </tr>
43
+ <% end %>
44
+ </table>
@@ -0,0 +1,13 @@
1
+
2
+ <% add_to_head do %>
3
+ <meta name="sidekiq-baseurl" content="<%= root_path %>" />
4
+ <% if dev_mode? %>
5
+ <link href="<%= root_path %>batches_assets/css/styles.less" media="screen" rel="stylesheet/less" type="text/css" />
6
+ <script async type="text/javascript" src="https://unpkg.com/less@4.1.1/dist/less.js" data-async="true"></script>
7
+ <% else %>
8
+ <link href="<%= root_path %>batches_assets/css/styles.less" media="screen" rel="stylesheet/less" type="text/css" />
9
+ <script async type="text/javascript" src="https://unpkg.com/less@4.1.1/dist/less.js" data-async="true"></script>
10
+ <%# TODO: Pre-compile LESS %>
11
+ <!-- <link href="<%= root_path %>batches_assets/css/styles.css" media="screen" rel="stylesheet" type="text/css" /> -->
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <table class="table table-striped table-bordered table-hover">
2
+ <thead>
3
+ <tr>
4
+ <th><%= t('JID') %></th>
5
+ <th><%= t('Job Class') %></th>
6
+ <th><%= t('Parameters') %></th>
7
+ </tr>
8
+ </thead>
9
+
10
+ <% @jobs.each do |job_desc| %>
11
+ <tr>
12
+ <td><%= job_desc[:jid] %></td>
13
+ <td><%= job_desc['job'] %></td>
14
+ <td>
15
+ <code class="code-wrap">
16
+ <div class="args-extended"><%= job_desc['parameters'].to_json %></div>
17
+ </code>
18
+ </td>
19
+ </tr>
20
+ <% end %>
21
+ </table>
@@ -0,0 +1,26 @@
1
+
2
+ <% if @total_size > @count %>
3
+ <ul class="pagination pull-right flip">
4
+ <% _page_key = defined?(page_key) ? page_key.to_s : 'page' %>
5
+
6
+ <li class="<%= 'disabled' if @current_page == 1 %>">
7
+ <a href="<%= url %>?<%= _page_key %>=1">&laquo;</a>
8
+ </li>
9
+ <% if @current_page > 1 %>
10
+ <li>
11
+ <a href="<%= url %>?<%= (qparams(page: @current_page - 1) || '').gsub('page', _page_key) %>"><%= @current_page - 1 %></a>
12
+ </li>
13
+ <% end %>
14
+ <li class="disabled">
15
+ <a href="<%= url %>?<%= (qparams(page: @current_page) || '').gsub('page', _page_key) %>"><%= @current_page %></a>
16
+ </li>
17
+ <% if @total_size > @current_page * @count %>
18
+ <li>
19
+ <a href="<%= url %>?<%= (qparams(page: @current_page + 1) || '').gsub('page', _page_key) %>"><%= @current_page + 1 %></a>
20
+ </li>
21
+ <% end %>
22
+ <li class="<%= 'disabled' if @total_size <= @current_page * @count %>">
23
+ <a href="<%= url %>?<%= (qparams(page: (@total_size.to_f / @count).ceil) || '').gsub('page', _page_key) %>">&raquo;</a>
24
+ </li>
25
+ </ul>
26
+ <% end %>
@@ -0,0 +1,81 @@
1
+ <%= erb get_template(:_common) %>
2
+
3
+ <h3><%= t('Batch') %></h3>
4
+ <% status = Joblin::Batching::Batch::Status.new(@batch) %>
5
+
6
+ <div class="table_container">
7
+ <table class="table table-striped table-bordered table-white table-hover">
8
+ <tbody>
9
+ <tr>
10
+ <th colspan="2" scope=row><%= t('Batch') %></td>
11
+ <td><%= @batch.bid %></td>
12
+ </tr>
13
+ <tr>
14
+ <th colspan="2" scope=row><%= t('Parent') %></td>
15
+ <td>
16
+ <% if @batch.parent_bid.present? %>
17
+ <a href="<%= root_path %>batches/<%= @batch.parent_bid %>"><%= @batch.parent_bid %></a>
18
+ <% else %>
19
+ ROOT
20
+ <% end %>
21
+ </td>
22
+ </tr>
23
+ <tr>
24
+ <th colspan="2" scope=row><%= t('Started') %></td>
25
+ <td><%= safe_relative_time(@batch.created_at.to_f) %></td>
26
+ </tr>
27
+ <tr>
28
+ <th colspan="2" scope=row><%= t('Description') %></td>
29
+ <td><%= @batch.description %></td>
30
+ </tr>
31
+ <tr>
32
+ <th colspan="2" scope=row><%= t('Context') %></td>
33
+ <td>
34
+ <code class="code-wrap batch-context">
35
+ <div class="args-extended"><%= format_context(@batch) %></div>
36
+ </code>
37
+ </td>
38
+ </tr>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+
43
+ <header class="row">
44
+ <div class="col-sm-5">
45
+ <h3>
46
+ <%= t('Child Batches') %>
47
+ </h3>
48
+ </div>
49
+ </header>
50
+
51
+ <%= erb get_template(:_batch_tree), locals: { } %>
52
+
53
+ <header class="row">
54
+ <div class="col-sm-5">
55
+ <h3>
56
+ <%= t('Jobs') %>
57
+ <i>(Queued and Current)</i>
58
+ </h3>
59
+ </div>
60
+ <%
61
+ @current_page = @current_jobs_page
62
+ @total_size = @total_jobs_size
63
+ %>
64
+ <% if @jobs.any? && @total_size > @count.to_i %>
65
+ <div class="col-sm-4">
66
+ <%= erb get_template(:_pagination), locals: { url: "#{root_path}batches/#{@batch.bid}", page_key: :job_page } %>
67
+ </div>
68
+ <% end %>
69
+ </header>
70
+
71
+ <% if @jobs.any? %>
72
+ <div class="table_container">
73
+ <%= erb get_template(:_jobs_table), locals: { jobs: @jobs } %>
74
+ </div>
75
+ <% end %>
76
+
77
+ <form class="form-horizontal" action="<%= root_path %>batches/<%= @batch.bid %>" method="post">
78
+ <%= csrf_tag %>
79
+ <a class="btn btn-default" href="<%= root_path %>batches"><%= t('GoBack') %></a>
80
+ <input class="btn btn-danger" type="submit" name="delete" value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSure') %>" />
81
+ </form>
@@ -0,0 +1,23 @@
1
+ <header class="row">
2
+ <div class="col-sm-5">
3
+ <h3>
4
+ <%= t('Batches') %>
5
+ </h3>
6
+ </div>
7
+ <% if @batches.any? && @total_size > @count.to_i %>
8
+ <div class="col-sm-4">
9
+ <%= erb get_template(:_pagination), locals: { url: "#{root_path}batches" } %>
10
+ </div>
11
+ <% end %>
12
+ </header>
13
+
14
+ <% if @batches.any? %>
15
+ <div class="table_container">
16
+ <%= erb get_template(:_batches_table), locals: { batches: @batches } %>
17
+ </div>
18
+ <% end %>
19
+
20
+ <form action="<%= root_path %>batches/all" method="post">
21
+ <%= csrf_tag %>
22
+ <input class="btn btn-danger btn-xs pull-right flip" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
23
+ </form>
@@ -0,0 +1,137 @@
1
+ <h3><%= t('Pool') %></h3>
2
+
3
+ <div class="table_container">
4
+ <table class="table table-striped table-bordered table-white table-hover">
5
+ <tbody>
6
+ <tr>
7
+ <th scope=row><%= t('PID') %></td>
8
+ <td><%= @pool.pid %></td>
9
+ </tr>
10
+ <tr>
11
+ <th scope=row><%= t('Created') %></td>
12
+ <td><%= safe_relative_time(@pool.created_at.to_f) %></td>
13
+ </tr>
14
+ <tr>
15
+ <th scope=row><%= t('Description') %></td>
16
+ <td><%= @pool.description %></td>
17
+ </tr>
18
+ <tr>
19
+ <th scope=row><%= t('Type') %></td>
20
+ <td><%= @pool.order.to_s.upcase %></td>
21
+ </tr>
22
+ <tr>
23
+ <th scope=row><%= t('Concurrency') %></td>
24
+ <td><%= @pool.concurrency %></td>
25
+ </tr>
26
+ <tr>
27
+ <th scope=row><%= t('Utilization') %></td>
28
+ <td><%= @pool.active_count %></td>
29
+ </tr>
30
+ <tr>
31
+ <th scope=row><%= t('Pending Tasks') %></td>
32
+ <td><%= @pool.pending_count %></td>
33
+ </tr>
34
+ <tr>
35
+ <th scope=row><%= t('Returned Tasks') %></td>
36
+ <td><%= @pool.complete_count %></td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+
42
+ <%# Queued/Active Tasks %>
43
+ <header class="row">
44
+ <div class="col-sm-5">
45
+ <h3>
46
+ <%= t('Queued/Active Tasks') %>
47
+ </h3>
48
+ </div>
49
+ </header>
50
+
51
+ <% if @active_tasks.any? %>
52
+ <div class="table_container">
53
+ <table class="table table-striped table-bordered table-hover">
54
+ <thead>
55
+ <tr>
56
+ <th><%= t('Wrapper Batch BID') %></th>
57
+ <th><%= t('Job Class') %></th>
58
+ <th><%= t('Parameters') %></th>
59
+ </tr>
60
+ </thead>
61
+
62
+ <% @active_tasks.each do |job_desc| %>
63
+ <tr>
64
+ <td><a href="<%= root_path %>batches/<%= job_desc['pool_wrapper_batch'] %>"><%= job_desc['pool_wrapper_batch'] %></a></td>
65
+ <td><%= job_desc['job'] %></td>
66
+ <td>
67
+ <code class="code-wrap">
68
+ <div class="args-extended">
69
+ <%= job_desc['args']&.to_json %>
70
+ <%= job_desc['kwargs']&.to_json %>
71
+ </div>
72
+ </code>
73
+ </td>
74
+ </tr>
75
+ <% end %>
76
+ </table>
77
+ </div>
78
+ <% end %>
79
+
80
+ <%# Pending Tasks %>
81
+ <header class="row">
82
+ <div class="col-sm-5">
83
+ <h3>
84
+ <%= t('Pending Tasks') %>
85
+ </h3>
86
+ </div>
87
+ <%
88
+ @current_page = @current_jobs_page
89
+ @total_size = @total_jobs_size
90
+ %>
91
+ <% if @jobs.any? && @total_size > @count.to_i %>
92
+ <div class="col-sm-4">
93
+ <%= erb get_template(:_pagination), locals: { url: "#{root_path}pools/#{@pool.pid}", page_key: :job_page } %>
94
+ </div>
95
+ <% end %>
96
+ </header>
97
+
98
+ <% if @jobs.any? %>
99
+ <div class="table_container">
100
+ <table class="table table-striped table-bordered table-hover">
101
+ <thead>
102
+ <tr>
103
+ <th><%= t('Job Class') %></th>
104
+ <th><%= t('Parameters') %></th>
105
+ <th><%= t('Wrapper Batch BID') %></th>
106
+ <% if @pool.order == 'priority' %>
107
+ <th><%= t('Priority') %></th>
108
+ <% end %>
109
+ </tr>
110
+ </thead>
111
+
112
+ <% @jobs.each do |job_desc| %>
113
+ <tr>
114
+ <td><%= job_desc['job'] %></td>
115
+ <td>
116
+ <code class="code-wrap">
117
+ <div class="args-extended">
118
+ <%= job_desc['args']&.to_json %>
119
+ <%= job_desc['kwargs']&.to_json %>
120
+ </div>
121
+ </code>
122
+ </td>
123
+ <td><a href="<%= root_path %>batches/<%= job_desc['pool_wrapper_batch'] %>"><%= job_desc['pool_wrapper_batch'] %></a></td>
124
+ <% if @pool.order == 'priority' %>
125
+ <td><%= job_desc['priority'] %></td>
126
+ <% end %>
127
+ </tr>
128
+ <% end %>
129
+ </table>
130
+ </div>
131
+ <% end %>
132
+
133
+ <form class="form-horizontal" action="<%= root_path %>pools/<%= @pool.pid %>" method="post">
134
+ <%= csrf_tag %>
135
+ <a class="btn btn-default" href="<%= root_path %>pools"><%= t('GoBack') %></a>
136
+ <input class="btn btn-danger" type="submit" name="delete" value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSure') %>" />
137
+ </form>
@@ -0,0 +1,47 @@
1
+ <header class="row">
2
+ <div class="col-sm-5">
3
+ <h3>
4
+ <%= t('Pools') %>
5
+ </h3>
6
+ </div>
7
+ <% if @pools.any? && @total_size > @count.to_i %>
8
+ <div class="col-sm-4">
9
+ <%= erb get_template(:_pagination), locals: { url: "#{root_path}pools" } %>
10
+ </div>
11
+ <% end %>
12
+ </header>
13
+
14
+ <% if @pools.any? %>
15
+ <div class="table_container">
16
+ <table class="table table-striped table-bordered table-hover">
17
+ <thead>
18
+ <tr>
19
+ <th><%= t('Created') %></th>
20
+ <th><%= t('PID') %></th>
21
+ <th><%= t('Description') %></th>
22
+ <th><%= t('Type') %></th>
23
+ <th><%= t('Concurrency') %></th>
24
+ <th><%= t('Utilization') %></th>
25
+ <th><%= t('Pending Tasks') %></th>
26
+ </tr>
27
+ </thead>
28
+
29
+ <% @pools.each do |pool| %>
30
+ <tr>
31
+ <td><%= safe_relative_time(pool.created_at.to_f) %></th>
32
+ <td><a href="<%= root_path %>pools/<%= pool.pid %>"><%= pool.pid %></a></td>
33
+ <td><%= pool.description %></th>
34
+ <td><%= pool.order.to_s.upcase %></th>
35
+ <td><%= pool.concurrency %></th>
36
+ <td><%= pool.active_count %></th>
37
+ <td><%= pool.pending_count %></th>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+ </div>
42
+ <% end %>
43
+
44
+ <form action="<%= root_path %>pools/all" method="post">
45
+ <%= csrf_tag %>
46
+ <input class="btn btn-danger btn-xs pull-right flip" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
47
+ </form>
@@ -0,0 +1,218 @@
1
+
2
+ begin
3
+ require "sidekiq/web"
4
+ rescue LoadError
5
+ # client-only usage
6
+ end
7
+
8
+ require_relative "web/helpers"
9
+
10
+ module Joblin::Batching::Compat::Sidekiq
11
+ module Web
12
+ DEV_MODE = (defined?(Rails) && !Rails.env.production?) || !!ENV["SIDEKIQ_WEB_TESTING"]
13
+ ::Sidekiq::WebHelpers::SAFE_QPARAMS << 'all_batches'
14
+ ::Sidekiq::WebHelpers::SAFE_QPARAMS << 'count'
15
+
16
+ def self.registered(app) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
17
+ app.helpers do
18
+ include Web::Helpers
19
+
20
+ def dev_mode?
21
+ DEV_MODE
22
+ end
23
+ end
24
+
25
+ # =============== BATCHES =============== #
26
+
27
+ app.get "/batches" do
28
+ @count = (params['count'] || 25).to_i
29
+
30
+ source_key = params['all_batches'] ? "batches" : "BID-ROOT-bids"
31
+ @current_page, @total_size, @batches = page(source_key, params['page'], @count)
32
+ @batches = @batches.map {|b, score| Joblin::Batching::Batch.new(b) }
33
+
34
+ erb(get_template(:batches))
35
+ end
36
+
37
+ app.get "/batches/:bid" do
38
+ @bid = params[:bid]
39
+ @batch = Joblin::Batching::Batch.new(@bid)
40
+
41
+ @tree_data = tree_data(@bid)
42
+
43
+ @count = (params['count'] || 25).to_i
44
+ @current_batches_page, @total_batches_size, @sub_batches = page("BID-#{@batch.bid}-bids", params['batch_page'], @count)
45
+ @sub_batches = @sub_batches.map {|b, score| Joblin::Batching::Batch.new(b) }
46
+
47
+ @current_jobs_page, @total_jobs_size, @jobs = page("BID-#{@batch.bid}-jids", params['job_page'], @count)
48
+ @jobs = @jobs.map do |jid, score|
49
+ { jid: jid, }
50
+ end
51
+
52
+ erb(get_template(:batch))
53
+ end
54
+
55
+ app.get "/batches/:bid/tree" do
56
+ @bid = params[:bid]
57
+
58
+ json(tree_data(@bid, slice: params[:slice]))
59
+ end
60
+
61
+ app.helpers do
62
+ def tree_data(root_bid, slice: nil)
63
+ tree_bids = Joblin::Batching::Batch.bid_hierarchy(root_bid, slice: slice)
64
+
65
+ Joblin::Batching::Batch.redis do |r|
66
+ layer_data = ->(layer, parent = nil) {
67
+ bid = layer[0]
68
+ batch = Joblin::Batching::Batch.new(bid)
69
+
70
+ jobs_total = r.hget("BID-#{bid}", "job_count").to_i
71
+ jobs_pending = r.hget("BID-#{bid}", 'pending').to_i
72
+ jobs_failed = r.scard("BID-#{bid}-failed").to_i
73
+ jobs_dead = r.scard("BID-#{bid}-dead").to_i
74
+ jobs_success = jobs_total - jobs_pending
75
+
76
+ batches_total = r.hget("BID-#{bid}", 'children').to_i
77
+ batches_success = r.scard("BID-#{bid}-batches-success").to_i
78
+ batches_pending = batches_total - batches_success
79
+ batches_failed = r.scard("BID-#{bid}-batches-failed").to_i
80
+
81
+ status = 'in_progress'
82
+ status = 'complete' if batches_pending == batches_failed && jobs_pending == jobs_failed
83
+ status = 'success' if batches_pending == 0 && jobs_pending == 0
84
+ status = 'deleted' if bid != root_bid && !batch.parent_bid
85
+
86
+ {
87
+ bid: bid,
88
+ created_at: r.hget("BID-#{bid}", 'created_at'),
89
+ status: status,
90
+ parent_bid: parent ? parent.bid : batch.parent_bid,
91
+ description: batch.description,
92
+ jobs: {
93
+ pending_count: jobs_pending,
94
+ successful_count: jobs_success,
95
+ failed_count: jobs_failed,
96
+ dead_count: jobs_dead,
97
+ total_count: jobs_total,
98
+ # items: batches.map{|b| layer_data[b] },
99
+ },
100
+ batches: {
101
+ pending_count: batches_pending,
102
+ successful_count: batches_success,
103
+ failed_count: batches_failed,
104
+ total_count: batches_total,
105
+ items: layer[1].map{|b| layer_data[b, batch] },
106
+ },
107
+ }
108
+ }
109
+
110
+ data = layer_data[tree_bids]
111
+ data[:batches][:slice] = slice if slice
112
+ data
113
+ end
114
+ end
115
+
116
+ def format_context(batch)
117
+ bits = []
118
+ own_keys = batch.context.own.keys
119
+ batch.context.flatten.each do |k,v|
120
+ added = own_keys.include? k
121
+ bits << " <span class=\"key #{added ? 'own' : 'inherited'}\">\"#{k}\": #{v.to_json},</span>"
122
+ end
123
+ bits = [
124
+ "{ // <span class=\"own\">Added</span> / <span class=\"inherited\">Inherited</span>",
125
+ *bits,
126
+ '}'
127
+ ]
128
+ bits.join("\n")
129
+ end
130
+ end
131
+
132
+ app.post "/batches/all" do
133
+ if params['delete']
134
+ index_key = Joblin::Batching::Batch::INDEX_ALL_BATCHES ? "batches" : "BID-ROOT-bids"
135
+ drain_zset(index_key) do |batches|
136
+ batches.each do |bid|
137
+ Joblin::Batching::Batch.cleanup_redis(bid)
138
+ end
139
+ end
140
+ end
141
+
142
+ redirect "#{root_path}batches"
143
+ end
144
+
145
+ app.post "/batches/:bid" do
146
+ @bid = params[:bid]
147
+ @batch = Joblin::Batching::Batch.new(@bid)
148
+
149
+ if params['delete']
150
+ Joblin::Batching::Batch.delete_prematurely!(@bid)
151
+ end
152
+
153
+ redirect_with_query("#{root_path}batches")
154
+ end
155
+
156
+ # =============== POOLS =============== #
157
+
158
+ app.get "/pools" do
159
+ @count = (params['count'] || 25).to_i
160
+ @current_page, @total_size, @pools = page('pools', params['page'], @count)
161
+ @pools = @pools.map {|b, score| Joblin::Batching::Pool.new(b) }
162
+
163
+ erb(get_template(:pools))
164
+ end
165
+
166
+ app.get "/pools/:pid" do
167
+ @pid = params[:pid]
168
+ @pool = Joblin::Batching::Pool.new(@pid)
169
+
170
+ @active_tasks = @pool.active_jobs
171
+
172
+ @count = (params['count'] || 25).to_i
173
+ @current_jobs_page, @total_jobs_size, @jobs = page("POOLID-#{@pool.pid}-jobs", params['job_page'], @count)
174
+ @jobs = @jobs.map {|desc, score=nil| JSON.parse(desc)[0] }
175
+
176
+ erb(get_template(:pool))
177
+ end
178
+
179
+ app.post "/pools/all" do
180
+ if params['delete']
181
+ drain_zset('pools') do |pools|
182
+ pools.each do |pid|
183
+ Joblin::Batching::Pool.from_pid(pid).cleanup_redis
184
+ end
185
+ end
186
+ end
187
+
188
+ redirect "#{root_path}pools"
189
+ end
190
+
191
+ app.post "/pools/:pid" do
192
+ @pid = params[:pid]
193
+ @pool = Joblin::Batching::Pool.from_pid(@pid)
194
+
195
+ if params['delete']
196
+ @pool.cleanup_redis
197
+ end
198
+
199
+ redirect_with_query("#{root_path}pools")
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ if defined?(::Sidekiq::Web)
206
+ rules = []
207
+ rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless Joblin::Batching::Compat::Sidekiq::Web::DEV_MODE
208
+
209
+ ::Sidekiq::Web.use Rack::Static, urls: ["/batches_assets"],
210
+ root: File.expand_path("#{File.dirname(__FILE__)}/web"),
211
+ cascade: true,
212
+ header_rules: rules
213
+
214
+ ::Sidekiq::Web.register Joblin::Batching::Compat::Sidekiq::Web
215
+ ::Sidekiq::Web.tabs["Batches"] = "batches"
216
+ ::Sidekiq::Web.tabs["Pools"] = "pools"
217
+ ::Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "locales")
218
+ end