Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.8] Prevent unnecessary JSON UPDATE queries on MySQL #28029

Closed
wants to merge 1 commit into from
Closed

[5.8] Prevent unnecessary JSON UPDATE queries on MySQL #28029

wants to merge 1 commit into from

Conversation

staudenmeir
Copy link
Contributor

MySQL normalizes JSON strings by adding a space between key and value ("foo":"bar""foo": "bar") and sorting the keys. This can prevent Eloquent from detecting equivalent values and causes unnecessary UPDATE queries:

class User extends Model
{
    protected $casts = ['options' => 'array'];

    protected $guarded = [];
}

Schema::create('users', function ($table) {
    $table->increments('id');
    $table->json('options');
    $table->timestamps();
});

$options = ['b' => 'bar', 'a' => 'foo'];

$user = User::create(['options' => $options]);
$user->refresh();

$user->options = $options;
$user->save(); // Executes unnecessary UPDATE query.

For collection and object casts, a simple value like ['a' => 'foo'] already causes this issue: Due to the additional whitespace, $current === $original fails and the created stdClass/Collection objects aren't identical.

We can fix this by recursively sorting the keys on the current and original value before comparing them. To minimize the overhead, this only happens when the previous comparisons fail.

This PR doesn't apply to nested objects, but they are probably rather rare.

In Laravel 5.9, we could make sortKeys() (optionally) recursive and remove sortKeysRecursively().

Fixes #27946.

@taylorotwell
Copy link
Member

I don't particularly think the code overhead of fixing this is worth it for a fairly minor "bug".

@staudenmeir staudenmeir deleted the json-mysql branch April 6, 2019 05:25
@zeyad82
Copy link

zeyad82 commented May 9, 2019

When logging or auditing the model updates, it's spamming and it creates many unnecessary records so it's better to find a better way to handle it than ignoring it at all.

@sigismund
Copy link

sigismund commented Mar 3, 2020

You can bypass this bug if you cast JSON attribute as object instead of an array.

@Tofandel
Copy link
Contributor

Tofandel commented Sep 18, 2020

@sigismund This saved my life, I was about to write a function to deeply sort the object before an update as I was using the isDirty check to recompute some other models and it was always true

@goyote
Copy link

goyote commented Mar 31, 2021

This is not fixed for AsCollection

netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 26, 2023
netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 27, 2023
netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 27, 2023
netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 30, 2023
netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 31, 2023
netzknecht added a commit to netzknecht/laravel-framework that referenced this pull request Oct 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Models with MySQL JSON fields get updated even though there was no change
6 participants