techfromsage / tripod-php

Object Graph Mapper for managing RDF data in Mongo
MIT License
29 stars 4 forks source link

Set etag in more compatible format. Fix microsecond precision #117

Closed rgubby closed 8 years ago

rgubby commented 8 years ago

The last release started using the new PHP mongo driver. Usage of MongoDate has been replaced with UTCDateTime. The problem with this is that the __toString() function for each of those return dates in a different format. MongoDate returns it in a microtime format, UTCDateTime return it in milliseconds since epoch.

If you only use one or the other, then there isn't a problem. But if you want to dual run the 2 versions (rolling upgrade of code) and you retrieve an etag from one server with the previous version on, but update to a newer server with new version on, you can get a mismatched etag.

We are converting the millisecond date format returned in the __toString() function of UTCDateTime into a float, then using the float part to return the microtime formatted date. This should now be compatible with MongoDate::__toString().

In addition to this - we have introduced a bug with means we are not storing the microsecond part of the date. We are doing a multiplication of 1000 AFTER rounding, rather than before rounding. So you end up with a value with 000 at the end - which always gives you a microsecond value of 000. If we don't store the microsecond part of the date, our new etag format will always return 0.00000000 as the microsecond part.

Todo

Resolves #115 Resolves #116

rgubby commented 8 years ago

Same etag as a MongoDate:

$updatedAt = new UTCDateTime(floor(microtime(true) * 1000));
echo $updatedAt;
MongoDB\BSON\UTCDateTime Object
(
    [milliseconds] => 1479817009038
)

$mongodate = new \MongoDate();
echo $mongodate;
0.03800000 1479817009

$seconds = $updatedAt->__toString() / 1000;
$eTag = number_format(($seconds - floor($seconds)), 6) . "00 " . floor($seconds);
echo print_r($eTag,true)
0.03800000 1479817009
rgubby commented 8 years ago

I have created a test script (https://github.com/talis/tripod-php/blob/a382b6ee9bd02cb54c24692d8a9b723668f6a5d3/scripts/mongo/testEtag.php) that gets all documents from a collection for a tenancy and outputs the etag.

I've run this for the surrey tenancy on CBD_config (71 records) and CBD_draft (244382 records).

I have then run a modified version of this script on the old mongo driver:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

require_once('./common.inc.php');
require_once('../../src/tripod.inc.php');

$config = json_decode(file_get_contents('../../test/rest-interface/config/tripod-config.json'), true);

\Tripod\Mongo\Config::setConfig($config);
\Tripod\Mongo\Config::getInstance()->setConfig($config);

$tripod = new \Tripod\Mongo\Driver('CBD_draft','surrey',array('defaultContext'=>'http://talisaspire.com/'));

require_once '../../src/tripod.inc.php';

$client = new \MongoClient();
$db = $client->selectDb('surrey');
$collection = $db->selectCollection('CBD_draft');
$cursor = $collection->find(array());
foreach($cursor as $doc) {
    $etag = isset($doc['_uts']) ? $doc['_uts']->__toString() : '' ;
    echo '_id.r: '.$doc['_id']['r'] . ' - ' . $etag . "\n";
}

Now I have both results saved out to a local file, I can then do a diff on the 2 files to see if there are any discrepancies.

I can confirm that there are none - the 2 files (one for CBD_config, one for CBD_draft) are identical. This confirms that the etag returned with the new method is equivalent of the MongoDate::__toString() function.

rgubby commented 8 years ago

Manual 👍 from @kiyanwang