Skip to content

Commit 3d7809e

Browse files
committed
perf: move campaign cache from socket to ETS
1 parent 7d8a6f6 commit 3d7809e

File tree

2 files changed

+84
-68
lines changed

2 files changed

+84
-68
lines changed

lib/algora/application.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ defmodule Algora.Application do
3636
tunnel -> children ++ [{Algora.Tunnel, tunnel}]
3737
end
3838

39+
# Start the ETS tables
40+
AlgoraWeb.Admin.CampaignLive.start_link()
41+
3942
# See https://hexdocs.pm/elixir/Supervisor.html
4043
# for other strategies and supported options
4144
opts = [strategy: :one_for_one, name: Algora.Supervisor]

lib/algora_web/live/admin/campaign_live.ex

Lines changed: 81 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ defmodule AlgoraWeb.Admin.CampaignLive do
2020

2121
require Logger
2222

23+
@repo_cache_table :campaign_repo_cache
24+
@user_cache_table :campaign_user_cache
25+
2326
defmodule Campaign do
2427
@moduledoc false
2528
use Ecto.Schema
@@ -45,6 +48,11 @@ defmodule AlgoraWeb.Admin.CampaignLive do
4548
end
4649
end
4750

51+
def start_link do
52+
:ets.new(@repo_cache_table, [:named_table, :set, :public])
53+
:ets.new(@user_cache_table, [:named_table, :set, :public])
54+
end
55+
4856
@impl true
4957
def mount(_params, _session, socket) do
5058
timezone = if(params = get_connect_params(socket), do: params["timezone"])
@@ -54,8 +62,6 @@ defmodule AlgoraWeb.Admin.CampaignLive do
5462
|> assign(:timezone, timezone)
5563
|> assign(:page_title, "Campaign")
5664
|> assign(:form, to_form(Campaign.changeset(%Campaign{})))
57-
|> assign(:repo_cache, %{})
58-
|> assign(:user_cache, %{})
5965
|> assign_preview()}
6066
end
6167

@@ -271,30 +277,33 @@ defmodule AlgoraWeb.Admin.CampaignLive do
271277
socket.assigns.csv_data
272278
|> Enum.map(&Map.get(&1, "email"))
273279
|> Enum.uniq()
280+
|> Enum.reject(fn key -> :ets.lookup(@user_cache_table, key) != [] end)
274281

275-
new_cache =
276-
Map.new(new_keys, fn key ->
277-
user = Accounts.get_user_by_email(key)
282+
Enum.each(new_keys, fn key ->
283+
user = Accounts.get_user_by_email(key)
278284

285+
timestamp =
279286
if user do
280-
{key, Util.next_occurrence_of_time(user.last_active_at || user.inserted_at)}
287+
Util.next_occurrence_of_time(user.last_active_at || user.inserted_at)
281288
else
282-
{key, Util.next_occurrence_of_time(Util.random_datetime())}
289+
Util.next_occurrence_of_time(Util.random_datetime())
283290
end
284-
end)
285291

286-
updated_cache = Map.merge(socket.assigns.user_cache, new_cache)
292+
:ets.insert(@user_cache_table, {key, timestamp})
293+
end)
287294

288295
csv_data =
289-
Enum.map(socket.assigns.csv_data, fn row -> Map.put(row, "timestamp", Map.get(updated_cache, row["email"])) end)
296+
Enum.map(socket.assigns.csv_data, fn row ->
297+
[{_, timestamp}] = :ets.lookup(@user_cache_table, row["email"])
298+
Map.put(row, "timestamp", timestamp)
299+
end)
290300

291301
csv_columns =
292302
csv_data
293303
|> Enum.flat_map(&Map.keys/1)
294304
|> Enum.uniq()
295305

296306
socket
297-
|> assign(:repo_cache, updated_cache)
298307
|> assign(:csv_data, csv_data)
299308
|> assign(:csv_columns, csv_columns)
300309
end
@@ -304,66 +313,34 @@ defmodule AlgoraWeb.Admin.CampaignLive do
304313
socket.assigns.csv_data
305314
|> Enum.map(&repo_key/1)
306315
|> Enum.reject(&is_nil/1)
307-
|> Enum.reject(&Map.has_key?(socket.assigns.repo_cache, &1))
316+
|> Enum.reject(fn key -> :ets.lookup(@repo_cache_table, key) != [] end)
308317
|> Enum.uniq()
309318

310-
new_cache =
311-
Map.new(new_keys, fn key ->
312-
filter =
313-
case key do
314-
{owner, name} ->
315-
token = Admin.token()
316-
317-
with {:ok, repository} <- Workspace.ensure_repository(token, owner, name),
318-
{:ok, _tech_stack} <- Workspace.ensure_repo_tech_stack(token, repository) do
319-
dynamic([r, _u], r.id == ^repository.id)
320-
else
321-
_ -> false
322-
end
323-
324-
org_handle ->
325-
dynamic([r, u], u.handle == ^org_handle)
326-
end
327-
328-
repo =
329-
Repo.one(
330-
from r in Repository,
331-
join: u in assoc(r, :user),
332-
where: ^filter,
333-
order_by: [desc: fragment("(?->>'stargazers_count')::integer", r.provider_meta)],
334-
select: %{
335-
repo_owner: u.provider_login,
336-
repo_name: r.name,
337-
tech_stack: fragment("COALESCE(NULLIF(?, '{}'), ?)", u.tech_stack, r.tech_stack)
338-
},
339-
limit: 1
340-
)
341-
342-
if repo && repo.tech_stack != [] do
343-
matches = Settings.get_tech_matches(List.first(repo.tech_stack))
344-
{key, {repo, matches}}
345-
else
346-
{key, nil}
347-
end
348-
end)
349-
350-
updated_cache = Map.merge(socket.assigns.repo_cache, new_cache)
319+
Enum.each(new_keys, fn key ->
320+
cache_value = fetch_repo_data(key)
321+
:ets.insert(@repo_cache_table, {key, cache_value})
322+
end)
351323

352324
csv_data =
353-
Enum.map(socket.assigns.csv_data, fn
354-
row ->
355-
case Map.get(updated_cache, repo_key(row)) do
356-
{repo, matches} ->
357-
Map.merge(row, %{
358-
"repo_owner" => repo.repo_owner,
359-
"repo_name" => repo.repo_name,
360-
"tech_stack" => repo.tech_stack,
361-
"matches" => Enum.map(matches, & &1.user.handle)
362-
})
363-
364-
_ ->
365-
row
366-
end
325+
Enum.map(socket.assigns.csv_data, fn row ->
326+
case repo_key(row) do
327+
nil ->
328+
row
329+
330+
key ->
331+
case :ets.lookup(@repo_cache_table, key) do
332+
[{_, {repo, matches}}] ->
333+
Map.merge(row, %{
334+
"repo_owner" => repo.repo_owner,
335+
"repo_name" => repo.repo_name,
336+
"tech_stack" => repo.tech_stack,
337+
"matches" => Enum.map(matches, & &1.user.handle)
338+
})
339+
340+
_ ->
341+
row
342+
end
343+
end
367344
end)
368345

369346
csv_columns =
@@ -372,11 +349,47 @@ defmodule AlgoraWeb.Admin.CampaignLive do
372349
|> Enum.uniq()
373350

374351
socket
375-
|> assign(:repo_cache, updated_cache)
376352
|> assign(:csv_data, csv_data)
377353
|> assign(:csv_columns, csv_columns)
378354
end
379355

356+
defp fetch_repo_data(key) do
357+
filter =
358+
case key do
359+
{owner, name} ->
360+
token = Admin.token()
361+
362+
with {:ok, repository} <- Workspace.ensure_repository(token, owner, name),
363+
{:ok, _tech_stack} <- Workspace.ensure_repo_tech_stack(token, repository) do
364+
dynamic([r, _u], r.id == ^repository.id)
365+
else
366+
_ -> false
367+
end
368+
369+
org_handle ->
370+
dynamic([r, u], u.handle == ^org_handle)
371+
end
372+
373+
repo =
374+
Repo.one(
375+
from r in Repository,
376+
join: u in assoc(r, :user),
377+
where: ^filter,
378+
order_by: [desc: fragment("(?->>'stargazers_count')::integer", r.provider_meta)],
379+
select: %{
380+
repo_owner: u.provider_login,
381+
repo_name: r.name,
382+
tech_stack: fragment("COALESCE(NULLIF(?, '{}'), ?)", u.tech_stack, r.tech_stack)
383+
},
384+
limit: 1
385+
)
386+
387+
if repo && repo.tech_stack != [] do
388+
matches = Settings.get_tech_matches(List.first(repo.tech_stack))
389+
{repo, matches}
390+
end
391+
end
392+
380393
defp assign_csv_data(socket, data) do
381394
csv_data =
382395
case String.trim(data) do

0 commit comments

Comments
 (0)