در حال بارگذاری ...

Explaining Race Conditions0:00

Have you ever heard the term race condition? It's a term that's used to describe the situation when two or more processes try to make changes on the same resource at the same time. For example, in this job we are deploying an update to a project. A deployment requires several changes to the file system. If we have two instances of this job running concurrently or at the same time by two different workers, we will enter a race condition where the two jobs are trying to update the same files on the disk at the same time. To see this in action, let's start multiple workers, php artisan queue:work here, and php artisan queue:work here. And we are going to dispatch the job two times, so one and two. So here worker one picked the job and processing it while worker two picked the job and is processing it at the same time. If we go back to php

Preventing with Cache Locks0:52

the job, so one and two. So here the worker one picked the job and processing it while worker two picked the job and is processing it at the same time. If we go back to php store and check the log file, we can see that job two started right after job one started and it finished right after job one has finished. That means both jobs ran concurrently or at the same time. To prevent this race condition, we can use the atomic locks functionality Laravel ships with. So let's go back to our deploy job and create a lock. So we are going to use the cache facade and use the lock method. We give this lock a name called deployments. So here we are creating a lock instance under the name deployments. Next we are going to call the block method on the lock instance in order to acquire the lock. This method accepts two arguments. First is the number of seconds

name deployments. Next we are going to call the block method on the lock instance in order to acquire the lock. This method accepts two arguments. First is the number of seconds it should keep trying to acquire the lock for. If it fails to acquire a lock during this time, an exception will be thrown. Let's use 10 here for the first argument. And the second argument is the closure where we put the code that should run if a lock was acquired successfully. So we are going to write a closure here and move all the code that should run inside the job to be inside the closure. Now let's dispatch this job two times like before, start two workers and see the locking in action. But first let's clear the lock file. And we go back to the terminal. Let's stop these workers, clear the terminal, start a new worker php artisan queue:work and php. Let's clear the terminal and then php artisan

file. And we go back to the terminal. Let's stop these workers, clear the terminal, start a new workers php artisan queue:work and php. Let's clear the terminal and then php artisan queue:work. Now we have both workers running. Now let's dispatch the job a couple of times. So one and two. So worker one big the job and worker two big the job, they are processing them concurrently. But because we have a lock jobs should not start at the same time. To verify that let's go to the lock file. So laravel.log. And we can see that job one started and finished and then job two it didn't start until job two finished and the lock was released. And that's only when job number two was able to acquire the lock and start running the business logic. Perfect. Besides locks, we can also use the Redis concurrency limiter. If we go back to our job in deploy.php and instead of using a lock, we are going to use

Using Redis Concurrency Limiter3:25

business logic. Perfect. Besides locks, we can also use the Redis concurrency limiter. If we go back to our job in deploy.php and instead of using a lock, we are going to use the Redis facade and call the funnel method. We'll call our funnel deployments as well. Using the Redis concurrency limiter gives us more flexibility. We can set any limit we want. So for example, we can use the limit method here to limit the concurrency to five. And that means only five instances of the same job can run at the same time. And similar to the block method on the cache, we can also call block and block the execution for 10 seconds to wait for a lock to be acquired. And for the closure, we can use the then method and provide the closure here and move the logic from inside the cache lock to be inside the closure for the Redis funnel. Perfect, so far we have learned how to configure

Rate Limiting with Throttle4:16

and provide the closure here and move the logic from inside the cache lock to be inside the closure for the Redis funnel. Perfect, so far we have learned how to configure concurrency using the cache or using the Redis funnel method. But Laravel also ships with another limiter, but this one is specialized in rate limiting. To use it, let's swap the funnel method with a throttle method. This limiter controls the amount of locks or the number of locks that can be acquired with that key during a given period of time. To define the limit and the time slot, we use the allow and every method. So instead of limit here, we're going to use the allow method. And we're going to allow 10 instance of this job to run for every 60 seconds. Now this lock will only allow 10 deployments to run every 60 seconds.

Job Middleware withoutOverlapping5:05

method. And we're going to allow 10 instance of this job to run for every 60 seconds. Now this lock will only allow 10 deployments to run every 60 seconds. So far so good, we have learned about the cache locks and using the Redis funnel and throttle methods. Another way of controlling the concurrency of jobs is by using the withoutOverlapping middleware. Similar to routes, you can define a set of middleware to be used with jobs. To do that, we need to implement a middleware method inside the job class. So let's move the business logic back to the handle method and remove the Redis funnel that we are using or the Redis rate limiter and implement a middleware method. Inside the middleware method, we are going to return an array.

that we are using or the Redis rate limiter and implement a middleware method. Inside the middleware method, we are going to return an array. And inside this array, we are going to put all the middleware that we want to run this job through. For our example, we need to use the withoutOverlapping middleware. So it's under Illuminate\Queue\Middleware\WithoutOverlapping. The constructor of the middleware instance accepts a key as the first parameter and we are going to use deployments as the key. This middleware will prevent running this job while another instance of the same job is in progress. However, the middleware will release the job back to the queue, it won't block like we have seen with the Redis limiters and the

is in progress. However, the middleware will release the job back to the queue, it won't block like we have seen with the Redis limiters and the cache lock. It won't block, but the middleware is going to release the job back to the queue if another instance is in progress. And it won't throw an exception. We can control the release delay by adding a second argument to the withoutOverlapping instance, so here let's add 10 for example. Now when the middleware releases a job it's going to be delayed for 10 seconds before a worker can attempt it again.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟