Skip to content

Commit ccfe4fa

Browse files
committed
Code for step 5
1 parent 7effd27 commit ccfe4fa

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

lib/elixir_popularity/application.ex

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ defmodule ElixirPopularity.Application do
1212
%{
1313
id: ElixirPopularity.RMQPublisher,
1414
start: {ElixirPopularity.RMQPublisher, :start_link, []}
15+
},
16+
%{
17+
id: ElixirPopularity.HackerNewsIdGenerator,
18+
start:
19+
{ElixirPopularity.HackerNewsIdGenerator, :start_link,
20+
[
21+
%{
22+
current_id: 2_306_006,
23+
end_id: 21_672_858,
24+
generate_threshold: 50_000,
25+
batch_size: 30_000,
26+
poll_rate: 30_000
27+
}
28+
]},
29+
restart: :transient
1530
}
1631
]
1732

lib/hn_id_generator.ex

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
defmodule ElixirPopularity.HackerNewsIdGenerator do
2+
use GenServer
3+
4+
require Logger
5+
6+
alias ElixirPopularity.RMQPublisher
7+
8+
# ---------------- Initialization Functions ----------------
9+
def start_link(state) do
10+
GenServer.start_link(__MODULE__, state, name: __MODULE__)
11+
end
12+
13+
@impl true
14+
def init(options) do
15+
state = Map.put(options, :timer_ref, nil)
16+
17+
{:ok, state}
18+
end
19+
20+
# ---------------- Public Functions ----------------
21+
def start_generating, do: GenServer.call(__MODULE__, :start_generating)
22+
23+
def stop_generating, do: GenServer.call(__MODULE__, :stop_generating)
24+
25+
# ---------------- Callback Functions ----------------
26+
@impl true
27+
def handle_info(:poll_queue_size, %{current_id: current_id, end_id: end_id} = state) when current_id > end_id do
28+
# All done with the configured work, nothing else to do
29+
Logger.info("No more HackerNews IDs to generate. My work is done. I can rest now.")
30+
31+
{:stop, :normal, state}
32+
end
33+
34+
def handle_info(:poll_queue_size, state) do
35+
# Get the size of the queue using the GenRMQ module
36+
queue_size = RMQPublisher.hn_id_queue_size()
37+
38+
# Determine if more HackerNews items IDs need to be put on the queue
39+
new_current_id =
40+
if queue_size < state.generate_threshold do
41+
upper_range = min(state.current_id + state.batch_size, state.end_id)
42+
43+
Logger.info("Enqueuing HackerNews items #{state.current_id} - #{upper_range}")
44+
45+
# Publish all the new HackerNews items IDs
46+
state.current_id..upper_range
47+
|> Enum.each(fn hn_id ->
48+
RMQPublisher.publish_hn_id("#{hn_id}")
49+
end)
50+
51+
upper_range + 1
52+
else
53+
Logger.info("Queue size of #{queue_size} is greater than the threshold of #{state.generate_threshold}")
54+
55+
state.current_id
56+
end
57+
58+
new_state =
59+
state
60+
|> Map.put(:current_id, new_current_id)
61+
|> Map.put(:timer_ref, schedule_next_poll(state.poll_rate))
62+
63+
{:noreply, new_state}
64+
end
65+
66+
@impl true
67+
def handle_call(:start_generating, _from, state) do
68+
send(self(), :poll_queue_size)
69+
70+
{:reply, :ok, state}
71+
end
72+
73+
@impl true
74+
def handle_call(:stop_generating, _from, state) do
75+
Process.cancel_timer(state.timer_ref)
76+
new_state = %{state | timer_ref: nil}
77+
78+
{:reply, :ok, new_state}
79+
end
80+
81+
defp schedule_next_poll(poll_rate) do
82+
Logger.info("Scheduling next queue poll")
83+
84+
Process.send_after(self(), :poll_queue_size, poll_rate)
85+
end
86+
end

0 commit comments

Comments
 (0)