Archive

Archive for the ‘WordPress’ Category

More WordPress woes - degradation over time

March 13th, 2017 No comments

Another day, another WP headache.

One of my clients contacted me saying his site is not performing properly and it has been getting worse. It's on a server I setup, so the server shouldn't be the issue, but I wanted to make sure there's not something weird going on with it, and see if I can find some causes.

He's using my CloudFlare full page caching method - so that's good. His frontend performance (for cached pages) is lightning fast. But the backend and uncached pages (and 404s) are abysmal.

I notice the standard stuff - UberMenu, Genesis theme - both very general purpose things, which we all know sacrifices performance for flexibility. UberMenu can be a beast. "I can do everything" themes often are too. Anyway, the site WAS just fine previously with those, so it's something else. In the PHP-FPM slowlog and New Relic I see "maybe_unserialize" keep coming up over and over. That function ideally would never exist if it wasn't for WP storing serialized and unserialized strings in the same places and having to detect if it's working with one or not (such an amazing design...) - but either way, it shouldn't be as slow as it is.

That function is used all over the place - but there are a couple key places to look. Especially when cron is disabled in wp-config.php and I am seeing calls to wp_get_schedule, wp_next_scheduled, and other cron-looking functions, on what seems nearly every page load. I wound up finding the culprit. The client had used a plugin called "Easy Social Metrics" and what it would do is add to the cron queue at least one row per post. So there were hundreds if not thousands of "easy_social_metrics_update_single_post" one-time cron items that were being autoloaded EVERY PAGE LOAD - because the "cron" option is an autoloaded option. So every page load inside of the WP frontend or in the admin area was loading tons of serialized text in this single variable, checking if it was serialized, unserializing it, possibly updating/modifying it, reserializing it, etc.

The sad thing was he didn't like the plugin so he had deleted it. But it left behind all the residue.

I've seen similar issues with other options, the "rewrite_rules" one was abused by some other plugin as well in the past on another site (I don't have the details anymore) but this is the second time I've had to forcefully kill off an option to actually rescue performance. Sadly, this is nothing any normal end user who is using only plugins to interact with their site would ever notice, until they installed some sort of cron manager and saw all these excess items hanging around. Not many people would install a cron manager though, or should need to. So that would just mean they have to hire someone now to diagnose why this magical CMS that can do anything is so abysmally slow.

It does have a nice looking admin interface, low barrier to entry for end users, but the foundation is horrible, the plugin and theme ecosystem seems rich because of the number of options and downloads, but the quality and performance on most are lacking.

Here's a chart of the performance. You'll see the first time I killed the "cron" option around 3:13pm, which was rewritten in full again and caused the second spike, and then when I finally purged it for good around 3:17pm. Site is back with sub-1 second average and remained that way. Which is about as good as you're gonna get with 20+ plugins including UberMenu and a kitchen sink theme. 🙂

Categories: WordPress

Happy day! PHP-FPM included with PHP 5.3.3RC1, and WordPress 3.0

June 17th, 2010 3 comments

Officially in the 5.3.3RC1 distribution. Sweet! From the NEWS file:

17 Jun 2010, PHP 5.3.3 RC1
...
- Added FastCGI Process Manager (FPM) SAPI. (Tony)
...

and on an unrelated note:

- Added support for JSON_NUMERIC_CHECK option in json_encode() that converts numeric strings to integers. (Ilia)

Shouldn't this be called JSON_NUMERIC_CONVERT? or JSON_FORCE_INTEGER? It's not just a "check" - guess it's too late now? 🙂

WordPress 3.0...
WordPress 3.0 came out today. Tonight I'll probably upgrade this site and see how well it works. I'm going to check it in to Subversion first so I can roll back if needed.

Some key changes I wanted to talk about...

  • One thing that was highlighted is the option to use custom header images - which can easily be done right now. I did it well over a year ago in a theme. With post meta you can always load metadata about a post and use it in the theme, so this update seems a bit specific to me, since themes were already customizable. Why build a feature that is so specific? Same with background images/colors...
  • Custom menus/menu editor - this could get cool, the menu editor is the more exciting piece as it will allow a visual way to manage the taxonomy. Not sure how it will mix in with tags and categories though, guess that's "I'll see it when I upgrade."
  • MU merge - finally, I can run multiple installs off the same WP install, hopefully, without wp-config.php hacks. How exactly it works I will have to find out.
  • Custom post types - now all of a sudden you can make any sort of object with custom attributes, which opens the door to things such as the item below.
  • WP e-Commerce says they're going to change from using all their extra tables to using core WP schema. That's awesome.

A couple bones to pick...

  • It's not a rewrite. It's still a blogging tool that is being extended further to be a full-featured that can handle "anything" - however the tables are still named "posts" even though now you can create an arbitrary type of item. I'd like to see it renamed and normalized.
  • All the plugins and themes and such are procedural code, but some inner workings such as the DB layer are OO. That seems amateur to me, and unnecessary.

I'd love to see WP get rewritten. It has a LOT of overhead and includes built in that need calling and a lot of other cruft that I stumble across. Go back to the drawing board with building a list of every feature it has, and look at it from a longer term perspective. It's great to see something keep growing, but when it comes down to it, it is still a fork of b2, which was made for blogging, not for anything and everything.

It's got the right idea with extensibility and such, but to me the core has a lot of code - and lots of code means more complicated execution paths, more "I'll just add this in instead of refactor this old code," more cruft. I'm quite sure I could get as much extensibility out of a fresh rewrite with less than half the code under the hood. Things like text styling for example should be moved to a plugin (I disable all the wptexturize filters for example... throw those in an include and make it enabled by default instead!)

Of course, WordPress does have millions of users so it has a proven track record. I can't complain that much, I do use it myself. For blogging, it's the best tool out there. For other things, it typically leverages plugins which may or may not have decent UIs or APIs to interact with. That's where it shows signs of weakness. It also isn't as strict as Drupal when it comes to code conventions either, which would greatly increase the usability of a lot of plugins.

Categories: PHP, PHP-FPM, WordPress

WordPress 2.8.x - hiding those unwanted dashboard items

November 18th, 2009 No comments

There's a completely new way now to hide dashboard items, and I finally think I found the easiest way to do it, after having to poke around looking for the right hooks. This basically applies the same set of "screen options" to every user regardless if they've customized it or not. It leaves only the "Right Now" and "Recent Drafts" on the dashboard, but feel free to customize this array by checking the name that you want to hide and adding it to the array.

Also you can easily add more options such as column layout and such by customizing it on a user account, going into the database and looking for the 'metaboxhidden_dashboard' and related keys in the usermeta table and force-applying those (which is essentially what I did here)

Just thought I'd share this little tidbit of information. It can be put in its own plugin or inside of an existing one, as long as the filter gets triggered you're good to go. I've got it working properly in 2.8.6 currently without an issue.

function hide_dashboard_stuff() {
   $hide = array(
      0 => 'dashboard_recent_comments',
      1 => 'dashboard_incoming_links',
      2 => 'dashboard_plugins',
      3 => 'dashboard_quick_press',
      4 => 'dashboard_primary',
      5 => 'dashboard_secondary',
   );
   return $hide;
}

add_filter('get_user_option_metaboxhidden_dashboard', 'hide_dashboard_stuff', 1);
Categories: WordPress

WordPress on Drizzle - beaten to the punch

June 17th, 2009 No comments

Looks like Jeff Waugh actually beat me to it.

Haven't seen the code... but he's done it and sounds like he's done a somewhat thorough job.

Sadly I learned this from Brian's presentation on Drizzle at OS Bridge. Doh.

Categories: Drizzle, WordPress

Don't forget the $args!

May 15th, 2009 11 comments

August 2010 UPDATE: Read here for new information!

I just realized when trying to use the try_files shortcut that either the behavior has changed or the code I've used it for has never had to take advantage of it, but what happens when you do something like this:

try_files $uri $uri/ /foo/index.php?u=$uri;

Is that the query string is stripped out and the only thing PHP sees is the $_GET['u']

to fix this, it's easy enough:

try_files $uri $uri/ /foo/index.php?u=$uri&$args;

I never noticed this behavior in WordPress (I've been using try_files with it for a while without noticable issue) but it was an issue with Drupal (which uses the Front-Controller Pattern just like WP does) and custom code we've written.

UPDATE: depending on what custom WordPress plugins you may be using that rely on the $_GET params, you should add this. This should be the final recipe from what I can tell. 🙂

I haven't asked Igor if the behavior has changed slightly with it, but the changelogs have not mentioned any behavior changes, from what I can tell.

Anyway if you notice your pagination or other GET params are being dropped, you might want to take a quick look at that 🙂

Categories: nginx, WordPress

Finally using nginx's "try_files" directive

March 19th, 2009 20 comments

OLD:

error_page 404 = /wordpress/index.php?q=$request_uri;

or:

if (!-e $request_filename) {
   rewrite ^/(.*) /wordpress/index.php?uri=$request_uri last;
}

NEW:

try_files $uri $uri/ /wordpress/index.php?q=$uri&$args;

Why make life more complicated if you don't need to?

Also thanks to Igor's patch you can have multiple of these, i.e.:

location /wordpress {
   try_files $uri $uri/ /wordpress/index.php?q=$uri&$args;
}

location /anotherapp {
   try_files $uri $uri/ /anotherapp/controllerfile.php?q=$uri&$args;
}

etc.

This is good, as Drupal, WordPress and many other packages (including anything I develop anymore) use the Front Controller pattern of development, which makes deploying applications a lot simpler and helps support a consistent framework across the entire application.

NOTE: Updated on 1/5/10 to include the $args 🙂

Categories: nginx, WordPress

Disabling autosave in WordPress (properly)

November 23rd, 2008 No comments

This is for 2.6.3 - not sure what other versions it will work on. Originally I got this from http://www.untwistedvortex.com/2008/06/27/adjust-wordpress-autosave-or-disable-it-completely/, however, it was still giving me a JavaScript error.

Note that this worked fine in FF 2.x, and IE6 for English; but for some reason it broke in Chinese and only in IE6. It kept telling me it was failing to update a post, even though it was a new post. I traced it down finally to autosave.js changing the value of the action to "editpost" instead of "post" - I am not exactly sure why the behavior is different in different browsers, but at this point I don't care. The autosave feature is not necessary for the blog site I am supporting for work.

At first I tried a wp-config.php attempt, from http://sheldon.lendrum.co.nz/disabling-wordpress-auto-save-and-revision-saving_227/04/ - but that must not work in 2.6.x - it still appeared to be autosaving. The next Google result was a plugin fashion that dequeued the autosave.js script - which was the culprit, but it wasn't quite complete (at least in my 2.6.3 install) - so here is the complete fix (no JavaScript errors that I can tell)

First, you need to make a file called "autosave.js" and throw it in wp-content/plugins, the content of which is:

function autosave() {
   return true;
}

Next, throw this in an existing plugin or make a standalone file (I have a file with a bunch of random WP "fixes" so I just threw it in there):

function disable_autosave() {
   wp_deregister_script('autosave');
   wp_enqueue_script('wphacks', '/wp-content/plugins/autosave.js', array(), '20081123');
}

add_action('wp_print_scripts', 'disable_autosave');

A big thanks to the guy at Untwisted Vortex for getting me on the right path. I'm sure there might be a couple other ways to do this, but this works just fine, and maps with the primary goal of not editing the core code of the package.

Categories: WordPress

Changing the WordPress $table_prefix - a word of caution

October 2nd, 2008 1 comment

I just changed the table prefix from "wp_" to "wp_en_us_" to support multiple installs in the same database, one for each locale. This seemed pretty straightforward - just rename the table names and change the table prefix in wp-config.php, right?

Wrong.

The WP code is littered with references to $table_prefix - including columns in the "usermeta" and "options" table. Key columns such as "wp_capabilities" in the "options" table need to be renamed appropriately, or your user roles are totally broken. In "usermeta" you need to make sure the "wp_user_level" and "wp_capabilities" meta_keys are updated too.

Sadly, after spending a couple hours var_dump() and exit()'ing throughout the code I realized the issue. I am not exactly sure the prefix of some of these keys needs to be aligned with the $table_prefix - maybe WPMU uses that or something. But anyway, what an annoying headache.

I am not sure but I think -all- "wp_" keys might need this same treatment. I've only really cared about the user roles and such since I had a lot of users trying to figure out why they can't post anymore. At least that's fixed.

Note: I don't know if this is clearly documented anywhere. If it is, I should be embarrased. However, I still want to voice a complaint that this isn't very straightforward. Maybe put a comment in wp-config.php?

Categories: WordPress

nginx + WordPress - redux

August 17th, 2008 1 comment

Note: this has been outdated once again. Now it can be simplified with one simple directive shown here.


There's been a minor tweak required in my original WordPress+nginx rewrite rule post.

I think we've finally got it right now. I've been exchanging emails ad nauseum with Igor and I believe the behavior now works properly (part of the exchange was regarding PHP+basic http auth, there were some bugs around the regexps/nested location blocks or something)

Anyway, here is a perfect working example, running this very website:

server {
   listen 80;
   server_name michaelshadle.com;
   index index.php;
   root /home/mike/web/michaelshadle.com/;
   include /etc/nginx/defaults.conf;
   include /etc/nginx/expires.conf;
   error_page 404 = /wordpress/index.php?q=$request_uri;
   location ^~ /wordpress/wp-admin {
      auth_basic "wordpress";
      auth_basic_user_file /home/mike/web/michaelshadle.com/.htpasswd;
      location ~ \.php$ {
         fastcgi_pass 127.0.0.1:11000;
      }
   }
   location ~ \.php$ {
      fastcgi_pass 127.0.0.1:11000;
   }
}

Likewise, you can also omit the basic HTTP authentication if you don't think you need it:

server {
   listen 80;
   server_name michaelshadle.com;
   index index.php;
   root /home/mike/web/michaelshadle.com/;
   include /etc/nginx/defaults.conf;
   include /etc/nginx/expires.conf;
   error_page 404 = /wordpress/index.php?q=$request_uri;
   location ~ \.php$ {
      fastcgi_pass 127.0.0.1:11000;
   }
}

Note: this does require a patched version of 0.7.10 - I assume he will put these changes into 0.7.11. Some of the changes required include the basic HTTP auth stuff. Also, when using error_page 404, it generated logfile noise in the error log. That was fixed as of 0.7.9 or 0.7.10, so no longer will you receive script-handled 404's in your error log.

This should be the last and final need to run WordPress properly under nginx. This has the approval of Igor, the creator - you cannot get better than that. (Note: this is WordPress 2.6.1, but I have not seen any reason the rewrite rule would be different since even before 2.x)

Let me know if it doesn't work! Or better yet, let the nginx mailing list know 🙂

Categories: nginx, WordPress

Simple WordPress hack - redirect the index.php!

August 16th, 2008 No comments

I don't know how or when, but I wound up getting indexed with index.php in some of my URLs.

For some reason, WordPress hasn't decided that they should parse and remove that. So for the interim, I've decided to finally throw in a couple quick lines of code to do the trick. Throw it in any plugin you want or make a standalone file, it works fine with my 2.6.1 so far.

add_action('init', 'chop_index');

function chop_index() {
   if(preg_match('/index\.php$/', $_SERVER['REQUEST_URI'])) {
        $url = preg_replace('/index\.php$/', '/', $_SERVER['REQUEST_URI']);
        $url = preg_replace('/\/\/$/', '/', $url);
        header("Location: http://".$_SERVER['HTTP_HOST'].$url, true, 301);
        exit();
   }
}
Categories: WordPress