@@ -5,7 +5,8 @@ defmodule ElixirQueue.WorkerPool do
5
5
alias ElixirQueue . {
6
6
WorkerSupervisor ,
7
7
WorkerPool ,
8
- Worker
8
+ Worker ,
9
+ Queue
9
10
}
10
11
11
12
def start_link ( opts ) do
@@ -17,60 +18,65 @@ defmodule ElixirQueue.WorkerPool do
17
18
@ spec init ( any ) :: { :ok , % { failed_jobs: [ ] , pids: [ ] , successful_jobs: [ ] } }
18
19
def init ( _opts ) do
19
20
pids =
20
- Fail proff for _ <- 1 .. Application . fetch_env! ( :elixir_queue , :workers ) do
21
+ for _ <- 1 .. Application . fetch_env! ( :elixir_queue , :workers ) do
21
22
{ :ok , pid } = DynamicSupervisor . start_child ( WorkerSupervisor , Worker )
22
- ref = Process . monitor ( pid )
23
- { pid , ref }
23
+ Process . monitor ( pid )
24
+ pid
24
25
end
25
26
27
+ :ets . new ( :worker_backup , [ :set , :protected , :named_table ] )
28
+
26
29
{ :ok , % { pids: pids , successful_jobs: [ ] , failed_jobs: [ ] } }
27
30
end
28
31
29
- # Server side functions
30
32
@ impl true
31
- def handle_call ( :workers , _from , state ) , do: { :reply , Enum . map ( state . pids , & elem ( & 1 , 0 ) ) , state }
33
+ def handle_call ( :workers , _from , state ) do
34
+ { :reply , state . pids , state }
35
+ end
32
36
33
37
def handle_call ( :failed_jobs , _from , state ) , do: { :reply , state . failed_jobs , state }
34
38
35
39
def handle_call ( :successful_jobs , _from , state ) , do: { :reply , state . successful_jobs , state }
36
40
37
- def handle_call ( { :add_successful_job , worker , job , result } , _from , state ) ,
38
- do: {
39
- :reply ,
40
- :ok ,
41
- Map . put ( state , :successful_jobs , [ { worker , job , result } | state . successful_jobs ] )
42
- }
41
+ def handle_call ( { :add_successful_job , worker , job , result } , _from , state ) do
42
+ { :reply , :ok ,
43
+ Map . put ( state , :successful_jobs , [ { worker , job , result } | state . successful_jobs ] ) }
44
+ end
43
45
44
- def handle_call ( { :add_failed_job , worker , job , err } , _from , state ) ,
45
- do: { :reply , :ok , Map . put ( state , :failed_jobs , [ { worker , job , err } | state . failed_jobs ] ) }
46
+ def handle_call ( { :backup_worker , worker , job } , _from , state ) do
47
+ :ets . insert ( :worker_backup , { worker , job } )
48
+ { :reply , :ok , state }
49
+ end
50
+
51
+ def handle_call ( { :clean_worker_backup , worker } , _from , state ) do
52
+ :ets . delete ( :worker_backup , worker )
53
+ { :reply , :ok , state }
54
+ end
46
55
47
56
@ impl true
48
- def handle_info ( { :DOWN , _ref , :process , dead_worker , reason } , state ) do
49
- { :ok , worker } = DynamicSupervisor . start_child ( WorkerSupervisor , Worker )
50
- worker_reference = Process . monitor ( worker )
57
+ def handle_info ( { :DOWN , _ , :process , dead_worker , reason } , state ) do
58
+ { :ok , pid } = DynamicSupervisor . start_child ( WorkerSupervisor , Worker )
59
+ Process . monitor ( pid )
51
60
pids = Enum . filter ( state . pids , & ( & 1 != dead_worker ) )
52
61
53
- reason |> IO . inspect ( label: "reason" )
62
+ { ^ dead_worker , backuped_job } =
63
+ :ets . lookup ( :worker_backup , dead_worker )
64
+ |> List . first ( )
54
65
55
- unless Mix . env ( ) == :test ,
56
- do: Logger . error ( "Unexpected worker error:
57
- Worker #{ inspect ( dead_worker ) } received EXIT SIGNAL.
58
- It have been replaced by #{ inspect ( worker ) } worker.
59
- All the job progress was lost and job failed.
60
- By default job returned to the end of queue and will be performed again later.
61
- " )
66
+ backuped_job = Map . put ( backuped_job , :retry_attempts , backuped_job . retry_attempts + 1 )
62
67
63
- { :noreply , Map . put ( state , :pids , [ { worker , worker_reference } | pids ] ) }
64
- end
68
+ state =
69
+ state
70
+ |> Map . put ( :pids , [ pid | pids ] )
71
+ |> Map . put ( :failed_jobs , [ { dead_worker , backuped_job , reason } | state . failed_jobs ] )
65
72
66
- def handle_info ( _msg , state ) ,
67
- do: { :noreply , state }
73
+ if backuped_job . retry_attempts < Application . fetch_env! ( :elixir_queue , :retries ) ,
74
+ do: Queue . perform_later ( backuped_job )
68
75
69
- # Client side functions #
76
+ { :noreply , state }
77
+ end
70
78
71
- @ doc """
72
- Returns _workers_ `PID`s kept in the state.
73
- """
79
+ # Client side functions
74
80
@ spec workers :: list ( )
75
81
def workers , do: GenServer . call ( __MODULE__ , :workers )
76
82
@@ -83,13 +89,17 @@ Fail proff for _ <- 1..Application.fetch_env!(:elixir_queue, :workers) do
83
89
@ spec successful_jobs :: list ( )
84
90
def successful_jobs , do: GenServer . call ( __MODULE__ , :successful_jobs )
85
91
86
- @ spec add_successful_job ( pid ( ) , ElixirQueue.Job . t ( ) , any ) :: :ok
87
- def add_successful_job ( worker , job , result ) ,
92
+ @ spec add_successful_job ( { pid ( ) , ElixirQueue.Job . t ( ) , any } ) :: :ok
93
+ def add_successful_job ( { worker , job , result } ) ,
88
94
do: GenServer . call ( __MODULE__ , { :add_successful_job , worker , job , result } )
89
95
90
- @ spec add_failed_job ( pid ( ) , ElixirQueue.Job . t ( ) , any ) :: :ok
91
- def add_failed_job ( worker , job , err ) ,
92
- do: GenServer . call ( __MODULE__ , { :add_failed_job , worker , job , err } )
96
+ @ spec backup_worker ( pid ( ) , ElixirQueue.Job . t ( ) ) :: true
97
+ def backup_worker ( worker , job ) ,
98
+ do: GenServer . call ( __MODULE__ , { :backup_worker , worker , job } )
99
+
100
+ @ spec clean_worker_backup ( pid ( ) ) :: true
101
+ def clean_worker_backup ( worker ) ,
102
+ do: GenServer . call ( __MODULE__ , { :clean_worker_backup , worker } )
93
103
94
104
@ spec idle_worker :: pid ( )
95
105
def idle_worker do
@@ -102,10 +112,9 @@ Fail proff for _ <- 1..Application.fetch_env!(:elixir_queue, :workers) do
102
112
@ spec perform ( ElixirQueue.Job . t ( ) ) :: no_return ( )
103
113
def perform ( job ) do
104
114
worker = WorkerPool . idle_worker ( )
105
-
106
115
Task . start ( fn ->
107
- { :ok , result } = Worker . perform ( worker , job )
108
- WorkerPool . add_successful_job ( worker , job , result )
116
+ Worker . perform ( worker , job )
117
+ |> WorkerPool . add_successful_job ( )
109
118
end )
110
119
end
111
120
end
0 commit comments