Skip to main content

Blog

This same functionality is theoretically possible for other list fields, but In Other Words does not currently support summarizing text and numeric lists. We have opened a feature request to do so, but do not currently have the need ourselves, so we welcome your involvement. Feel free to open issues about things you think would make the module better, too!

A final note. We went through considerable effort to allow In Other Words' conjoining symbols and words, as well as surrounding text, to appear immediately before or after the items in a list with no whitespace. (Note: This does not work when Twig debugging is enabled.) This means you can have information presented as a sentence that ends with a period, "Open Tuesday–Friday." It also means that if you want spacing you need to ensure you add it as part of your join symbol or separator, your final join word, and your before and after text. This is a common gotcha in configuring In Other Words field formatters that you, perhaps, can now avoid!

Mauricio head photo

If you would like me to speak at your event, et me know the details below.

In recent articles, we have presented some recommendations and tools to debug Drupal migrations. Using a proper debugger is definitely the best way to debug Drupal be it migrations or other substems. In today’s article, we are going to learn how to configure XDebug inside DrupalVM to connect to PHPStorm. First, via the command line using Drush commands. And then, via the user interface using a browser. Let’s get started.

Important: User interfaces tend to change. Screenshots and referenced on-screen text might differ in new versions of the different tools. They can also vary per operating system. This article uses menu items from Linux. Refer the the official DrupalVM documentation for detailed installation and configuration instructions. For this article, it is assumed that VirtualBox, Vagrant, and Ansible are already installed. If you need help with those, refer to the DrupalVM’s installation guide.

Getting DrupalVM

First, get a copy of DrupalVM by cloning the repository or downloading a ZIP or TAR.GZ file from the available releases. If you downloaded a compressed file, expand it to have access to the configuration files. Before creating the virtual machine, make a copy of default.config.yml into a new file named config.yml. The latter will be used by DrupalVM to configure the virtual machine (VM). In this file, make the following changes:

# config.yml file
# Based off default.config.yml
vagrant_hostname: migratedebug.test
vagrant_machine_name: migratedebug
# For dynamic IP assignment the 'vagrant-auto_network' plugin is required.
# Otherwise, use an IP address that has not been used by any other virtual machine.
vagrant_ip: 0.0.0.0

# All the other extra packages can remain enabled.
# Make sure the following three get installed by uncommenting them.
installed_extras:
  - drupalconsole
  - drush
  - xdebug

php_xdebug_default_enable: 1
php_xdebug_cli_disable: no

The vagrant_hostname is the URL you will enter in your browser’s address bar to access the Drupal installation. Set vagrant_ip to an IP that has not been taken by another virtual machine. If you are unsure, you can set the value to 0.0.0.0 and install the vagrant-auto_network plugin. The plugin will make sure that an available IP is assigned to the VM. In the installed_extras section, uncomment xdebug and drupalconsole. Drupal Console is not necessary for getting XDebug to work, but it offers many code introspection tools that are very useful for Drupal debugging in general. Finally, set php_xdebug_default_enable to 1 and php_xdebug_cli_disable to no. These last two settings are very important for being able to debug Drush commands.

Then, open a terminal and change directory to where the DrupalVM files are located. Keep the terminal open are going to execute various commands from there. Start the virtual machine by executing vagrant up. If you had already created the VM, you can still make changes to the config.yml file and then reprovision. If the virtual machine is running, execute the command: vagrant provision. Otherwise, you can start and reprovision the VM in a single command: vagrant up --provision. Finally, SSH into the VM executing vagrant ssh.

By default, DrupalVM will use the Drupal composer template project to get a copy of Drupal. That means that you will be managing your module and theme dependencies using composer. When you SSH into the virtual machine, you will be in the /var/www/drupalvm/drupal/web directory. That is Drupal’s docroot. The composer file that manages the installation is actually one directory up. Normally, if you run a composer command from a directory that does not have a composer.json file, composer will try to find one up in the directory hierarchy. Feel free to manually go one directory up or rely on composer’s default behaviour to locate the file.

For good measure, let’s install some contributed modules. Inside the virtual machine, in Drupal’s docroot, execute the following command: composer require drupal/migrate_plus drupal/migrate_tools. You can also create directory in /var/www/drupalvm/drupal/web/modules/custom and place the custom module we have been working on throughout the series. You can get it at https://github.com/dinarcon/ud_migrations.

To make sure things are working, let’s enable one example modules by executing: drush pm-enable ud_migrations_config_entity_lookup_entity_generate. This module comes with one migration: udm_config_entity_lookup_entity_generate_node. If you execute drush migrate:status the example migration should be listed.

Configuring PHPStorm

With Drupal already installed and the virtual machine running, let’s configure PHPStorm. Start a new project pointing to the DrupalVM files. Feel free to follow your preferred approach to project creation. For reference, one way to do it is by going to "Files > Create New Project from Existing Files". In the dialog, select "Source files are in a local directory, no web server is configured yet." and click next. Look for the DrupalVM directory, click on it, click on “Project Root”, and then “Finish”. PHPStorm will begin indexing the files and detect that it is a Drupal project. It will prompt you to enable the Drupal coding standards, indicate which directory contains the installation path, and if you want to set PHP include paths. All of that is optional but recommended, especially if you want to use this VM for long term development.

Now the important part. Go to “Files > Settings > Language and Frameworks > PHP”. In the panel, there is a text box labeled “CLI Interpreter”. In the right end, there is a button with three dots like an ellipsis (...). The next step requires that the virtual machine is running because PHPStorm will try to connect to it. After verifying that it is the case, click the plus (+) button at the top left corner to add a CLI Interpreter. From the list that appears, select “From Docker, Vagrant, VM, Remote...”. In the “Configure Remote PHP Interpreter” dialog select “Vagrant”. PHPStorm will detect the SSH connection to connect to the virtual machine. Click “OK” to close the multiple dialog boxes. When you go back to the “Languages & Frameworks” dialog, you can set the “PHP language level” to match the same version from the Remote CLI Interpreter.

Remote PHP interpreter.

CLI interpreters.

You are almost ready to start debugging. There are a few things pending to do. First, let’s create a breakpoint in the import method of the MigrateExecutable class. You can go to “Navigate > Class” to the project by class name. Or click around in the Project structure until you find the class. It is located at ./drupal/web/core/modules/migrate/src/MigrateExecutable.php in the VM directory. You can add a breakpoint by clicking on the bar to the left of the code area. A red circle will appear, indicating that the breakpoint has been added.

Then, you need to instruct PHPStorm to listen for debugging connections. For this, click on “Run > Start Listening for PHP Debugging Connections”. Finally, you have to set some server mappings. For this you will need the IP address of the virtual machine. If you configured the VM to assign the IP dynamically, you can skip this step momentarily. PHPStorm will detect the incoming connection, create a server with the proper IP, and then you can set the path mappings.

Triggering the breakpoint

Let’s switch back to the terminal. If you are not inside the virtual machine, you can SSH into the VM executing vagrant ssh. Then, execute the following command (everything in one line):

XDEBUG_CONFIG="idekey=PHPSTORM" /var/www/drupalvm/drupal/vendor/bin/drush migrate:import udm_config_entity_lookup_entity_generate_node

For the breakpoint to be triggered, the following needs to happen:

  • You must execute Drush from the vendor directory. DrupalVM has a globally available Drush binary located at /usr/local/bin/drush. That is not the one to use. For debugging purposes, always execute Drush from the vendor directory.
  • The command needs to have XDEBUG_CONFIG environment variable set to “idekey=PHPSTORM”. There are many ways to accomplish this, but prepending the variable as shown in the example is a valid way to do it.
  • Because the breakpoint was set in the import method, we need to execute an import command to stop at the breakpoint. The migration in the example Drush command is part of the example module that was enabled earlier.

When the command is executed, a dialog will appear in PHPStorm. In it, you will be asked to select a project or a file to debug. Accept what is selected by default for now.  By accepting the prompt, a new server will be configured using the proper IP of the virtual machine.  After doing so, go to “Files > Settings > Language and Frameworks > PHP > Servers”. You should see one already created. Make sure the “Use path mappings” option is selected. Then, look for the direct child of “Project files”. It should be the directory in your host computer where the VM files are located. In that row, set the “Absolute path on the server” column to  /var/www/drupalvm. You can delete any other path mapping. There should only be one from the previous prompt. Now, click “OK” in the dialog to accept the changes.

Incoming Drush connection.

Path mappings

Finally, run the Drush command from inside the virtual machine once more. This time the program execution should stop at the breakpoint. You can use the Debug panel to step over each line of code and see how the variables change over time. Feel free to add more breakpoints as needed. In the previous article, there are some suggestions about that. When you are done, let PHPStorm know that it should no longer listen for connections. For that, click on “Run > Stop Listening for PHP Debugging Connections”. And that is how you can debug Drush commands for Drupal migrations.

Path mappings.

Debugging from the user interface

If you also want to be able to debug from the user interface, go to this URL and generate the bookmarklets for XDebug: https://www.jetbrains.com/phpstorm/marklets/ The IDE Key should be PHPSTORM. When the bookmarklets are created, you can drag and drop them into your browser’s bookmarks toolbar. Then, you can click on them to start and stop a debugging session. The IDE needs to be listening for incoming debugging connections as it was the case for Drush commands.

PHPStorm bookmarlets generator

Note: There are browser extensions that let you start and stop debugging sessions. Check the extensions repository of your browser to see which options are available.

Finally, set breakpoints as needed and go to a page that would trigger them. If you are following along with the example, you can go to http://migratedebug.test/admin/structure/migrate/manage/default/migrations/udm_config_entity_lookup_entity_generate_node/execute Once there, select the “Import” operation and click the “Execute” button. This should open a prompt in PHPStorm to select a project or a file to debug. Select the index.php located in Drupal’s docroot. After accepting the connection, a new server should be configured with the proper path mappings. At this point, you should hit the breakpoint again.

Incoming web connections.

 

Happy debugging! :-)

What did you learn in today’s blog post? Did you know how to debug Drush commands? Did you know how to trigger a debugging session from the browser? Share your answers in the comments. Also, I would be grateful if you shared this blog post with others.

And don't miss the final blog post in the series, on the many modules available for migrations to Drupal.

This blog post series, cross-posted at UnderstandDrupal.com as well as here on Agaric.coop, is made possible thanks to these generous sponsors: Drupalize.me by Osio Labs has online tutorials about migrations, among other topics, and Agaric provides migration trainings, among other services.  Contact Understand Drupal if your organization would like to support this documentation project, whether it is the migration series or other topics.

Drupal Migrations and Upgrades

Quick and dirtiest

mkdir ~/sandbox
cd ~/sandbox

Now copy the four commands beginning with php from the top of Composer's quick install documentation. Paste them into the terminal in your sandbox directory. It's best to use Composer's commands, which they update to verify the hash after every release, which is why we don't have the commands for you to copy here. Once that is done, continue:

php composer.phar create-project drupal-composer/drupal-project:8.x-dev just-drupal --no-interaction
cd just-drupal
php ../composer.phar require drush/drush

If that works, you're good to go!

To use Drush:

vendor/bin/drush

To start PHP's built-in webserver and see the site, use:

php web/core/scripts/drupal quick-start standard

If it works, it will install Drupal with the Standard installation profile and log you in, opening your local site in your browser. Your terminal window, meanwhile, is dedicated to running the server. Open a new tab in your terminal, at the same location (cd ~/sandbox/just-drupal in our example) to be able to run more composer or other commands.

In the migration training for instance we have people use composer to get the code for the address module, so from ~/sandbox/just-drupal in our example we would run:

php ../composer.phar require drupal/address

And to enable the address module downloaded just above:

vendor/bin/drush en address

Note that the site must be 'running' with the php web/core/scripts/drupal quick-start command you can run at any time to get things started and log back in (don't worry if you get "Access Denied" while also seeing the administration menu (starting with "Manage" at the top left of your screen; this just means you were already logged in).

Caveat

This minimalist approach might not work either for your computer! If it doesn't, there may be more PHP things to install. For instance, if you run into an error about SQLite, you may need to enable or install SQLite with PHP first.  We'll update this blog post with further fixes and workarounds as they come up for our content migration or other training students.

Quick and just slightly polished

You may have noticed that typing php ../composer.phar and vendor/bin/drush is pretty ugly. This can be fixed while retaining essentially the same setup as above by installing Composer globally (for GNU/Linux and Mac OS X, or with the Windows installer for Microsoft Windows) and installing the Drush launcher. Once you've done that, you'll be able to use composer instead of php ../composer.phar and drush instead of vendor/bin/drush.

Warning and reminder

This is for a local development environment or sandbox testing site only!  PHP's built-in server, which is relied upon in the above, is absolutely not intended to be used in production environments.  Neither, for Drupal, is SQLite, which we're also using.  To repeat, this is not meant to be used live!

Prior art

Updated.  I knew it was out there, but didn't find this when i started writing.  This is very similar in approach to an article last year by MediaCurrent celebrating this capability coming to Drupal.  The main difference is that in our blog post here we use the Composer template for Drupal 8 projects.  This avoids having Git as a requirement (but you should always develop by committing to Git!) and also starts with a best-practices composer setup.  Distributions like Drutopia take the same approach.

Estudiantes marchando en la calle por la huelga climática.

350.org

Equipar a activistas de justicia climática con herramientas de movilización.

  1. We do recommend moving directly to Drupal 9 (which was released on June 3rd of 2020), however:

  2. Moving to Drupal 8 or to Drupal 9 is much the same. Drupal 8 starts what i call the "modern Drupal" era. Whereas for going from Drupal 5 to 6 or 6 to 7 or 7 to 8 broke backward compatibility and might as well be a full rebuild (so we would often recommend hopping a version, say, stay on Drupal 6 and wait for Drupal 8 to be ready) going from Drupal 8 to 9 is closer to going from Drupal 8.8 to 8.9— an in-place upgrade from 8.9 to 9.0. Going from 9 to 10 will work the same, and that's the plan and promise for Drupal 8 on out.

  3. All that said, if anything significant needs fixing on your current Drupal 7 site, or you are looking to make any improvements, you'll want to do that on Drupal 8+ or Drupal 8/9 as we phrased it back when Drupal 9 was still a pretty recent release, but now we can just say Drupal 9— or, as i call it to emphasize the decreased importance of major version numbers, modern Drupal.

Agaric is always happy to discuss more! Mostly what i'm saying here is the useful things to talk about are the specific goals for the sites—when you want to accomplish what—because the official support cycles are a distraction in the current context of Drupal. So make sure your current site is maintained, but take your time to get clear on your objectives, and contact Agaric or the Drupal professionals of your choice when you think it might make sense to upgrade your site into the era of modern Drupal.

Just got back to Boston from GLADCamp, the Greater Los Angeles Drupal Camp, a free community three-day event and one of the best camps I have attended. The theme was "Drupal for Good" and it delivered from the opening keynote to the closing barn raising.

I was glad to present two sessions at this camp, which was organized by volunteers from the Greater Los Angeles Drupal community—whose user group boasts 5 to 10 events a month—such as Christefano Reyes and Lee Vodra of Exaltation of Larks and an extensive list of Drupal community members and participating sponsors.  The free conference took place on March 7th, 8th & 9th, 2014, at the Hilton Pasadena & Convention Center in Pasadena, California. This camp had a lot to offer in the way of sessions and was one of the best camps I have attended due to...

  • Sessions organized into logical tracks.
  • Transparency of finances and process.
  • High level of interaction between attendees and presenters.
  • Top-notch keynote speakers and topics.

Intro to Drupal 1,2,3 - with Lee Vodra - Exhaltaion of LarksThe sessions were set up specifically so that attendees would not have to change conference rooms as often - and it worked, as I heard several people make positive remarks about the schedule. GLADCamp session tracking was chosen very wisely, and lots of them contained valuable information for start-ups, non-profits and people just starting to learn Drupal. The track for advanced developers was also very rich, and well planned. Most of the sessions offered Q+A time and/or hands on learning.

See a list of all the GLADCamp sessions here: https://gladcamp.org/2014/program/sessions

To the right is a pic from the Intro to Drupal 1, 2, 3 session led by Lee Vodra a co-founder of Exaltation of Larks.  

Advanced coders had excellent choices such as:

  • Search API Solr Setup
  • AJAX and jQuery with Drupal: Loading Nodes into the DOM without Reloading the Page
  • Advanced Views
  • Responsive Layouts with Omega4, SingularityGS and Breakpoint
  • The Symfony Way

Beginners and non-coders had choices such as:

  • Drupal Install Fest
  • Intro to Drupal 1, 2, 3 - Two Hour Session
  • Introduction to Views in Drupal 8
  • VoipDrupal - Your Site and a Regular Phone
  • Estimation for Drupal Projects: Reducing Uncertainty & Defining the Client Relationship

As FreeScholar, I presented two sessions - my first was tracked in "Business, Strategy & Case Studies" and was titled: "Drupal Community - The big picture - How do I earn $". This session was mostly a discussion around ways of forming a collective of Drupalists that take on projects that suit their goals and ideas. As a collective people can determine the best way to divide and use their 'collective' profits to fund causes that are near and dear to their hearts - such as FSF.org and EFF.org.  My second session was on VoipDrupal, a suite of modules developed at the MIT Media Lab by Dr. Leo Burd and a team of developers. The session was titled "VoipDrupal - Your Site and a Regular Phone", and it was tracked in "Site Building & Using Drupal".  VoipDrupal allows your website to interact with a regular telephone using a VOIP telephony service. This session was an overview of the core VoipDrupal modules and a live demonstration of the conference calling on the fly feature, some people caught up with me between the next sessions and were actively interested in using VoipDrupal on their current Drupal projects.

A fantastic time was had by all! Pasadena Media captured the whole GLADCamp event and will be incorporating the slides into the footage of the sessions. Of all the sessions, the Barn Raising was one of the MOST impressive. Pasadena Media was the recipient of the effort and they were gifted with about 15-20 developers/designers collaborating on the start of a Drupal 7 site that now has content types and some roles defined along with some custom menus - all created using Drupal's Best Practices!.

Respetar su privacidad y ser responsable con los datos que recopilamos de usted es de suma importancia para nosotros. No usaremos ni compartiremos su información con nadie, excepto como se describe en esta política de privacidad.

Recopilación y uso de la información.

Si elige dejar un comentario o un mensaje privado a través de nuestro formulario de contacto, es posible que le solicitemos que nos proporcione cierta información de identificación personal, incluido su nombre y dirección de correo electrónico. La información que recopilamos se utilizará para contactarlo o identificarlo.

Dato de registro

Queremos informarle que cada vez que visite nuestro sitio web, recopilamos la información que su navegador nos envía, que se llama Datos de registro. Estos datos de registro pueden incluir información como la dirección del Protocolo de Internet ("IP") de su computadora, la versión del navegador, las páginas de nuestro sitio web que visite, la fecha y hora de su visita, el tiempo dedicado a esas páginas y otras estadísticas.

Cookies

No utilizamos cookies para los visitantes de nuestro sitio. (Las cookies son archivos con una pequeña cantidad de datos que se utilizan comúnmente como un identificador único anónimo).

Proveedores de servicio

Empleamos una compañía de terceros, Google, para ayudarnos a analizar cómo se utiliza nuestro sitio web. Sólo se recopilan datos anónimos.

Hotjar nos ayuda a proporcionar a nuestros usuarios finales una mejor experiencia y servicio, así como a diagnosticar problemas técnicos y analizar las tendencias de los usuarios. Lo más importante es que a través de los servicios de Hotjar, la funcionalidad del sitio puede mejorarse, haciéndolos más fáciles de usar, más valiosos y más fáciles de usar para los usuarios finales.

Puede optar por no permitir que Hotjar recopile su información cuando visite un sitio habilitado para Hotjar en cualquier momento visitando nuestra página de exclusión y haciendo clic en "Deshabilitar Hotjar" o habilitando No rastrear (DNT) en su navegador.

Cambios a esta política de privacidad

Podemos actualizar nuestra Política de Privacidad de vez en cuando. Por lo tanto, le recomendamos que revise esta página periódicamente para cualquier cambio. Le notificaremos cualquier cambio mediante la publicación de la nueva Política de privacidad en esta página. Estos cambios entrarán en vigencia inmediatamente después de que se publiquen en esta página.

Contáctenos

Si tiene alguna pregunta o sugerencia sobre nuestra política de privacidad, no dude en ponerse en contacto con nosotros.

Find It Features and Functionality: Broadening Educational Opportunities for Youth

For clients whose sites we built and clients whose sites we inherited, Agaric frequently provides these services through one simple monthly retainer:

  • Security updates
  • Support, documentation improvement, and bugfixes
  • Continuous improvements as requested
  • Consulting and advice

We roll over hours month-to-month so we can put the work in when you need it.

Let us know how we can help you!

For long-term projects one of the engineering challenges is to cope with technical evolution. The languages, libraries, and third party software like database engines and web servers we are using are under continuous development; new features are only added to the current development branches and security updates are only applied to currently supported releases. Using the example of one of our projects I will point out specific challenges and possible ways to deal with them in a series of posts.

On https://premium.zeit.de the German weekly DIE ZEIT offers subscriptions to digital formats of their newspaper in epub, mobi, pdf and mp3. Since 2011 Drupal 6 with Ubercart was used to take orders and deliver the content. Fulfillment is handled by a third party also providing the service to the company's print publications. User accounts and customer information are stored by a dedicated service running behind the company’s firewall.

In 2013 several challenges slowed down development: The web stack was still running on Ubuntu 8 with PHP 5.2 which had reached end of life. Lack of a comprehensive test suite meant upgrading would be a risky operation or require extensive end-user testing. Also it had turned out Ubercart was not a good fit for the project. Just maintaining a catalog of products and taking orders to be sent to the fulfillment provider does not require a full e-commerce solution.

The first step of reviving the project and upgrading to Drupal 7 was to set up a virtual machine for development, resembling the intended production environment (Debian 7 with Apache, PHP and MySQL). Together with the client and our partners Spry Group, we decided to use Vagrant to create a virtual machine from configuration and provisioning scripts which were committed to the repository. Before writing the provisioning scripts, tests were written to ensure the configuration of the development environment matched expectations.

  /**
   * Tests if a Drupal web site is served on port 80, and there are no errors.
   */
  public function testDrupalWorks() {
    $data = file_get_contents('http://127.0.0.1/');
    $pos = stripos($data, 'name="Generator" content="Drupal 7"');
    $this->assertTrue($pos !== FALSE);
  }

  /**
   * Tests that drush is properly configured.
   */
  public function testDrushWorks() {
    $output = shell_exec('drush st --pipe');
    $this->assertRegExp('#drush_configuration=/etc/drush/drushrc.php#', $output);
  }

Above code sample shows two of our infrastructure tests written in PHPUnit. They are executed inside the virtual machine which is supposed to serve a fully working Drupal installation. The second test asserts drush is available and is using the appropriate configuration file. We chose PHPUnit as our testing framework, and we also used it for functional testing of the Drupal web site later. In one of the upcoming posts I am going to explain the approach we developed with Spry Group. These and other infrastructure test suites proved very useful during our transition to provisioning with Chef.

This combination—of a virtual machine to simulate the production environment with corresponding tests validating its functionality—enabled us to adapt to technological evolution in a well-controlled environment. For example, trying out a new stable release of the operating system or even a different OS is a simple matter of creating a new virtual machine and running the test suite against it. The more code we would cover with tests the more likely it would be to detect any issues introduced by a newer version of PHP for example.

Over the coming months I will share further insights that we have learned from this long-term project.

Usted – nuestros clientes, colegas, y admiradores locos – es la razón por la que hacemos lo que hacemos. Nos encantaría saber de usted.

Get the most out of (and into) your page cache: Leave AJAX disabled in your Views, especially with exposed filters

Enabling AJAX for a Views page can have a performance-harming side effect one might not think of. On a recently built site we observed a relatively low Varnish cache hit rate of 30% using the Munin plugin for Varnish. This hit rate was much lower than expected after prelaunch tests with Jmeter. (The site has almost exclusively anonymous visitors and if caching the pages worked efficiently the same low cost server could handle a lot more traffic.)

An analysis of the most visited paths on the live site showed one ranking two orders of magnitude above all else: views/ajax.

The Views pages on Studio Daniel Libeskind have exposed filters, and with AJAX enabled new data is fetched via POST request to views/ajax when a visitor applies a filter. This happens because the Drupal AJAX Framework leveraged by views exclusively uses POST requests. See issue Ensure it is possible to use AJAX with GET requests for a good explanation of why it is currently this way and the effort to allow GET requests in later versions of Drupal.

As a consequence of all AJAX views using the same path and all filter information being hidden in the request body, Varnish has no straightforward means of caching the content coming from views/ajax. Another downside: It's not easy to share a link to a filtered version of such a page.

If AJAX is not enabled (which is the default) filters are implemented as query parameters in the URL so there's a unique URL for each filtered page. That plays well with reverse proxy caches like Varnish and works well for people wanting to share links, so we disabled AJAX again and the Varnish page cache hit rate has risen to over 90% since.

Here are links and notes from the presentation "Iterative UX: Find It Cambridge" (most recently given at Drupaldelphia).