Closed kralos closed 6 years ago
Okay I just found something very interesting, I located the jobs from run 5b355cbc58004200011ed5a2
and all of them have a different worker name to this exception. I suspect 2 runs fired with exactly the same id and 1 failed to start but another one worked. I'm actually running this in cron:
*/5 * * * * /my-project/bin/console -v dtc:queue:run -d 300 my-worker-1
*/5 * * * * /my-project/bin/console -v dtc:queue:run -d 300 my-worker-2
*/5 * * * * /my-project/bin/console -v dtc:queue:run -d 300 my-worker-3
*/5 * * * * /my-project/bin/console -v dtc:queue:run -d 300 my-worker-4
project / worker names obfuscated for privacy.
The above exception happened on my-worker-1
but ALL jobs were processed by my-worker-2
. No other jobs were processed in the 300 second time window.
I suspect this issue is caused by http://php.net/manual/en/class.mongoid.php using purely time based Id generation.
See https://jira.mongodb.org/browse/PHP-166
Actually I just realised we're using alcaeus/mongo-php-adapter
1.1.5
so MongoId
generation is handled by alcaeus/mongo-php-adapter
, not the legacy driver...
DtcQueueBundle
uses:
/**
* @Grid\Column(sortable=true, order=1)
* @ODM\Id
*/
protected $id;
Which in DoctrineODM
means use AUTO
- Uses the native generated MongoId.
DoctrineODM
calls:
public function generate(DocumentManager $dm, $document)
{
return new \MongoId();
}
Which alcaeus/mongo-php-adapter
provides:
class MongoId implements Serializable, TypeInterface, JsonSerializable
{
public function __construct($id = null)
{
$this->createObjectID($id);
}
/********/
private function createObjectID($id)
{
try {
if (is_string($id)) {
$this->objectID = new ObjectID($id);
} elseif ($id instanceof self || $id instanceof ObjectID) {
$this->objectID = new ObjectID((string) $id);
} else {
$this->objectID = new ObjectID();
}
} catch (\Exception $e) {
throw new MongoException('Invalid object ID', 19);
}
}
}
Since MongoId
is constructed without a parameter, $this->objectID = new ObjectID();
is called.
Meaning the Id collision is happening inside ext-mongodb
ObjectId.c
static PHP_METHOD(ObjectId, __construct)
{
php_phongo_objectid_t* intern;
zend_error_handling error_handling;
char* id = NULL;
phongo_zpp_char_len id_len;
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
intern = Z_OBJECTID_OBJ_P(getThis());
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &id, &id_len) == FAILURE) {
zend_restore_error_handling(&error_handling TSRMLS_CC);
return;
}
zend_restore_error_handling(&error_handling TSRMLS_CC);
if (id) {
php_phongo_objectid_init_from_hex_string(intern, id, id_len TSRMLS_CC);
} else {
php_phongo_objectid_init(intern);
}
}
php_phongo_objectid_init(intern);
is called when no params are passed.
static bool php_phongo_objectid_init(php_phongo_objectid_t* intern)
{
bson_oid_t oid;
intern->initialized = true;
bson_oid_init(&oid, NULL);
bson_oid_to_string(&oid, intern->oid);
return true;
}
Id generation appears to happen in bson_oid_init
in libbson
Proof of bug in or below mongodb/mongodb
layer
test-lib-mongodb.php
#!/usr/bin/env php
<?php
// mongodb/mongodb
require __DIR__.'/vendor/autoload.php';
$client = new MongoDB\Client('mongodb://127.0.0.1');
$collection = $client->collisions->test;
$objectId = new MongoDB\BSON\ObjectId();
$collection->insertOne([
'_id' => $objectId,
'name' => 'mongodb/mongodb',
]);
crontab
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
* * * * * /test/test-lib-mongodb.php
(There are 20 entries)
wait for the next minute
mongo cli
> use collisions
switched to db collisions
> db.test.find().count();
0
> db.test.find().count();
17
I have 3 emails from cron errors like this:
Fatal error: Uncaught MongoDB\Driver\Exception\BulkWriteException: E11000 duplicate key error collection: collisions.test index: _id_ dup key: { : ObjectId('5b3590c2cb6caa00012c2fa2') } in /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php on line 114
MongoDB\Driver\Exception\BulkWriteException: E11000 duplicate key error collection: collisions.test index: _id_ dup key: { : ObjectId('5b3590c2cb6caa00012c2fa2') } in /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php on line 114
Call Stack:
0.0001 389728 1. {main}() /test/test-lib-mongodb.php:0
0.1888 879936 2. MongoDB\Collection->insertOne() /test/test-lib-mongodb.php:12
0.2000 896712 3. MongoDB\Operation\InsertOne->execute() /test/vendor/mongodb/mongodb/src/Collection.php:799
0.2001 897392 4. MongoDB\Driver\Server->executeBulkWrite() /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php:114
PHP Fatal error: Uncaught MongoDB\Driver\Exception\BulkWriteException: E11000 duplicate key error collection: collisions.test index: _id_ dup key: { : ObjectId('5b3590c2cb6caa00012c2fa2') } in /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php:114
Stack trace:
#0 /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php(114): MongoDB\Driver\Server->executeBulkWrite('collisions.test', Object(MongoDB\Driver\BulkWrite), Array)
#1 /test/vendor/mongodb/mongodb/src/Collection.php(799): MongoDB\Operation\InsertOne->execute(Object(MongoDB\Driver\Server))
#2 /test/test-lib-mongodb.php(12): MongoDB\Collection->insertOne(Array)
#3 {main}
thrown in /test/vendor/mongodb/mongodb/src/Operation/InsertOne.php on line 114
I also tried this at the ext-mongodb
layer but i cannot reproduce it.
#!/usr/bin/env php
<?php
// ext-mongodb
$manager = new MongoDB\Driver\Manager('mongodb://127.0.0.1');
$bulk = new MongoDB\Driver\BulkWrite();
$objectId = new MongoDB\BSON\ObjectId();
$bulk->insert([
'_id' => $objectId,
'name' => 'ext-mongodb',
]);
$manager->executeBulkWrite('collisions.test', $bulk);
Since I've proven this is not an issue in DtcQueueBundle
I'm closing this ticket and re-opening on mongodb/mongodb
mmucklo/DtcQueueBundle
4.7.1
symfony/symfony
3.4.12
doctrine/mongodb
1.6.2
doctrine/mongodb-odm
1.2.4
alcaeus/mongo-php-adapter
1.1.5
ext-mongodb
1.4.3
libbson
1.9.4
MongoDB3.6.3
PHP7.2.6
Not sure how this is happening yet but i've seen it in multiple environments / servers etc.
I'm starting my runs from crontab like this:
Problem seems to be hard to re-produce.
Exception trace (happened at Jun 28, 2018 10:10:04 PM UTC):
After the exception happened I looked at the Live Runs page:
Then the run got archived: