Skip to content

Commit f613489

Browse files
authored
Merge pull request #3 from fsanggang/feature/per_queue_suspend_and_resume
Support suspend and resume per queue
2 parents fc58be7 + 1672efa commit f613489

File tree

7 files changed

+88
-56
lines changed

7 files changed

+88
-56
lines changed

.travis.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
language: elixir
22
elixir:
3-
- 1.5.1
4-
- 1.6.1
53
- 1.7.1
64
- 1.8.1
75
otp_release:
8-
- 19.3
96
- 20.0
7+
- 21.0
8+
9+
env:
10+
- DATABASE_URL=postgres://[email protected]:5432/test
1011

1112
services:
1213
- postgresql

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ You can generate a migration to set up the required db tables with
6666
$ mix honeydew_ecto_notify_queue.db.gen.migration
6767
```
6868

69+
You will need to populate the `job_configs` table with the 'suspended' value yourself, which you can do by editing the migration generated in the previous step. For example:
70+
71+
```
72+
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'suspended', false, now(), now())"
73+
74+
// or, if you have multiple queues and want to be able to suspend and resume them individually
75+
76+
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'my_first_queue_suspended', false, now(), now())"
77+
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'my_second_queue_suspended', false, now(), now())"
78+
```
79+
80+
6981
### Starting the queue
7082

7183
Note: You should read [how to install honeydew here first](https://github.com/koudelka/honeydew)
@@ -109,7 +121,7 @@ def start(_type, _args) do
109121
children = [
110122
# ... The rest of your app's supervision tree
111123
] ++ background_job_processes
112-
124+
113125
Supervisor.start_link(children, opts)
114126
end
115127
```
@@ -119,6 +131,10 @@ end
119131
```bash
120132
$ MIX_ENV=test mix do ecto.create, ecto.migrate
121133
$ mix test
134+
135+
or
136+
137+
$ docker-compose up test
122138
```
123139

124140
## Custom job configuration persistence
@@ -130,5 +146,5 @@ across instances.
130146

131147
An example of this may be the disabling of automatic queuing of a job when an API is hit.
132148

133-
You can see an example of how to listen for configuration changes in
149+
You can see an example of how to listen for configuration changes in
134150
`examples/configuration_listener.ex`

lib/honeydew_ecto_notify_queue.ex

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,45 @@ defmodule HoneydewEctoNotifyQueue do
2929
:config_notification_ref,
3030
:jobs_notification_ref,
3131
:database_suspended,
32-
:quiet_locking_errors
32+
quiet_locking_errors: true,
33+
per_queue_suspension: false
3334
]
3435
end
3536

3637
@impl true
37-
def init(
38-
queue_name,
39-
[
40-
repo: repo,
41-
max_job_time: max_job_time,
42-
retry_seconds: retry_seconds,
43-
notifier: notifier
44-
] = opts
45-
) do
38+
@spec init(String.t(), list()) :: {:ok, %QState{}}
39+
def init(queue_name, opts) when is_list(opts) do
40+
allowed_opts_map =
41+
opts
42+
|> Map.new()
43+
|> Map.take([
44+
:repo,
45+
:max_job_time,
46+
:retry_seconds,
47+
:notifier,
48+
:quiet_locking_errors,
49+
:per_queue_suspension
50+
])
51+
52+
%{repo: _, max_job_time: _, retry_seconds: _, notifier: _} = allowed_opts_map
53+
54+
do_init(queue_name, allowed_opts_map)
55+
end
56+
57+
@spec do_init(String.t(), map()) :: {:ok, %QState{}}
58+
defp do_init(queue_name, %{notifier: notifier} = opts) do
4659
{:ok, config_notification_ref} = start_config_notifier(notifier)
4760
{:ok, jobs_notification_ref} = start_jobs_notifier(notifier)
4861

49-
quiet_locking_errors = Keyword.get(opts, :quiet_locking_errors, true)
50-
51-
state = %QState{
52-
repo: repo,
53-
queue_name: queue_name,
54-
max_job_time: max_job_time,
55-
retry_seconds: retry_seconds,
56-
config_notification_ref: config_notification_ref,
57-
jobs_notification_ref: jobs_notification_ref,
58-
quiet_locking_errors: quiet_locking_errors
59-
}
60-
61-
state = refresh_config(state)
62+
state =
63+
%QState{}
64+
|> Map.merge(opts)
65+
|> Map.merge(%{
66+
queue_name: queue_name,
67+
config_notification_ref: config_notification_ref,
68+
jobs_notification_ref: jobs_notification_ref
69+
})
70+
|> refresh_config()
6271

6372
{:ok, state}
6473
end
@@ -350,19 +359,18 @@ defmodule HoneydewEctoNotifyQueue do
350359
|> Honeydew.Job.from_record()
351360
end
352361

353-
defp refresh_config(state) do
354-
with {:ok, config} <-
355-
HoneydewEctoNotifyQueue.Config.get_config(state.repo, JobConfig.suspended_key()) do
356-
suspended = config.value == "true"
357-
358-
case suspended do
359-
true ->
360-
debug_log("Synchronised queue status to suspended")
361-
Honeydew.suspend(state.queue_name)
362-
363-
false ->
364-
debug_log("Synchronised queue status to resumed")
365-
Honeydew.resume(state.queue_name)
362+
@spec refresh_config(%QState{}) :: :ok | {:error, any()}
363+
defp refresh_config(%QState{repo: repo, queue_name: queue_name, per_queue_suspension: per_queue_suspension} = state) do
364+
with suspended_key <- JobConfig.suspended_key(queue_name, per_queue_suspension),
365+
{:ok, config} <- HoneydewEctoNotifyQueue.Config.get_config(repo, suspended_key) do
366+
suspended = String.to_existing_atom(config.value)
367+
368+
if suspended do
369+
debug_log("Synchronised queue status to suspended")
370+
Honeydew.suspend(queue_name)
371+
else
372+
debug_log("Synchronised queue status to resumed")
373+
Honeydew.resume(queue_name)
366374
end
367375

368376
Map.put(state, :database_suspended, suspended)

lib/honeydew_ecto_notify_queue/jobs/job_config.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ defmodule HoneydewEctoNotifyQueue.JobConfig do
2020
|> validate_required([:key, :value])
2121
end
2222

23-
def suspended_key, do: "suspended"
23+
@spec suspended_key(String.t(), boolean()) :: String.t()
24+
def suspended_key(_, false), do: "suspended"
25+
def suspended_key(queue_name, true), do: "#{queue_name}_suspended"
2426
end

priv/repo/migrations/20180702161041_honeydew_ecto_notify_jobs.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ defmodule HoneydewEctoNotifyQueue.Repo.Migrations.CreateHoneydewEctoNotifyTables
3131
create unique_index(:job_configs, [:key], using: :btree)
3232

3333
execute "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""
34-
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'suspended', false, now(), now())"
34+
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'my_queue_suspended', false, now(), now())"
3535

3636
execute """
3737
CREATE FUNCTION f_notify_config_change()

priv/templates/migration.exs.eex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ defmodule <%= module_prefix %>.Repo.Migrations.CreateHoneydewEctoNotifyTables do
3131
create unique_index(:job_configs, [:key], using: :btree)
3232

3333
execute "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""
34-
execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'suspended', false, now(), now())"
34+
35+
# Uncomment the following line or replace with your own custom suspended key
36+
# execute "INSERT INTO job_configs VALUES (uuid_generate_v4(), 'suspended', false, now(), now())"
3537

3638
execute """
3739
CREATE FUNCTION f_notify_config_change()

test/honeydew_ecto_notify_queue_integration_test.exs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,23 @@ defmodule HoneydewEctoNotifyQueueIntegrationTest do
3434
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
3535
Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()})
3636

37-
queue = :"#{:erlang.monotonic_time()}_#{:erlang.unique_integer()}"
37+
queue = :my_queue
3838

3939
spec =
40-
Honeydew.queue_spec(queue,
41-
queue:
42-
{HoneydewEctoNotifyQueue,
43-
[
44-
repo: HoneydewEctoNotifyQueue.Repo,
45-
# seconds
46-
max_job_time: 3_600,
47-
# seconds,
48-
retry_seconds: 15,
49-
notifier: Notifier
50-
]},
40+
Honeydew.queue_spec(
41+
queue,
42+
queue: {
43+
HoneydewEctoNotifyQueue,
44+
[
45+
repo: HoneydewEctoNotifyQueue.Repo,
46+
# seconds
47+
max_job_time: 3_600,
48+
# seconds,
49+
retry_seconds: 15,
50+
notifier: Notifier,
51+
per_queue_suspension: true
52+
]
53+
},
5154
failure_mode: {Honeydew.FailureMode.Retry, times: 3}
5255
)
5356

0 commit comments

Comments
 (0)