<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>maxgarrick.com &#187; performance</title>
	<atom:link href="http://maxgarrick.com/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>http://maxgarrick.com</link>
	<description>Come take a look under the hood</description>
	<lastBuildDate>Sun, 08 Mar 2009 23:32:09 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Doctrine 1.0.4 Fixes SoftDelete</title>
		<link>http://maxgarrick.com/doctrine-104-fixes-softdelete/</link>
		<comments>http://maxgarrick.com/doctrine-104-fixes-softdelete/#comments</comments>
		<pubDate>Wed, 10 Dec 2008 06:02:21 +0000</pubDate>
		<dc:creator>max</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[doctrine orm]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[softdelete]]></category>

		<guid isPermaLink="false">http://maxgarrick.com/?p=104</guid>
		<description><![CDATA[Just wanted to post an update to my previous blog entry about Doctrine ORM gotchas.  Since 1.0.4 was released, a seriously limiting bug was fixed in the SoftDelete template.  This bug was preventing typical performance optimizations that used LEFT JOINs to reduce the number of database queries.  The idea is that a [...]]]></description>
			<content:encoded><![CDATA[<p>Just wanted to post an update to my previous blog entry about Doctrine ORM gotchas.  Since 1.0.4 was released, a seriously limiting bug was fixed in the SoftDelete template.  This bug was preventing typical performance optimizations that used LEFT JOINs to reduce the number of database queries.  The idea is that a page generally loads much faster by executing few efficient JOIN&#8217;ed queries than many single-table queries (do your joins in MySQL, not PHP!)</p>
<p>I had posted a workaround to this bug ($query->addWhere(&#8221;deleted = 0 OR deleted IS NULL&#8221;) to all of your LEFT JOINs).  This was cumbersome and I felt violated the principle of the SoftDelete event listener.</p>
<p>With this bug resolved, I&#8217;ve been more freely adding custom finders for specific pages.  One page in particular (the MessageBoard thread index) went from 12 seconds to 4 seconds for a very large data set.  The number of DB queries also was cut from 1200+ to about 300.</p>
<p>Now I just have to get the page down to 10 queries and we can call it optimized.</p>
]]></content:encoded>
			<wfw:commentRss>http://maxgarrick.com/doctrine-104-fixes-softdelete/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using Doctrine ORM for more effective and productive development</title>
		<link>http://maxgarrick.com/effective-development-using-doctrine-orm/</link>
		<comments>http://maxgarrick.com/effective-development-using-doctrine-orm/#comments</comments>
		<pubDate>Mon, 01 Dec 2008 05:14:19 +0000</pubDate>
		<dc:creator>max</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://maxgarrick.com/?p=70</guid>
		<description><![CDATA[Doctrine ORM is a PHP library that implements the ActiveRecord pattern we have all grown to love.  I&#8217;ve been using it for the past 7 months and feel it is one of the reasons we were able to deliver the MessageBoards web application on-time and within budget.
This doesn&#8217;t mean using Doctrine has been all flowers [...]]]></description>
			<content:encoded><![CDATA[<p><a href='http://www.doctrine-project.org/'>Doctrine ORM</a> is a PHP library that implements the <a href="http://en.wikipedia.org/wiki/Active_record_pattern">ActiveRecord pattern</a> we have all grown to love.  I&#8217;ve been using it for the past 7 months and feel it is one of the reasons we were able to deliver the MessageBoards web application on-time and within budget.</p>
<p>This doesn&#8217;t mean using Doctrine has been all flowers and sunshine: <strong>Doctrine will kick you in the face when you&#8217;re not paying attention.</strong></p>
<p>Today I&#8217;d like to revisit all of those black eyes and bloody noses in the hope of helping fellow developers avoid the same missteps I made.  Doctrine is complex and quirky, and has some unanticipated architectural &#8220;features&#8221; that are not well documented.</p>
<p><strong>Use Record::toArray() and Record::fromArray() to store/retrieve Doctrine objects from the session.</strong></p>
<ul>
<li>Save space in the session store by adding only the column attributes of Record objects to the session.</li>
<li>The session will quickly fill up otherwise, as Doctrine adds considerable bulk to model objects.</li>
</ul>
<p><strong>Improve performance by extending Doctrine_Table and implement custom DQL queries for complex and frequently used queries.</strong></p>
<ul>
<li>If the controller or view will need a record&#8217;s related record, use a DQL query to join with the related table.</li>
</ul>
<p><strong>Optimize performance by getting to know and love Doctrine_Connection_Profiler.</strong></p>
<ul>
<li>Add the connection listener at the beginning of execution and print SQL queries at the end of execution in order to identify areas of effective performance optimization.</li>
<li>Example code that adds the listener and renders query events as HTML:
<pre class="brush: php">

// Set the connection listener

$profiler = new Doctrine_Connection_Profiler();
Doctrine_Manager::connection()-&gt;setListener($profiler);

// Code goes here...

// Render database connection events as HTML:

$query_count = 0;
$time = 0;
echo &quot;&lt;table width=&#039;100%&#039; border=&#039;1&#039;&gt;&quot;;
foreach ( $profiler as $event ) {
    if ($event-&gt;getName() != &#039;execute&#039;) {
        continue;
    }
    $query_count++;
    echo &quot;&lt;tr&gt;&quot;;
    $time += $event-&gt;getElapsedSecs() ;
    echo &quot;&lt;td&gt;&quot; . $event-&gt;getName() . &quot;&lt;/td&gt;&lt;td&gt;&quot; . sprintf ( &quot;%f&quot; , $event-&gt;getElapsedSecs() ) . &quot;&lt;/td&gt;&quot;;
    echo &quot;&lt;td&gt;&quot; . $event-&gt;getQuery() . &quot;&lt;/td&gt;&quot; ;
    $params = $event-&gt;getParams() ;
    if ( ! empty ( $params ) ) {
          echo &quot;&lt;td&gt;&quot;;
          echo join(&#039;, &#039;, $params);
          echo &quot;&lt;/td&gt;&quot;;
    }
    echo &quot;&lt;/tr&gt;&quot;;
}
echo &quot;&lt;/table&gt;&quot;;
echo &quot;Total time: &quot; . $time . &quot;, query count: $query_count &lt;br&gt;\n &quot;;
</pre>
</li>
</ul>
<p><strong>Effectively mitigate performance issues with <a href="http://www.danga.com/memcached/">memcache</a>.</strong></p>
<ul>
<li>The query and result cache can drastically offset Doctrine&#8217;s performance overhead.</li>
<li>If you already have memcached running, this is one of the most cost-effective performance tweaks you can do.</li>
<li>Note: I received mysterious fatal errors when using INDEXBY in DQL queries.  After removing the INDEXBY, the errors stopped.</li>
</ul>
<p><strong>Play nice with fellow coders or testers by automating database migrations.</strong></p>
<ul>
<li>Add a <a href="http://git.or.cz/">git</a> merge hook that runs the Doctrine migration.</li>
<li>Alternatively, check if a migration is needed on every page view while in development mode.</li>
</ul>
<p><strong>Play nice with other web applications by prefixing database tables.</strong></p>
<ul>
<li>Set the table name prefix by calling $this->setTableName(&#8217;zzz_model_name&#8217;), where &#8216;zzz&#8217; is the tool&#8217;s prefix.</li>
</ul>
<p><strong>Create a &#8220;resource-oriented&#8221; URL structure that closely follows the application&#8217;s models.</strong></p>
<ul>
<li>This is borrowed from the <a href="http://oreilly.com/catalog/9780596529260/">RESTful</a> architecture, and not necessarily Doctrine-specific.</li>
<li>For example an HTTP GET on http://site.com/messageboard/m34/f11/ would display forum ID 11 in messageboard 34 in tool &#8220;messagebord&#8221;.</li>
</ul>
<p><strong>Use a workaround when using LEFT JOINs on models with actAs(&#8217;SoftDelete&#8217;) behavior.</strong></p>
<ul>
<li>SoftDelete will automatically add the WHERE condition &#8220;deleted = 0&#8243; to all queries.</li>
<li>This prevents queries with LEFT JOIN from returning a row where &#8220;deleted IS NULL&#8221;.</li>
<li>Either use INNER JOIN instead, or add the following to DQL queries: $query->andWhere(&#8217;deleted = 0 or deleted IS NULL&#8217;);</li>
</ul>
<p><strong>Timestampable cannot be disabled temporarily, causing challenges when importing data with dates.</strong></p>
<ul>
<li>Doctrine provides no way to easily disable or override the timestamp behavior in order to import a pre-existing date.</li>
<li>Until this behavior is resolved, try using <a href='http://trac.phpdoctrine.org/ticket/1590'>this patch</a> to set the &#8216;disabled&#8217; option of Timestampable.</li>
</ul>
<p><strong>Use &#8220;cascade => array(&#8217;delete&#8221;)&#8221; to propagate soft deletes through model relations.</strong></p>
<ul>
<li>The faster onDelete => &#8216;CASCADE&#8217; performs the delete in MySQL, which does not set the deleted flag.</li>
</ul>
<p><strong>Put authorization code in one place, when possible.</strong></p>
<p><strong>Implement checkbox plus text input as two columns in a model.</strong></p>
<ul>
<li>For example:<br />
<input type='checkbox' /> Require password:<br />
<input type='password' /></li>
<li>This approach simplifies validation of optional attributes.</li>
</ul>
<p><strong>Use $model->setAttribute(Doctrine::ATTR_COLL_KEY, &#8216;id&#8217;) to key collections off of the primary key.</strong></p>
<ul>
<li>If this attribute is not set, Collections will be indexed starting from 0 and counting upwards.</li>
<li>This can simplify controller logic.</li>
</ul>
<p><strong>Use Doctrine_Pager only for the most basic views.</strong></p>
<p><strong>Use actAs(&#8217;NestedSet&#8217;) to model hierarchies that are read more frequently than written.</strong></p>
<p><strong>Use unix timestamps in Timestampable columns to ease formatting.</strong></p>
<ul>
<li>Using a datetime works fine if the view never needs to change how a datetime is displayed.</li>
<li>A unix timestamp allows for flexibly changing how dates are rendered, e.g.: &#8220;Jan 1st 2008&#8243; or &#8220;Yesterday&#8221;.</li>
<li>Example actAs() code:
<pre class="brush: php">
$this-&gt;actAs(&#039;Timestampable&#039;, array(
    &#039;created&#039; =&gt; array(&#039;name&#039; =&gt; &#039;created_at&#039;,
        &#039;type&#039;    =&gt;  &#039;integer&#039;,
        &#039;format&#039;  =&gt;  &#039;U&#039;,
        &#039;disabled&#039; =&gt; false,
        &#039;options&#039; =&gt;  array()),
    &#039;updated&#039; =&gt; array(&#039;name&#039;    =&gt;  &#039;updated_at&#039;,
        &#039;type&#039;    =&gt;  &#039;integer&#039;,
        &#039;format&#039;  =&gt;  &#039;U&#039;,
        &#039;disabled&#039; =&gt; false,
        &#039;options&#039; =&gt;  array())));
</pre>
</li>
</ul>
<p><strong>For multi-step forms, add a &#8217;state&#8217; column to aid in validating each step.</strong></p>
<ul>
<li>In the model&#8217;s validate() function, use the state column to switch between validation logic.</li>
<li>For instance, in state 1, validate columns a and b.  In state 2, validate columns a, b, c, and d. In state 3, validate the complete object.</li>
</ul>
<p>I hope this list saves some heartache on what is really a very elegant ActiveRecord implementation in PHP!</p>
]]></content:encoded>
			<wfw:commentRss>http://maxgarrick.com/effective-development-using-doctrine-orm/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
