Skip to content

Instantly share code, notes, and snippets.

@v0idpwn
Created May 21, 2022 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save v0idpwn/dc621ca348837a590dac01fd909126e2 to your computer and use it in GitHub Desktop.
Save v0idpwn/dc621ca348837a590dac01fd909126e2 to your computer and use it in GitHub Desktop.
Mix.install([
{:ecto_sql, "~> 3.7.0"},
{:postgrex, "~> 0.15.0"}
])
Application.put_env(:foo, Repo, database: "mix_install_examples", password: "postgres", username: "postgres")
defmodule Repo do
use Ecto.Repo,
adapter: Ecto.Adapters.Postgres,
otp_app: :foo
end
defmodule Migration0 do
use Ecto.Migration
def change do
create table("posts") do
add(:title, :string)
timestamps(type: :utc_datetime_usec)
end
create unique_index("posts", :title)
end
end
defmodule Post do
use Ecto.Schema
schema "posts" do
field(:title, :string)
timestamps(type: :utc_datetime_usec)
end
end
defmodule Main do
def setup do
children = [Repo]
_ = Repo.__adapter__().storage_down(Repo.config())
:ok = Repo.__adapter__().storage_up(Repo.config())
{:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
Ecto.Migrator.run(Repo, [{0, Migration0}], :up, all: true, log_migrations_sql: true)
end
defp send_and_print(pid, message) do
IO.puts "#{inspect(self())} sent #{inspect(message)} to #{inspect(pid)}"
send(pid, message)
end
def conflict do
parent = self()
p1 = spawn(fn ->
Repo.transaction(fn ->
receive do
{:start, p2} ->
Repo.insert(%Post{title: "foo"})
send_and_print(p2, :step1)
receive do
:step2 -> :ok
end
# Here it is locked, waiting for p2's transaction to commit, because bar
# is locked
Repo.insert(%Post{title: "bar"})
send(p2, :step3)
end
end)
end)
_p2 = spawn(fn ->
Repo.transaction(fn ->
send_and_print(p1, {:start, self()})
receive do
:step1 -> :ok
end
# Acquiring lock inserting bar
Repo.insert(%Post{title: "bar"})
send_and_print(p1, :step2)
# This insert will wait for lock, since "foo" was already inserted on p1
# But p1 will never release the lock, since its waiting for `bar` to be
# released
Repo.insert(%Post{title: "foo"})
send_and_print(parent, :end)
end)
end)
receive do
:end -> :ok
end
end
end
Main.setup()
Main.conflict()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment