1818
1919namespace Cassandra ;
2020
21- class PagingIntegrationTest extends BasicIntegrationTest
22- {
21+ class PagingIntegrationTest extends BasicIntegrationTest {
2322 public function setUp () {
2423 parent ::setUp ();
2524
@@ -40,6 +39,66 @@ public function setUp() {
4039 }
4140 }
4241
42+ /**
43+ * Generate a random string
44+ *
45+ * @param int $length Length of string to generate (DEFAULT: random length
46+ * from 1 - 1024 characters)
47+ * @return string Randomly genreated text
48+ */
49+ private function randomString ($ length = -1 ) {
50+ // Determine if the length should be random
51+ if ($ length < 0 ) {
52+ $ length = mt_rand (1 , 1024 );
53+ }
54+
55+ // Generate the random string from the below character set
56+ $ characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ' ;
57+ $ charactersLength = strlen ($ characters );
58+ $ randomString = '' ;
59+ foreach (range (1 , $ length ) as $ i ) {
60+ $ randomString .= $ characters [rand (0 , $ charactersLength - 1 )];
61+ }
62+ return $ randomString ;
63+ }
64+
65+ /**
66+ * Page through the results while validating no memory leaks exists
67+ *
68+ * @param $start Starting memory value
69+ * @return int Number of rows visited
70+ */
71+ private function validatePageResults ($ rows ) {
72+ // Get the starting memory usage
73+ $ start = memory_get_usage () / 1024 ;
74+ if (Integration::isDebug () && Integration::isVerbose ()) {
75+ fprintf (STDOUT , "Start Usage: %dkb " . PHP_EOL , $ start );
76+ }
77+
78+ // Page over each result set and count the number of rows visited
79+ $ count = $ rows ->count ();
80+ while ($ rows = $ rows ->nextPage ()) {
81+ if ($ rows ->count () != 0 ) {
82+ $ count += $ rows ->count ();
83+ if (Integration::isDebug () && Integration::isVerbose ()) {
84+ fprintf (STDOUT , "Page %d: Current memory usage is %dkb " . PHP_EOL ,
85+ ($ count / 2 ), ((memory_get_usage () / 1024 ) - $ start ));
86+ }
87+ }
88+ }
89+
90+ // Get the final memory usage (and apply a tolerance to compensate for GC)
91+ $ end = memory_get_usage () / 1024 ;
92+ if (Integration::isDebug () && Integration::isVerbose ()) {
93+ fprintf (STDOUT , "End Usage: %dkb [%dkb] " . PHP_EOL , $ end , ($ end - $ start ));
94+ }
95+ $ difference = ($ end - $ start ) - 20 ; // 20KB tolerance
96+ $ this ->assertLessThanOrEqual (0 , $ difference );
97+
98+ // Return the number of rows visited
99+ return $ count ;
100+ }
101+
43102 /**
44103 * Use paging state token
45104 *
@@ -119,4 +178,72 @@ public function testNullToken() {
119178
120179 $ result = $ this ->session ->execute ($ statement , $ options );
121180 }
181+
182+ /**
183+ * Paging advancement does not create memory leak
184+ *
185+ * This test will ensure that the driver does not create memory leaks
186+ * associated advancing to the next page of results.
187+ *
188+ * @test
189+ * @ticket PHP-101
190+ */
191+ public function testNoPagingMemoryLeak () {
192+ // Create the user types and table for the test
193+ $ this ->session ->execute (new SimpleStatement (
194+ "DROP TABLE {$ this ->tableNamePrefix }"
195+ ));
196+ $ this ->session ->execute (new SimpleStatement (
197+ "CREATE TYPE price_history (time timestamp, price float) "
198+ ));
199+ $ priceHistory = Type::userType (
200+ "time " , Type::timestamp (),
201+ "price " , Type::float ());
202+ $ this ->session ->execute (new SimpleStatement (
203+ "CREATE TYPE purchase_stats (day_of_week int, total_purchases int) "
204+ ));
205+ $ purchaseStats = Type::userType (
206+ "day_of_week " , Type::int (),
207+ "total_purchases " , Type::int ());
208+ $ this ->session ->execute (new SimpleStatement (
209+ "CREATE TABLE {$ this ->tableNamePrefix } (id uuid PRIMARY KEY,
210+ history frozen<price_history>, stats frozen<purchase_stats>,
211+ comments text) "
212+ ));
213+
214+ // Populate the table with some random data
215+ $ totalInserts = 500 ;
216+ $ statement = $ this ->session ->prepare ("INSERT INTO {$ this ->tableNamePrefix }
217+ (id, history, stats, comments) VALUES (?, ?, ?, ?) " );
218+ foreach (range (1 , $ totalInserts ) as $ i ) {
219+ // Create the values for the insert
220+ $ history = $ priceHistory ->create (
221+ "time " , new Timestamp (mt_rand (1270094400000 , 1459483200000 )), // 04-01-2010 - 04-01-2016
222+ "price " , new Float ((mt_rand (1 , 1000 ) / 100 ))
223+ );
224+ $ stats = $ purchaseStats ->create (
225+ "day_of_week " , mt_rand (0 , 6 ),
226+ "total_purchases " , mt_rand (0 , 1000 )
227+ );
228+ $ values = array (
229+ new Uuid (),
230+ $ history ,
231+ $ stats ,
232+ $ this ->randomString ()
233+ );
234+
235+ $ options = new ExecutionOptions (array ("arguments " => $ values ));
236+ $ this ->session ->execute ($ statement , $ options );
237+ }
238+
239+ // Select all the rows in the table using paging
240+ $ statement = new SimpleStatement ("SELECT * FROM {$ this ->tableNamePrefix }" );
241+ $ options = new ExecutionOptions (array ("page_size " => 2 ));
242+ $ rows = $ this ->session ->execute ($ statement , $ options );
243+
244+
245+ // Validate paging and ensure all the rows were read
246+ $ count = $ this ->validatePageResults ($ rows );
247+ $ this ->assertEquals ($ totalInserts , $ count );
248+ }
122249}
0 commit comments