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

ramirovjnr's avatar

Repeated values from Str::random()

Hi,

First of all I don't think that this is a Laravel issue, I'm really trying to figure out the problem of Str::random() generating repeated values - as it uses random_bytes().

Our scenario:

  • 3 VPS servers running on Digital Ocean, 2 NYC1 and 1 NYC3 connected through VPN
  • Ubuntu 18.04
  • PHP 7.4 running with Laravel 7.12
  • MySQL Galera Cluster 5.7.29
  • Supervisor 3.3.1
  • Redis Server 5.0.8 and Redis Sentinel 5.0.8

On each server, we have queues running using Supervisor with a config like this (modified to be posted here)

[group:my-app]
programs=my-app-queue-1,my-app-queue-2,my-app-queue-3

[program:my-app-queue-1]
process_name=%(program_name)s_%(process_num)02d
command=php7.4 /dir/app/artisan queue:work --queue=cron --sleep=3 --tries=2
directory=/dir/app
autostart=true
autorestart=true
startretries=10
numprocs=3
serverurl=AUTO
stopasgroup=true
stopsignal=TERM
killasgroup=true
stopwaitsecs=90

[program:my-app-queue-2]
process_name=%(program_name)s_%(process_num)02d
command=php7.4 /dir/app/artisan queue:work --queue=backup --sleep=3 --tries=2
directory=/dir/app
autostart=true
autorestart=true
startretries=10
numprocs=1
serverurl=AUTO
stopasgroup=true
stopsignal=TERM
killasgroup=true
stopwaitsecs=90

[program:my-app-queue-3]
process_name=%(program_name)s_%(process_num)02d
command=php7.4 /dir/app/artisan queue:work --sleep=3 --tries=2
directory=/dir/app
autostart=true
autorestart=true
startretries=10
numprocs=3
serverurl=AUTO
stopasgroup=true
stopsignal=TERM
killasgroup=true
stopwaitsecs=90

We have an API route /api/cron which receives a call from AWS Lambda every 1 minute and running the following code:

Artisan::queue('schedule:run')
    ->onQueue('cron');

return response()->json();

Our schedule method app/Console/Kernel.php is like this:

    protected function schedule(Schedule $schedule) {
        $crons = Cron::enabled()->get();

        foreach ($crons as $cron) {
            switch ($cron->cron_type) {
                case CronType::ANSIBLE:
                    $schedule
                        ->job(
                            new AnsibleCronJob($cron, Str::random(64)),
                            'cron'
                        )
                        ->cron($cron->cron_expression);
                    break;
                case CronType::COMNAND:
                    $schedule
                        ->job(
                            new CommandCronJob($cron, Str::random(64)),
                            'cron'
                        )
                        ->cron($cron->cron_expression);
                    break;
            }
        }
    }

What is really strange is Str::random(64) generating repeated values and more than once, some values have repeated more than 100 times.

Things we have already made:

  • Rebooted the servers
  • Restarted the Supervisor service
  • When we deploy, we run php artisan queue:restart, we also tried by restarting the Supervisor service

We don't thing it's a racing condition in this case, because we generate the value and only after we store it in the database, since Str::random() uses random_bytes, it should provide enough uniqueness for this situation. Str::uuid() falls into the same case, we were using it before. Our guess is that something could be left in memory on the worker, but it's not clear.

We would really appreciate some ideas on the implementation / problem. We are running things like this to avoid single point of failure (SPOF) and provide high availability and fault tolerance, as our previous scenario were running on a single server.

0 likes
4 replies
jlrdw's avatar

I have had issues with random also. For unique numbers you could either:

A. Once a random number is used, place it into another table. Then when using random again, make sure it's not in the table.

B. Or have a large generated table of random numbers, once used, either delete or mark as used.

Just a couple of ideas, I have used B in the past. But if I had it to do again I'd go with A.

ramirovjnr's avatar

We tried something similar, like checking the database if the generated code was already used, but then we need to use transactions to avoid collision and running this kind of operation in multi-primary scenario can have other issues.

The point we're trying to understand is how random_bytes with a length of 64 could be repeating even on different servers.

ramirovjnr's avatar

@royduin What we did to solve our issue was creating a function to generate a random value with:

  • Str::random()
  • mt_rand()
  • random_int()
  • Ramsey UUID lib

We generated values using these functions, concatenated them and used it as a parameter for

hash('sha256', $randomStringValues);

It didn't make sense this issue, as it uses random_bytes underneath if I'm not mistaken, but doing this solved our problem.

P.S.: You can also add the values to an array an shuffle it before implode() to be more random.

Please or to participate in this conversation.