Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't store cache lock #2609

Closed
VietTQ8 opened this issue Sep 8, 2023 · 7 comments · Fixed by #2877
Closed

Can't store cache lock #2609

VietTQ8 opened this issue Sep 8, 2023 · 7 comments · Fixed by #2877

Comments

@VietTQ8
Copy link

VietTQ8 commented Sep 8, 2023

  • Laravel-mongodb Version: #.#.#
  • PHP Version: #.#.#
  • Database Driver & Version:

Description:

I have issue when store data to cache lock with MongoDB driver. error also occurs when dispatch a job implements ShouldBeUnique.

Method acquire Illuminate\Cache\DatabaseLock catch QueryException to update record cache lock table. But with mongodb driver connection throw MongoDB\Driver\Exception\BulkWriteException so can not excute snippet code in catch block

Illuminate\Cache\DatabaseLock

public function acquire()
    {
        $acquired = false;

        try {
            $this->connection->table($this->table)->insert([
                'key' => $this->name,
                'owner' => $this->owner,
                'expiration' => $this->expiresAt(),
            ]);

            $acquired = true;
        } catch (QueryException $e) {
            $updated = $this->connection->table($this->table)
                ->where('key', $this->name)
                ->where(function ($query) {
                    return $query->where('owner', $this->owner)->orWhere('expiration', '<=', time());
                })->update([
                    'owner' => $this->owner,
                    'expiration' => $this->expiresAt(),
                ]);

            $acquired = $updated >= 1;
        }

        if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
            $this->connection->table($this->table)->where('expiration', '<=', time())->delete();
        }

        return $acquired;
    }

Steps to reproduce

$lock = Cache::lock('foo', 10);
$lock->block(5);

Job:

class ExampleJob implements ShouldQueue, ShouldBeUnique {
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UsesBackoffStrategy;

        public RequestQueue $request;
        public $tries = 2;

        /**
         * The number of seconds the job can run before timing out.
         *
         * @var int
         */
        public $timeout = 600;

        /**
         * Indicate if the job should be marked as failed on timeout.
         *
         * @var bool
         */
        public $failOnTimeout = true;

        /**
         * Create a new job instance.
         *
         * @return void
         */
        public function __construct() {
            //
        }

        /**
         * Execute the job.
         *
         * @return void
         */
        public function handle() {
       }

dispatch job:
ExampleJob ::dispatch();

Logs: Exception message like:

local.ERROR: E11000 duplicate key error collection: mes.cache_locks index: key_1 dup key: { key: "_cachefoo" } {"userId":"admin","exception":"[object] (MongoDB\\Driver\\Exception\\BulkWriteException(code: 11000): E11000 duplicate key error collection: mes.cache_locks index: key_1 dup key: { key: \"_cachefoo\" } at /var/www/vendor/mongodb/mongodb/src/Operation/InsertMany.php:157)

Could you suggest any ways solve this issue ?

@alcaeus
Copy link
Member

alcaeus commented Oct 19, 2023

Thank you for reporting this! It looks like one way would be to only throw a QueryException from the MongoDB connection. However, this is not really desirable, as QueryException extends PDOException, which definitely isn't the right inheritance chain. Instead, we would either have to provide our own DatabaseStore for people to use with MongoDB, or teach the DatabaseLock class in Laravel itself that there may be other exceptions that needed to be caught (e.g. by providing a marker interface instead of a class to extend).

We'll take a look internally and discuss this further. Could you also please share your cache configuration so we have an idea how you configured the cache and can reproduce it in our own tests? Thank you!

@VietTQ8
Copy link
Author

VietTQ8 commented Oct 19, 2023

Thank you for reporting this! It looks like one way would be to only throw a QueryException from the MongoDB connection. However, this is not really desirable, as QueryException extends PDOException, which definitely isn't the right inheritance chain. Instead, we would either have to provide our own DatabaseStore for people to use with MongoDB, or teach the DatabaseLock class in Laravel itself that there may be other exceptions that needed to be caught (e.g. by providing a marker interface instead of a class to extend).

We'll take a look internally and discuss this further. Could you also please share your cache configuration so we have an idea how you configured the cache and can reproduce it in our own tests? Thank you!

Yes sure this is my cache config

<?php

use Illuminate\Support\Str;

return [

    /*
    |--------------------------------------------------------------------------
    | Default Cache Store
    |--------------------------------------------------------------------------
    |
    | This option controls the default cache connection that gets used while
    | using this caching library. This connection is used when another is
    | not explicitly specified when executing a given caching function.
    |
    | Supported: "apc", "array", "database", "file",
    |            "memcached", "redis", "dynamodb"
    |
    */

    'default' => env('CACHE_DRIVER', 'file'),

    /*
    |--------------------------------------------------------------------------
    | Cache Stores
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the cache "stores" for your application as
    | well as their drivers. You may even define multiple stores for the
    | same cache driver to group types of items stored in your caches.
    |
    */

    'stores' => [

        'array' => [
            'driver' => 'array',
            'serialize' => false,
        ],

        'database' => [
            'driver' => 'database',
            'table' => 'cache',
            'connection' => null,
        ],

        'file' => [
            'driver' => 'file',
            'path' => storage_path('framework/cache/data'),
        ],

    ],


    'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),

];

in .env set CACHE_DRIVER=database

Hope we can find the way to solve it, @alcaeus .
Thanks!

@GromNaN
Copy link
Member

GromNaN commented Nov 8, 2023

Ticked by PHPORM-99

@VietTQ8
Copy link
Author

VietTQ8 commented Jan 17, 2024

Ticked by PHPORM-99

Hi @GromNaN could you have any information about to resolve this ticket ?

@GromNaN
Copy link
Member

GromNaN commented Jan 19, 2024

The DatabaseLock can't be used. We need to create a dedicated MongoDBStore (cache) and MongoDBLock (lock), like there is for DynamoDB and Redis in the Laravel Framework.

Are you interested in working on this topic?

@VietTQ8
Copy link
Author

VietTQ8 commented Feb 1, 2024

The DatabaseLock can't be used. We need to create a dedicated MongoDBStore (cache) and MongoDBLock (lock), like there is for DynamoDB and Redis in the Laravel Framework.

Are you interested in working on this topic?

Yes thanks @GromNaN , I think it is the best solution. Could you detail about this topic?

@GromNaN
Copy link
Member

GromNaN commented Feb 1, 2024

How I think it should be implemented (but I never worked with Laravel lock and cache store before):

Cache

Lock

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants