Harnessing Controlled Randomness with Laravel's Lottery

Harnessing Controlled Randomness with Laravel's Lottery

In the world of web development, there are times when you need to introduce controlled randomness into your application. Whether it's for A/B testing, gradual feature rollouts, or sampling large datasets, Laravel's Lottery class provides an elegant solution. Let's dive into how you can leverage this powerful feature in your Laravel projects.

Basic Usage of Lottery

At its core, Laravel's Lottery allows you to execute code based on specified odds. Here's a simple example:

use Illuminate\Support\Lottery;

Lottery::odds(1, 20)
    ->winner(fn () => $user->won())
    ->loser(fn () => $user->lost())
    ->choose();

In this case, there's a 1 in 20 chance that the winner callback will be executed, and a 19 in 20 chance for the loser callback.

Lottery with Database Queries

One powerful application of Lottery is in database query monitoring. You can use it to report only a small percentage of slow queries:

use Carbon\CarbonInterval;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Lottery;

DB::whenQueryingForLongerThan(
    CarbonInterval::seconds(2),
    Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')),
);

This setup will report only 1% of queries that take longer than 2 seconds, helping you to monitor performance without overwhelming your logging system.

Gradual Feature Rollout

Lottery is perfect for implementing gradual feature rollouts:

if (Lottery::odds(1, 10)->choose()) {
    return view('new-feature');
} else {
    return view('old-feature');
}

This code will show the new feature to 10% of users, allowing you to test it with a small subset of your user base.

A/B Testing

You can easily implement A/B testing using Lottery:

$version = Lottery::odds(1, 2)->choose() ? 'A' : 'B';

return view("landing-page-{$version}");

This will randomly assign users to version A or B of your landing page with equal probability.

Sampling Large Datasets

When dealing with big data, you might want to process only a sample of your dataset:

User::chunk(1000, function ($users) {
    foreach ($users as $user) {
        Lottery::odds(1, 100)->winner(function () use ($user) {
            ProcessUserData::dispatch($user);
        });
    }
});

This code will process data for approximately 1% of your users, which can be useful for generating reports or running intensive computations on a representative sample.

Testing Lotteries

Laravel provides methods to make testing Lottery-based code straightforward:

use Illuminate\Support\Lottery;

// In your test method
public function testFeatureRollout()
{
    Lottery::alwaysWin();
    
    $response = $this->get('/feature');
    
    $response->assertViewIs('new-feature');
    
    Lottery::alwaysLose();
    
    $response = $this->get('/feature');
    
    $response->assertViewIs('old-feature');
}

You can also set up a sequence of wins and losses:

Lottery::fix([true, false, true]);

This will make the lottery win, then lose, then win again, before returning to normal behavior.

Real-World Example: Error Sampling in Production

In a high-traffic production environment, you might want to sample only a portion of errors to prevent overwhelming your error tracking service:

use Illuminate\Support\Facades\App;
use Illuminate\Support\Lottery;

class Handler extends ExceptionHandler
{
    public function report(Throwable $e)
    {
        if (App::environment('production')) {
            Lottery::odds(1, 100)->winner(function () use ($e) {
                parent::report($e);
            });
        } else {
            parent::report($e);
        }
    }
}

This setup will report only 1% of errors in production, while still reporting all errors in other environments.

Laravel's Lottery class provides a powerful and flexible way to introduce controlled randomness into your applications. Whether you're gradually rolling out features, conducting A/B tests, or sampling large datasets, Lottery offers an elegant solution. By leveraging this feature, you can create more sophisticated, data-driven applications while maintaining clean, expressive code.

If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe