Skip to content

Commit b2d85bf

Browse files
committed
PHPC-980: Driver session spec tests
1 parent d1d9d4a commit b2d85bf

File tree

4 files changed

+271
-12
lines changed

4 files changed

+271
-12
lines changed

php_phongo.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ static bool process_read_preference(zval *option, bson_t *mongoc_opts, zval **zr
538538
return true;
539539
}
540540

541-
static bool process_session(zval *option, bson_t *mongoc_opts TSRMLS_DC)
541+
static bool process_session(zval *option, bson_t *mongoc_opts, zval **zsession, mongoc_client_t *client TSRMLS_DC)
542542
{
543543
const mongoc_client_session_t *client_session;
544544

@@ -549,11 +549,20 @@ static bool process_session(zval *option, bson_t *mongoc_opts TSRMLS_DC)
549549

550550
client_session = Z_SESSION_OBJ_P(option)->client_session;
551551

552+
if (client != mongoc_client_session_get_client(client_session)) {
553+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Cannot use Session started from a different Manager");
554+
return false;
555+
}
556+
552557
if (!mongoc_client_session_append(Z_SESSION_OBJ_P(option)->client_session, mongoc_opts, NULL)) {
553558
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error appending \"session\" option");
554559
return false;
555560
}
556561

562+
if (zsession) {
563+
*zsession = option;
564+
}
565+
557566
return true;
558567
}
559568

@@ -580,7 +589,7 @@ static bool process_write_concern(zval *option, bson_t *mongoc_opts, zval **zwri
580589
return true;
581590
}
582591

583-
static int phongo_execute_parse_options(mongoc_client_t* client, int server_id, zval *driver_options, int type, bson_t *mongoc_opts, zval **zreadPreference, zval **zwriteConcern TSRMLS_DC)
592+
static int phongo_execute_parse_options(mongoc_client_t* client, int server_id, zval *driver_options, int type, bson_t *mongoc_opts, zval **zreadPreference, zval **zwriteConcern, zval **zsession TSRMLS_DC)
584593
{
585594
if (driver_options && Z_TYPE_P(driver_options) == IS_ARRAY) {
586595
HashTable *ht_data = HASH_OF(driver_options);
@@ -604,7 +613,7 @@ static int phongo_execute_parse_options(mongoc_client_t* client, int server_id,
604613
return false;
605614
}
606615
} else if ((!strcmp(ZSTR_VAL(string_key), "session"))) {
607-
if (!process_session(driver_option, mongoc_opts)) {
616+
if (!process_session(driver_option, mongoc_opts, zsession, client)) {
608617
return false;
609618
}
610619
} else if ((!strcasecmp(ZSTR_VAL(string_key), "writeConcern")) && (type & PHONGO_COMMAND_WRITE)) {
@@ -640,8 +649,8 @@ static int phongo_execute_parse_options(mongoc_client_t* client, int server_id,
640649
if (!process_read_preference(*driver_option, mongoc_opts, zreadPreference, client, server_id TSRMLS_CC)) {
641650
return false;
642651
}
643-
} else if ((!strcmp(ZSTR_VAL(string_key), "session"))) {
644-
if (!process_session(*driver_option, mongoc_opts)) {
652+
} else if ((!strcasecmp(ZSTR_VAL(string_key), "session"))) {
653+
if (!process_session(*driver_option, mongoc_opts, zsession, client TSRMLS_CC)) {
645654
return false;
646655
}
647656
} else if ((!strcasecmp(string_key, "writeConcern")) && (type & PHONGO_COMMAND_WRITE)) {
@@ -666,6 +675,7 @@ bool phongo_execute_bulk_write(mongoc_client_t *client, const char *namespace, p
666675
mongoc_bulk_operation_t *bulk = bulk_write->bulk;
667676
php_phongo_writeresult_t *writeresult;
668677
zval *zwriteConcern = NULL;
678+
zval *zsession = NULL;
669679
const mongoc_write_concern_t *write_concern;
670680
bson_t opts = BSON_INITIALIZER;
671681

@@ -682,7 +692,7 @@ bool phongo_execute_bulk_write(mongoc_client_t *client, const char *namespace, p
682692
/* FIXME: Legacy way of specifying the writeConcern option into this function */
683693
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_writeconcern_ce TSRMLS_CC)) {
684694
zwriteConcern = options;
685-
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_WRITE, &opts, NULL, &zwriteConcern TSRMLS_CC)) {
695+
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_WRITE, &opts, NULL, &zwriteConcern, &zsession TSRMLS_CC)) {
686696
bson_destroy(&opts);
687697
return false;
688698
}
@@ -693,6 +703,10 @@ bool phongo_execute_bulk_write(mongoc_client_t *client, const char *namespace, p
693703
mongoc_bulk_operation_set_collection(bulk, bulk_write->collection);
694704
mongoc_bulk_operation_set_client(bulk, client);
695705

706+
if (zsession) {
707+
mongoc_bulk_operation_set_client_session(bulk, Z_SESSION_OBJ_P(zsession)->client_session);
708+
}
709+
696710
/* If a write concern was not specified, libmongoc will use the client's
697711
* write concern; however, we should still fetch it for the write result. */
698712
write_concern = phongo_write_concern_from_zval(zwriteConcern TSRMLS_CC);
@@ -774,7 +788,6 @@ int phongo_execute_query(mongoc_client_t *client, const char *namespace, zval *z
774788
char *collname;
775789
mongoc_collection_t *collection;
776790
zval *zreadPreference = NULL;
777-
bson_t opts = BSON_INITIALIZER;
778791

779792
if (!phongo_split_namespace(namespace, &dbname, &collname)) {
780793
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s: %s", "Invalid namespace provided", namespace);
@@ -793,13 +806,10 @@ int phongo_execute_query(mongoc_client_t *client, const char *namespace, zval *z
793806
/* FIXME: Legacy way of specifying the readPreference option into this function */
794807
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_readpreference_ce TSRMLS_CC)) {
795808
zreadPreference = options;
796-
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_READ, &opts, &zreadPreference, NULL TSRMLS_CC)) {
797-
bson_destroy(&opts);
809+
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_READ, query->opts, &zreadPreference, NULL, NULL TSRMLS_CC)) {
798810
return false;
799811
}
800812

801-
bson_destroy(&opts);
802-
803813
cursor = mongoc_collection_find_with_opts(collection, query->filter, query->opts, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC));
804814
mongoc_collection_destroy(collection);
805815

@@ -857,7 +867,7 @@ int phongo_execute_command(mongoc_client_t *client, php_phongo_command_type_t ty
857867
/* FIXME: Legacy way of specifying the readPreference option into this function */
858868
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_readpreference_ce TSRMLS_CC)) {
859869
zreadPreference = options;
860-
} else if (!phongo_execute_parse_options(client, server_id, options, type, &opts, &zreadPreference, NULL TSRMLS_CC)) {
870+
} else if (!phongo_execute_parse_options(client, server_id, options, type, &opts, &zreadPreference, NULL, NULL TSRMLS_CC)) {
861871
return false;
862872
}
863873

tests/session/session-001.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
MongoDB\Driver\Session spec test: Pool is LIFO
3+
--SKIPIF--
4+
<?php if (getenv("TRAVIS")) exit("skip This currently fails on Travis because it doesn't run 3.6 yet"); ?>
5+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
6+
<?php NEEDS('STANDALONE'); ?>
7+
--FILE--
8+
<?php
9+
require_once __DIR__ . "/../utils/basic.inc";
10+
11+
$manager = new MongoDB\Driver\Manager(STANDALONE);
12+
13+
$firstSession = $manager->startSession();
14+
$firstSessionId = $firstSession->getLogicalSessionId();
15+
16+
unset($firstSession);
17+
18+
$secondSession = $manager->startSession();
19+
$secondSessionId = $secondSession->getLogicalSessionId();
20+
21+
var_dump($firstSessionId == $secondSessionId);
22+
23+
?>
24+
===DONE===
25+
<?php exit(0); ?>
26+
--EXPECT--
27+
bool(true)
28+
===DONE===

tests/session/session-002.phpt

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
--TEST--
2+
MongoDB\Driver\Session spec test: $clusterTime in commands
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 $lastSeenClusterTime;
13+
14+
public function aggregate()
15+
{
16+
$this->lastSeenClusterTime = null;
17+
18+
MongoDB\Driver\Monitoring\addSubscriber($this);
19+
20+
$manager = new MongoDB\Driver\Manager(REPLICASET);
21+
$session = $manager->startSession();
22+
23+
$command = new MongoDB\Driver\Command([
24+
'aggregate' => COLLECTION_NAME,
25+
'pipeline' => [],
26+
'cursor' => new stdClass(),
27+
]);
28+
$manager->executeReadWriteCommand(DATABASE_NAME, $command, ['session' => $session]);
29+
$manager->executeReadWriteCommand(DATABASE_NAME, $command, ['session' => $session]);
30+
31+
printf("Session reports last seen \$clusterTime: %s\n", ($session->getClusterTime() == $this->lastSeenClusterTime) ? 'yes' : 'no');
32+
33+
MongoDB\Driver\Monitoring\removeSubscriber($this);
34+
}
35+
36+
public function find()
37+
{
38+
$this->lastSeenClusterTime = null;
39+
40+
MongoDB\Driver\Monitoring\addSubscriber($this);
41+
42+
$manager = new MongoDB\Driver\Manager(REPLICASET);
43+
$session = $manager->startSession();
44+
45+
$query = new MongoDB\Driver\Query([]);
46+
$manager->executeQuery(NS, $query, ['session' => $session]);
47+
$manager->executeQuery(NS, $query, ['session' => $session]);
48+
49+
printf("Session reports last seen \$clusterTime: %s\n", ($session->getClusterTime() == $this->lastSeenClusterTime) ? 'yes' : 'no');
50+
51+
MongoDB\Driver\Monitoring\removeSubscriber($this);
52+
}
53+
54+
public function insert()
55+
{
56+
$this->lastSeenClusterTime = null;
57+
58+
MongoDB\Driver\Monitoring\addSubscriber($this);
59+
60+
$manager = new MongoDB\Driver\Manager(REPLICASET);
61+
$session = $manager->startSession();
62+
63+
$bulk = new MongoDB\Driver\BulkWrite();
64+
$bulk->insert(['x' => 1]);
65+
$manager->executeBulkWrite(NS, $bulk, ['session' => $session]);
66+
67+
$bulk = new MongoDB\Driver\BulkWrite();
68+
$bulk->insert(['x' => 2]);
69+
$manager->executeBulkWrite(NS, $bulk, ['session' => $session]);
70+
71+
printf("Session reports last seen \$clusterTime: %s\n", ($session->getClusterTime() == $this->lastSeenClusterTime) ? 'yes' : 'no');
72+
73+
MongoDB\Driver\Monitoring\removeSubscriber($this);
74+
}
75+
76+
public function ping()
77+
{
78+
$this->lastSeenClusterTime = null;
79+
80+
MongoDB\Driver\Monitoring\addSubscriber($this);
81+
82+
$manager = new MongoDB\Driver\Manager(REPLICASET);
83+
$session = $manager->startSession();
84+
85+
$command = new MongoDB\Driver\Command(['ping' => 1]);
86+
$manager->executeCommand(DATABASE_NAME, $command, ['session' => $session]);
87+
$manager->executeCommand(DATABASE_NAME, $command, ['session' => $session]);
88+
89+
printf("Session reports last seen \$clusterTime: %s\n", ($session->getClusterTime() == $this->lastSeenClusterTime) ? 'yes' : 'no');
90+
91+
MongoDB\Driver\Monitoring\removeSubscriber($this);
92+
}
93+
94+
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
95+
{
96+
$command = $event->getCommand();
97+
$hasClusterTime = isset($command->{'$clusterTime'});
98+
99+
printf("%s command includes \$clusterTime: %s\n", $event->getCommandName(), $hasClusterTime ? 'yes' : 'no');
100+
101+
if ($hasClusterTime && $this->lastSeenClusterTime !== null) {
102+
printf("%s command uses last seen \$clusterTime: %s\n", $event->getCommandName(), ($command->{'$clusterTime'} == $this->lastSeenClusterTime) ? 'yes' : 'no');
103+
}
104+
}
105+
106+
public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
107+
{
108+
$reply = $event->getReply();
109+
$hasClusterTime = isset($reply->{'$clusterTime'});
110+
111+
printf("%s command reply includes \$clusterTime: %s\n", $event->getCommandName(), $hasClusterTime ? 'yes' : 'no');
112+
113+
if ($hasClusterTime) {
114+
$this->lastSeenClusterTime = $reply->{'$clusterTime'};
115+
}
116+
}
117+
118+
public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
119+
{
120+
}
121+
}
122+
123+
echo "\nTesting aggregate command\n";
124+
(new Test)->aggregate();
125+
126+
echo "\nTesting find command\n";
127+
(new Test)->find();
128+
129+
echo "\nTesting insert command\n";
130+
(new Test)->insert();
131+
132+
echo "\nTesting ping command\n";
133+
(new Test)->ping();
134+
135+
?>
136+
===DONE===
137+
<?php exit(0); ?>
138+
--EXPECT--
139+
Testing aggregate command
140+
aggregate command includes $clusterTime: yes
141+
aggregate command reply includes $clusterTime: yes
142+
aggregate command includes $clusterTime: yes
143+
aggregate command uses last seen $clusterTime: yes
144+
aggregate command reply includes $clusterTime: yes
145+
Session reports last seen $clusterTime: yes
146+
147+
Testing find command
148+
find command includes $clusterTime: yes
149+
find command reply includes $clusterTime: yes
150+
find command includes $clusterTime: yes
151+
find command uses last seen $clusterTime: yes
152+
find command reply includes $clusterTime: yes
153+
Session reports last seen $clusterTime: yes
154+
155+
Testing insert command
156+
insert command includes $clusterTime: yes
157+
insert command reply includes $clusterTime: yes
158+
insert command includes $clusterTime: yes
159+
insert command uses last seen $clusterTime: yes
160+
insert command reply includes $clusterTime: yes
161+
Session reports last seen $clusterTime: yes
162+
163+
Testing ping command
164+
ping command includes $clusterTime: yes
165+
ping command reply includes $clusterTime: yes
166+
ping command includes $clusterTime: yes
167+
ping command uses last seen $clusterTime: yes
168+
ping command reply includes $clusterTime: yes
169+
Session reports last seen $clusterTime: yes
170+
===DONE===

tests/session/session-003.phpt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
MongoDB\Driver\Session spec test: session cannot be used for different clients
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php NEEDS('STANDALONE'); CLEANUP(REPLICASET); ?>
6+
--FILE--
7+
<?php
8+
require_once __DIR__ . "/../utils/basic.inc";
9+
10+
// Vary heartbeatFrequencyMS to ensure each Manager gets a different client
11+
$manager = new MongoDB\Driver\Manager(STANDALONE, ['heartbeatFrequencyMS' => 60000]);
12+
$otherManager = new MongoDB\Driver\Manager(STANDALONE, ['heartbeatFrequencyMS' => 90000]);
13+
14+
// Create a session with the second Manager (associated with different client)
15+
$session = $otherManager->startSession();
16+
17+
echo "\nTesting executeBulkWrite()\n";
18+
echo throws(function() use ($manager, $session) {
19+
$bulk = new MongoDB\Driver\BulkWrite();
20+
$bulk->insert(['x' => 1]);
21+
$manager->executeBulkWrite(NS, $bulk, ['session' => $session]);
22+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
23+
24+
echo "\nTesting executeCommand()\n";
25+
echo throws(function() use ($manager, $session) {
26+
$command = new MongoDB\Driver\Command(['ping' => 1]);
27+
$manager->executeCommand(DATABASE_NAME, $command, ['session' => $session]);
28+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
29+
30+
echo "\nTesting executeQuery()\n";
31+
echo throws(function() use ($manager, $session) {
32+
$query = new MongoDB\Driver\Query([]);
33+
$manager->executeQuery(NS, $query, ['session' => $session]);
34+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
35+
36+
?>
37+
===DONE===
38+
<?php exit(0); ?>
39+
--EXPECT--
40+
Testing executeBulkWrite()
41+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
42+
Cannot use Session started from a different Manager
43+
44+
Testing executeCommand()
45+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
46+
Cannot use Session started from a different Manager
47+
48+
Testing executeQuery()
49+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
50+
Cannot use Session started from a different Manager
51+
===DONE===

0 commit comments

Comments
 (0)