3 namespace RedUNIT\Base;
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
11 * The Query Writer Cache tries to avoid unnecessary queries
12 * by using cache markers. This means repeatingly fetching the
13 * same parent bean for instance (in loop) won't hurt performance.
14 * The Query Writer Cache gets emptied if the chain of markers
15 * gets broken. All 'non destructive' queries are marked, as long
16 * as no other queries have been executed we can safely assume
17 * nothing in the database has changed for the current request.
18 * You might want to turn this form of caching off in long running
21 * @file RedUNIT/Base/Writecache.php
22 * @desc Tests the Query Writer cache implemented in the
23 * @author Gabor de Mooij and the RedBeanPHP Community
24 * @license New BSD/GPLv2
26 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
27 * This source file is subject to the New BSD/GPLv2 License that is bundled
28 * with this source code in the file license.txt.
30 class Writecache extends Base
34 * What drivers should be loaded for this test pack?
36 public function getTargetDrivers()
38 return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
42 * Test whether cache size remains constant (per type).
43 * Avoiding potential memory leaks. (Issue #424).
47 public function testCacheSize()
50 R::useWriterCache( TRUE );
51 $writer = R::getWriter();
52 $bean = R::dispense( 'bean' );
55 $writer->flushCache( 20 );
56 $count = $writer->flushCache();
58 R::find( 'bean', ' prop < ? ', array( 1 ) );
59 $count = $writer->flushCache();
61 R::find( 'bean', ' prop < ? ', array( 2 ) );
62 $count = $writer->flushCache();
64 R::find( 'bean', ' prop < ? ', array( 2 ) );
65 $count = $writer->flushCache();
67 for( $i = 0; $i < 40; $i ++ ) {
68 R::find( 'bean', ' prop < ? ', array( $i ) );
70 $count = $writer->flushCache();
72 for( $i = 0; $i < 120; $i ++ ) {
73 R::find( 'bean', ' prop < ? ', array( $i ) );
75 $count = $writer->flushCache( 1 );
77 for( $i = 0; $i < 20; $i ++ ) {
78 R::find( 'bean', ' prop < ? ', array( $i ) );
80 $count = $writer->flushCache( 20 );
85 * When using fetchAs(), Query Cache does not recognize objects
86 * that have been previously fetched, see issue #400.
88 public function testCachingAndFetchAs()
90 testpack( 'Testing whether you can cache multiple records of the same type' );
92 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
94 $coauthor1 = R::dispense( 'author' );
95 $coauthor1->name = 'John';
96 $book = R::dispense( 'book' );
97 $book->title = 'a Funny Tale';
98 $book->coauthor = $coauthor1;
99 $id = R::store( $book );
100 $coauthor = R::dispense( 'author' );
101 $coauthor->name = 'Pete';
102 $book = R::dispense( 'book' );
103 $book->title = 'a Funny Tale 2';
104 $book->coauthor = $coauthor;
105 $id = R::store( $book );
106 $book = R::dispense( 'book' );
107 $book->title = 'a Funny Tale 3';
108 $book->coauthor = $coauthor1;
109 $id = R::store( $book );
110 $books = R::find( 'book' );
113 $authorsByName = array();
114 foreach($books as $book) {
115 $coAuthor = $book->with( ' ORDER BY title ASC ' )
116 ->fetchAs( 'author' )->coauthor;
117 $authors[] = $coAuthor->name;
118 $authorsByName[ $coAuthor->name ] = $coAuthor;
120 asrt( count( $logger->grep( 'SELECT' ) ), 2 ); //must be 2! 3 if cache does not work!
121 asrt( count( $authors ), 3 );
122 asrt( isset( $authorsByName[ 'John' ] ), TRUE );
123 asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
126 $authorsByName = array();
127 foreach($books as $book) {
128 $coAuthor = $book->with( ' ORDER BY title DESC ' )
129 ->fetchAs( 'author' )->coauthor;
130 $authors[] = $coAuthor->name;
131 $authorsByName[ $coAuthor->name ] = $coAuthor;
133 asrt( count( $logger->grep( 'SELECT' ) ), 0 ); //must be 0!
134 asrt( count( $authors ), 3 );
135 asrt( isset( $authorsByName[ 'John' ] ), TRUE );
136 asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
140 * Test effects of cache.
144 public function testCachingEffects()
146 testpack( 'Testing WriteCache Query Writer Cache' );
147 R::setNarrowFieldMode( FALSE );
148 R::useWriterCache( FALSE );
150 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
151 $book = R::dispense( 'book' )->setAttr( 'title', 'ABC' );
152 $book->ownPage[] = R::dispense( 'page' );
153 $id = R::store( $book );
154 // Test load cache -- without
156 $book = R::load( 'book', $id );
157 $book = R::load( 'book', $id );
158 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
160 R::useWriterCache( TRUE );
162 $book = R::load( 'book', $id );
163 $book = R::load( 'book', $id );
164 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
165 R::useWriterCache( FALSE );
168 $book = R::find( 'book' );
169 $book = R::find( 'book' );
170 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
172 R::getWriter()->setUseCache( TRUE );
174 $book = R::find( 'book' );
175 $book = R::find( 'book' );
176 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
177 R::getWriter()->setUseCache( FALSE );
180 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
182 R::batch( 'book', array( $id ) );
183 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
185 R::batch( 'book', array( $id ) );
186 asrt( count( $logger->grep( 'SELECT' ) ), 6 );
188 R::getWriter()->setUseCache( TRUE );
190 R::batch( 'book', array( $id ) );
191 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
193 $book = R::findOne( 'book', ' id = ? ', array( $id ) );
195 asrt( count( $logger->grep( 'SELECT' ) ), 3 );
196 R::getWriter()->setUseCache( FALSE );
199 $book = R::findOne( 'book' );
202 $book = R::findOne( 'book' );
203 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
205 R::getWriter()->setUseCache( TRUE );
207 $book = R::findOne( 'book' );
211 $book = R::findOne( 'book' );
212 // Now the same, auto flushed
213 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
214 R::getWriter()->setUseCache( FALSE );
215 // Test whether delete flushes as well (because uses selectRecord - might be a gotcha!)
216 R::store( R::dispense( 'garbage' ) );
217 $garbage = R::findOne( 'garbage' );
219 $book = R::findOne( 'book' );
220 R::trash( $garbage );
221 $book = R::findOne( 'book' );
222 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
223 R::store( R::dispense( 'garbage' ) );
224 $garbage = R::findOne( 'garbage' );
226 R::getWriter()->setUseCache( TRUE );
228 $book = R::findOne( 'book' );
229 R::trash( $garbage );
230 $book = R::findOne( 'book' );
231 // Now the same, auto flushed
232 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
233 R::getWriter()->setUseCache( FALSE );
234 R::store( R::dispense( 'garbage' ) );
235 $garbage = R::findOne( 'garbage' );
237 R::getWriter()->setUseCache( TRUE );
239 $book = R::findOne( 'book' );
240 R::getWriter()->queryRecord( 'garbage', array( 'id' => array( $garbage->id ) ) );
241 $book = R::findOne( 'book' );
242 // Now the same, auto flushed
243 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
244 $page = R::dispense('page');
245 $book->sharedPage[] = $page;
248 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
249 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
250 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
251 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
252 R::getWriter()->setUseCache( FALSE );
253 $link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
254 asrt( count( $logger->grep( 'SELECT' ) ), 2 );
255 R::getWriter()->setUseCache( TRUE );
256 R::setNarrowFieldMode( TRUE );
260 * Try to fool the cache :)
264 public function testRegressions()
266 testpack( 'Testing possible regressions: Try to fool the cache' );
267 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' WHERE ( ' . R::getWriter()->esc( 'id', TRUE ) . ' IN ( 1) ) ';
268 $bean = R::dispense( 'bean' );
269 $bean->title = 'abc';
270 $id = R::store( $bean );
271 $bean = R::load( 'bean', $id );
272 $bean->title = 'xxx';
274 // Fire exact same query so cache may think no other query has been fired
276 $bean = R::load( 'bean', $id );
277 asrt( $bean->title, 'xxx' );
281 * Test keep-cache comment.
285 public function testKeepCacheCommentInSQL()
287 $bean = R::dispense( 'bean' );
288 $bean->title = 'abc';
289 $id = R::store( $bean );
290 $bean = R::load( 'bean', $id );
291 $bean->title = 'xxx';
293 // Causes flush even though it contains -- keep-cache (not at the end, not intended)
294 R::findOne( 'bean', ' title = ? ', array( '-- keep-cache' ) );
295 $bean = R::load( 'bean', $id );
296 asrt( $bean->title, 'xxx' );
301 * Same as above.. test keep cache.
305 public function testInstructNoDrop()
307 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
308 $bean = R::dispense( 'bean' );
309 $bean->title = 'abc';
310 $id = R::store( $bean );
311 $bean = R::load( 'bean', $id );
312 $bean->title = 'xxx';
315 $bean = R::load( 'bean', $id );
316 asrt( $bean->title, 'abc' );
318 // Now INSTRUCT the cache to not drop the cache CASE 2
319 $str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
320 $bean = R::dispense( 'bean' );
321 $bean->title = 'abc';
322 $id = R::store( $bean );
323 $bean = R::load( 'bean', $id );
324 $bean->title = 'xxx';
326 R::findOne( 'bean', ' title = ? ', array( 'cache' ) );
327 $bean = R::load( 'bean', $id );
328 asrt( $bean->title, 'xxx' );
332 * Can we confuse the cache?
336 public function testConfusionRegression()
338 testpack( 'Testing possible confusion regression' );
339 $bean = R::dispense( 'bean' );
340 $bean->title = 'abc';
341 $id1 = R::store( $bean );
342 $bean = R::dispense( 'bean' );
343 $bean->title = 'abc2';
344 $id2 = R::store( $bean );
345 $bean = R::load( 'bean', $id1 );
346 asrt( $bean->title, 'abc' );
347 $bean = R::load( 'bean', $id2 );
348 asrt( $bean->title, 'abc2' );
352 * Test Ghost beans....
356 public function testGhostBeans()
358 testpack( 'Testing ghost beans' );
359 $bean = R::dispense( 'bean' );
360 $bean->title = 'abc';
361 $id1 = R::store( $bean );
363 $bean = R::load( 'bean', $id1 );
364 asrt( (int) $bean->id, 0 );
368 * Test explicit flush.
372 public function testExplicitCacheFlush()
374 testpack( 'Test cache flush (explicit)' );
375 R::setNarrowFieldMode( FALSE );
377 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
378 $bean = R::dispense( 'bean' );
379 $bean->title = 'abc';
380 $id1 = R::store( $bean );
382 $bean = R::load( 'bean', $id1 );
383 asrt( $bean->title, 'abc' );
384 asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
385 $bean = R::load( 'bean', $id1 );
386 asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
387 R::getWriter()->flushCache();
388 $bean = R::load( 'bean', $id1 );
389 asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
390 R::getWriter()->flushCache();
391 R::getWriter()->setUseCache( FALSE );
392 R::setNarrowFieldMode( TRUE );