stop spawning off for advert updates

This commit is contained in:
byte[] 2020-05-27 17:14:22 -04:00
parent dd7aca809a
commit cc4103fcea
5 changed files with 100 additions and 28 deletions

View file

@ -19,7 +19,6 @@ defmodule Philomena.Adverts do
|> order_by(asc: fragment("random()")) |> order_by(asc: fragment("random()"))
|> limit(1) |> limit(1)
|> Repo.one() |> Repo.one()
|> record_impression()
end end
def random_live_for(image) do def random_live_for(image) do
@ -33,25 +32,6 @@ defmodule Philomena.Adverts do
|> order_by(asc: fragment("random()")) |> order_by(asc: fragment("random()"))
|> limit(1) |> limit(1)
|> Repo.one() |> Repo.one()
|> record_impression()
end
def click(%Advert{} = advert) do
spawn(fn ->
query = where(Advert, id: ^advert.id)
Repo.update_all(query, inc: [clicks: 1])
end)
end
defp record_impression(nil), do: nil
defp record_impression(advert) do
spawn(fn ->
query = where(Advert, id: ^advert.id)
Repo.update_all(query, inc: [impressions: 1])
end)
advert
end end
defp sfw?(image) do defp sfw?(image) do

View file

@ -17,6 +17,9 @@ defmodule Philomena.Application do
# Start the Ecto repository # Start the Ecto repository
Philomena.Repo, Philomena.Repo,
# Background queueing system
Philomena.ExqSupervisor,
# Starts a worker by calling: Philomena.Worker.start_link(arg) # Starts a worker by calling: Philomena.Worker.start_link(arg)
# {Philomena.Worker, arg}, # {Philomena.Worker, arg},
Philomena.Servers.ImageProcessor, Philomena.Servers.ImageProcessor,
@ -28,6 +31,7 @@ defmodule Philomena.Application do
{Phoenix.PubSub, [name: Philomena.PubSub, adapter: Phoenix.PubSub.PG2]}, {Phoenix.PubSub, [name: Philomena.PubSub, adapter: Phoenix.PubSub.PG2]},
# Start the endpoint when the application starts # Start the endpoint when the application starts
PhilomenaWeb.AdvertUpdater,
PhilomenaWeb.StatsUpdater, PhilomenaWeb.StatsUpdater,
PhilomenaWeb.UserFingerprintUpdater, PhilomenaWeb.UserFingerprintUpdater,
PhilomenaWeb.UserIpUpdater, PhilomenaWeb.UserIpUpdater,

View file

@ -0,0 +1,81 @@
defmodule PhilomenaWeb.AdvertUpdater do
alias Philomena.Adverts.Advert
alias Philomena.Repo
import Ecto.Query
def child_spec([]) do
%{
id: PhilomenaWeb.AdvertUpdater,
start: {PhilomenaWeb.AdvertUpdater, :start_link, [[]]}
}
end
def start_link([]) do
{:ok, spawn_link(&init/0)}
end
def cast(type, advert_id) when type in [:impression, :click] do
pid = Process.whereis(:advert_updater)
if pid, do: send(pid, {type, advert_id})
end
defp init do
Process.register(self(), :advert_updater)
run()
end
defp run do
# Read impression counts from mailbox
{impressions, clicks} = receive_all()
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
# Create insert statements for Ecto
impressions = Enum.map(impressions, &impressions_insert_all(&1, now))
clicks = Enum.map(clicks, &clicks_insert_all(&1, now))
# Merge into table
impressions_update = update(Advert, inc: [impressions: fragment("EXCLUDED.impressions")])
clicks_update = update(Advert, inc: [clicks: fragment("EXCLUDED.clicks")])
Repo.insert_all(Advert, impressions, on_conflict: impressions_update, conflict_target: [:id])
Repo.insert_all(Advert, clicks, on_conflict: clicks_update, conflict_target: [:id])
:timer.sleep(:timer.seconds(10))
run()
end
defp receive_all(impressions \\ %{}, clicks \\ %{}) do
receive do
{:impression, advert_id} ->
impressions = Map.update(impressions, advert_id, 1, &(&1 + 1))
receive_all(impressions, clicks)
{:click, advert_id} ->
clicks = Map.update(clicks, advert_id, 1, &(&1 + 1))
receive_all(impressions, clicks)
after
0 ->
{impressions, clicks}
end
end
defp impressions_insert_all({advert_id, impressions}, now) do
%{
id: advert_id,
impressions: impressions,
created_at: now,
updated_at: now
}
end
defp clicks_insert_all({advert_id, clicks}, now) do
%{
id: advert_id,
clicks: clicks,
created_at: now,
updated_at: now
}
end
end

View file

@ -1,7 +1,7 @@
defmodule PhilomenaWeb.AdvertController do defmodule PhilomenaWeb.AdvertController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias Philomena.Adverts alias PhilomenaWeb.AdvertUpdater
alias Philomena.Adverts.Advert alias Philomena.Adverts.Advert
plug :load_resource, model: Advert plug :load_resource, model: Advert
@ -9,9 +9,8 @@ defmodule PhilomenaWeb.AdvertController do
def show(conn, _params) do def show(conn, _params) do
advert = conn.assigns.advert advert = conn.assigns.advert
Adverts.click(advert) AdvertUpdater.cast(:click, advert.id)
conn redirect(conn, external: advert.link)
|> redirect(external: advert.link)
end end
end end

View file

@ -1,4 +1,5 @@
defmodule PhilomenaWeb.AdvertPlug do defmodule PhilomenaWeb.AdvertPlug do
alias PhilomenaWeb.AdvertUpdater
alias Philomena.Adverts alias Philomena.Adverts
alias Plug.Conn alias Plug.Conn
@ -9,15 +10,14 @@ defmodule PhilomenaWeb.AdvertPlug do
image = conn.assigns[:image] image = conn.assigns[:image]
show_ads? = show_ads?(user) show_ads? = show_ads?(user)
conn maybe_assign_ad(conn, image, show_ads?)
|> maybe_assign_ad(image, show_ads?)
end end
defp maybe_assign_ad(conn, nil, true), defp maybe_assign_ad(conn, nil, true),
do: Conn.assign(conn, :advert, Adverts.random_live()) do: Conn.assign(conn, :advert, record_impression(Adverts.random_live()))
defp maybe_assign_ad(conn, image, true), defp maybe_assign_ad(conn, image, true),
do: Conn.assign(conn, :advert, Adverts.random_live_for(image)) do: Conn.assign(conn, :advert, record_impression(Adverts.random_live_for(image)))
defp maybe_assign_ad(conn, _image, _false), defp maybe_assign_ad(conn, _image, _false),
do: conn do: conn
@ -27,4 +27,12 @@ defmodule PhilomenaWeb.AdvertPlug do
defp show_ads?(_user), defp show_ads?(_user),
do: true do: true
defp record_impression(nil), do: nil
defp record_impression(advert) do
AdvertUpdater.cast(:impression, advert.id)
advert
end
end end