<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xml:base="http://www.pixelite.co.nz"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
    xmlns:cc="http://web.resource.org/cc/">
  <channel>
    <title>Pixelite - drupalplanet</title>
    <link>http://www.pixelite.co.nz</link>
    <description>Posts categorized as 'DrupalPlanet'</description>
    <language>en</language>
    
      <item>
        <title>10 things I learnt building in Drupal 8</title>
        <link>http://www.pixelite.co.nz/article/10-things-i-learnt-building-in-drupal-8/</link>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;I have had the chance to be involved with 2 fresh builds with Drupal 8 now, I thought I would describe some of the neat things I have found during this time and some of my lessons learned. My hope is that blog post will help you in your journey with Drupal 8.&lt;/p&gt;

&lt;h2 id=&quot;drupal-console-is-awesome&quot;&gt;1. Drupal Console is awesome&lt;/h2&gt;

&lt;p&gt;Every time you need to generate a custom module, or a new block in a custom module, you can quickly and easily use &lt;a href=&quot;https://drupalconsole.com/&quot;&gt;Drupal Console&lt;/a&gt; to produce the code scaffolding for you. This quite easily makes the job of a developer a lot less stressful, and allows you to focus on actually writing code that delivers functionality.&lt;/p&gt;

&lt;p&gt;I plucked these example commands that I use frequently from my bash history:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;drupal site:mode dev
drupal generate:module
drupal generate:plugin:block
drupal generate:routesubscriber
drupal generate:form:config&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Documentation is &lt;a href=&quot;http://docs.drupalconsole.com/en/index.html&quot;&gt;online&lt;/a&gt; but for the most part, the commands are self documenting, if you use the &lt;code&gt;--help&lt;/code&gt; option, then you get a great summary on the command, and the other options you can pass in.&lt;/p&gt;

&lt;p&gt;The other nice thing is that this is a &lt;a href=&quot;http://symfony.com/doc/current/components/console/introduction.html&quot;&gt;Symfony Console&lt;/a&gt; application, so it should feel very familiar to you if you used another tool written in the same framework.&lt;/p&gt;

&lt;h2 id=&quot;custom-block-types-are-amazing&quot;&gt;2. Custom block types are amazing&lt;/h2&gt;

&lt;p&gt;In Drupal 7 land there was &lt;a href=&quot;https://www.drupal.org/project/bean&quot;&gt;bean&lt;/a&gt; which was an attempt to stop making ‘meta’ nodes to fill in content editable parts of complex landing pages. Now, fast forward to Drupal 8, and custom block types are now in Drupal Core.&lt;/p&gt;

&lt;p&gt;This basically means as a site builder you now have another really powerful tool at your disposal in order to model content effectively in Drupal 8.&lt;/p&gt;

&lt;p&gt;Each custom block type can have it’s own fields, it’s own display settings, and form displays.&lt;/p&gt;

&lt;p&gt;Here are the final custom block types on a recent Drupal 8 build:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/d8/blocktypes.png&quot; alt=&quot;Custom block types in Drupal 8&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One downside is that there is no access control per custom block type (just a global permission “administer blocks”), no doubt contrib will step in to fill this hole in the future (does anyone know a module that can help here?). In the mean time there is &lt;a href=&quot;https://www.drupal.org/node/1975064&quot;&gt;drupal.org issue&lt;/a&gt; on the subject.&lt;/p&gt;

&lt;p&gt;I also found it weird that the custom blocks administration section was not directly under the ‘structure’ section of the site, there is another &lt;a href=&quot;https://www.drupal.org/node/2501691&quot;&gt;drupal.org issue&lt;/a&gt; about normalising this as well. Setting up some default shortcuts really helped me save some time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/d8/shortcuts.png&quot; alt=&quot;Shortcuts are handy in Drupal 8 to remember where the custom block section is&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;view-modes-on-all-the-things&quot;&gt;3. View modes on all the things&lt;/h2&gt;

&lt;p&gt;To create custom view modes in Drupal 7 required either a custom module or Dave Reid’s &lt;a href=&quot;https://www.drupal.org/project/entity_view_mode&quot;&gt;entity_view_mode&lt;/a&gt; contrib module. Now this is baked into Drupal 8 core.&lt;/p&gt;

&lt;p&gt;View modes on your custom block types takes things to yet another level still as well. This is one more feather in the Drupal site builder’s cap.&lt;/p&gt;

&lt;h2 id=&quot;twig-is-the-best&quot;&gt;4. Twig is the best&lt;/h2&gt;

&lt;p&gt;In Drupal 7 I always found it weird that you could not unleash a front end developer upon your site and expect to have a pleasant result. In order to be successful the themer would need to know PHP, preprocess hooks, template naming standards, the mystical specific order in which the templates apply and so on. This often meant that a backend and front end developer would need to work together in order to create a good outcome.&lt;/p&gt;

&lt;p&gt;With the introduction of Twig, I now feel that theming is back in the hands of the front end developer, and knowledge of PHP is no longer needed in order to override just about any markup that Drupal 8 produces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt; - use the Drupal Console command &lt;code&gt;drupal site:mode dev&lt;/code&gt; to enable Twig development options, and disable Drupal caching. Another positive side effect is that Twig will then render the entire list of templates that you could be using, and which one you actually are using (and where that template is located).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/d8/twigdevel.png&quot; alt=&quot;Twig developer comments in HTML are the best&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; - If you want to use a template per custom block type (to which I did), then you can use this PHP snippet in your theme’s &lt;code&gt;.theme&lt;/code&gt; file (taken from &lt;a href=&quot;https://www.drupal.org/node/2460893#comment-10766412&quot;&gt;drupal.org&lt;/a&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; * Implements hook_theme_suggestions_HOOK_alter() for form templates.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; *&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; * @param array $suggestions&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; * @param array $variables&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;THEMENAME_theme_suggestions_block_alter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elements&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;#block_content&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;array_splice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;block__bundle__&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elements&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;#block_content&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bundle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;panelizer--panels-ipe-is-a-formidable-site-building-tool&quot;&gt;5. Panelizer + panels IPE is a formidable site building tool&lt;/h2&gt;

&lt;p&gt;When looking for a layout manager to help build the more complex landing pages, I came across &lt;a href=&quot;https://www.drupal.org/project/panelizer&quot;&gt;panelizer&lt;/a&gt; + &lt;a href=&quot;https://www.drupal.org/project/panels&quot;&gt;panels IPE&lt;/a&gt;. Using panelizer you are able to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;create per node layout variants&lt;/li&gt;
  &lt;li&gt;apply a single layout to all nodes of a particular bundle (e.g. all your news articles have the same layout)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other neat thing is that the layouts themselves are now standardised between all the various layout managers using a contrib module called &lt;a href=&quot;https://www.drupal.org/project/layout_plugin&quot;&gt;layout_plugin&lt;/a&gt;. Also they are just YAML and Twig. Simple. There is even an effort to get this &lt;a href=&quot;https://www.drupal.org/node/2296423&quot;&gt;merged into Drupal 8.2&lt;/a&gt; which I think would be a great idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downside&lt;/strong&gt; - all JS is still rendered on the page even though the user (e.g. anonymous users) have no access to panelizer. There is a patch on &lt;a href=&quot;https://www.drupal.org/node/2688951&quot;&gt;drupal.org&lt;/a&gt; to help fix this.&lt;/p&gt;

&lt;p&gt;Since starting this build there has also been a stable release of &lt;a href=&quot;https://www.drupal.org/project/ds&quot;&gt;display suite&lt;/a&gt; come out for Drupal 8 as well giving you even more options.&lt;/p&gt;

&lt;h2 id=&quot;you-can-build-a-rather-complex-site-with-very-little-contributed-modules&quot;&gt;6. You can build a rather complex site with very little contributed modules&lt;/h2&gt;

&lt;p&gt;For this most recent site I build I got away with using only 10 contributed modules (one of which - &lt;a href=&quot;https://www.drupal.org/project/devel&quot;&gt;devel&lt;/a&gt; was purely for debugging purposes).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ctools&lt;/li&gt;
  &lt;li&gt;google_analytics&lt;/li&gt;
  &lt;li&gt;metatag&lt;/li&gt;
  &lt;li&gt;panels&lt;/li&gt;
  &lt;li&gt;token&lt;/li&gt;
  &lt;li&gt;contact_block&lt;/li&gt;
  &lt;li&gt;devel&lt;/li&gt;
  &lt;li&gt;layout_plugin&lt;/li&gt;
  &lt;li&gt;panelizer&lt;/li&gt;
  &lt;li&gt;pathauto&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means you are inherently building a more stable and supportable site, as most of the functionality now comes out of Drupal core.&lt;/p&gt;

&lt;h2 id=&quot;the-contact-module-now-is-supercharged&quot;&gt;7. The contact module now is supercharged&lt;/h2&gt;

&lt;p&gt;In Drupal 7, the contact module was one of those modules to which I never turned on, as it was rather inflexible. You could not change the fields in a UI, nor add email recipients, or have more than 1 form. Now in Drupal 8 you can have as many “contact” forms as you want, each one is fieldable, and can send emails to as many people as needed.&lt;/p&gt;

&lt;p&gt;You can also enhance the core module with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.drupal.org/project/contact_block&quot;&gt;contact_block&lt;/a&gt; - allows you to place the contact form in a block&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.drupal.org/project/contact_storage&quot;&gt;contact_storage&lt;/a&gt; - allows you to store the submissions in the database, rather than firing an email and forgetting about it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is still a place for webform, namely:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;large complex form with lots of fields&lt;/li&gt;
  &lt;li&gt;multi-step forms&lt;/li&gt;
  &lt;li&gt;forms you want to ‘save draft’&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read more about this in the &lt;a href=&quot;https://www.ostraining.com/blog/drupal/drupal-8-contact-forms/#comment-2333911795&quot;&gt;OS training blog post&lt;/a&gt; on the contact module.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downside&lt;/strong&gt; - I wanted to have a plain page use the path &lt;code&gt;/contact&lt;/code&gt; but the contact module registers this path, so pathauto gave my contact page a path of &lt;code&gt;/contact-0&lt;/code&gt;. Luckily creating a &lt;a href=&quot;https://www.drupal.org/node/2187643&quot;&gt;route subscriber&lt;/a&gt; with Drupal Console was painless, so altering the contact module route was very simple to do. I can paste the code here if needed, but most of it is the code that Drupal Console generates for you.&lt;/p&gt;

&lt;h2 id=&quot;phpunit-is-bundled-into-core&quot;&gt;8. PHPunit is bundled into core&lt;/h2&gt;

&lt;p&gt;Now that Drupal 8 is largely Object Oriented (OO), you are able to test classes using PHPunit. I have &lt;a href=&quot;/article/writing-phpunit-tests-for-your-custom-modules-in-drupal-8/&quot;&gt;wrote about phpunit&lt;/a&gt; in the past if you want to know more.&lt;/p&gt;

&lt;h2 id=&quot;views-is-in-core&quot;&gt;9. Views is in core&lt;/h2&gt;

&lt;p&gt;This was the main reason why adoption of Drupal 7 was so slow after it’s initial 7.0 release, as everyone needed views to be stable before jumping ship. Now with views bundled into core, views plugins are also being ported at a great rate of knots too.&lt;/p&gt;

&lt;h2 id=&quot;ckeditor-is-in-core&quot;&gt;10. CKEditor is in core&lt;/h2&gt;

&lt;p&gt;I often found that this was one library that never (or hardly ever) got updated on sites that had been around for a while. More worryingly, CKEditor (the library) would from time to time fix security related issues. Now that this comes with Drupal 8 core, it is just one less thing to worry about.&lt;/p&gt;

&lt;p&gt;Also I would love to shout out to &lt;a href=&quot;https://www.drupal.org/u/wim-leers&quot;&gt;Wim Leers&lt;/a&gt; (and other contributors) for &lt;a href=&quot;https://www.drupal.org/node/2027181&quot;&gt;revamping the image dialog&lt;/a&gt; with alignment and caption options. I cannot tell you how much pain and suffering this caused me in Drupal 7.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/d8/image.png&quot; alt=&quot;The image dialog bundled with Drupal 8 in CKeditor is amazing&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you have built a site recently in Drupal 8 and have found anything interesting or exciting, please let me know in the comments. Also keen to see what sites people have built, so post a link to it if it is public.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Thu, 19 May 2016 00:00:00 +1200</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/10-things-i-learnt-building-in-drupal-8/</guid>
        <comments>http://www.pixelite.co.nz/article/10-things-i-learnt-building-in-drupal-8/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Writing PHPunit tests for your custom modules in Drupal 8</title>
        <link>http://www.pixelite.co.nz/article/writing-phpunit-tests-for-your-custom-modules-in-drupal-8/</link>
        <description>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;I have been doing a bit of Drupal 8 development as of recent, and am loving the new changes, and entities everywhere. I am passionate about automated testing, and when I saw that integrating PHPunit into your custom modules is now even easier, I set out to see how this all worked.&lt;/p&gt;

&lt;h2 id=&quot;why-is-phpunit-important&quot;&gt;Why is PHPunit important&lt;/h2&gt;

&lt;p&gt;There are are number of reasons why PHPunit is a great idea&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it forces you to write testable code in the first place, this means small classes, with methods that do a single thing&lt;/li&gt;
  &lt;li&gt;it runs in only a few seconds, there is also no need to have a working Drupal install&lt;/li&gt;
  &lt;li&gt;integrates with PHPStorm, allowing you run your tests from within the IDE&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-1-set-up-your-phpunitxmldist-file&quot;&gt;Step 1, set up your phpunit.xml.dist file&lt;/h2&gt;

&lt;p&gt;There is a file that comes included with Drupal 8 core, but by default it will not scan any sub-directories under &lt;code&gt;/modules&lt;/code&gt; (e.g. like the very common &lt;code&gt;/modules/custom&lt;/code&gt;). I stumbled across this question on &lt;a href=&quot;http://drupal.stackexchange.com/questions/162719/phpunit-with-custom-contrib-and-submodules-structure&quot;&gt;stackoverflow&lt;/a&gt;. So you have a couple of options from here:&lt;/p&gt;

&lt;h3 id=&quot;option-1---create-and-use-your-own-phpunitxmldist-file&quot;&gt;Option 1 - Create and use your own phpunit.xml.dist file&lt;/h3&gt;

&lt;p&gt;You can simply copy (and modify) Drupal 8 core’s phpunit.xml.dist file into git repo somewhere (perhaps outside the webroot), and use this file for all your custom module tests.&lt;/p&gt;

&lt;h3 id=&quot;option-2---patch-drupal-8-core&quot;&gt;Option 2 - Patch Drupal 8 core&lt;/h3&gt;

&lt;p&gt;Another option (which is the option I took) was to apply a simple patch to Drupal core. There is an &lt;a href=&quot;https://www.drupal.org/node/2499239&quot;&gt;open issue on drupal.org&lt;/a&gt; to look at scanning all sub-directories for test files. At the time of writing it was uncertain whether this patch would be accepted by the community.&lt;/p&gt;

&lt;h2 id=&quot;step-2-write-your-tests&quot;&gt;Step 2, write your tests&lt;/h2&gt;

&lt;p&gt;There are some general conventions you should use when writing your PHPunit tests:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the suffix of the filename should be &lt;code&gt;Test.php&lt;/code&gt;, e.g. &lt;code&gt;MonthRangeTest.php&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;the files should all reside in either the directory &lt;code&gt;/MY_MODULE/tests/src/Unit/&lt;/code&gt; or a sub directory of that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More information on the requirements can be found on the &lt;a href=&quot;https://www.drupal.org/node/2116043&quot;&gt;drupal.org documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;dataproviders&quot;&gt;Dataproviders&lt;/h3&gt;

&lt;p&gt;Data providers are pretty much the best thing to happen to automated testing. Instead of testing a single scenario, you can instead test a whole range of permutations in order to find those bugs. You start by declaring an annotation &lt;code&gt;@dataProvider&lt;/code&gt; for your test method:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * @covers ::getMonthRange&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * @dataProvider monthRangeDataProvider&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   */&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testGetMonthRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$expected_start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$expected_end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$month_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ... more code&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You then declare a method &lt;code&gt;monthRangeDataProvider&lt;/code&gt; that returns an array of test cases (which are also arrays). The items in the data provider method are passed one at a time to the testing method, in the same order they are declared (so you can map them to friendly names).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * Data provider for testGetMonthRange().&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * @return array&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *   Nested arrays of values to check:&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *   - $expected_start&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *   - $expected_end&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *   - $month_offset&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *   - $now&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   */&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;monthRangeDataProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Feb 29 @ noon.&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1454284800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1456790399&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1456747200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// ... more tests follow&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;More information can be found in the &lt;a href=&quot;https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers&quot;&gt;phpunit documentation for data providers&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;testing-for-expected-exceptions&quot;&gt;Testing for expected exceptions&lt;/h3&gt;

&lt;p&gt;Just as important as testing valid inputs, you should also test invalid inputs as well. This is easily achieved with &lt;code&gt;@expectedException&lt;/code&gt; annotations above your test method:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * Tests that an end date that is before the start date produces an exception.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   *&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * @expectedException        Exception&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * @expectedExceptionMessage Start date must be before end date&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   */&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;testGetWorkingDaysInRangeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ... more code in here&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-3-enhance-your-test-class-with-phpunit-metadata&quot;&gt;Step 3, enhance your test class with PHPunit metadata&lt;/h2&gt;

&lt;p&gt;You can annotate both the test class and the methods to provide additional information and metadata about your tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@covers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is mainly used for PHPunit’s automated code coverage report, but I find it also very helpful for developers to up front state what method that are testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@coversDefaultClass&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is used at a class level, and saves you having to write rather lengthy @covers statement for all your testing methods, if they all test the same class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@depends&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a certain test makes no sense to run unless a previous test passed, then you can add in a ‘depends’ annotation above the test method in question. You can depend on multiple other tests too. Note, that this does not change the execution order of the tests, they are still executed top to bottom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@group&lt;/strong&gt; or &lt;strong&gt;@author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can think of adding a ‘group’ to a PHPunit class the same as tagging in. It is free tagging in that sense, and you can tag a single class with many tags. This should allow you to categorise your tests. @author is an alias of group, the idea being you can run all tests written by a particular developer.&lt;/p&gt;

&lt;p&gt;More information can be found in the &lt;a href=&quot;https://phpunit.de/manual/3.7/en/appendixes.annotations.html&quot;&gt;PHPunit documentation on annotations&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-4-run-your-test-suite&quot;&gt;Step 4, run your test suite&lt;/h2&gt;

&lt;p&gt;This section assumes you have opted to use Drupal core’s phpunit.xml.dist file (modify the paths as appropriate if you are using a file in another location).&lt;/p&gt;

&lt;p&gt;List groups (or tags)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;core/
../vendor/bin/phpunit --list-groups&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Run all tests that are tags with a particular group (or tag)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;core/
../vendor/bin/phpunit --group tamdash&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Example CLI output&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;../vendor/bin/phpunit --group tamdash
PHPUnit 4.8.11 by Sebastian Bergmann and contributors.
...........
Time: 5.01 seconds, Memory: 144.25Mb
OK &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt; tests, &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt; assertions&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you are using PHPStorm, spend a few minutes and &lt;a href=&quot;https://www.drupal.org/node/2288559&quot;&gt;set this up too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/phpunit/phpstorm.png&quot; alt=&quot;Set up PHPStorm to run PHPunit tests&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Example output&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/phpunit/phpstorm-run.png&quot; alt=&quot;Running PHPunit tests in PHPStorm&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So now there is no need to flip back to your terminal if you just want to quickly run a group of tests.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;PHPunit is a great way to be able to run quick tests on isolated parts of your code. Tests often take less than 10 seconds to run, so developer feedback is near instant. It also forces your developers to write better more testible code from the get go. This can only be a good thing. Personally I am very excited to see PHPunit join Drupal 8, and cannot wait to see what people do with it.&lt;/p&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;There seems to be quite healthy debate on whether contrib or custom modules should ship with their own phpunit.xml.dist file or whether Drupal core’s file should cover both. I am keen to hear anyone’s thoughts on this. Also let me know if you have any contrib modules in the wild shipping their own phpunit.xml.dist files, and how you found that process.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Thu, 24 Mar 2016 00:00:00 +1300</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/writing-phpunit-tests-for-your-custom-modules-in-drupal-8/</guid>
        <comments>http://www.pixelite.co.nz/article/writing-phpunit-tests-for-your-custom-modules-in-drupal-8/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Deleting multiple fields with large amounts of data in Drupal 7</title>
        <link>http://www.pixelite.co.nz/article/deleting-multiple-fields-with-large-amounts-of-data-in-drupal-7/</link>
        <description>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;I was recently tasked with cleaning up a legacy Drupal 7 database to which had accumulated a lot of data in several fields. The fields were no longer used, and the data could be deleted. The data however totalled more than &lt;em&gt;30 GB&lt;/em&gt; with those 7 fields, and this presented a few challenges.&lt;/p&gt;

&lt;h2 id=&quot;existing-solutions-that-did-not-quite-cut-it&quot;&gt;Existing solutions that did not quite cut it&lt;/h2&gt;

&lt;p&gt;I search around the internet to see the best way to delete fields in Drupal 7 using an update hook. I came across this &lt;a href=&quot;http://drupal.stackexchange.com/questions/46085/how-to-programmatically-remove-a-field-from-a-node&quot;&gt;stack overflow post&lt;/a&gt; to which advised using something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EXAMPLE_update_7001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;field_delete_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;field_name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;field_purge_batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The main issue with the above code is that it only marks the data for deletion, and then slowly removes it on cron. This could take  weeks to fully remove the data. It was not an ideal solution in our case.&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;If you want to expatiate the process of removing data from fields, and also the fields themselves, we found the code below would remove the 30 GB of data, remove the tables, update the content types, all in one nice update hook. It does rely on having removed the fields from your features, so that are the respective content type can be reverted successfully.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EXAMPLE_update_7001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$fields_to_delete&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_c&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_d&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_e&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_f&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;field_example_g&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fields_to_delete&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;db_truncate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;field_data_&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;db_truncate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;field_revision_&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;field_delete_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;watchdog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;EXAMPLE&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Deleted the field @field from all content types.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;@field&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * The fields aren&amp;#39;t really deleted until the purge function runs, ordinarily&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * during cron. Count the number of fields we need to purge, and add five in&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   * case a few other miscellaneous fields are in there somehow.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;   */&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;field_purge_batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fields_to_delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Revert features to update the content type in Drupal to drop the field.&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;features_revert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;EXAMPLE&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;field&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;A couple of lessons can be learnt here&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Truncations, even on large data sets, are extremely quick, and awesome. The above update hook takes less than 1 minute to run end to end.&lt;/li&gt;
  &lt;li&gt;If you are going to programmatically update nodes with a high degree of frequency, consider &lt;a href=&quot;https://www.drupal.org/project/field_sql_norevisions&quot;&gt;disabling revisions&lt;/a&gt; on those content types. Your database thanks you in advance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you have had to delete fields in update hooks in Drupal, let me know if you used another method and how that went for you.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Wed, 17 Feb 2016 00:00:00 +1300</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/deleting-multiple-fields-with-large-amounts-of-data-in-drupal-7/</guid>
        <comments>http://www.pixelite.co.nz/article/deleting-multiple-fields-with-large-amounts-of-data-in-drupal-7/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Estimating Drupal projects</title>
        <link>http://www.pixelite.co.nz/article/estimating-drupal-projects/</link>
        <description>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;Estimating Drupal projects can be tricky, and there are a number of tools and guides out there to help make your life easier in this topic. I thought it would be a great idea to aggregate this data, and present some of the best information out there. It is up to you to choose the best match for your team.&lt;/p&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;

&lt;h3 id=&quot;palantirnet&quot;&gt;palantir.net&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/estimation/palantir.jpg&quot; alt=&quot;&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.palantir.net/blog/developing-drupal-sites-plan-or-perish&quot;&gt;Developing Drupal sites: Plan or Perish&lt;/a&gt; - by Larry Garfield (2013)&lt;/p&gt;

&lt;p&gt;Even though this article is nearly 3 years old, it still is completely valid for Drupal. There is an &lt;a href=&quot;https://docs.google.com/spreadsheets/d/15htLLWLguhwiuTLg_nndQNpgWVdUMy6UaR_d1q-v6iw/edit#gid=0&quot;&gt;included spreadsheet&lt;/a&gt; that you get the tech lead, product owner and another engineer to collaborate on prior to building.&lt;/p&gt;

&lt;p&gt;What I like about this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Emphasis on involving tech leads early to perform scoping and breaking up functionality into tangible Drupal elements. This allows you to have less experienced developers do the actual build, as largely everything has been speced out.&lt;/li&gt;
  &lt;li&gt;View modes are used extensively which is a great for re-usability of content and maintaining flexibility&lt;/li&gt;
  &lt;li&gt;Questions, or opportunities for simplification can come up &lt;em&gt;before&lt;/em&gt; anything has been built&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lullabotcom&quot;&gt;lullabot.com&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/estimation/lullabot.jpg&quot; alt=&quot;&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are 3 articles on lullabot.com that are worth reading, they all follow on from one another, so it would pay to start at the beginning and work your way to the end.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.lullabot.com/articles/the-art-of-estimation&quot;&gt;The Art of Estimation&lt;/a&gt; by Seth Brown (2011)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.lullabot.com/articles/an-update-on-the-art-of-estimation&quot;&gt;An Update on the Art of Estimation&lt;/a&gt; by Jerad Bitner (2012)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.lullabot.com/articles/handling-uncertainty-when-estimating-software-projects&quot;&gt;Handling Uncertainty When Estimating Software Projects&lt;/a&gt; by Darren Petersen (2015)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In here they introduce a &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1y-3AliSRiHxnAkNhHa9JvQjX3EHpkiulxjlec4B7isA&quot;&gt;spreadsheet&lt;/a&gt; that attempts to combine 2 developers estimates into a single number with variance.&lt;/p&gt;

&lt;p&gt;What I like about this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It exposes the fact that all website builds have range to which it can be completed in&lt;/li&gt;
  &lt;li&gt;It helps to explain the ‘Wideband Delphi’ method of estimation&lt;/li&gt;
  &lt;li&gt;Excellent explanation on how to use the spreadsheet is provided&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;wunderkrautcom&quot;&gt;wunderkraut.com&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/estimation/wunderkraut.jpg&quot; alt=&quot;&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.wunderkraut.com/blog/resources-for-my-session-on-early-estimating/2010-08-24&quot;&gt;Resources for my session on early estimating&lt;/a&gt; by Jakob Persson (2010)&lt;/p&gt;

&lt;p&gt;Included in the article here is the &lt;a href=&quot;https://docs.google.com/spreadsheets/d/13MGHIxFOtbJ2Qxygc_GxKzxqghLiK1-7YgNiq95ypWE/edit?hl=en#gid=0&quot;&gt;Drupal early estimation sheet v3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What I like about this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Walks you through an actual example on how an email was turned into a concrete estimate, and how that was analysed&lt;/li&gt;
  &lt;li&gt;Introduces often overlooked things such as:
    &lt;ul&gt;
      &lt;li&gt;migration of data&lt;/li&gt;
      &lt;li&gt;producing help or documentation&lt;/li&gt;
      &lt;li&gt;deployments&lt;/li&gt;
      &lt;li&gt;third party system integration&lt;/li&gt;
      &lt;li&gt;working with a third party (e.g. external design vendor)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;drupalizeme&quot;&gt;drupalize.me&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/estimation/drupalize.png&quot; alt=&quot;&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are several training videos on drupalize.me that would be worth checking out if you have some time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://drupalize.me/videos/introduction-project-management?p=2203&quot;&gt;Introduction to Project Management&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://drupalize.me/videos/estimation-drupal-projects?p=2203&quot;&gt;Estimation in Drupal Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have an existing Acquia subscription you might already have access to Drupalize.me, so this might be worth looking into.&lt;/p&gt;

&lt;h2 id=&quot;personal-thoughts&quot;&gt;Personal thoughts&lt;/h2&gt;

&lt;p&gt;Having been involved in doing a lot of this in my previous role, and one additional point I would like to make is to never start from a fresh Drupal install, it often makes sense to standardise on a Drupal technology suite, and installation profiles make this easy.&lt;/p&gt;

&lt;p&gt;For instance, Acquia typically use an installation profile called &lt;a href=&quot;https://www.drupal.org/project/lightning&quot;&gt;lightning&lt;/a&gt; which provides built in ways to do lots of common tasks, e.g. layout, workflow, media management. This saves you having to re-invent the wheel on every project, and should help you provide more solid estimates when it comes to the base features of the site.&lt;/p&gt;

&lt;p&gt;Another common theme across the different methods is to involve 1 or 2 tech leads early on to help break the requirements down into Drupal functionality. Having these early conversations with the product owner early on can often lead to a better solution not only in time to build, but feature set too.&lt;/p&gt;

&lt;p&gt;Also remember, the above articles and spreadsheets may not work perfectly for you and your organsiation, so feel free to adapt them to suit your needs.&lt;/p&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you know of some other method to help here, please let me know in the comments. Especially keen to know of any other training content or spreadsheets which other companies use to help estimate Drupal projects.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Fri, 12 Feb 2016 00:00:00 +1300</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/estimating-drupal-projects/</guid>
        <comments>http://www.pixelite.co.nz/article/estimating-drupal-projects/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Measuring Drupal content growth over time</title>
        <link>http://www.pixelite.co.nz/article/measuring-drupal-content-growth-over-time/</link>
        <description>&lt;p&gt;On a recent project I was tasked with working out why the database was so large, in particular certain tables like &lt;code&gt;field_revision_body&lt;/code&gt; and &lt;code&gt;field_data_body&lt;/code&gt; had grown to be several gigabytes in size.&lt;/p&gt;

&lt;h2 id=&quot;sql-to-the-rescue&quot;&gt;SQL to the rescue&lt;/h2&gt;

&lt;p&gt;Here is a simple SQL statement you can execute on your production database to retrieve a count of nodes created grouped by day.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example.com sqlq &lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT count(nid) as count, DATE_FORMAT(FROM_UNIXTIME(created),&amp;#39;%d-%m-%Y&amp;#39;) AS month FROM node  GROUP BY YEAR(FROM_UNIXTIME(created)), MONTH(FROM_UNIXTIME(created)), DAY(FROM_UNIXTIME(created))&amp;quot;&lt;/span&gt; &amp;gt; /tmp/example-nodes.txt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL users&lt;/strong&gt; - you can add a custom function to add &lt;code&gt;FROM_UNIXTIME&lt;/code&gt; functionality:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from_unixtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;RETURNS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;     SELECT to_timestamp($1)::timestamp AS result&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LANGUAGE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;SQL&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;cleaning-up-results&quot;&gt;Cleaning up results&lt;/h3&gt;

&lt;p&gt;The SQL will return in a few seconds, and now we need to clean up the data before it is graphed. First lets trim the start of the file, here you can see a few nodes with no valid date, and 1 created in 1980, we want to remove these lines.&lt;/p&gt;

&lt;pre&gt;
head /tmp/example-nodes.txt

count	month
662	NULL
1	08-06-1980
194	30-06-1992
198	01-07-1992
186	02-07-1992
...
&lt;/pre&gt;

&lt;p&gt;Now if we look at the tail you can see a few nodes that are created in the future, I also like to remove these from the results as they tend to blow out the timescale of any graphs:&lt;/p&gt;

&lt;pre&gt;
tail /tmp/example-nodes.txt

1	15-04-2022
12	30-05-2022
1	19-06-2022
1	24-06-2022
1	27-06-2022
...
&lt;/pre&gt;

&lt;h3 id=&quot;conversion-to-csv&quot;&gt;Conversion to CSV&lt;/h3&gt;

&lt;p&gt;By default the SQL will return a tab separated values file, and with blank lines in there, lets convert this to CSV and enforce consistent line endings:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;cat /tmp/example-nodes.txt &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sed &lt;span class=&quot;s1&quot;&gt;$&amp;#39;s/\r//&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; tr &lt;span class=&quot;s2&quot;&gt;&amp;quot;\\t&amp;quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;,&amp;quot;&lt;/span&gt; &amp;gt; /tmp/example-nodes.txt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;graphing-results&quot;&gt;Graphing results&lt;/h2&gt;

&lt;p&gt;Now that you have clean data you can import into your favourite graphing tool, here is Excel making a graph of that data. Here are the number of nodes created by day:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/growth/day.jpg&quot; alt=&quot;Drupal content growth by day over time&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And if you make the total cumulative, you can track total growth in Drupal, and use this to predict into the future:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/growth/total.jpg&quot; alt=&quot;Drupal total content growth over time&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;wait-what-about-taxonomy-terms&quot;&gt;Wait, what about taxonomy terms&lt;/h2&gt;

&lt;p&gt;You can extend this method to work with taxonomy terms as well with a slight modification to the SQL (Drupal 7):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example.com sqlq &lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT count(tid) as count, DATE_FORMAT(FROM_UNIXTIME(created),&amp;#39;%d-%m-%Y&amp;#39;) AS month FROM taxonomy_term_data GROUP BY YEAR(FROM_UNIXTIME(created)), MONTH(FROM_UNIXTIME(created)), DAY(FROM_UNIXTIME(created))&amp;quot;&lt;/span&gt; &amp;gt; /tmp/example-terms.txt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;where-to-from-here&quot;&gt;Where to from here&lt;/h2&gt;

&lt;p&gt;Based on the analysis, you might be able to make recommendations to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;perhaps look at exporting (e.g. with &lt;a href=&quot;https://www.drupal.org/project/node_export&quot;&gt;node_export&lt;/a&gt;) and removing content from Drupal’s database after X number of years. This will reduce the database size immediately&lt;/li&gt;
  &lt;li&gt;removing unused revisions from content where the content is older than X number of years&lt;/li&gt;
  &lt;li&gt;predict into the future how many nodes you will have in 1 year, 5 years etc and be able to plan for this in terms of hardware capacity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you are having challenges with Drupal content growing over time, how are you keeping track of this? Are there any other tips and tricks you know about?&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Thu, 03 Dec 2015 00:00:00 +1300</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/measuring-drupal-content-growth-over-time/</guid>
        <comments>http://www.pixelite.co.nz/article/measuring-drupal-content-growth-over-time/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Integrating multisite Drupal with Apache Solr</title>
        <link>http://www.pixelite.co.nz/article/integrating-multisite-drupal-with-apache-solr/</link>
        <description>&lt;p&gt;There are a number of ways this can be solved with Drupal 7:&lt;/p&gt;

&lt;h2 id=&quot;use-a-search-core-per-site&quot;&gt;1. Use a search core per site&lt;/h2&gt;

&lt;p&gt;Using this approach, you create an unique Solr core (N.B. a single Solr application can contain 0 or more cores) per site and either:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.drupal.org/project/acquia_search_multi_subs&quot;&gt;Acquia Search Multiple Indexes&lt;/a&gt; module (if using &lt;a href=&quot;https://www.acquia.com/free&quot;&gt;Acquia Cloud&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;settings.php switch statements to switch on domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to connect the appropriate search cores to the appropriate Drupal environments. Then you can enable (but not no need to configure) the &lt;a href=&quot;https://www.drupal.org/project/apachesolr_multisitesearch&quot;&gt;apachesolr_multisitesearch&lt;/a&gt; module.&lt;/p&gt;

&lt;h3 id=&quot;pros&quot;&gt;Pros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Each site’s index is isolate from each other, so there is no interfering with other indexes, and no ability to drop other indexes&lt;/li&gt;
  &lt;li&gt;You can customise the Solr config per core&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cons&quot;&gt;Cons&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Need to provision a new Solr core every time you add a new site&lt;/li&gt;
  &lt;li&gt;Need to change settings.php every time you add a new site&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;use-a-single-shared-core-and-apachesolrmultisitesearch&quot;&gt;2. Use a single shared core, and apachesolr_multisitesearch&lt;/h2&gt;

&lt;p&gt;In this option you utilise a single shared Solr core, and send the documents from all sites into it. Using special attributes on the documents, you are able to query and get only the results you desire back out again.&lt;/p&gt;

&lt;p&gt;The module you need to enable is &lt;a href=&quot;https://www.drupal.org/project/apachesolr_multisitesearch&quot;&gt;apachesolr_multisitesearch&lt;/a&gt;. Just enabling the module makes your standard search pages return results only for your site. This is accomplished internally by adding a filtering query to each Solr query using &lt;a href=&quot;http://cgit.drupalcode.org/apachesolr_multisitesearch/tree/apachesolr_multisitesearch.module#n243&quot;&gt;hook_apachesolr_query_alter()&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can see the site hashes are different based on the &lt;a href=&quot;http://cgit.drupalcode.org/apachesolr/tree/apachesolr.module#n577&quot;&gt;base_url&lt;/a&gt; in Drupal already.&lt;/p&gt;

&lt;h3 id=&quot;example-of-this-working&quot;&gt;Example of this working&lt;/h3&gt;

&lt;p&gt;Note the site hashes are different:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example1.com vget apachesolr_site_hash
apachesolr_site_hash: &lt;span class=&quot;s1&quot;&gt;&amp;#39;lm0ujj&amp;#39;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example2.com.au vget apachesolr_site_hash
apachesolr_site_hash: &lt;span class=&quot;s1&quot;&gt;&amp;#39;9qt6o6&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;example-of-this-not-working&quot;&gt;Example of this not working&lt;/h3&gt;

&lt;p&gt;Note the site hashes are the same, thus the search results will be blended:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example1.com vget apachesolr_site_hash
apachesolr_site_hash: &lt;span class=&quot;s1&quot;&gt;&amp;#39;92ub55&amp;#39;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;drush --uri&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;www.example2.com vget apachesolr_site_hash
apachesolr_site_hash: &lt;span class=&quot;s1&quot;&gt;&amp;#39;92ub55&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You will find yourself in this predicament if you clone new sites from old sites (thus the database variable persists).&lt;/p&gt;

&lt;h3 id=&quot;in-order-to-fix-blended-site-hashes&quot;&gt;In order to fix blended site hashes&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;drop the current index (both sites)&lt;/li&gt;
  &lt;li&gt;delete the variable ‘apachesolr_site_hash’ (both sites)&lt;/li&gt;
  &lt;li&gt;visit the admin page solr page (both sites) - this will regenerate the site hash&lt;/li&gt;
  &lt;li&gt;verify the site hash is now different (both sites)&lt;/li&gt;
  &lt;li&gt;reindex (both sites)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;pros-1&quot;&gt;Pros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;No need to provision a new Solr core every time you add a new site&lt;/li&gt;
  &lt;li&gt;No need to change settings.php every time you add a new site&lt;/li&gt;
  &lt;li&gt;Hooks into the delete query to prevent dropping the entire index&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cons-1&quot;&gt;Cons&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;You need to ensure the site hash is different for each site, so if you clone one, you will need to delete the variable &lt;code&gt;apachesolr_site_hash&lt;/code&gt; straight away&lt;/li&gt;
  &lt;li&gt;If you ever drop Solr’s index, this will drop all documents across all sites. This should be hard to do (you will need to disable the apachesolr_multisitesearch module)&lt;/li&gt;
  &lt;li&gt;You must share Solr config (e.g. the &lt;a href=&quot;https://wiki.apache.org/solr/SchemaXml&quot;&gt;schema XML&lt;/a&gt;), so you cannot change this per site&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you run a multisite setup, and make use of Apache Solr, I am keen to hear how you integrated it, and if you have any tips or tricks to share.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Thu, 15 Oct 2015 00:00:00 +1300</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/integrating-multisite-drupal-with-apache-solr/</guid>
        <comments>http://www.pixelite.co.nz/article/integrating-multisite-drupal-with-apache-solr/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Dynamic content caching based on attributes in Drupal 7</title>
        <link>http://www.pixelite.co.nz/article/dynamic-content-caching-based-on-attributes-in-drupal-7/</link>
        <description>&lt;p&gt;Out of the box, Drupal 7 comes with the ability to set the global cache lifetime for all pages on the site. I find this works generally quite well with small sites with not a lot of content or complex caching requirements. &lt;a href=&quot;https://pantheon.io/docs/articles/drupal/drupal-performance-and-caching-settings/&quot;&gt;Pantheon have a nice write&lt;/a&gt; up on the performance settings page that comes with Drupal 7 core and what each of the settings mean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; In this article I am assuming you are running some form of reverse proxy (e.g. varnish) in front of Drupal (most Drupal managed cloud vendors like Acquia or Pantheon provide varnish as a part of it’s offering as standard).&lt;/p&gt;

&lt;h2 id=&quot;the-problem-with-drupal-7-core-settings&quot;&gt;The problem with Drupal 7 core settings&lt;/h2&gt;

&lt;p&gt;What happens when you are running a large site, with a very &lt;a href=&quot;https://en.wikipedia.org/wiki/Long_tail&quot;&gt;long tail of content&lt;/a&gt;? All of a sudden running a global cache &lt;abbr title=&quot;Time To Live&quot;&gt;TTL&lt;/abbr&gt; of 10 minutes can result in a very poor hit rate in varnish. Having a poor hit rate in varnish ultimately means your web servers end up doing more work, which often leads to having to shell out for additional hardware.&lt;/p&gt;

&lt;p&gt;Generally, faced with this issue, you have 2 options:&lt;/p&gt;

&lt;h2 id=&quot;varnish-with-purging&quot;&gt;1) Varnish with purging&lt;/h2&gt;

&lt;p&gt;In this option, you set a global high cache TTL, so that all content lives in varnish for an excessively long time (say 12 hours), and when certain events occur (say a news article gets published), then purge requests occur telling varnish to drop certain URLs from it’s cache. I don’t want to go into detail to much on this, but I will say there are a number of drawbacks with this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When you publish 1 node, often this node can potentially appear on dozens of pages (e.g. on landing pages as a teaser, or in a view), this means the purge rules get overly complex in a hurry. If you ever re-architect a portion of the site, often this means hundreds of purge rules need to be rewritten and tested, this can be very costly.&lt;/li&gt;
  &lt;li&gt;Running a &lt;abbr title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/abbr&gt; (e.g. Akamai, or Cloudflare) can complicate things further, as you need to purge varnish first, then the CDN &lt;em&gt;and never in the reverse order&lt;/em&gt;, else the CDN will end up caching the stale content all over again&lt;/li&gt;
  &lt;li&gt;Having exposed filters on the page means you will need to use bans in varnish (to utlise regex), this is another topic for another day&lt;/li&gt;
  &lt;li&gt;All too often, content editors resort to &lt;em&gt;nuking the varnish cache from orbit&lt;/em&gt; and dropping the whole lot when the rules do not function as desired. This is especially bad as it means all the content in there needs to be entirely rebuilt again. This can (and often does) take down busy sites when it occurs in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;varnish-with-intelligent-cache-ttls&quot;&gt;2) Varnish with intelligent cache TTLs&lt;/h2&gt;

&lt;p&gt;Another option to which I want to discuss in this blog post is around using the attributes of the node in order to determine at runtime how long this piece of content should be cached in varnish.&lt;/p&gt;

&lt;p&gt;Attributes you can use to help you work out the most effective cache TTL:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;content type&lt;/li&gt;
  &lt;li&gt;content created age (now - created)&lt;/li&gt;
  &lt;li&gt;recency of most recent revision&lt;/li&gt;
  &lt;li&gt;when the last comment was made&lt;/li&gt;
  &lt;li&gt;current time of day&lt;/li&gt;
  &lt;li&gt;any other attribute of the node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do I mean by this? Well let’s say you run a large digital media organisation, and publish hundreds of fresh news articles per day, but after a week or so, they generally get no edits done to them at all. Your cache TTL rules could be:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;if content type == ‘news_article’ then&lt;/li&gt;
  &lt;li&gt;if age &amp;lt; 1 week, then the cache TTL inherits from the global configuration&lt;/li&gt;
  &lt;li&gt;else if age &amp;gt; 1 week, then increase cache TTL to 1 week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, your new content is able to get upgrades quite promptly, and your long tail of older news articles have a massively inflated cache TTL, meaning your varnish server should really be able to help out here. It is important to note, that these rules apply at a node by node level, and can be made to suit your business requirements.&lt;/p&gt;

&lt;h3 id=&quot;modules-out-there-already&quot;&gt;Modules out there already&lt;/h3&gt;

&lt;p&gt;I had a look around on Drupal.org to see what others had done in this area in the past, I was genuinely surprised at the lack of modules in this area, here is a short summary:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.drupal.org/project/cacheexclude&quot;&gt;cacheexclude&lt;/a&gt; - used to exclude certain URLs from cache, kind of the opposite of what we want here.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.drupal.org/project/max_age&quot;&gt;max_age&lt;/a&gt; - an (abandoned?) Drupal 6 module that aimed to provide different cache TTLs per content type in addition to the site wide default. This is close to what we want, there even is a &lt;a href=&quot;https://www.drupal.org/node/1322158&quot;&gt;patch to help port the module to Drupal 7&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;example-code&quot;&gt;Example code&lt;/h3&gt;

&lt;p&gt;Rather than using any existing contrib modules, the code required to do what we want is very simple. Here is some simple custom code that can be used as a starting point for your code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; * Implements hook_page_build().&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; *&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; * Responsible for setting the cache TTL based on the content attributes.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt; */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MYMODULE_page_build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;menu_get_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;node&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;drupal_page_is_cacheable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$age_since_last_change&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;REQUEST_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;news_article&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// If the news article has not been edited in a week, then set the cache&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// TTL to a week.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$age_since_last_change&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;604800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;$max_age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;604800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;drupal_add_http_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Cache-Control&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;public,max-age=&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$max_age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;drupal_add_http_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Expires&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;gmdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DATE_RFC1123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;REQUEST_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$max_age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The above example is simple, but hopefully gives you a taste of what can be achieved with very little code.&lt;/p&gt;

&lt;h3 id=&quot;how-to-test-your-code&quot;&gt;How to test your code&lt;/h3&gt;

&lt;p&gt;I have a few cURL aliases in my &lt;code&gt;.bashrc&lt;/code&gt; that will come in handy if you do a lot of this type of thing:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# cURL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; curlh&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; curl -sLIXGET &lt;span class=&quot;s2&quot;&gt;&amp;quot;$@&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; curlc&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; curl -sLIXGET &lt;span class=&quot;s2&quot;&gt;&amp;quot;$@&amp;quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; grep -E -i &lt;span class=&quot;s2&quot;&gt;&amp;quot;^(Cache-Control|Age|Expires|Set-Cookie|X-Cache|X-Varnish|X-Hits|Vary)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So if you are after all response headers you can:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curlh www.example.com
HTTP/1.1 &lt;span class=&quot;m&quot;&gt;200&lt;/span&gt; OK
Accept-Ranges: bytes
Cache-Control: max-age&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;604800
Content-Type: text/html
Date: Sun, &lt;span class=&quot;m&quot;&gt;06&lt;/span&gt; Sep &lt;span class=&quot;m&quot;&gt;2015&lt;/span&gt; 22:52:40 GMT
Etag: &lt;span class=&quot;s2&quot;&gt;&amp;quot;359670651&amp;quot;&lt;/span&gt;
Expires: Sun, &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt; Sep &lt;span class=&quot;m&quot;&gt;2015&lt;/span&gt; 22:52:40 GMT
Last-Modified: Fri, &lt;span class=&quot;m&quot;&gt;09&lt;/span&gt; Aug &lt;span class=&quot;m&quot;&gt;2013&lt;/span&gt; 23:54:35 GMT
Server: ECS &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;cpm/F9D5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
X-Cache: HIT
x-ec-custom-error: 1
Content-Length: 1270
Connection: Keep-Alive
Age: 2265&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you just want the relevant caching response headers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curlc www.example.com
Cache-Control: max-age&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;604800
Expires: Sun, &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt; Sep &lt;span class=&quot;m&quot;&gt;2015&lt;/span&gt; 22:52:40 GMT
X-Cache: HIT
Age: 2249&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;what-this-technique-relies-on&quot;&gt;What this technique relies on&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Expectation setting&lt;/strong&gt; - you will need to ensure all content authors and stakeholders are aware what this means. If a really old news article is edited, there potentially will be lag before the changes are live.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enough RAM in varnish&lt;/strong&gt; to house all your objects. If you are RAM constrained, and are seeing a lot of evictions at present, then this technique will not have the impact you will want it to. You might be better to upsize varnish first, then look at this technique.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;I am keen to hear what other large websites do in the area, especially around multiple layers of external cache invalidation and/or if custom cache TTL headers are used. Also if you know of any other contrib modules that can help here, let me know.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Sun, 06 Sep 2015 00:00:00 +1200</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/dynamic-content-caching-based-on-attributes-in-drupal-7/</guid>
        <comments>http://www.pixelite.co.nz/article/dynamic-content-caching-based-on-attributes-in-drupal-7/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Debugging Drupal performance with Cache Debug module</title>
        <link>http://www.pixelite.co.nz/article/debugging-drupal-performance-with-cache-debug/</link>
        <description>&lt;h2 id=&quot;preface&quot;&gt;Preface&lt;/h2&gt;
&lt;p&gt;This blog post is for developers, not site builders, as the analysis for cache debugging requires knowledge about the runtime stack of Drupal.&lt;/p&gt;

&lt;h2 id=&quot;the-problem-with-caching-in-drupal-7&quot;&gt;The Problem with Caching in Drupal 7&lt;/h2&gt;
&lt;p&gt;To obtain performance in Drupal 7, Drupal relies heavily on caching. That is, to process something and cache the end result so that same work doesn’t need to be repeated. Conditions also have to be created for when that cache expires or is invalidated.
Drupal has a caching layer to help with this. When you want to store something in cache, you use &lt;a href=&quot;https://api.drupal.org/api/drupal/includes%21cache.inc/function/cache_set/7&quot;&gt;cache_set&lt;/a&gt;, to retrieve it, you use &lt;a href=&quot;https://api.drupal.org/api/drupal/includes%21cache.inc/function/cache_get/7&quot;&gt;cache_get&lt;/a&gt; and to wipe the cache bin clean, you can use &lt;a href=&quot;https://api.drupal.org/api/drupal/includes%21cache.inc/function/cache_clear_all/7&quot;&gt;cache_clear_all&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Often, modules can implicitly clear or set cache unintentionally. This can lead to more caching overhead than you need. For example, theme registry clearing, use of the variable_set function or calls to other modules that call cache_clear_all. The problem is, how do you track down culprits to fix the issue?&lt;/p&gt;

&lt;h2 id=&quot;enter-cache-debug&quot;&gt;Enter Cache Debug&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.drupal.org/project/cache_debug&quot;&gt;Cache Debug&lt;/a&gt; is a module that wraps around the caching layer and adds logging. Including stacktrace information. It means when a cache_set or cache_clear_all is called, you can trace back to what called it - understand the problem and fix it. Very quickly.&lt;/p&gt;

&lt;p&gt;It comes with three logging options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;watchdog&lt;/strong&gt; - good if you’re using syslog module but deadly if you’re using dblog.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;error_log&lt;/strong&gt; - logs to your php error log&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;arbitrary file&lt;/strong&gt; - specify your own log file to log to&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;configuring-cache-debug&quot;&gt;Configuring Cache Debug&lt;/h2&gt;
&lt;p&gt;Because the caching system is so highly utilized, cache logging can be incredibly verbose. Perhaps this is why there is no logging around this in Drupal core. Fortunately, Cache Debug is highly configurable to control what to log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Because the caching system is loaded and used before Drupal’s variable system which manages configuration, it is best to set configuration in settings.php rather than in the database. However, there is a web UI that does set configuration in the database for ease of use.&lt;/p&gt;

&lt;h3 id=&quot;basic-configuration&quot;&gt;Basic configuration&lt;/h3&gt;
&lt;p&gt;If you’ve used the memcache module before, this should feel familiar. In order to use Cache Debug, you need to set it as the cache handler:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_backends&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;sites/all/modules/cache_debug/cache_debug.inc&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_default_class&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;DrupalDebugCache&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This tells Drupal that there is a cache backend located in the path provided (make sure its correct for your Drupal site!) and that the default class for all cache bins is the DrupalDebugCache class. If you only want to monitor a single bin you may want to omit this option.&lt;/p&gt;

&lt;p&gt;Since Cache Debug is a logger and not an actual caching system, it needs to pass cache requests onto a real cache system. By default, Debug Cache will use Drupal core’s Database Cache system for cache storage, but if you’re using memcache, redis or similar, you may want to set that as the handler for Cache Debug:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_class&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;MemCacheDrupal&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_cache_debug_form&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;DrupalDatabaseCache&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You need to also configure those modules accordingly.&lt;/p&gt;

&lt;p&gt;At this point, you’ll be logging all cache calls and stack traces to set and clear calls to the php error log.&lt;/p&gt;

&lt;h3 id=&quot;configure-the-logging-location&quot;&gt;Configure the logging location&lt;/h3&gt;

&lt;p&gt;You may want to choose your own logging location. For example, if you use dblog, then you won’t want to log to watchdog because it will bloat your database. Likewise, if you don’t want to bloat  our php error log, then you may want to log to an arbitrary file. You can choose your logging location by setting &lt;code&gt;cache_debug_logging_destination&lt;/code&gt; to error_log (default), watchdog or file. For file you will also need to provide the location:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_logging_destination&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;file&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_filepath&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/tmp/cachedebug.log&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;configuring-logging-options&quot;&gt;Configuring logging options&lt;/h3&gt;

&lt;p&gt;You can choose to log calls to cache get, getMulti, set and clear. You can also choose to log a stacktrace of these calls to show the stack that triggered the call. This is most useful for calls to SET and CLEAR. For a minimal logging option with the most about of insight, you might want to try this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_getMulti&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_set&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_clear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_stacktrace_set&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_stacktrace_clear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;logging-per-cache-bin&quot;&gt;Logging per cache bin&lt;/h3&gt;

&lt;p&gt;You don’t have to log the entire caching layer if you know which bin to look at for the caching issue you’re observing. For example, if you’re looking for misuse of variable_set, you only need to log the cache_bootstrap bin. In which case you could do this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Do not log to all cache bins so ensure this line is removed (from above):&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# $conf[&amp;#39;cache_default_class&amp;#39;] = &amp;#39;DrupalDebugCache&amp;#39;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_bootstrap_class&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;DrupalDebugCache&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;configure-for-common-issues&quot;&gt;Configure for common issues&lt;/h3&gt;
&lt;p&gt;Variable set calls and theme registry rebuilds are the two most common issues and so Cache Debug has use cases for these issues built in. So long as Cache Debug is the cache handler for the bin, you can turn off logging and turn on these features and Cache Debug will only log when these issues occur:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_default_class&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;DrupalDebugCache&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_common_settings&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&amp;#39;variables&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&amp;#39;theme_registry&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Turn off logging&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_getMulti&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_set&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cache_debug_log_clear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;analysing-the-logged-data&quot;&gt;Analysing the logged data&lt;/h2&gt;

&lt;p&gt;Cache debug logs to a log file like the example below:
&lt;img src=&quot;http://www.pixelite.co.nz/img/cache-debug/cache_debug-example.png&quot; alt=&quot;Example output of cache debug logging&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this snapshot of log output you can see both how cache debug logs cache calls and the stacktracing in action.&lt;/p&gt;

&lt;h3 id=&quot;log-format-structure&quot;&gt;Log format structure&lt;/h3&gt;
&lt;p&gt;A log line starts with a value that describes the cache bin, the cache command and the cache id. E.g. &lt;code&gt;cache_bootstrap-&amp;gt;set-&amp;gt;variables&lt;/code&gt; would bet a cache_set call to the cache_bootstrap cache bin to set the variables cache key.
Some calls also log additional data, for example, cache clear also indicates if the call was a wildcard clear. Set calls also log how much data (length) was set.&lt;/p&gt;

&lt;h3 id=&quot;stack-trace-logs&quot;&gt;Stack trace logs&lt;/h3&gt;
&lt;p&gt;When stack tracing is enabled for specific commands, a stack trace will be logged immediately after the log event that triggered it. The trace rolls back through each function that led to the current cache command being triggered. In the example above you can see that cache_clear_all was called by drupal_theme_rebuild which was called by an include from phptemplate_init. If you look at the source code in phptemplate_init, you’ll see that this means a cache rebuild was triggered from including template.php. In this case it was that Zen base theme had the theme registry rebuild left on.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Wed, 01 Jul 2015 00:00:00 +1200</pubDate>
        <dc:creator>Josh Waihi</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/debugging-drupal-performance-with-cache-debug/</guid>
        <comments>http://www.pixelite.co.nz/article/debugging-drupal-performance-with-cache-debug/#disqus_thread</comments>
      </item>
    
      <item>
        <title>How to profile PHP memory with Drupal</title>
        <link>http://www.pixelite.co.nz/article/how-to-profile-php-memory-with-drupal/</link>
        <description>&lt;h2 id=&quot;why-is-this-important&quot;&gt;Why is this important&lt;/h2&gt;

&lt;p&gt;How to size the PHP setting &lt;code&gt;max_memory&lt;/code&gt; is actually really important for the health of your Drupal application. Size this too small, and you risk getting PHP fatals due to not enough memory allocated. Size this too large, and you are essentially under-utilising your hardware, which in turn can lead to more cost.&lt;/p&gt;

&lt;h2 id=&quot;how-to-record-every-drupal-requests-php-max-memory-usage&quot;&gt;How to record every Drupal requests PHP max memory usage&lt;/h2&gt;

&lt;p&gt;Tim Hillard created this really nice module called &lt;a href=&quot;https://www.drupal.org/project/memory_profiler&quot;&gt;Memory profiler&lt;/a&gt;, which probably wins some sort of award for being around one of the smallest modules on drupal.org. Essentially this module registers a &lt;a href=&quot;https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_register_shutdown_function/7&quot;&gt;shutdown function&lt;/a&gt; that gets called at the end of every normal Drupal request.&lt;/p&gt;

&lt;p&gt;The module is lightweight enough to run on production and only produces an extra syslog line per request.&lt;/p&gt;

&lt;h2 id=&quot;analyse-the-data&quot;&gt;Analyse the data&lt;/h2&gt;

&lt;p&gt;The data for memory profiler flows into watchdog, so if you run syslog (which you should), you can use CLI tools to analyse the data.&lt;/p&gt;

&lt;h3 id=&quot;what-does-a-single-request-look-like&quot;&gt;What does a single request look like&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;memory profiler&amp;quot;&lt;/span&gt; drupal-watchdog.log &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; head -n 1

May &lt;span class=&quot;m&quot;&gt;26&lt;/span&gt; 06:25:21 10.212.4.16 sitename: https://www.sitename.com&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;1432621521&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;memory profiler&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;1.152.97.17&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;https://www.sitename.com/&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;https://www.sitename.com/home&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;0&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;4.75 MB - home &lt;span class=&quot;nv&quot;&gt;request_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;v-fc9573dc-036f-11e5-a8c0-22000af91462&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This comes from your syslog format (which can be changed on a per site basis):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;drush vget syslog_format

syslog_format: &lt;span class=&quot;s1&quot;&gt;&amp;#39;!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;extract-the-data-from-syslog&quot;&gt;Extract the data from syslog&lt;/h3&gt;

&lt;p&gt;From here you can tokenise the parts you actually care about, in other words the:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;URL requested (part 5)&lt;/li&gt;
  &lt;li&gt;PHP max memory (part 9)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using more bash foo&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;memory profiler&amp;quot;&lt;/span&gt; drupal-watchdog.log &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; head -n &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk -F&lt;span class=&quot;s1&quot;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; -v &lt;span class=&quot;nv&quot;&gt;OFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;{print $5, $9}&amp;#39;&lt;/span&gt;

https://www.sitename.com/,4.75 MB - home &lt;span class=&quot;nv&quot;&gt;request_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;v-fc9573dc-036f-11e5-a8c0-22000af91462&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On Acquia Cloud a request ID is added to all requests, we don’t need this. Also having the string ‘MB’ there is superfluous.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;memory profiler&amp;quot;&lt;/span&gt; drupal-watchdog.log &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; head -n &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk -F&lt;span class=&quot;s1&quot;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; -v &lt;span class=&quot;nv&quot;&gt;OFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;{print $5, $9}&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sed &lt;span class=&quot;s1&quot;&gt;&amp;#39;s/ MB.*//&amp;#39;&lt;/span&gt;

https://www.sitename.com/,4.75&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Perfect.&lt;/p&gt;

&lt;p&gt;So in order to create a CSV for analysing in a spreadsheet you could do:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;request_uri,max_memory&amp;quot;&lt;/span&gt; &amp;gt; /tmp/memory.csv &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;memory profiler&amp;quot;&lt;/span&gt; drupal-watchdog.log &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk -F&lt;span class=&quot;s1&quot;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; -v &lt;span class=&quot;nv&quot;&gt;OFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;{print $5, $9}&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sed &lt;span class=&quot;s1&quot;&gt;&amp;#39;s/ MB.*//&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /tmp/memory.csv&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then you can make pretty graphs if you want:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.pixelite.co.nz/img/profile/graph.png&quot; alt=&quot;Graph showing PHP memory usage sorted by smallest to largest&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Or if you just want to find the top requests to your application by memory you can do&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;memory profiler&amp;quot;&lt;/span&gt; drupal-watchdog.log &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; awk -F&lt;span class=&quot;s1&quot;&gt;&amp;#39;|&amp;#39;&lt;/span&gt; -v &lt;span class=&quot;nv&quot;&gt;OFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;{print $5, $9}&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sed &lt;span class=&quot;s1&quot;&gt;&amp;#39;s/ MB.*//&amp;#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; sort -t, -k+2 -n -r &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; head -n 20&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Based on your findings in the logs, you should be able to come up with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A better understanding of your request memory profile&lt;/li&gt;
  &lt;li&gt;Better max memory settings for your Drupal application&lt;/li&gt;
  &lt;li&gt;Potentially identify poor performing pages (memory wise) and can look to optimise them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h2&gt;

&lt;p&gt;This module will only work if:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;hook_boot()&lt;/code&gt; is called (which might not be the case if you run custom lightweight PHP scripts that do not bootstrap Drupal)&lt;/li&gt;
  &lt;li&gt;The Drupal request is not terminated with a SIGTERM or SIGKILL signal&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;Let me know if you found this helpful, or if you have any changes to my bash foo. If you have profiled your Drupal application recently, what methods and tools did you use?&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Wed, 27 May 2015 00:00:00 +1200</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/how-to-profile-php-memory-with-drupal/</guid>
        <comments>http://www.pixelite.co.nz/article/how-to-profile-php-memory-with-drupal/#disqus_thread</comments>
      </item>
    
      <item>
        <title>Top 10 DrupalCon LA sessions</title>
        <link>http://www.pixelite.co.nz/article/top-10-drupalcon-la-sessions/</link>
        <description>&lt;h2 id=&quot;drupalcon-la&quot;&gt;DrupalCon LA&lt;/h2&gt;

&lt;p&gt;So I did not make it along to DrupalCon Los Angeles, but I did spend some time reading twitter, and watching the sessions online. Here are some of the sessions I found entertaining and insightful and would recommend to others.&lt;/p&gt;

&lt;h2 id=&quot;driesnote-keynote&quot;&gt;Driesnote Keynote&lt;/h2&gt;

&lt;p&gt;Dries, as always, sets the lay of the land with Drupal. He also goes into the early days of Drupal, and how some key people he was involved with and have now gone on to form organisations that centre around Drupal.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Best quote:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Obstacles don’t block the path, they are the path&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;
&lt;iframe width=&quot;1280&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/uNRtZDAS0xI&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;no&quot;&gt;No&lt;/h2&gt;

&lt;p&gt;Larry Garfield gives an interesting talk on why sometimes it is best to say NO in order to give focus to the things that actually matter.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Best quote:&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Case and point, the new Macbook Airs, they say NO TO EVERYTHING.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/eXYs09gxIzE&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;php-containers-at-scale-5k-containers-per-server&quot;&gt;PHP Containers at Scale: 5K Containers per Server&lt;/h2&gt;

&lt;p&gt;David Strauss explains the history of web hosting, and how this is now far more complex. David is CTO of &lt;a href=&quot;https://pantheon.io/&quot;&gt;Pantheon&lt;/a&gt;, and they now run 100,000+ websites, all with dev + test + production environments. Pantheon run 150+ containers on a 30GB box (205MB each on average). Really interesting talk on how to run large amounts of sites efficiently.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/hFqEsqRFB9s&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;decoupled-drupal-when-why-and-how&quot;&gt;Decoupled Drupal: When, Why, and How&lt;/h2&gt;

&lt;p&gt;Amitai Burstein and Josh Koenig give a really entertaining presentation on monolithical architectures and some developer frustrations. And then introduce REST web services in Drupal 8, and how this can be used to provide better consumer interfaces for other frameworks.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/CSrWpXe4nzw&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;features-for-drupal-8&quot;&gt;Features for Drupal 8&lt;/h2&gt;

&lt;p&gt;Mike Potter goes through what role features played in Drupal 7, and how features will adapt in Drupal 8 now that CMI is in. Features in Drupal 8 will be going back to it’s roots and provide ‘bundles’ of configuration for re-use.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/-91v3QvMkc0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;meet-commerce-2x&quot;&gt;Meet Commerce 2.x&lt;/h2&gt;

&lt;p&gt;Ryan and Bojan go through 1.x on Drupal 7, and how they have chosen to develop Commerce 2.x on Drupal 8. This is a complete rewrite. The hierarchical product model is really exciting.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/I65tHQtJSJY&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;how-when-and-why-to-patch-a-module&quot;&gt;How, When and Why to Patch a Module&lt;/h2&gt;

&lt;p&gt;Joshua Turton goes over what a patch is, when you should patch contributed modules, and how to keep track of these with Drush make.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/rsNEF_JZ-2o&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;My colleague Josh also wrote a blog post on &lt;a href=&quot;https://www.acquia.com/blog/patching-drush-make&quot;&gt;how to use Drush make&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;ci-for-css-creating-a-visual-regression-testing-workflow&quot;&gt;CI for CSS: Creating a Visual Regression Testing Workflow&lt;/h2&gt;

&lt;p&gt;I topic that I am passionate about is visual regressions, here Kate Kligman goes through some tools that can help you test your site for visual changes. Tools covered include PhantomJS, SlimerJS, Selenium, &lt;a href=&quot;https://github.com/BBC-News/wraith&quot;&gt;Wraith&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/D63FWeYhISU&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;speeding-up-drupal-8-development-using-drupal-console&quot;&gt;Speeding up Drupal 8 development using Drupal Console&lt;/h2&gt;

&lt;p&gt;Eduardo and Jesus give us an introduction to your new best friend in Drupal 8. Drupal console is a Symfony CLI application to help you write boilerplate code, e.g. to create a new module. Personally, I am excited for the form API generator, and the ability to create a new entity with a single command.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/8Eu43RXKHmw&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;For more information see &lt;a href=&quot;http://drupalconsole.com/&quot;&gt;drupalconsole.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;qa-with-dries&quot;&gt;Q&amp;amp;A with Dries&lt;/h2&gt;

&lt;p&gt;As Drupal heads down from 130 critical issues down to &lt;a href=&quot;https://www.drupal.org/project/issues/search/drupal?project_issue_followers=&amp;amp;status%5B%5D=1&amp;amp;status%5B%5D=13&amp;amp;status%5B%5D=8&amp;amp;status%5B%5D=14&amp;amp;status%5B%5D=15&amp;amp;status%5B%5D=4&amp;amp;status%5B%5D=16&amp;amp;priorities%5B%5D=400&amp;amp;version%5B%5D=8.x&amp;amp;issue_tags_op=%3D&quot;&gt;22&lt;/a&gt; currently, what are some key concerns by people. The questions are answered by dries, xjm, webchick and alexpott.&lt;/p&gt;

&lt;div class=&quot;embed-responsive embed-responsive-4by3&quot;&gt;
&lt;iframe width=&quot;960&quot; height=&quot;720&quot; src=&quot;https://www.youtube.com/embed/nAzQpbdZGGI&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;where-can-i-find-more-videos&quot;&gt;Where can I find more videos&lt;/h2&gt;

&lt;p&gt;Don’t worry there are plenty more videos on the &lt;a href=&quot;https://www.youtube.com/user/DrupalAssociation/videos&quot;&gt;Drupal Association Youtube page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;

&lt;p&gt;If you have any awesome sessions that I have missed let me know in the comments.&lt;/p&gt;
</description>
        <creativeCommons:license>http://creativecommons.org/licenses/by-nc/4.0/</creativeCommons:license>
        <pubDate>Fri, 22 May 2015 00:00:00 +1200</pubDate>
        <dc:creator>Sean Hamlin</dc:creator>
        <guid isPermaLink="true">http://www.pixelite.co.nz/article/top-10-drupalcon-la-sessions/</guid>
        <comments>http://www.pixelite.co.nz/article/top-10-drupalcon-la-sessions/#disqus_thread</comments>
      </item>
    
  </channel>
</rss>
