Monthly Archives: July 2009

Multiple Sites Driven By One WordPress Installation

This is early experimental. And, I’ve also marked this post into the “personal” category, because you wouldn’t want your clients to have too much access, especially if they share a single WordPress installation. Now I know there’s the WordPress MU project, but I guess I can’t use it in this case, because WordPress MU assumes your URLs will be within the same domain (either subdomains or directories).

The reason I want multiple sites to be driven by one single WordPress installation is because I’m really tired of upgrading everytime. Upgrading the WordPress Core once in a while is okay, but when you’ve got a list of 30 plugins, it’s a pain in the neck upgrading two or three every day on every single blog and website you run. Automatic updates is not a choice, as I want to take a look at what I’m updating to before actually doing it, at least once.

I won’t be doing this from scratch. I’ll start by merging this blog and the Foller.me blog into a single installation. Single doesn’t mean they share the same database, all they share is the WordPress core files, plugins and themes. Yes, this may be dangerous, because not all the plugins store the data in the database (though I believe they should, at least when they’re capable of doing that). Now imagine the Next Gen Gallery (or perhaps any other gallery plugin) being shared over two websites within one WordPress installation. The albums are stored in one folder called gallery. So there might be a conflict if two albums have the same name. There might be an option to store the files in a different directory, and hope that option is stored in the database, will check on that later.

One more issue.. Remember I said personal projects? And assigned the post to the personal category? If you’ve got some clients who are hosted on WordPress, and you’re doing some admin things for them but they DO have admin rights in their admin panels, then I wouldn’t go with this stuff, as it’ll be quite difficult to restrict them from changing eachothers themes and plugins that they share. Get my point?

kay, now the trick will be in the wp-config.php file. We’ll basically look at the incoming address using some regular expression or whatever. If it’s based on kovshenin.com, then we connect to database 1, otherwise, if it’s based on blog.foller.me we connect to the 2nd database, and so on. Pretty simple, huh? If you’re a total freak you might wanna try changing just the prefix, thus having multiple websites, one WordPress installation, one database and a bunchload of tables ;)

I’ve no idea if this will alter the overall performance, but keeping total visitors under ~ 20,000 per day should be just fine ;) I’ll get back at you with another post next week, hopefully with some tests and some results. Cheers!



New WordPress Plugin: Twitter Followers Widgets

Hey, there’s a new widget that can display user pics of people you follow (and people who follow you) on Twitter. It updates whenever you gain a new follower.

That’s a new widget for WordPress I wrote a few days ago, and received aproval from the WordPress.org plugin directory today, so it’s the official launch. Go get yours right over here: Twitter Followers Widgets and don’t forget about the feedback! Yup, themes are cool, but widgets are way cooler! Also, as announced on the Foller.me blog today, we’ve finally released the public version of the Foller.me API so we’re gonna have more Twitter widgets developed next month, wohoo!

You may also dive into the development of your own stuff using the Foller.me API, it’s pretty simple, seriously, check out the API wiki and start making your own widgets! Because widgets are sexy ;)



Customize Posts Order in WordPress via Custom Fields

I came across this last night, might be helpful for some of you ;) Sorting posts in a customized order is pretty simple with the WordPress custom fields mechanism and custom SQL queries. Start off with a basic query:

$querystr = "
	SELECT wposts.*
	FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
	WHERE wposts.ID = wpostmeta.post_id
	AND wpostmeta.meta_key = 'order'
	AND wposts.post_type = 'post'
	AND wposts.post_status = 'publish'
	ORDER BY wpostmeta.meta_value ASC
";

That will sort the posts in ascending order using the custom field called order. This is how you actually process the query and get into The Loop:

$pageposts = $wpdb->get_results($querystr, OBJECT);

if ($pageposts)
{
	foreach ($pageposts as $post)
	{
		setup_postdata($post);
		// you can use template tags from now on
	}
}

One more thing. How to use more than one custom field in your query? Well, yes, this is SQL, not WordPress, but anyways. Let’s say you want to select all the posts marked complete (custom field) and sorted by order (another custom field). Here you go:

SELECT wposts.*
FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta, $wpdb->postmeta wpostmeta2
WHERE wposts.ID = wpostmeta.post_id
AND wposts.ID = wpostmeta2.post_id
AND wpostmeta.meta_key = 'order'
AND wpostmeta2.meta_key = 'complete'
AND wpostmeta2.meta_value = '1'
AND wposts.post_type = 'post'
AND wposts.post_status = 'publish'
ORDER BY wpostmeta.meta_value ASC

Assuming the complete custom field takes a 1 when the post is complete. I used this method in a simple task manager theme I did for WordPress. Did I overuse the word “custom”? ;)



Cloud Tips: Amazon EC2 Email & S3 CNAME Issues

So you moved your blog or website (or whatever) to Amazon EC2 and wondering why your e-mail notices have stopped working? Now I know there’s bunch of articles about the EC2 email issues, and most of them state that the letters are getting into the spam boxes or aren’t getting delivered at all, because Amazon’s IP pool has been blacklisted by most e-mail providers.

Don’t panic! Not just yet.. You might as well try the postfix via google mail or perhaps some paid mail relay servers, but hey, the php mail function requires the sendmail daemon to be running, and if you’re using the Fedora Core 8 AMI on EC2, you might as well try to turn it on:

service sendmail start

Worked for me, and the messages aren’t being marked as spam, while I’m still getting messages from my WordPress installation on MediaTemple marked as Junk by Windows Live Mail ;) I don’t believe Amazon’s in the blacklists… Really… Anyone, but not Amazon .. Right?

The next AWS issue a novice is going to bump into is the CNAME dillema. It’s so straightforward though, really… Let’s say I want an S3 bucket on s3.foller.me instead of the good old s3.amazonaws.com address. Create a new bucket called s3.foller.me, go to your DNS editor and add a CNAME record for s3.foller.me pointing to s3.foller.me.s3.amazonaws.com. Done. The bucket name and the CNAME have to be the same and this is the one and only trick.

Happy clouding, cheers!



Twitter API: Picking the Right Source

I’m sure you noticed that a few weeks ago Twitter changed the source that came unsigned via the API from web to API which could basically reveal any robot that is trying to act human, right? Well if you look at the statuses/update method in the Twitter API documentation they don’t say anything about the source parameter. Strange, right?

Well I read something about it on some forums and the Twitter API development talk Google Group had a discussion about this I believe, but most people still think that it’s the X-Twitter- headers that determine the source, but it’s not. It’s way too simple and it took me a few hours to figure it out using an HTTP sniffer on TweetDeck, Seesmic Desktop and the others.

Turns out it’s the source parameter that is passed via POST together with the status text during the statuses/update call. So usually you would do something like:

$postargs = array("status" => "I'm tweeting via API!");
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postargs);

Now, how about Seesmic Desktop?

$postargs = array(
    "status" => "I'm tweeting via Seesmic Desktop!",
    "source" => "seesmicdesktop"
);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postargs);

Or TweetDeck perhaps?

$postargs = array(
    "status" => "I'm tweeting via TweetDeck!",
    "source" => "tweetdeck"
);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postargs);

Pretty cool, huh? Here’s a brief list of sources to pick from:

And I do believe that it’s the same way for all the other clients. Lowercase and no spaces. It is funny though watching a robot tweet 10 tweets per minute via TwitterFon ;)



Working With Amazon EC2: Tips & Tricks

It’s been a while now since I’ve been hosting on Amazon Web Services and I’d just like to point out some issues I had and quick ways of solving them. We’re gonna talk about setting up a server that would serve not only you, but your clients too, cause $100/mo is quite expensive, isn’t it? So let’s begin and keep this as straightforward as possible. If you don’t understand something, it’s probably because you haven’t read the official EC2 docs and haven’t searched the forums. This is not a tutorial, it’s just a set of rules you may want to follow to make things right.

Once you start a new instance from an Amazon predefined AMI (Fedora Core 8 for example) I suggest you start building your structure right straight away. Attach an EBS volume to you instance (I mount it to /ebs) and start creating your users with their home directories in /ebs/home/kovshenin not the regular /home/kovshenin. Also point your MySQL server to keep your database files in /ebs/mysql. There are plenty tutorials out there on how to do that.

Now, edit your httpd.conf, add your vhosts, point them to the right users dirs, install an ftp server and make sure you chroot the users to their home directories. That way they won’t be able to mess up with eachothers files and folders, peek passwords etc. You might want to change the root user’s home directory to / instead of /root in case you’ll want to use ftp via your root user (which is quite dangerous).

Now comes the fun part. The HTTP server runs under the apache user by default in FC8 and I recommend you don’t touch this. Damn it took me quite some time to figure out how the heck can the apache user execute and write to files not belonging to apache. I messed up big time with the groups, adding apache to all my client’s users groups, but thank god I found mod_suphp in the end. Install that one and make sure you use it and there’s no need to change the users umasks anymore.

Note: There’s a little issue with the mod_suphp in Fedora as far as I know, which doesn’t let you use the suPHP_UserGroup directive in the httpd.conf yelling that it does not exist. Most of the man pages on the net say you have to use that directive, but I’m good without it. It seems that suphp can figure out what user to run on its own, look closely at the config files, and also make sure you’re running php-cgi, not the CLI version. By the way, this is the part where WordPress stops asking you your FTP credentials on plugins/themes update, install, remove and core upgrade too. Speeds up the whole process ;)

I used the following code to test how mod_suphp works (or doesnt):

<?php echo system("id"); ?>

Which should output what’s the current user. Make sure you check everything works before going public, and do not set your min_uid and min_gid in suphp lower than 50. It’s safer to chown -R files and folders than to let suphp run your scripts via root or some other powerful user.

Backing up your EC2 and EBS

This is very important. Once you have everything set up and running, DO backup. Backing up the EBS is quite simple, just create a snapshot from the Amazon EC2 Management Console. Backing up the running AMI (instance) is a little bit mroe complex. You have to use the ec2 command line tools to bundle a new volume, upload it to an Amazon S3 bucket and register the AMI. There are plenty tutorials on the net on how to do that. Shouldn’t take you more than half an hour to figure it out.

Just make sure you have copies of all the major config files (httpd.conf, crontab, fstab, ..) backed up on your /ebs/config for instance. You might need them in the future (when you loose everything, haha ;) Restoring a backed up AMI instance is simple. Launch a new instance using the AMI you generated, attach the Amazon Elastic IP address to it and voila. Way too simple.

About the EBS, there are quite a few things you should be able to do with it before continuing. Restoring a backed up Snapshot: Create Volume from Snapshot, umount /ebs, deattach old volume, attach new volume, mount /ebs. Cool? Be careful when you’re resizing your EBS. The xfs filesystem automatically grows as far as I know, but in my case I use the ext3 filesystem. So if you need to grow your ext3 EBS you’ll go:

  1. Create a Snapshot
  2. Create a new EBS Volume from that Snapshot you created (say 10 GB if you were running 5 GB)
  3. Attach it to your Instance, say /dev/sdg
  4. Use the resize2fs command to resize the partition to 10GB
  5. Mount it to /ebs2 or whatever
  6. Check to see if everything’s in place
  7. Unmount /ebs2, deattach /ebs2, unmount /ebs, deattach /ebs
  8. Attach the 10GB volume to where /ebs was attached (/dev/sdf)
  9. Mount /ebs and start your services

There you go, back to work, server. By the way, when working with Amazon AWS, note that you should be working in the same region where your AMI is (us, eu, east, 1c, …) otherwise some of the options (when attaching, etc) might just not come up. Beware of that.

Well, I guess those are pretty much all the basics. Don’t forget to read the Amazon S3 tutorials and API, pretty sweet stuff! Good luck.



Foller.me: MySQL Tweaking & Optimization

As I mentioned in the interview with @enked on his website Chidimar.com, I had serious problems with MySQL database optimization on the Foller.me project. The current public stable version (beta-1) is using the MyISAM engine and it’s not holding much data – profiles, locations, geo points for the followers geography.

In the new version (currently dev-1 and hopefully beta-2 in a few days) I changed most of the old tables and added new ones, and using InnoDB this time. You see, it’s not very easy to scan through ~1,000,000 relations for the @mashable account ;) and I bumped into a ~10 second delay before the @mashable profile showed up at Foller.me. Slow query showed that one of the simplest queries caused that slow-mo – it took 6 seconds to execute! The guys at Stack Overflow helped me optimize the query and the two tables I was having problems with, so I came down to ~2 seconds for that query, neat!

Digging further I managed to tune the MySQL server up a little bit (caching, all sorts of buffers, etc – you should definitely take a look at MySQLTuner, it’s a perl script that helps you tune pretty much all the MySQL config) which decreased the query execution time to 1 s. The peeps at Stack Overflow said it’s pretty okay for that kind of query to execute for 3 seconds on over 2 million rows, so I thought that 1 second is final. Phew! :)

Now, think about the MySQL query cache. It doesn’t work in my situation, simply because I shoot UPDATEs and INSERTs at the relations table every five minutes or so (via a cron job), so there actually is a way to perform even higher. Thought of temporary tables, views and triggers (and even stored procedures). Nah.. Simply caching that query would be good, right? I mean if I cache the whole profile for an hour, why wouldn’t I cache the relations result set? Cache the query.. Aha, but I thought slightly further. Why not cache the whole page with memcached? I’ll keep you updated with the results.



Have You Tried the Amazon Web Services?

Amazon EC2, EBS, S3.. I’ve been looking for the perfect web hosting for over two years now. Is this it?

A few months ago I really liked MediaTemple cause they offered pretty good US hosting starting from $20/mo, which was quite good for the Foller.me project, so at the starting point I chose them. Their service is cool, definitely worth the money, but. A few weeks have passed, along with some major development on the service update and I got stuck with MySQL and overall server performance. It’s pretty tough to scan through 2,000,000 relations from @cnnbrk and then geocode their locations so I thought that I need to fine-tune MySQL and work out a more powerful caching system.

Yes, MediaTemple do offer dedicated MySQL grids for $50/mo, so that’s $70/mo overall. Not that bad, but thinking ahead, I’d also like to tweak up my http server, so that’d be a virtual dedicated plan for $50/mo, which makes $100/mo in total. Woah! And that’s just the start (around 500 megs RAM, 20 GB disk space and 1 TB bandwidth).

Now the Amazon Web Services offers a 2 GB RAM, 1.6 GHz virtual machine for only $0.10/hr, that makes ~$70/mo. Put up an Elastic Block Store (EBS) up to 1 TB and attach it to the instance around $20/mo. and perhaps an Amazon S3 bucket $10/mo. That makes about $100/mo in total. It’s not just the price though, I loved the way you’re in total control of whatever is happening on your server. You tune it however you like, whenever you like. Save bundled volumes and start over at any time. One-click EBS volume backups, elastic IP address and up to 20 instanced running simultaneously (you can increase this number by contacting Amazon). You also get to pick whatever OS you’d like to run (they’re called AMIs). You can build your own bundled OSs and make them available public.

Oh, and one of the best things about Amazon EC2 (Elastic Cloud) is that it’s so flexible! Switching servers has never been so easy. Start a new instance, attach an EBS, tune it up. Associate your old Elastic IP address to the new instance and voila! Go ahead and terminate your old instance, cause you’re riding your new mustang now!

I’m also sure that you can setup multiple servers and network balancers.. Like clustered computing y’know, the possibilities are endless! But I’m too far away from that at the moment, though I’m sure that whenever I have some free time, I will throw some experiments in that field ;) I’ve already setup Trac and SVN server a few days ago, works great!

Virtual Private Servers, Dedicated Servers, blah blah blah. Those are from the past. It’s Amazon Web Services. Go get your account right now ;)