Skip to content

Commit 1567352

Browse files
committed
Merge pull request #710
2 parents 98c4d09 + d99153b commit 1567352

20 files changed

+1064
-2
lines changed

src/MongoDB/Session.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,61 @@
2727

2828
zend_class_entry *php_phongo_session_ce;
2929

30+
static bool php_phongo_session_get_timestamp_parts(zval *obj, uint32_t *timestamp, uint32_t *increment TSRMLS_DC)
31+
{
32+
bool retval = false;
33+
#if PHP_VERSION_ID >= 70000
34+
zval ztimestamp;
35+
zval zincrement;
36+
37+
zend_call_method_with_0_params(obj, NULL, NULL, "getTimestamp", &ztimestamp);
38+
39+
if (Z_ISUNDEF(ztimestamp) || EG(exception)) {
40+
goto cleanup;
41+
}
42+
43+
zend_call_method_with_0_params(obj, NULL, NULL, "getIncrement", &zincrement);
44+
45+
if (Z_ISUNDEF(zincrement) || EG(exception)) {
46+
goto cleanup;
47+
}
48+
49+
*timestamp = Z_LVAL(ztimestamp);
50+
*increment = Z_LVAL(zincrement);
51+
#else
52+
zval *ztimestamp = NULL;
53+
zval *zincrement = NULL;
54+
55+
zend_call_method_with_0_params(&obj, NULL, NULL, "getTimestamp", &ztimestamp);
56+
57+
if (Z_ISUNDEF(ztimestamp) || EG(exception)) {
58+
goto cleanup;
59+
}
60+
61+
zend_call_method_with_0_params(&obj, NULL, NULL, "getIncrement", &zincrement);
62+
63+
if (Z_ISUNDEF(zincrement) || EG(exception)) {
64+
goto cleanup;
65+
}
66+
67+
*timestamp = Z_LVAL_P(ztimestamp);
68+
*increment = Z_LVAL_P(zincrement);
69+
#endif
70+
71+
retval = true;
72+
73+
cleanup:
74+
if (!Z_ISUNDEF(ztimestamp)) {
75+
zval_ptr_dtor(&ztimestamp);
76+
}
77+
78+
if (!Z_ISUNDEF(zincrement)) {
79+
zval_ptr_dtor(&zincrement);
80+
}
81+
82+
return retval;
83+
}
84+
3085
/* {{{ proto void MongoDB\Driver\Session::advanceClusterTime(array|object $clusterTime)
3186
Advances the cluster time for this Session */
3287
static PHP_METHOD(Session, advanceClusterTime)
@@ -56,6 +111,30 @@ static PHP_METHOD(Session, advanceClusterTime)
56111
bson_destroy(&cluster_time);
57112
} /* }}} */
58113

114+
/* {{{ proto void MongoDB\Driver\Session::advanceOperationTime(MongoDB\BSON\Timestamp $timestamp)
115+
Advances the operation time for this Session */
116+
static PHP_METHOD(Session, advanceOperationTime)
117+
{
118+
php_phongo_session_t *intern;
119+
zval *ztimestamp;
120+
uint32_t timestamp = 0;
121+
uint32_t increment = 0;
122+
SUPPRESS_UNUSED_WARNING(return_value_ptr) SUPPRESS_UNUSED_WARNING(return_value_used)
123+
124+
125+
intern = Z_SESSION_OBJ_P(getThis());
126+
127+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &ztimestamp, php_phongo_timestamp_interface_ce) == FAILURE) {
128+
return;
129+
}
130+
131+
if (!php_phongo_session_get_timestamp_parts(ztimestamp, &timestamp, &increment TSRMLS_CC)) {
132+
return;
133+
}
134+
135+
mongoc_client_session_advance_operation_time(intern->client_session, timestamp, increment);
136+
} /* }}} */
137+
59138
/* {{{ proto object|null MongoDB\Driver\Session::getClusterTime()
60139
Returns the cluster time for this Session */
61140
static PHP_METHOD(Session, getClusterTime)
@@ -123,18 +202,51 @@ static PHP_METHOD(Session, getLogicalSessionId)
123202
#endif
124203
} /* }}} */
125204

205+
/* {{{ proto MongoDB\BSON\Timestamp MongoDB\Driver\Session::getOperationTime()
206+
Returns the operation time for this Session */
207+
static PHP_METHOD(Session, getOperationTime)
208+
{
209+
php_phongo_session_t *intern;
210+
uint32_t timestamp, increment;
211+
SUPPRESS_UNUSED_WARNING(return_value_ptr) SUPPRESS_UNUSED_WARNING(return_value_used)
212+
213+
214+
intern = Z_SESSION_OBJ_P(getThis());
215+
216+
if (zend_parse_parameters_none() == FAILURE) {
217+
return;
218+
}
219+
220+
mongoc_client_session_get_operation_time(intern->client_session, &timestamp, &increment);
221+
222+
/* mongoc_client_session_get_operation_time() returns 0 for both parts if
223+
* the session has not been used. According to the causal consistency spec,
224+
* the operation time for an unused session is null. */
225+
if (timestamp == 0 && increment == 0) {
226+
RETURN_NULL();
227+
}
228+
229+
php_phongo_new_timestamp_from_increment_and_timestamp(return_value, increment, timestamp TSRMLS_CC);
230+
} /* }}} */
231+
126232
/* {{{ MongoDB\Driver\Session function entries */
127233
ZEND_BEGIN_ARG_INFO_EX(ai_Session_advanceClusterTime, 0, 0, 1)
128234
ZEND_ARG_INFO(0, clusterTime)
129235
ZEND_END_ARG_INFO()
130236

237+
ZEND_BEGIN_ARG_INFO_EX(ai_Session_advanceOperationTime, 0, 0, 1)
238+
ZEND_ARG_INFO(0, timestamp)
239+
ZEND_END_ARG_INFO()
240+
131241
ZEND_BEGIN_ARG_INFO_EX(ai_Session_void, 0, 0, 0)
132242
ZEND_END_ARG_INFO()
133243

134244
static zend_function_entry php_phongo_session_me[] = {
135245
PHP_ME(Session, advanceClusterTime, ai_Session_advanceClusterTime, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
246+
PHP_ME(Session, advanceOperationTime, ai_Session_advanceOperationTime, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
136247
PHP_ME(Session, getClusterTime, ai_Session_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
137248
PHP_ME(Session, getLogicalSessionId, ai_Session_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
249+
PHP_ME(Session, getOperationTime, ai_Session_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
138250
ZEND_NAMED_ME(__construct, PHP_FN(MongoDB_disabled___construct), ai_Session_void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
139251
ZEND_NAMED_ME(__wakeup, PHP_FN(MongoDB_disabled___wakeup), ai_Session_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
140252
PHP_FE_END
@@ -243,6 +355,29 @@ static HashTable *php_phongo_session_get_debug_info(zval *object, int *is_temp T
243355
cs_opts = mongoc_client_session_get_opts(intern->client_session);
244356
ADD_ASSOC_BOOL_EX(&retval, "causalConsistency", mongoc_session_opts_get_causal_consistency(cs_opts));
245357

358+
{
359+
uint32_t timestamp, increment;
360+
361+
mongoc_client_session_get_operation_time(intern->client_session, &timestamp, &increment);
362+
363+
if (timestamp && increment) {
364+
#if PHP_VERSION_ID >= 70000
365+
zval ztimestamp;
366+
367+
php_phongo_new_timestamp_from_increment_and_timestamp(&ztimestamp, increment, timestamp TSRMLS_CC);
368+
ADD_ASSOC_ZVAL_EX(&retval, "operationTime", &ztimestamp);
369+
#else
370+
zval *ztimestamp;
371+
372+
MAKE_STD_ZVAL(ztimestamp);
373+
php_phongo_new_timestamp_from_increment_and_timestamp(ztimestamp, increment, timestamp TSRMLS_CC);
374+
ADD_ASSOC_ZVAL_EX(&retval, "operationTime", ztimestamp);
375+
#endif
376+
} else {
377+
ADD_ASSOC_NULL_EX(&retval, "operationTime");
378+
}
379+
}
380+
246381
return Z_ARRVAL(retval);
247382
} /* }}} */
248383
/* }}} */
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Causal consistency: new session has no operation time
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php NEEDS('REPLICASET'); ?>
6+
--FILE--
7+
<?php
8+
require_once __DIR__ . "/../utils/basic.inc";
9+
10+
$manager = new MongoDB\Driver\Manager(REPLICASET);
11+
$session = $manager->startSession();
12+
13+
echo "Initial operation time:\n";
14+
var_dump($session->getOperationTime());
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECT--
20+
Initial operation time:
21+
NULL
22+
===DONE===
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Causal consistency: first read in session does not include afterClusterTime
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php NEEDS('REPLICASET'); ?>
6+
--FILE--
7+
<?php
8+
require_once __DIR__ . "/../utils/basic.inc";
9+
require_once __DIR__ . "/../utils/observer.php";
10+
11+
(new CommandObserver)->observe(
12+
function() {
13+
$manager = new MongoDB\Driver\Manager(REPLICASET);
14+
$session = $manager->startSession();
15+
16+
$query = new MongoDB\Driver\Query([]);
17+
$manager->executeQuery(NS, $query, ['session' => $session]);
18+
},
19+
function(stdClass $command)
20+
{
21+
$hasAfterClusterTime = isset($command->readConcern->afterClusterTime);
22+
printf("Read includes afterClusterTime: %s\n", ($hasAfterClusterTime ? 'yes' : 'no'));
23+
}
24+
);
25+
26+
?>
27+
===DONE===
28+
<?php exit(0); ?>
29+
--EXPECT--
30+
Read includes afterClusterTime: no
31+
===DONE===
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
--TEST--
2+
Causal consistency: first read or write in session updates operationTime
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php NEEDS('REPLICASET'); CLEANUP(REPLICASET); ?>
6+
--FILE--
7+
<?php
8+
require_once __DIR__ . "/../utils/basic.inc";
9+
10+
class Test implements MongoDB\Driver\Monitoring\CommandSubscriber
11+
{
12+
private $lastSeenOperationTime;
13+
14+
public function executeBulkWrite()
15+
{
16+
$this->lastSeenOperationTime = null;
17+
18+
MongoDB\Driver\Monitoring\addSubscriber($this);
19+
20+
$manager = new MongoDB\Driver\Manager(REPLICASET);
21+
$session = $manager->startSession();
22+
23+
$bulk = new MongoDB\Driver\BulkWrite;
24+
$bulk->insert(['x' => 1]);
25+
$manager->executeBulkWrite(NS, $bulk, ['session' => $session]);
26+
27+
printf("Session reports last seen operationTime: %s\n", ($session->getOperationTime() == $this->lastSeenOperationTime) ? 'yes' : 'no');
28+
29+
MongoDB\Driver\Monitoring\removeSubscriber($this);
30+
}
31+
32+
public function executeCommand()
33+
{
34+
$this->lastSeenOperationTime = null;
35+
36+
MongoDB\Driver\Monitoring\addSubscriber($this);
37+
38+
$manager = new MongoDB\Driver\Manager(REPLICASET);
39+
$session = $manager->startSession();
40+
41+
$command = new MongoDB\Driver\Command(['ping' => 1]);
42+
$manager->executeCommand(DATABASE_NAME, $command, ['session' => $session]);
43+
44+
printf("Session reports last seen operationTime: %s\n", ($session->getOperationTime() == $this->lastSeenOperationTime) ? 'yes' : 'no');
45+
46+
MongoDB\Driver\Monitoring\removeSubscriber($this);
47+
}
48+
49+
public function executeQuery()
50+
{
51+
$this->lastSeenOperationTime = null;
52+
53+
MongoDB\Driver\Monitoring\addSubscriber($this);
54+
55+
$manager = new MongoDB\Driver\Manager(REPLICASET);
56+
$session = $manager->startSession();
57+
58+
$query = new MongoDB\Driver\Query([]);
59+
$manager->executeQuery(NS, $query, ['session' => $session]);
60+
61+
printf("Session reports last seen operationTime: %s\n", ($session->getOperationTime() == $this->lastSeenOperationTime) ? 'yes' : 'no');
62+
63+
MongoDB\Driver\Monitoring\removeSubscriber($this);
64+
}
65+
66+
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
67+
{
68+
}
69+
70+
public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
71+
{
72+
$reply = $event->getReply();
73+
$hasOperationTime = isset($reply->{'operationTime'});
74+
75+
printf("%s command reply includes operationTime: %s\n", $event->getCommandName(), $hasOperationTime ? 'yes' : 'no');
76+
77+
if ($hasOperationTime) {
78+
$this->lastSeenOperationTime = $reply->operationTime;
79+
}
80+
}
81+
82+
public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
83+
{
84+
}
85+
}
86+
87+
echo "Testing executeBulkWrite()\n";
88+
(new Test)->executeBulkWrite();
89+
90+
echo "\nTesting executeCommand()\n";
91+
(new Test)->executeCommand();
92+
93+
echo "\nTesting executeQuery()\n";
94+
(new Test)->executeQuery();
95+
96+
?>
97+
===DONE===
98+
<?php exit(0); ?>
99+
--EXPECT--
100+
Testing executeBulkWrite()
101+
insert command reply includes operationTime: yes
102+
Session reports last seen operationTime: yes
103+
104+
Testing executeCommand()
105+
ping command reply includes operationTime: yes
106+
Session reports last seen operationTime: yes
107+
108+
Testing executeQuery()
109+
find command reply includes operationTime: yes
110+
Session reports last seen operationTime: yes
111+
===DONE===

0 commit comments

Comments
 (0)