Skip to content

Commit 919d587

Browse files
authored
Merge pull request #87 from datastax/115
115 - Fix: Duplicate UUIDs generated in forked processes
2 parents 27af30d + 7f46c98 commit 919d587

File tree

7 files changed

+128
-14
lines changed

7 files changed

+128
-14
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# 1.2.2
2+
3+
Bug Fixes:
4+
5+
* [PHP-88] \Cassandra\Timestamp::toDateTime segfault with PHP7
6+
* [PHP-112] Freeing a null future as result of a failure in \Cassandra\DefaultSession::executeAsync()
7+
* [PHP-115] \Cassandra\UUID returning duplicate UUIDs
8+
19
# 1.2.1
210

311
Bug Fixes:

ext/package.xml

+8-6
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,25 @@ protocol and Cassandra Query Language v3.
1414
<email>[email protected]</email>
1515
<active>yes</active>
1616
</lead>
17-
<date>2016-07-28</date>
18-
<time>08:37:25</time>
17+
<date>2016-08-08</date>
18+
<time>11:25:32</time>
1919
<version>
20-
<release>1.2.1</release>
21-
<api>1.2.1</api>
20+
<release>1.2.2</release>
21+
<api>1.2.2</api>
2222
</version>
2323
<stability>
2424
<release>stable</release>
2525
<api>stable</api>
2626
</stability>
2727
<license uri="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</license>
2828
<notes>
29-
# 1.2.1
29+
# 1.2.2
3030

3131
Bug Fixes:
3232

33-
* [PHP-113] pecl install of 1.2.0 fails because sourcecode is missing FutureRows.h
33+
* [PHP-88] \Cassandra\Timestamp::toDateTime segfault with PHP7
34+
* [PHP-112] Freeing a null future as result of a failure in \Cassandra\DefaultSession::executeAsync()
35+
* [PHP-115] \Cassandra\UUID returning duplicate UUIDs
3436
</notes>
3537
<contents>
3638
<dir name="/">

ext/php_cassandra.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ static PHP_GINIT_FUNCTION(cassandra)
273273
{
274274
uv_once(&log_once, php_cassandra_log_initialize);
275275

276-
cassandra_globals->uuid_gen = cass_uuid_gen_new();
276+
cassandra_globals->uuid_gen = NULL;
277+
cassandra_globals->uuid_gen_pid = 0;
277278
cassandra_globals->persistent_clusters = 0;
278279
cassandra_globals->persistent_sessions = 0;
279280
PHP5TO7_ZVAL_UNDEF(cassandra_globals->type_varchar);
@@ -297,7 +298,9 @@ static PHP_GINIT_FUNCTION(cassandra)
297298

298299
static PHP_GSHUTDOWN_FUNCTION(cassandra)
299300
{
300-
cass_uuid_gen_free(cassandra_globals->uuid_gen);
301+
if (cassandra_globals->uuid_gen) {
302+
cass_uuid_gen_free(cassandra_globals->uuid_gen);
303+
}
301304
php_cassandra_log_cleanup();
302305
}
303306

ext/php_cassandra.h

+14
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@
3838
#include <Zend/zend_exceptions.h>
3939
#include <Zend/zend_interfaces.h>
4040

41+
#ifdef HAVE_SYS_TYPES_H
42+
#include <sys/types.h>
43+
#endif
44+
45+
#ifdef HAVE_UNISTD_H
46+
#include <unistd.h>
47+
#endif
48+
49+
#ifdef PHP_WIN32
50+
typedef int pid_t;
51+
#include <process.h>
52+
#endif
53+
4154
#if PHP_VERSION_ID < 50304
4255
# error PHP 5.3.4 or later is required in order to build the driver
4356
#endif
@@ -478,6 +491,7 @@ PHP_MINFO_FUNCTION(cassandra);
478491

479492
ZEND_BEGIN_MODULE_GLOBALS(cassandra)
480493
CassUuidGen *uuid_gen;
494+
pid_t uuid_gen_pid;
481495
unsigned int persistent_clusters;
482496
unsigned int persistent_sessions;
483497
php5to7_zval type_varchar;

ext/util/uuid_gen.c

+23-3
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,40 @@
2020

2121
ZEND_EXTERN_MODULE_GLOBALS(cassandra)
2222

23+
static CassUuidGen* get_uuid_gen(TSRMLS_D) {
24+
/* Create a new uuid generator if our PID has changed. This prevents the same
25+
* UUIDs from being generated in forked processes.
26+
*/
27+
if (CASSANDRA_G(uuid_gen_pid) != getpid()) {
28+
if (CASSANDRA_G(uuid_gen)) {
29+
cass_uuid_gen_free(CASSANDRA_G(uuid_gen));
30+
}
31+
CASSANDRA_G(uuid_gen) = cass_uuid_gen_new();
32+
CASSANDRA_G(uuid_gen_pid) = getpid();
33+
}
34+
return CASSANDRA_G(uuid_gen);
35+
}
36+
2337
void
2438
php_cassandra_uuid_generate_random(CassUuid *out TSRMLS_DC)
2539
{
26-
cass_uuid_gen_random(CASSANDRA_G(uuid_gen), out);
40+
CassUuidGen* uuid_gen = get_uuid_gen(TSRMLS_C);
41+
if (!uuid_gen) return;
42+
cass_uuid_gen_random(uuid_gen, out);
2743
}
2844

2945
void
3046
php_cassandra_uuid_generate_time(CassUuid *out TSRMLS_DC)
3147
{
32-
cass_uuid_gen_time(CASSANDRA_G(uuid_gen), out);
48+
CassUuidGen* uuid_gen = get_uuid_gen(TSRMLS_C);
49+
if (!uuid_gen) return;
50+
cass_uuid_gen_time(uuid_gen, out);
3351
}
3452

3553
void
3654
php_cassandra_uuid_generate_from_time(long timestamp, CassUuid *out TSRMLS_DC)
3755
{
38-
cass_uuid_gen_from_time(CASSANDRA_G(uuid_gen), (cass_uint64_t) timestamp, out);
56+
CassUuidGen* uuid_gen = get_uuid_gen(TSRMLS_C);
57+
if (!uuid_gen) return;
58+
cass_uuid_gen_from_time(uuid_gen, (cass_uint64_t) timestamp, out);
3959
}

ext/version.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
#define PHP_CASSANDRA_NAME "cassandra"
66
#define PHP_CASSANDRA_MAJOR 1
77
#define PHP_CASSANDRA_MINOR 2
8-
#define PHP_CASSANDRA_RELEASE 1
8+
#define PHP_CASSANDRA_RELEASE 2
99
#define PHP_CASSANDRA_STABILITY "stable"
10-
#define PHP_CASSANDRA_VERSION "1.2.1"
11-
#define PHP_CASSANDRA_VERSION_FULL "1.2.1"
10+
#define PHP_CASSANDRA_VERSION "1.2.2"
11+
#define PHP_CASSANDRA_VERSION_FULL "1.2.2"
1212

1313
#endif /* PHP_CASSANDRA_VERSION_H */

tests/unit/Cassandra/UuidTest.php

+67
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,71 @@ public function notEqualTypes()
6161
array(new Uuid('2a5072fa-7da4-4ccd-a9b4-f017a3872304'), new Uuid('3b5072fa-7da4-4ccd-a9b4-f017a3872304')),
6262
);
6363
}
64+
65+
/**
66+
* Ensure UUIDs are unique for fork/child processes
67+
*
68+
* This test will ensure that the PHP driver is producing unique UUIDs for
69+
* all child processes that get created during fork() operations in web
70+
* servers (e.g. Apache, nginx, ...etc).
71+
*
72+
* @test
73+
* @ticket PHP-115
74+
*/
75+
public function testUniqueInChild() {
76+
if (!function_exists("pcntl_fork")) {
77+
$this->markTestSkipped("Unable to Execute testUniqueInChild Unit Test: pcntl_fork() does not exists");
78+
} else {
79+
// Create a PHP script to call within a PHPUnit test (exit call fails test)
80+
$script = <<<EOF
81+
<?php
82+
// Get and open the file for appending child UUIDs
83+
\$uuidsFilename = \$_SERVER['argv'][1];
84+
\$numberOfForks = \$_SERVER['argv'][2];
85+
86+
// Create requested children process; create UUIDs and append to a file
87+
\$children = array();
88+
foreach (range(1, \$numberOfForks) as \$i) {
89+
// Create the child process
90+
\$pid = pcntl_fork();
91+
92+
// Ensure the child process was create successfully
93+
if (\$pid < 0) {
94+
die("Unable to Create Fork: Unique UUID test cannot complete");
95+
} else if (\$pid === 0) {
96+
// Create a UUID and add it to the file
97+
\$uuid = new \Cassandra\Uuid();
98+
file_put_contents(\$uuidsFilename, \$uuid->uuid() . PHP_EOL, FILE_APPEND);
99+
100+
// Terminate child process
101+
exit(0);
102+
} else {
103+
// Parent process: Add the process ID to force waiting on children
104+
\$children[] = \$pid;
105+
}
106+
}
107+
108+
// Wait on each child process to finish
109+
foreach (\$children as \$pid) {
110+
pcntl_waitpid(\$pid, \$status);
111+
}
112+
?>
113+
EOF;
114+
$numProcesses = 64;
115+
116+
// Execute the PHP script passing in the filename for the UUIDs to be stored
117+
$uuidsFilename = tempnam(sys_get_temp_dir(), "uuid");
118+
$scriptFilename = tempnam(sys_get_temp_dir(), "uuid");
119+
file_put_contents($scriptFilename, $script, FILE_APPEND);
120+
exec(PHP_BINARY . " {$scriptFilename} {$uuidsFilename} $numProcesses");
121+
unlink($scriptFilename);
122+
123+
// Get the contents of the file
124+
$uuids = file($uuidsFilename);
125+
unlink($uuidsFilename);
126+
127+
// Ensure all the UUIDs are unique
128+
$this->assertEquals($numProcesses, count(array_unique($uuids)));
129+
}
130+
}
64131
}

0 commit comments

Comments
 (0)