Queues and Stateful Services
Tags: laravel, queues, statefulservices, php
Published: 2019-03-22 15:29:03

---
title: Queues and Stateful Services
published: true
tags: laravel,queues,stateful-services, php
canonical_url: https://smcrow.net/2017/10/04/queues-and-stateful-services/
---

_Edit: I originally thought that Laravel’s service container defaulted to binding as singletons. This doesn’t appear to be the case. I have made updates where appropriate. I also added a section on statics!_

_Edit Edit: When I say singleton I specifically mean a class that has been instantiated once, and then reused by the service container. I’m not referring to the singleton pattern created by using statics._

Laravel offers Queues to defer time-consuming tasks such as sending emails and batch processing. To learn about using Queues you can reference the [Laravel Documentation: Queues](https://laravel.com/docs/5.5/queues).

## Queue Worker

Laravel provides a Queue Worker that can be used to process new jobs as they are added to the Queues.

What you might not realize, is that by using stateful services within your queued jobs you may run into issues. From the [Laravel Documentation: Queues](https://laravel.com/docs/5.5/queues):

> Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started.

While that does a nice job informing the user that they should restart their queue worker should they make any changes to the application, it doesn’t point out that keeping the application in a booted state means that the same state is going to be used for each job. This can cause issues if your services have any sort of state and you’re binding them as singletons.

First, let’s look into what causes the application’s booted state to be stored in memory.

### The Queue Worker Command

The `php artisan queue:work` command starts the Queue Worker and will continue to run until you manually cancel the process or close your terminal.

The `WorkCommand` has an injected `Worker` object which is used to process the jobs. At this point the application has already been booted.

### The Worker

The worker itself can be told to run either once, where it will process a single job, or in daemon mode where it will continue to run until cancelled barring the queue being paused, or memory limits have been reached.

Daemon mode is defined as the `daemon` function in `Illuminate\Queue\Worker`. In this `daemon` function we can see:

```php
public function daemon($connectionName, $queue, WorkerOptions $options)
{
	$this->listenForSignals();
	
	$lastRestart = $this->getTimestampOfLastRestart();
	
	while (true) {
	// ... stuff
	}
}
```

The `while` loop is exactly what keeps the application booted into memory. It will keep the thread processing until it is told to cancel programmatically, cancelled manually by the user, or encounters some sort of scenario that requires it to cancel.

## Why do I need to worry about this?

Another thing that Laravel provides for us is its service container which allows dependencies to be injected instead of hard-coded into classes that need them. One of the other big benefits of dependency injection, is that the dependency can be instantiated once and then shared among each consumer. You can find out more about the service container at [Laravel Documentation: Service Container](https://laravel.com/docs/5.5/container).

Whether or not your services have been bound to the container, or they have been discovered via reflection, the container keeps track of the dependencies that have already been instantiated. And, if you’re binding them as singletons, it will provide you the same instance to each of your jobs.

Normally, in the process of handling requests in your web app, the application container is booted for each request that passes through. However, we’ve already seen that the application stays in memory for the full duration of the queue worker running. That means that for every job that has dependencies, the same object will be injected into each of them. This is why stateless services are so important, when working with singletons.

### Stateless vs Stateful Services

I’ll go into more detail in a later article but let’s look at a small example. Don’t worry about what the services are trying to accomplish.

#### Stateful Service

```php
class ServiceA()
{
	private $someDependency;
	
	private $listOfNames;
	
	public function __construct(SomeDependency $someDependency)
	{
		$this->someDependency = $someDependency;
	}
	
	public function getListOfNames()
	{
		// Lazy Loading Technique
		if (empty($this->listOfNames)) {
			$this->listOfNames = SomeHelper::buildListOfNames();
		}
		
		return $this->listOfNames;
	}
}
```

This service has a single dependency on `SomeDependency` and has a method for returning the value in the `$listOfNames` array. Notice that the array is _lazy loaded_. The value of the array is set by calling `SomeHelper::buildListOfNames()` and is only done when the method is first invoked.

Having the `$listOfNames` variable gives `ServiceA` state or statefulness.

#### Stateless Service

```php
class ServiceB()
{
	private $someDependency;
	
	public function __construct(SomeDependency $someDependency)
	{
		$this->someDependency = $someDependency;
	}
	
	public function getListOfNames()
	{
		return SomeHelper::buildListOfNames();
	}
}
```

This service has had its state removed. It will always return a fresh list of names provided by another class.

Note that the dependency on `$someDependency` does not provide state as long as `SomeDependency` is also stateless.

### The Consequences of Stateful Services

Consider a job with the following handle method:

```php
public function handle(ServiceA $service)
{
	$listOfNames = $service->getListOfNames();
	doSomething($listOfNames);
}
```

When this job is executed the `ServiceA` class will be injected. The `getListOfNames()` method will be invoked. The first time that this job is executed, maybe when the queue is first starting to be worked, the `$listOfNames` array on `ServiceA` will be initialized.

However, for _every job that runs_ the `$listOfNames` array will always remain the same because the instance of `ServiceA` is only going to be created when the queue worker first starts, because we’re binding as a singleton.

If something changed in the database where `SomeHelper::buildListOfNames()` gets its data, the new list won’t be built until the worker has been restarted.

Keep this in mind the next time you start to write a stateful service, especially when working with queues.

## But wait, I’m not using Singletons, am I safe?

By default Laravel will issue a new instance from the container unless you explicitely ask for singleton binding. However, that doesn’t mean that you’re safe! There is another way for your services to have state, consider the following modification to `ServiceA`:

```php
class ServiceA()
{
	private $someDependency;
	
	public static $listOfNames = [];
	
	public function __construct(SomeDependency $someDependency)
	{
		$this->someDependency = $someDependency;
	}
	
	public static function getListOfNames()
	{
		// Lazy Loading Technique
		if (empty(self::listOfNames)) {
			self::listOfNames = SomeHelper::buildListOfNames();
		}
		
		return self::listOfNames;
	}
}
```

Notice how how the `$listOfNames` and `getListOfNames` are static. This means that both the method and the variable don’t live on the class instance, they’re not _instance variables_. Instead, PHP (based on an assumption I’m making from Java) scopes the variable to the class instead of the instance. That means that any code referencing the variable will share the same variable reference. Some people refer to statics as global-scoped variables.

In this example, the first time that `getListOfNames` is called, the static variable `$listOfNames` will be populated. Any subsequent calls to this method will reference _the same_ variable, and it won’t allow it to be loaded again. Since the workers are keeping the PHP thread open, PHP is unable to clear out the memory as it would when working with HTTP requests. That means that the variable is persisted to each and every job that runs, until the thread is closed and the memory can be cleared out.

This is a similar problem to singletons, because that’s really what statics are. Single references that are persisted for the duration of execution.

Another thing to keep in mind! :)