Software Design Enthusiast And Engineer

Queue the work of hydrating a Laravel session

This doesn't happen often but there are times when it's helpful to be able to throw a job on the queue that will hydrate the session data for a user that just logged in -or any scenario, really.

The logic is simple and being able to throw a callback on the queue makes this possible with just a few lines of code.

As I mentioned, you could do this for any scenario, but I'm going to use the 'user just logged in' scenario to walk us through this.

For this example, we'll set it all up within an event listener that fires when the user logs in:

// AppServiceProvider.php

use Illuminate\Auth\Events\Authenticated;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use SessionHandlerInterface;


class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app['events']->listen(function (Authenticated $event) {
            $sessionId = session()->getId();

            // We can put some 'simple values' in the session 
            // right here that we'll then be able to use 
            // within our queue job. Typically this is anything
            // that will execute very fast so our user isn't waiting
            // For example:
            session()->put('requested_at', $this->app[Kernel::class]->requestStartedAt()->toString());

            session()->put('user_ip', request()->ip());

            // At the least, it's not a bad idea to put a 'marker'
            // in the session that will allow us to know our session
            // hasn't expired and/or been deleted:
            session()->put('marker', $marker = Str::random());

            // etc...

            // You can obviously create a Laravel Job for this,
            // but we can just throw this work on the queue, on the fly!
            dispatch(function () use ($sessionId, $marker) {
                /** @var SessionHandlerInterface $handler */
                $handler = app('session')->driver()->getHandler();

                $session = new Store(
                    config('session.cookie'),
                    $handler,
                    $sessionId,
                );

                $session->setRequestOnHandler(request());

                // start the session, which reads the data from whichever driver you're using...
                $session->start();

                // we now ensure our session is still intact.
                // if not, we'll just bail out early...
                if ($marker !== $session->get('marker')) {
                  return;
                }

                // and now we can now pull our 'simple values' from the rehydrated session...
                $usersIp = $session->get('user_ip');
                $requestedAt = $session->get('requested_at');

                // Do some long-running or heavy work...
                
                // perform queries
                // make api calls
                // etc...

                // put the results of the work in the session...
                $session->put('query_results', $queryResult);
                $session->put('api_results', $apiResult);

                // and persist...
                $session->save();
            });
        });
    }
}


Now, the next request that comes in for this user, you'll be able to use session()->get('query_results') (or whatever you've put in the session) for an extremely fast data lookup!

It's worth mentioning that this would be a lot easier to do by using the cache, likely with the users id included in the cache key, but if you're specifically wanting to use the session, this is the way!

It's also worth noting that if you were to throw, let's say 5 jobs on 5 different queues from within the main process - then wanted to wait for all 5 to finish prior to returning the response to the user from within the main process (yea, getting a little nutty) - you'd want to make sure you call session()->start() from within the main process after everything is done. As for when you know everything is done, I leave that up to your creativity, but unless you restart the session after doing all this work, you'll get bupkis!