Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.6k
[Lock] add mongodb store#31889
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
[Lock] add mongodb store#31889
Uh oh!
There was an error while loading.Please reload this page.
Conversation
kralos commentedJun 6, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
nicolas-grekas commentedJun 6, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Please target |
55526dc
to0e77ec6
Comparerebased from |
kralos commentedJun 6, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I've stripped it from
Added insymfony/symfony-docs#11735 |
This PR was merged into the 4.3 branch.Discussion----------[Lock] mongodb store removed from symfony 4.3MongoDbStore wasn't actually added in symfony 4.3, it will be added in 4.4.Seesymfony/symfony#31889Commits-------75ad033 #27345 Removed Lock MongoDbStore, it will be added in symfony 4.4
/** | ||
* MongoDbStore is a StoreInterface implementation using MongoDB as a storage | ||
* engine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
What about adding that it requires an external lib?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The patternpublic static function isSupported(): bool
has been added toMongoDbStore
as well as@requires extension mongodb
on the class. I've also moved theCAUTION:
phpdoc up to the class to be inline with other stores.
753d398
tof3f8568
CompareI'm looking into the failing test later today |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Nice feature !
Status: Needs Work
* | ||
* @internal | ||
*/ | ||
public static function isSupported(): bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
imho this is not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I did this because some of Jérémy's stores do it. I thought it might be useful later if a bundle extension needs to check support to auto default/configure something supported, but maybe we could add an interface for this later to all stores if we ever did something like that. Also thinking about it more... you could never auto configure a MongoDbStore since the database name option is mandatory. So I have removedstatic::isSupported()
.
*/ | ||
public function __construct(Client $mongo, array $options, float $initialTtl = 300.0) | ||
{ | ||
if (!static::isSupported()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
You can directly check if it is supported here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
done
* | ||
* @see https://docs.mongodb.com/manual/applications/replication/ | ||
*/ | ||
public function __construct(Client $mongo, array $options, float $initialTtl = 300.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
you should not type-hint the mongodb client, what happens if he does not exist ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Then the developer wouldn't be able to construct aMongoDB\Client
. If they pass something that isn't aMongoDB\Client
they would get a TypeError:
Uncaught TypeError: Argument 1 passed to Symfony\Component\Lock\Store\MongoDbStore::__construct() must be an instance of MongoDB\Client, x given
This is what I would want to happen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
What happens if you don't have MongoDB ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
if you don't havemongodb/mongodb
you can't construct aMongoDB\Client
if you don't haveext-mongodb
you can'tcomposer require mongodb/mongodb
if you somehow have installedmongodb/mongodb
and don't haveext-mongodb
you will fail to construct aMongoDB\Client
(this ismongodb/mongodb
's responsibility):
<?phprequire__DIR__.'/vendor/autoload.php';$mongoClient =newMongoDB\Client('mongodb://127.0.0.1');$store =newSymfony\Component\Lock\Store\MongoDbStore($mongoClient, ['database' =>'test',]);
$ docker run --rm -it -v $PWD:$PWD -w $PWD php php ./test.phpFatal error: Uncaught Error: Class 'MongoDB\Driver\Manager' not found in /home/kralos/src/symfony/vendor/mongodb/mongodb/src/Client.php:87Stack trace:#0 /home/kralos/src/symfony/test.php(5): MongoDB\Client->__construct('mongodb://127.0...')#1 {main} thrown in /home/kralos/src/symfony/vendor/mongodb/mongodb/src/Client.php on line 87
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
MongoDB\Driver\Manager
is provided byext-mongodb
MongoDB\Client
is provided bymongodb/mongodb
Symfony\Component\Lock\Store\MongoDbStore
depends onmongodb/mongodb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think you not type-hint MongoDB\Client for that reason. maybe i'm wrong cc@jderusse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I removed theif (!\extension_loaded('mongodb')) {
check in the constructor since it's impossible to hit it. You can't pass aMongoDB\Client
withoutmongodb/mongodb
which you can't have withoutext-mongodb
.
We still have in the class docblock@requires extension mongodb
if (!isset($this->options['database'])) { | ||
throw new InvalidArgumentException( | ||
'You must provide the "database" option for MongoDBStore' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
you should inline this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
done
if ($this->options['gcProbablity'] < 0.0 || $this->options['gcProbablity'] > 1.0) { | ||
throw new InvalidArgumentException(sprintf( | ||
'"%s" gcProbablity must be a float from 0.0 to 1.0, "%f" given.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
you should inline this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
done
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
6df4973
toa1fe6d0
Compare
|
This one looks ready for Symfony 4.4/5.0 👍 |
|| (random_int(0, PHP_INT_MAX) / PHP_INT_MAX) <= $this->options['gcProbablity'] | ||
) | ||
) { | ||
if ($this->getDatabaseVersion() < '2.2') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This works by accident,version_compare
should be used for these
>>> '2.10' < '2.2'=> true
instances: 2
But why are we adding support for legacy version anyway? 2.2 was EOL-ed in2014. We are paying the price not just with added complexity but with extra query on each request as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yeah, I'd prefer not to support<2.2
. removing support and documenting requirements
{ | ||
$this->getCollection()->deleteMany([ // filter | ||
'expires_at' => [ | ||
'$lte' => $this->createDateTime(microtime(true)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This is inconsistent. IMHO parameter should either be non-optional, or not specified here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
agreed, I've opted for not optional. the objective of the function is to create a MongoDb compliantUTCDateTime
to millisecond precision.
if (1 === \count($writeErrors)) { | ||
$code = $writeErrors[0]->getCode(); | ||
} else { | ||
$code = $e->getCode(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Minor thing, but else branch could be easily avoided here by specifying this as default. This is super cheap call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
updated
kralos commentedOct 15, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@nicolas-grekas can you let me know if you're happy for this to be merged? I'll rebase/squash etc if everyone is happy. The appveyor CI failure is not Lock component by the way...
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Could you update theStoreFactory
to add theMongoDB\Client
andmongodb://
in the list of managed DSN.
Need to update theCHANGELOG.md
d2ac16e
to30d9d2c
Comparekralos commentedNov 5, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
In order to add I have therefore added support for passing a I have also added DSN support to Neither
CI is currently passing (the errors from travis are related to components other then I've left the commit history for review purposes, if we are happy with the current iteration let me know and I can squash (if required). |
Uh oh!
There was an error while loading.Please reload this page.
@@ -62,7 +65,6 @@ public static function createStore($connection) | |||
case 0 === strpos($connection, 'flock://'): | |||
return new FlockStore(substr($connection, 8)); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
revert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
fixed, something funny happened with myrebase
} | ||
if ($this->options['gcProbablity'] < 0.0 || $this->options['gcProbablity'] > 1.0) { | ||
throw new InvalidArgumentException(sprintf('"%s" gcProbablity must be a float from 0.0 to 1.0, "%f" given.', __METHOD__, $this->options['gcProbablity'])); | ||
throw new InvalidArgumentException(sprintf('%s() gcProbablity must be a float from 0.0 to 1.0, "%f" given.', __METHOD__, $this->options['gcProbablity'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
remove"
in"%f" given
too (for consistency)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
updated
} | ||
if (null === $this->collection && !isset($this->options['database'])) { | ||
throw new InvalidArgumentException(sprintf('%s() requires the "database" option when constructing with a %s', __METHOD__, \is_object($mongo) ? \get_class($mongo) : \gettype($mongo))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
database and collection names could be defined in the DSN (similar to PDO). dsn =mongodb://[username:password@]host1[:port1][,host2[:port2:],...]/[database/][collection]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The database you see there is the authentication database, not the database to use for data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
isn't it the purpose of the querystring parameterauthSource
?https://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/installation.html#authentication
anyway, if thepath
component is a standard to declare the auth Database, we can use custom queryString parameters likemongodb://localhost/?lockDatabase=foo&lockCollection=bar
If we can not create the Store from a DSN, developpers using the FrameworkBundle won't be able to use it from the configurationframework.lock = '%env(LOCK_DNS)%
(seesymfony/symfony-docs#12523)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
isn't it the purpose of the querystring parameter
authSource
?
Actually I just tested this (mongodb 4.2.0) and if both/path
and?authSource
are specified/path
means application database and?authSource
means authentication database. We are still stuck for getting the collection name.
we can use custom queryString parameters
Seems to be the only way, do you have any objection to us taking?collection=bar
? I feel like if we ever had a conflict it would be because MongoDB is using this param for the exact same purpose.
I'm going to enforce when$mongo
is a:
MongoDB\Collection
:$options['database']
is ignored$options['collection']
is ignored
MongoDB\Client
:$options['database']
is mandatory$options['collection']
is mandatory
- MongoDB Connection URI string:
/path
is used otherwise$options['database']
, at least one is mandatory?collection=
is used otherwise$options['collection']
, at least one is mandatory
$database = parse_url($mongo, PHP_URL_PATH); | ||
if (null !== $database) { | ||
$this->options['database'] = $database; | ||
} | ||
$query = parse_url($mongo, PHP_URL_QUERY); | ||
$queryParams = []; | ||
parse_str($query ?? '', $queryParams); | ||
$collection = $queryParams['collection'] ?? null; | ||
if (null !== $collection) { | ||
$this->options['collection'] = $collection; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
what's about something similare to Messenger PDO
if (false ===$parsedUrl =parse_url($dsn)) {thrownewInvalidArgumentException(sprintf('The given Mongodb DSN "%s" is invalid.',$dsn));}$query = [];if (isset($parsedUrl['query'])) {parse_str($parsedUrl['query'],$query);}$this->option =$option +$query;$this->options['database'] =$this->options['database'] ??ltrim($parsedUrl['path'] ??'','/') ?:null;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
You prefer$options
to take precedence over/path?collection=
?
<?phpdeclare(strict_types=1);$mongo ='mongodb://127.0.0.1/uri-db?collection=uri-col';$options = ['database' =>'option-db','collection' =>'option-col',];if (false ===$parsedUrl =parse_url($mongo)) {thrownew \InvalidArgumentException(sprintf('The given MongoDB Connection URI "%s" is invalid.',$mongo));}$query = [];if (isset($parsedUrl['query'])) {parse_str($parsedUrl['query'],$query);}$options +=$query;$options['database'] =$options['database'] ??ltrim($parsedUrl['path'] ??'','/') ?:null;print_r($options);/* Array( [database] => option-db [collection] => option-col) */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I've implemented this but without polluting$this->options
with querystring params other thancollection
.
if (false ===$parsedUrl =parse_url($mongo)) {thrownewInvalidArgumentException(sprintf('The given MongoDB Connection URI "%s" is invalid.',$mongo));}$query = [];if (isset($parsedUrl['query'])) {parse_str($parsedUrl['query'],$query);}$this->options['collection'] =$this->options['collection'] ??$query['collection'] ??null;$this->options['database'] =$this->options['database'] ??ltrim($parsedUrl['path'] ??'','/') ?:null;
@jderusse happy with current implementation? want me to squash? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
LGTM. Thanks@kralos
@nicolas-grekas can we get this merged? do you need a squash first? I don't want to miss |
af7825d
toa6bfa59
CompareThank you@kralos. |
Uh oh!
There was an error while loading.Please reload this page.
ext-mongodb
andmongodb/mongodb
to test)4.45.1 Doc PRLooks like I messed up
kralos:27345-lock-mongodb
with a force push (trying to fix ci issues) right before it was merged tomaster
(4.3.0
).see#27648
Description
We should support Semaphore Locks with a MongoDB back end to allow those that already use MongoDB as a distributed storage engine.
Symfony already partially supports MongoDB for session storage:
Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler
Example