Reputation: 67968
I am new to Phoenix and I was just playing around in iex when I noticed this happened. I was trying to insert a record and it had a validation error. After successfully inserting it, I found out the ID was not the next expected ID, so in my database it just has 2 records with ID 1 and 4.
Here is a log for example:
This is the first error, and notice the ID is 2.
iex(7)> Test.Projects.create_project(%{name: "Test", user: user})
[debug] QUERY ERROR db=3.6ms queue=6.4ms idle=1633.0ms
INSERT INTO "projects" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Test", ~N[2021-12-15 09:37:46], ~N[2021-12-15 09:37:46]]
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "user_id" of relation "projects" violates not-null constraint
table: projects
column: user_id
Failing row contains (2, Test, null, 2021-12-15 09:37:46, 2021-12-15 09:37:46).
(ecto_sql 3.7.1) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.7.1) lib/ecto/repo/schema.ex:744: Ecto.Repo.Schema.apply/4
(ecto 3.7.1) lib/ecto/repo/schema.ex:367: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
This is the second error, and notice the ID is 3.
iex(7)> user.id
1
iex(8)> Test.Projects.create_project(%{name: "Test", user_id: user.id})
[debug] QUERY ERROR db=2.1ms queue=2.1ms idle=1264.9ms
INSERT INTO "projects" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Test", ~N[2021-12-15 09:38:02], ~N[2021-12-15 09:38:02]]
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "user_id" of relation "projects" violates not-null constraint
table: projects
column: user_id
Failing row contains (3, Test, null, 2021-12-15 09:38:02, 2021-12-15 09:38:02).
(ecto_sql 3.7.1) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.7.1) lib/ecto/repo/schema.ex:744: Ecto.Repo.Schema.apply/4
(ecto 3.7.1) lib/ecto/repo/schema.ex:367: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
This was a successful insert, but notice it's ID is 4. I would have expected 2.
iex(8)> recompile
Compiling 2 files (.ex)
:ok
iex(9)> Test.Projects.create_project(%{name: "Test", user_id: user.id})
[debug] QUERY OK db=9.7ms queue=5.4ms idle=1907.0ms
INSERT INTO "projects" ("name","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" ["Test", 1, ~N[2021-12-15 09:38:26], ~N[2021-12-15 09:38:26]]
{:ok,
%Test.Projects.Project{
__meta__: #Ecto.Schema.Metadata<:loaded, "projects">,
id: 4,
inserted_at: ~N[2021-12-15 09:38:26],
name: "Test",
updated_at: ~N[2021-12-15 09:38:26],
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}}
Upvotes: 2
Views: 353
Reputation: 3047
In short, this is expected behavior from the database to avoid "... To avoid blocking concurrent transactions..."
If you log into Postgres and do a \d+ <table_name>
on your table, you should see something like this:
my_db=# \d+ accounts
Table "public.accounts"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-------------+--------------------------------+-----------+----------+--------------------------------------+----------+--------------+-------------
id | bigint | | not null | nextval('accounts_id_seq'::regclass) | plain | |
name | character varying(255) | | | | extended | |
balance | integer | | | | plain | |
inserted_at | timestamp(0) without time zone | | not null | | plain | |
updated_at | timestamp(0) without time zone | | not null | | plain | |
Indexes:
"accounts_pkey" PRIMARY KEY, btree (id)
"accounts_name_index" UNIQUE, btree (name)
Access method: heap
The id
column is set to nextval('accounts_id_seq'::regclass)
which is a sequence. In the PG Docs on Sequences it says that even if the transaction fails, as in your first instance, the nextval
, will not be rolled back. Thus, the next transaction will receive the next number in the sequence.
This sort of bleeds into being more of a Postgres-related question rather than a Phoenix/Elixir/Ecto, but I can certainly understand the confusion for anyone getting started with things.
Upvotes: 3