michaeldyrynda / laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models
MIT License
442 stars 46 forks source link

How to re-generate UUID on duplicate? #118

Closed intellent closed 9 months ago

intellent commented 2 years ago

Foremost: Great project. This should make it into the core!

Although very unlikely, it’s still theoretically possible for GeneratesUuid to produce duplicates. So we have to account for that when persisting a model to a unique database field.

try {
    $model->save();
} catch (QueryException $e) {
    if (isset($e->errorInfo[1]) && $e->errorInfo[1] == 1062) {
        // We have a duplicate, what to do now?
    } else {
        throw $e;
    }
}

What is the recommended way to make laravel-model-uuid generate a new UUID in such cases? Is it sufficient to just trigger $model->save() again? Or do I need to reset the model somehow (e.g., by setting the UUID field to null first?

michaeldyrynda commented 2 years ago

I think this is something you'll want to handle in your app, rather than in the package.

You can catch the exception and generate a new UUID yourself, but then you'd be making an assumption that it's definitely the UUID that's duplicated and not some other unique constraint in the table (that we won't know about in the package).

In short, yes, something like the above will suffice. It's contrived, but probably the simplest way of handling it, depending on whether this is repeated elsewhere.


try {
    $model->save();
} catch (QueryException $e) {
    $model->uuid = Str::uuid()->__toString();
    $model->save();
}

UUIDs are intended to be very difficult to collide, so I'm curious if this is something you've run in to, or if it's something you're catering to just in case?
intellent commented 2 years ago

I think this is something you'll want to handle in your app, rather than in the package.

Absolutely. I didn’t want to imply otherwise. 🙂

So I need to set a new UUID manually via Str::uuid()? Is laravel-model-uuid using Str::uuid() internally, as well? I was hoping to use the same mechanism, the original UUID has been created with.

UUIDs are intended to be very difficult to collide, so I'm curious if this is something you've run in to, or if it's something you're catering to just in case?

The latter. Just want to make my code as robust as possible.

joelmellon commented 9 months ago

There are lots of retry options. I find the topic interesting, but not in the least with regard to UUIDv4 collisions.

Start by making sure you have a unique constraint on your uuid column so your code it throws an exception when a collision (statistically never) occurs. In your migration:

$table->unique('uuid');

Then pick one of these:

Yes, it's possible to get a collision. But, in the time since UUIDv4 was invented, maybe a handful of collisions have occurred across the entirety of computing on the planet. I don't mean UUIDv4s have collided in the same system either. For example, it's possible Google has generated a UUIDv4 for an email in Gmail, and Apple may have generated the same one for an iMessage.

But let's say you're the unluckiest UUIDv4 generator in the next 80 millennia and you see a few collisions. So what? You have a couple users affected? They can presumably retry whatever operation that broke, and move on.

As a production implementation, I think it's likely to cause more bugs than a collision or two in the lifetime of an app (eg. 10 years). I'd decline a code review if I saw something like this. As an exercise in retry logic, I think it's fun though.