Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

sajioljpo's avatar

race conditions on user registration

My app's user table cannot use a unique column. So I've done this with the Laravel validation. The problem with this is that the "race condition" problem sometimes occurs when registering a user.

I have tried several ways to fix the problem

I used firstOrCreate and now firstOrNew but the problem didn't fix.

Although I have checked the record for non-duplicate records in the previous lines, I also checked the record's duplication status when adding records to the database, but the problem was not fixed.

<?php
class RegisterController extends Controller
{
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'phone' => ['required', 'digits:11', 'unique:users,phone,null,id,deleted_at,NULL', phoneRegExValidateRule()],
            // other fields ...
        ]);
    }

    protected function create(array $data)
    {
        $user = User::firstOrNew([
            'phone' => $data['phone'],
        ]);

        if ($user->exists) {
            flash()->error('error', 'duplicate user');

            return redirect()->route('home');
        } else {
            $user->fill([
                // fields ...
            ]);

            $user->save();
        }



        $user->meta()->create([
            // fields ...
        ]);

        return $user;
    }
}

Since the "race condition" problem occurs at the last moment and when the record is inserted into the database, it seems that I should use table locking. But I don't know what happens to other parts of the program that use this table when locked. I also don't know how to write this code

0 likes
18 replies
click's avatar

My app's user table cannot use a unique column.

With "unique" you mean database property unique ? Because in your code it looks like phone is your unique column right? So why can't you have a unique column?

Because a solution to this is to make your phone field in the database unique. Just do an insert. And when it fails because of the unique constraint you show the error and redirect the user.

You can check the exception error code to detect "duplicate mysql errors". See: https://stackoverflow.com/questions/27878719/laravel-catch-eloquent-unique-field-error

sajioljpo's avatar

The phone column is unique but not in all cases.

The SoftDelete feature is used in the User model. So when a user deletes their account, a record is not deleted from the table.

also if the user wants to sign up again with the same number, he can do so.

So in this table it is not possible to use a unique field.

click's avatar

@sajioljpo You can create a unique constraint of multiple columns in mysql. You could make a unique constraint of the combination: phone + deleted_at this could work I think.

jlrdw's avatar

My app's user table cannot use a unique column.

Why.

Snapey's avatar

whats the point of keepiing the old record if the user cannot re-sign and be connected to it

either delete the phone number, or change it so that it wont interfere in the future, eg change 768-456-321 to 768-456-321-20190809

Anyway, why do you think you have a race condition? You have loads of people trying to register the same phone number?

jlrdw's avatar

Phone numbers can't or shouldn't be used anyway for a unique row. Case in point me and my wife share the same cell phone and home phone. Hopefully enough said. Many older couples just use their home phone for the doctor, I don't think Mary Smith and Joe Smith have their on land lines. I figured I'd better explain.

sajioljpo's avatar

@click I didn't know it was possible!

So I make the phone + deleted_at fields unique.

Question: Will a user who has already registered with a phone number be prevented from re-registering? Can he re-register if he deletes his account?

If it does, my problem will be solved

sajioljpo's avatar

@jlrdw

Because I used deleted_at in the table. So the phone field should be unique unless the previous records have a value in deleted_at.

sajioljpo's avatar

@snapey

If a user deletes their account, they can sign up again with their previous phone number.

I cannot change or delete information. I need to keep my user registration records.

I looked at the users' table and found records repeated. All information is the same even created_at and updated_at. It is only possible to repeat the data once the user has deleted and re-registered his account, but two consecutive duplicate records mean race condition. I think this is due to the slow speed of internet users.

Probably two queries at a time pass the validation done in Laravel and the duplicate record is created because there is no unique field in the table.

jlrdw's avatar

Because I used deleted_at in the table.

Then have a "user_back" table so a record can be unique. I just don't think a phone number is so unique. A whole family can share the same land line. It would be easier if a customer number was issued.

John doe, id 227, custno 1457894, phone 999-99-9999

Now wife who has same telephone number can also do whatever if she gets her own customer number.

There are some very large families out there, maybe 7 children, but only one phone number for all.

If you start designing an app with pencil and paper first, things will make more sense.

sajioljpo's avatar

@jlrdw

The phone number may not be unique on many sites but on my site it is unique and no one shares their number with another. I'm sure of that.

If I want to save information to another table, there is a redundancy of information. Several other tables are associated with the User table.

Snapey's avatar

perhaps you actually just need some feedback on the front-end. Disable the button after form is submitted for instance.

You could retain the original account but change it as suggested. By adding date of removal to tge phon number field, you ensure it can be added again, and therefore can use unique field

sajioljpo's avatar

@snapey I don't think Front-end changes can solve this problem, but I'll try.

I can't change the structure of the tables. Because it causes Data Redundancy. Changing information also causes problems when reusing it.

sajioljpo's avatar

As far as I know, programming languages must use algorithms like Semaphore to prevent race condition. Now I don't know how php does it.

Anyone have any information about locking tables or using transactions? How does it work and how does using these methods affect the performance of the rest of the program?

I want to solve this problem without changing the structure of the tables, through coding.

Snapey's avatar

Change to firstOrCreate instead of firstOrNew

1 like
sajioljpo's avatar

@snapey As I said in the first post, using firstOrCreate also doesn't solve the problem.

jlrdw's avatar

Instead of soft deleting then just delete. That way if you go to insert that person again they will absolutely not be in there.

It sounds like someone wants you to play regular poker but is only giving you half the deck to play with.

Snapey's avatar

FirstOrCreate looks like this

    public function firstOrCreate(array $attributes, array $values = [])
    {
        if (! is_null($instance = $this->where($attributes)->first())) {
            return $instance;
        }
        return tap($this->newModelInstance($attributes + $values), function ($instance) {
            $instance->save();
        });
    }

so you are saying that two requests from the client hit this bit of code at exactly the same moment. Is your database really slow? For example, it takes a long time to find if there is already the same phone number because there are a lot of records and the phone number field is not indexed?

Have you tried installing barryvdh/laravel-debugbar ?

This will show you how long the find and the create take.

Please or to participate in this conversation.