Skip to content

Commit 29e5121

Browse files
committed
PHPC-433: Persist mongoc_client_t objects in a HashTable
Clients will be hashed by Manager constructor arguments. Since they must persist between requests, the Manager destructor will no longer free the client and we'll need to start using persistent memory allocation for libbson and libmongoc.
1 parent ad0ae24 commit 29e5121

File tree

4 files changed

+154
-6
lines changed

4 files changed

+154
-6
lines changed

php_phongo.c

Lines changed: 150 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@
3939
#include <Zend/zend_exceptions.h>
4040
#include <ext/spl/spl_iterators.h>
4141
#include <ext/spl/spl_exceptions.h>
42+
#include <ext/standard/php_var.h>
43+
44+
#if PHP_VERSION_ID >= 70000
45+
# include <Zend/zend_smart_str.h>
46+
#else
47+
# include <ext/standard/php_smart_str.h>
48+
#endif
49+
4250
/* Stream wrapper */
4351
#include <main/php_streams.h>
4452
#include <main/php_network.h>
@@ -1378,6 +1386,84 @@ static void php_phongo_free_ssl_opt(mongoc_ssl_opt_t *ssl_opt)
13781386
efree(ssl_opt);
13791387
}
13801388

1389+
/* Creates a hash for a client by concatenating the URI string with serialized
1390+
* options arrays. On success, a persistent string is returned (i.e. pefree()
1391+
* should be used to free it) and hash_len will be set to the string's length.
1392+
* On error, an exception will have been thrown and NULL will be returned. */
1393+
static char *php_phongo_manager_make_client_hash(const char *uri_string, zval *options, zval *driverOptions, size_t *hash_len TSRMLS_DC)
1394+
{
1395+
char *hash = NULL;
1396+
smart_str var_buf = {0};
1397+
php_serialize_data_t var_hash;
1398+
1399+
#if PHP_VERSION_ID >= 70000
1400+
zval args;
1401+
1402+
array_init_size(&args, 3);
1403+
ADD_ASSOC_STRING(&args, "uri", (char *) uri_string);
1404+
1405+
if (options) {
1406+
ADD_ASSOC_ZVAL_EX(&args, "options", options);
1407+
Z_ADDREF_P(options);
1408+
} else {
1409+
ADD_ASSOC_NULL_EX(&args, "options");
1410+
}
1411+
1412+
if (driverOptions) {
1413+
ADD_ASSOC_ZVAL_EX(&args, "driverOptions", driverOptions);
1414+
Z_ADDREF_P(driverOptions);
1415+
} else {
1416+
ADD_ASSOC_NULL_EX(&args, "driverOptions");
1417+
}
1418+
1419+
PHP_VAR_SERIALIZE_INIT(var_hash);
1420+
php_var_serialize(&var_buf, &args, &var_hash);
1421+
PHP_VAR_SERIALIZE_DESTROY(var_hash);
1422+
1423+
if (!EG(exception)) {
1424+
*hash_len = ZSTR_LEN(var_buf.s);
1425+
hash = pestrndup(ZSTR_VAL(var_buf.s), *hash_len, 1);
1426+
}
1427+
1428+
zval_ptr_dtor(&args);
1429+
#else
1430+
zval *args;
1431+
1432+
MAKE_STD_ZVAL(args);
1433+
array_init_size(args, 3);
1434+
ADD_ASSOC_STRING(args, "uri", (char *) uri_string);
1435+
1436+
if (options) {
1437+
ADD_ASSOC_ZVAL_EX(args, "options", options);
1438+
Z_ADDREF_P(options);
1439+
} else {
1440+
ADD_ASSOC_NULL_EX(args, "options");
1441+
}
1442+
1443+
if (driverOptions) {
1444+
ADD_ASSOC_ZVAL_EX(args, "driverOptions", driverOptions);
1445+
Z_ADDREF_P(driverOptions);
1446+
} else {
1447+
ADD_ASSOC_NULL_EX(args, "driverOptions");
1448+
}
1449+
1450+
PHP_VAR_SERIALIZE_INIT(var_hash);
1451+
php_var_serialize(&var_buf, &args, &var_hash TSRMLS_CC);
1452+
PHP_VAR_SERIALIZE_DESTROY(var_hash);
1453+
1454+
if (!EG(exception)) {
1455+
*hash_len = var_buf.len;
1456+
hash = pestrndup(var_buf.c, *hash_len, 1);
1457+
}
1458+
1459+
zval_ptr_dtor(&args);
1460+
#endif
1461+
1462+
smart_str_free(&var_buf);
1463+
1464+
return hash;
1465+
}
1466+
13811467
static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, mongoc_ssl_opt_t *ssl_opt TSRMLS_DC) /* {{{ */
13821468
{
13831469
const char *mongoc_version, *bson_version;
@@ -1419,10 +1505,38 @@ static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, mo
14191505

14201506
void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, zval *options, zval *driverOptions TSRMLS_DC) /* {{{ */
14211507
{
1508+
char *hash = NULL;
1509+
size_t hash_len = 0;
14221510
bson_t bson_options = BSON_INITIALIZER;
14231511
mongoc_uri_t *uri = NULL;
14241512
mongoc_ssl_opt_t *ssl_opt = NULL;
14251513

1514+
#if PHP_VERSION_ID >= 70000
1515+
zval *client_ptr;
1516+
zval new_client_ptr;
1517+
#else
1518+
mongoc_client_t **client_ptr;
1519+
#endif
1520+
1521+
if (!(hash = php_phongo_manager_make_client_hash(uri_string, options, driverOptions, &hash_len TSRMLS_CC))) {
1522+
/* Exception should already have been thrown and there is nothing to free */
1523+
return;
1524+
}
1525+
1526+
#if PHP_VERSION_ID >= 70000
1527+
if ((client_ptr = zend_hash_str_find(&MONGODB_G(clients), hash, hash_len)) && Z_TYPE_P(client_ptr) == IS_PTR) {
1528+
MONGOC_DEBUG("Found client for hash: %s\n", hash);
1529+
manager->client = (mongoc_client_t *)Z_PTR_P(client_ptr);
1530+
goto cleanup;
1531+
}
1532+
#else
1533+
if (zend_hash_find(&MONGODB_G(clients), hash, hash_len + 1, (void**) &client_ptr) == SUCCESS) {
1534+
MONGOC_DEBUG("Found client for hash: %s\n", hash);
1535+
manager->client = *client_ptr;
1536+
goto cleanup;
1537+
}
1538+
#endif
1539+
14261540
if (options) {
14271541
phongo_zval_to_bson(options, PHONGO_BSON_NONE, &bson_options, NULL TSRMLS_CC);
14281542
}
@@ -1455,9 +1569,22 @@ void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string,
14551569

14561570
if (!manager->client) {
14571571
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "Failed to create Manager from URI: '%s'", uri_string);
1572+
goto cleanup;
14581573
}
14591574

1575+
MONGOC_DEBUG("Created client hash: %s\n", hash);
1576+
#if PHP_VERSION_ID >= 70000
1577+
ZVAL_PTR(&new_client_ptr, manager->client);
1578+
zend_hash_str_update(&MONGODB_G(clients), hash, hash_len, &new_client_ptr);
1579+
#else
1580+
zend_hash_update(&MONGODB_G(clients), hash, hash_len + 1, &manager->client, sizeof(mongoc_client_t *), NULL);
1581+
#endif
1582+
14601583
cleanup:
1584+
if (hash) {
1585+
pefree(hash, 1);
1586+
}
1587+
14611588
bson_destroy(&bson_options);
14621589

14631590
if (uri) {
@@ -1723,22 +1850,22 @@ zend_object_iterator *php_phongo_cursor_get_iterator(zend_class_entry *ce, zval
17231850
/* {{{ Memory allocation wrappers */
17241851
static void* php_phongo_malloc(size_t num_bytes) /* {{{ */
17251852
{
1726-
return emalloc(num_bytes);
1853+
return pemalloc(num_bytes, 1);
17271854
} /* }}} */
17281855

17291856
static void* php_phongo_calloc(size_t num_members, size_t num_bytes) /* {{{ */
17301857
{
1731-
return ecalloc(num_members, num_bytes);
1858+
return pecalloc(num_members, num_bytes, 1);
17321859
} /* }}} */
17331860

17341861
static void* php_phongo_realloc(void *mem, size_t num_bytes) { /* {{{ */
1735-
return erealloc(mem, num_bytes);
1862+
return perealloc(mem, num_bytes, 1);
17361863
} /* }}} */
17371864

17381865
static void php_phongo_free(void *mem) /* {{{ */
17391866
{
17401867
if (mem) {
1741-
efree(mem);
1868+
pefree(mem, 1);
17421869
}
17431870
} /* }}} */
17441871

@@ -1869,6 +1996,18 @@ PHP_GINIT_FUNCTION(mongodb)
18691996
}
18701997
/* }}} */
18711998

1999+
#if PHP_VERSION_ID >= 70000
2000+
static void php_phongo_client_dtor(zval *zv)
2001+
{
2002+
mongoc_client_destroy((mongoc_client_t *) Z_PTR_P(zv));
2003+
}
2004+
#else
2005+
static void php_phongo_client_dtor(void *client)
2006+
{
2007+
mongoc_client_destroy(*((mongoc_client_t **) client));
2008+
}
2009+
#endif
2010+
18722011
/* {{{ PHP_MINIT_FUNCTION */
18732012
PHP_MINIT_FUNCTION(mongodb)
18742013
{
@@ -1890,6 +2029,9 @@ PHP_MINIT_FUNCTION(mongodb)
18902029
/* Initialize libbson */
18912030
bson_mem_set_vtable(&MONGODB_G(bsonMemVTable));
18922031

2032+
/* Initialize HashTable for persistent clients */
2033+
zend_hash_init(&MONGODB_G(clients), 0, NULL, php_phongo_client_dtor, 1);
2034+
18932035
/* Prep default object handlers to be used when we register the classes */
18942036
memcpy(&phongo_std_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
18952037
phongo_std_object_handlers.clone_obj = NULL;
@@ -1956,6 +2098,10 @@ PHP_MSHUTDOWN_FUNCTION(mongodb)
19562098
{
19572099
(void)type; /* We don't care if we are loaded via dl() or extension= */
19582100

2101+
/* Destroy HashTable for persistent clients. The HashTable destructor will
2102+
* destroy any mongoc_client_t objects contained within. */
2103+
zend_hash_destroy(&MONGODB_G(clients));
2104+
19592105
bson_mem_restore_vtable();
19602106
/* Cleanup after libmongoc */
19612107
mongoc_cleanup();

php_phongo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mongodb)
4444
char *debug;
4545
FILE *debug_fd;
4646
bson_mem_vtable_t bsonMemVTable;
47+
HashTable clients;
4748
ZEND_END_MODULE_GLOBALS(mongodb)
4849

4950
#if PHP_VERSION_ID >= 70000

src/MongoDB/Manager.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ static void php_phongo_manager_free_object(phongo_free_object_arg *object TSRMLS
409409
zend_object_std_dtor(&intern->std TSRMLS_CC);
410410

411411
if (intern->client) {
412-
mongoc_client_destroy(intern->client);
412+
MONGOC_DEBUG("Not destroying persistent client for Manager");
413+
intern->client = NULL;
413414
}
414415

415416
#if PHP_VERSION_ID < 70000

tests/manager/manager-debug-002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ $bulk = new MongoDB\Driver\BulkWrite();
1414
$bulk->insert(array('_id' => 1, 'x' => 1));
1515
$result = $manager->executeBulkWrite(NS, $bulk);
1616

17-
ini_set("mongodb.debug", "off");
1817
?>
1918
===DONE===
2019
<?php exit(0); ?>
@@ -23,3 +22,4 @@ ini_set("mongodb.debug", "off");
2322
[%s] PHONGO: DEBUG > Creating Manager, phongo-1.%d.%d%S[%s] - mongoc-1.%s(%s), libbson-1.%s(%s), php-%s
2423
%a
2524
===DONE===
25+
%a

0 commit comments

Comments
 (0)