Repository: laravel/framework (php)

Issue:
ShouldBeUnique with DynamoDB provider doesn't work as expected with default configuration
- Laravel Version: v9.19.0
- PHP Version: 8.1.6

### Description:

According to [documentation](https://laravel.com/docs/9.x/queues#unique-jobs), implementing ShouldBeUnique on job class is enough to ensure only one instance of given job is present in the queue. However, using `dynamodb` cache driver without setting `$uniqueFor` explicitly makes the lock item expire after 1 second, because `expires_at` is set to current timestamp when `$uniqueFor` is 0 (default value).

This is the change event in DynamoDB table after `putItem` request when trying to acquire job lock:

```
{
  "awsRegion": "eu-central-1",
  "eventID": "4cb2d5b3-4954-451e-8332-01671dd2bf3e",
  "eventName": "INSERT",
  "userIdentity": null,
  "recordFormat": "application/json",
  "tableName": "vapor_cache",
  "dynamodb": {
    "ApproximateCreationDateTime": 1658222276198,
    "Keys": {
      "key": {
        "S": "cloud-api-production:cloud-api-production:laravel_unique_job:App\\Jobs\\<job-name>"
      }
    },
    "NewImage": {
      "value": {
        "S": "s:16:\"D7X1915ojxbQW6sU\";"
      },
      "key": {
        "S": "cloud-api-production:cloud-api-production:laravel_unique_job:App\\Jobs\\<job-name>"
      },
      "expires_at": {
        "N": "1658222276"
      }
    },
    "SizeBytes": 237
  },
  "eventSource": "aws:dynamodb"
}
```

`expires_at` is equal to current timestamp (see `ApproximateCreationDateTime`), so trying to acquire the lock in the following second will work because of used condition:

https://github.com/laravel/framework/blob/d425952e8cc664b01de8bd654336c1be8dcf7444/src/Illuminate/Cache/DynamoDbStore.php#L273

It seems like the behavior is not consistent across other cache providers, e.g. `database` cache provider uses lock expiration time of 1 day if no time was provided:

https://github.com/laravel/framework/blob/d425952e8cc664b01de8bd654336c1be8dcf7444/src/Illuminate/Cache/DatabaseLock.php#L92-L95

`redis` provider creates non-expiring item:

https://github.com/laravel/framework/blob/d425952e8cc664b01de8bd654336c1be8dcf7444/src/Illuminate/Cache/RedisLock.php#L35-L42

`memcached` items doesn't expire with expiration time set to 0.

While `dynamodb` items expire in the next second after the lock is acquired.

Possible solution would be to use 1 day as default expiration value, as in `database` cache provider case.

### Steps To Reproduce:

1. Use `dynamodb` cache provider
2. Create a job implementing `ShouldBeUnique` that takes longer than a few seconds to complete
3. Try to dispatch the job two times, more than 1 second apart
4. The job will be executed concurrently despite implementing `ShouldBeUnique`

Repository demonstrating the issue: https://github.com/Wowu/laravel-dynamodb-bug-report

<!-- If possible, please provide a GitHub repository to demonstrate your issue -->
<!-- laravel new bug-report --github="--public" -->


Pull request:
[8.x] Fix DynamoDB locks with 0 seconds duration
I've added a default lock duration of 1 day for DynamoDB locks.

This makes the behavior consistent with `DatabaseLock`:
https://github.com/laravel/framework/blob/d425952e8cc664b01de8bd654336c1be8dcf7444/src/Illuminate/Cache/DatabaseLock.php#L92-L95


Fix #43280

Patch:
diff --git a/src/Illuminate/Auth/EloquentUserProvider.php b/src/Illuminate/Auth/EloquentUserProvider.php
index b945cd486d..54dff6b87f 100755
--- a/src/Illuminate/Auth/EloquentUserProvider.php
+++ b/src/Illuminate/Auth/EloquentUserProvider.php
@@ -49,7 +49,7 @@ public function retrieveById($identifier)
         $model = $this->createModel();
 
         return $this->newModelQuery($model)
-                    ->where($model->qualifyColumn($model->getAuthIdentifierName()), $identifier)
+                    ->where($model->getAuthIdentifierName(), $identifier)
                     ->first();
     }
 
@@ -65,7 +65,7 @@ public function retrieveByToken($identifier, $token)
         $model = $this->createModel();
 
         $retrievedModel = $this->newModelQuery($model)->where(
-            $model->qualifyColumn($model->getAuthIdentifierName()), $identifier
+            $model->getAuthIdentifierName(), $identifier
         )->first();
 
         if (! $retrievedModel) {
diff --git a/src/Illuminate/Cache/DynamoDbLock.php b/src/Illuminate/Cache/DynamoDbLock.php
index 54eec53f78..9222607929 100644
--- a/src/Illuminate/Cache/DynamoDbLock.php
+++ b/src/Illuminate/Cache/DynamoDbLock.php
@@ -34,9 +34,11 @@ public function __construct(DynamoDbStore $dynamo, $name, $seconds, $owner = nul
      */
     public function acquire()
     {
-        return $this->dynamo->add(
-            $this->name, $this->owner, $this->seconds
-        );
+        if ($this->seconds > 0) {
+            return $this->dynamo->add($this->name, $this->owner, $this->seconds);
+        } else {
+            return $this->dynamo->add($this->name, $this->owner, 86400);
+        }
     }
 
     /**
diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php
index 33ed783590..bf3d21c34e 100755
--- a/src/Illuminate/Foundation/Application.php
+++ b/src/Illuminate/Foundation/Application.php
@@ -33,7 +33,7 @@ class Application extends Container implements ApplicationContract, CachesConfig
      *
      * @var string
      */
-    const VERSION = '8.83.21';
+    const VERSION = '8.83.22';
 
     /**
      * The base path for the Laravel installation.


Test patch:
diff --git a/tests/Auth/AuthEloquentUserProviderTest.php b/tests/Auth/AuthEloquentUserProviderTest.php
index a16f1f322c..ae34a1b4a0 100755
--- a/tests/Auth/AuthEloquentUserProviderTest.php
+++ b/tests/Auth/AuthEloquentUserProviderTest.php
@@ -22,8 +22,7 @@ public function testRetrieveByIDReturnsUser()
         $mock = m::mock(stdClass::class);
         $mock->shouldReceive('newQuery')->once()->andReturn($mock);
         $mock->shouldReceive('getAuthIdentifierName')->once()->andReturn('id');
-        $mock->shouldReceive('qualifyColumn')->with('id')->andReturn('users.id');
-        $mock->shouldReceive('where')->once()->with('users.id', 1)->andReturn($mock);
+        $mock->shouldReceive('where')->once()->with('id', 1)->andReturn($mock);
         $mock->shouldReceive('first')->once()->andReturn('bar');
         $provider->expects($this->once())->method('createModel')->willReturn($mock);
         $user = $provider->retrieveById(1);
@@ -40,8 +39,7 @@ public function testRetrieveByTokenReturnsUser()
         $mock = m::mock(stdClass::class);
         $mock->shouldReceive('newQuery')->once()->andReturn($mock);
         $mock->shouldReceive('getAuthIdentifierName')->once()->andReturn('id');
-        $mock->shouldReceive('qualifyColumn')->with('id')->andReturn('users.id');
-        $mock->shouldReceive('where')->once()->with('users.id', 1)->andReturn($mock);
+        $mock->shouldReceive('where')->once()->with('id', 1)->andReturn($mock);
         $mock->shouldReceive('first')->once()->andReturn($mockUser);
         $provider->expects($this->once())->method('createModel')->willReturn($mock);
         $user = $provider->retrieveByToken(1, 'a');
@@ -55,8 +53,7 @@ public function testRetrieveTokenWithBadIdentifierReturnsNull()
         $mock = m::mock(stdClass::class);
         $mock->shouldReceive('newQuery')->once()->andReturn($mock);
         $mock->shouldReceive('getAuthIdentifierName')->once()->andReturn('id');
-        $mock->shouldReceive('qualifyColumn')->with('id')->andReturn('users.id');
-        $mock->shouldReceive('where')->once()->with('users.id', 1)->andReturn($mock);
+        $mock->shouldReceive('where')->once()->with('id', 1)->andReturn($mock);
         $mock->shouldReceive('first')->once()->andReturn(null);
         $provider->expects($this->once())->method('createModel')->willReturn($mock);
         $user = $provider->retrieveByToken(1, 'a');
@@ -81,8 +78,7 @@ public function testRetrieveByBadTokenReturnsNull()
         $mock = m::mock(stdClass::class);
         $mock->shouldReceive('newQuery')->once()->andReturn($mock);
         $mock->shouldReceive('getAuthIdentifierName')->once()->andReturn('id');
-        $mock->shouldReceive('qualifyColumn')->with('id')->andReturn('users.id');
-        $mock->shouldReceive('where')->once()->with('users.id', 1)->andReturn($mock);
+        $mock->shouldReceive('where')->once()->with('id', 1)->andReturn($mock);
         $mock->shouldReceive('first')->once()->andReturn($mockUser);
         $provider->expects($this->once())->method('createModel')->willReturn($mock);
         $user = $provider->retrieveByToken(1, 'a');