szymon_zak's avatar

Unique validation problems

I've encountered problem while validating unique columns. My users login column should be unique so I created appropriate validation rule.

'login' => 'unique:users'

However I was able to bypass validation by performing two simultaneous API requests in the same second.

Edit: If delay between requests is slightly higher I am receiving proper unique validation error.

I am currently using 5.4 version.

0 likes
9 replies
Waldemar's avatar

You must add flag in create_users_table migration

$table->string('login')->unique();
szymon_zak's avatar

@Waldemar I've got that covered. Request ends with database unique violation.

But first of all, shouldn't this be detected by unique validation?

By perfoming some kind of row access lock to be sure that data won't be outdated before updating record?

Waldemar's avatar

@szymon_zak if you use the $table->string('login')->unique(); This error at the database level. The database itself does not allow you to create the same records

if you want your errors try this

$validator = Validator::make($request->all(), [
            'title' => 'required|unique:posts|max:255',
        ]);

        if ($validator->fails()) {
            return redirect('post/create')
                        ->withErrors($validator)
                        ->withInput();
        }
Waldemar's avatar

also you can use this

public function messages()
{
    return [
        'email.required' => 'Er, you forgot your email address!',
        'email.unique' => 'Email already taken m8',
    ];
}
szymon_zak's avatar

It still sometimes ends with Laravel validation error and sometimes with database violation error.

When I use unique rule I should be sure that given input is always unique after passed validation and never get database level error.

For example if I create two users using my panel I always receive laravel validator errors as indended.

However if I use script with two threads to perform two identical store actions at the same time it sometimes bypass Laravel unique validation.

sutherland's avatar

That's because two users simultaneously being saved will likely both flow through the validator before any data is written to the database. This shouldn't be a common occurrence in your example, so the database unique constraint should be sufficient.

Is there a reason why this is critical in your use case? There could be a better solution but knowing why two users would be likely to set their email to the same thing at the same second could help point to how to deal with the issue.

szymon_zak's avatar

@sutherland Login is just an example because I can't share production application code due to legal issues.

It has an API available which is used by 3rd party clients and I cannot use database unique constraint in my case thanks to API versioning.

My main problem is that validation bypass happened multiple times because currently clients generate huge amount of requests.

I've managed to solve problem by not using Laravel's unique and acquiring row access database lock manually so I can be sure that validation will not pass before data is written to it.

However I'm not happy how unique works, I shouldn't be forced to workround it.

Edit: I am using PostgreSQL 9.4

Please or to participate in this conversation.