framework: Duplicate entries on updateOrCreate
- Laravel Version: 5.4
- PHP Version: 7.1
- Database Driver & Version: MySQL 5.7
Description:
I want to save mobile devices information in the database using their UUIDs. The uuid
field has a unique index. The code is:
$device = Device::updateOrCreate(
['uuid' => $request->input('uuid')],
[
'info' => $request->input('info'),
]
);
But I’m getting a lot of errors in logs:
Next Doctrine\DBAL\Driver\PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'aa18269c-cf32-4c6b-b450-e84a5dba0a0c' for key 'devices_device_uuid_unique' in /var/www/api/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:93
Stack trace:
I also tried to use firstOrNew:
$device = Device::firstOrNew(['uuid' => $request->input('uuid')]);
$device->info = $request->input('info');
$device->save();
But it’s equal I guess.
If I remove the unique index from the uuid
field the problem is gone but I get duplicate entries. That’s strange because I thought updateOrCreate
method guarantees uniqueness of the entries.
Is it a bug of the updateOrCreate
method?
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 13
- Comments: 47 (11 by maintainers)
Commits related to this issue
- Wrapping updateOrCreate in try/catch See this issue: https://github.com/laravel/framework/issues/19372 — committed to EscherLabs/Graphene by timcortesi 6 years ago
this is still problem.
Please reopen this ticket. I’d agree with other commenters that this is an actual bug since it is surprising behavior for any moderately busy service where collisions are possible. While the implementation does not concern me the syntax implies it is somehow atomic.
This could be achieved in a variety of ways:
updated_at
columns (typically needs sub-second precision).In my opinion
firstOrCreate
also has this weakness and likely any...OrCreate
method.All that said, I can see why the
...OrNew
methods cannot be atomic since they only return unsaved, dirty objects. The others do not have that restriction.Feel free to open a PR if you have a better idea, meanwhile I’m closing this since it’s not actually a bug.
Just remove your foreign key from the $fillable array property and it will work. Foreign key should not be mass assigned. It worked for me.
Manage race conditions manually when using updateOrCreate with an ORM in 21st century?? C’mon!
Yes, I could. But that would force me to add locks everywhere to handle updateOrCreate problems. I think the function should be atomic. 😃
Methods
firstOrCreate
andupdateOrCreate
are totally unusable for real application which allows simultaneous requests. Solve this or rename to firstOrDuplicate. 😃Since it happens on random occasions I believe it might be a race condition with the record is being created via another request in between the check and insert inside updateOrCreate().
Is it possible to have different fixes for different database engines with the same outcome?
We should at least update the docs with a warning about using firstOrCreate/updateOrCreate methods: https://laravel.com/docs/5.8/eloquent#inserting-and-updating-models
I’ve created an UPSERT package for all databases: https://github.com/staudenmeir/laravel-upsert
@themsaid this really is a bug. Race conditions are a big problem with updateOrCreate. We are fighting this at present, upsert is a safe method with mysql
is this fixed ? cause I got the same problem, Laravel 6
I solved this problem by overriding Illuminate\Database\Eloquent\Model and creating a custom firstOrCreate method that returns the model if there is a duplicate error exception. This should work for mySQL connections.
Is it possible to use
INSERT ... ON DUPLICATE KEY UPDATE Syntax
?Yeah, this is definitely still a problem. Whats the point in an updateORcreate method if it does the exact opposite
@didioh 's approach works for me, as does adding a trait to the affected Model to override the updateOrCreate and firstOrCreate methods, as described here.
I agree with others that this should be default behaviour.
Still, I am getting the same issue.
(3/3) QueryException SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘344b879b-279d-f8e2-01e2-59083ac6f538’ for key ‘employee_id_unique’ (SQL: insert into
employee
(id
,title
,first_name
,last_name
,office_phone
,department
,mobile
,fax
,primary_address
,city
,state
,country
,postal_code
,email
,back_account
,citizenship
,dob
,doe
,gender
,salary
,type_of_employeement
,employee_no
,is_sync
,updated_at
,created_at
) values (344b879b-279d-f8e2-01e2-59083ac6f538, , Vitali, Berby, , , , , , , , , , , , , , , male, , , , 1, 2018-07-30 03:48:35, 2018-07-30 03:48:35))At the very least the docs should be explicit that these methods are not implemented using native database SQL queries, and are therefore not “transaction safe”.
I’ve got the same issue (sometimes): SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
It’s a race condition you should leverage the lock logic.
This might be of help: Maybe we should make a pullrequest for this trait?
https://gist.github.com/troatie/def0fba42fcfb70f873b7f033fbe255f
Problem still exists. Laravel 7.5.2
@hxgdzyuyi Please see https://github.com/laravel/ideas/issues/1090#issuecomment-377351778 why that is not a universal solution for
updateOrCreate()
.I think, if you use index and have updateorcreate method, and put that inside try catch block, all is good.
Imagine two threads both enter the if statement. So both of them are gonna write into the database. when one will start inserting it, the table will be locked for the other one. So first one gets inserted, and another one waits. when first is done and second one starts inserting, it’s gonna throw exception of duplicate event entries. YOu got the catch block right? so you catch it and nothing is a problem.
Let’s tweet to TO
On Wed, Jul 17, 2019 at 11:01 AM Peter Duda notifications@github.com wrote:
–
Ali Gajani Founder at Mr. Geek www.mrgeek.me www.aligajani.com
I had the same issue. I found a situation in which
$added_products
would contain records with the same primary key as records in DB.So (because there isn’t
updateOrCreateMany()
) I changed this to:But that didn’t work. Then I figured out that I hadn’t specified the
$primaryKey
in the model file. After I addedprotected $primaryKey = ['order_id', 'product_id'];
to the model file, it still didn’t work because apparently Laravel supporting composite primary keys would make Eloquent too complex.My solution, with composite primary key support
And in the helper class:
Also,
Query\Builder::updateOrInsert()
seems vulnerable to race conditions:Shouldn’t the method use DB::transaction? What if the record get deleted between
! $this->where($attributes)->exists()
and$this->insert(array_merge($attributes, $values))
?This is still a problem, albeit occurs very rarely.
Guys, please open a PR if you have a different approach that works on all supported database engines 😃 PRs are welcomed.
i think you could use lock to prevent simultaneous calls.
or something like that i’ve not tested it.