Improving local hosting

On my tweet "no innovation in local hosting industry. All we got is Yet Another Cpanel Hosting.", r0kawa of Zenpipe replied on what's my suggestion to improve our local hosting industry. My quick answer to him was to just look at how webfaction run their hosting services. Now I have some time to write a little more, I'd like to share my insight on this topic.

To fully embrace what webfaction has done in hosting services maybe a bit too much so I have 2 things that I'd like to see in our local hosting service.

  • Process isolation

All local shared hosting that I know use mod_php in their infrastructure running as global main process that was shared for every users on that server. To enable functionality such as file upload, users has to allow write access to their directory to this global process which as a result, made it writable to everyone on that system. Workaround has been taken such as by enabling safe_mode directive in PHP but most of the time it caused more trouble than the problem it try to solve.

Webfaction solved this by using mpm_peruser instead of the usual mpm_prefork or mpm_worker which allow them to assign each user dedicated apache process independent of each other. Another possible solution is mpm_itk which also can accomplish the same thing. It's just a matter of benchmark to see which one perform the best in massive deployment. My experience on webfaction quite satisfying. Webfaction also goes one step further by allowing user to have their own Apache environment stack running on custom port that was then proxied through by the main apache process (later on they switch to nginx as their main front end server). This mean user's process truly isolated from the main process and other users on that shared server.

  • Shell acces

These days, I just can't imagine deploying my site using FTP. If you can't give full shell access, at least make it possible to use tools such rsync or scp to transfer files to the server. While I realize the fear of granting shell access to random users, webfaction include it even in their basic plan so there must be some way to this correctly.

There could be much more but this the things that I'd like to see from local shared hosting provider. Yes I can get all this from VPS but to developer like me, shared hosting such as webfaction much more appealing because it free me from doing all the sys admin job to maintain the server while at the same time still have all the tools that I like for development. Otherwise, you are just 'Yet Another Cpanel Hosting'.

Mercurial: HTTP push with nginx + SSL

Setting up hgwebdir.cgi on Apache which then proxied through Nginx at the front. SSL was enabled at Nginx level but not apache since it just running at localhost. Work perfectly fine except when pushing, I got this error:-

searching for changes
ssl required

Zenpipe VPS hosting

Tags: 

Two weeks ago, I signed up to Zenpipe VPS hosting. It's a local hosting company run by quite a well known figure in the local development community. I currently using slicehost and webfaction for my hosting need and to me they are the best so far in VPS and shared hosting business respectively. The only thing they lack is something that out of their control - latency. Two weeks ago latency to international link from .my was really bad. It jumped up 600-700ms, unacceptable for most sites hosted outside of local Internet link. Things back to normal now (300+ms) but we don't know when it would happened again.

Understanding CCK data storage

Tags: 

When we add new fields to Drupal Content type, CCK would create a new table inside the database as content_type_<content_type_name>. Example:-

mysql> desc content_type_tweet;
+--------------------+------------------+------+-----+---------+-------+
| Field              | Type             | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+-------+
| vid                | int(10) unsigned | NO   | PRI | 0       |       | 
| nid                | int(10) unsigned | NO   | MUL | 0       |       | 
| field_follow_value | longtext         | YES  |     | NULL    |       | 
+--------------------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

The table basically store the reference to nid and vid of that node + the value for that field. Any new fields added to this content type through will be added as a new column into this table. The column naming is fixed and you can't customize it, CCK would always create it as field_<field_name>_value. Things look so simple so far. It look cools and allow us to add new field on the fly and have it as a new column in the content type table. But it's not.

The sad state of PHP

I currently use Kohana as a PHP framework. I'd already lost faith to PHP framework so I built my applications straightly from Drupal. But after starting my new (current) job, we need to use a framework. The current framework used at that time was Code Igniter, which was embedded inside Drupal so we don't have to deal with any authentication and acl issues and have a CMS out of the box. I'm not a fan of CodeIgniter. You know what, it suggest people to write HTML form by hand, one by one and it validation library is the fugliest I've ever seen in my life. You define the rules inside an array and if you want friendly error message for that validation, you have to define another array repeating yourself again. Wtf ! The way the validation library was designed make it really hard to create a wrapper that would automate all the tedious tasks. We try our best and we have a wrapper that would automate form generation from a schema file and automatically handle the validation part but it's not fun with ugly hack here and there which is really hard to maintain and add new features. Every time user comes with a feature request, oh damn I have to hack it again.

Replicate webfaction django wsgi setup

Webfaction Django setup use user's apache instance running on custom port which then would be proxied by the main apache process running at the front. I try to replicate this setup on my laptop so I can easily play around with the setup without actually using webfaction server through the slow ssh connection. Webfaction use Centos while my laptop is running Ubuntu 8.04 but look's like the setup can easily be replicate (once you get through the hardest part).

The hardest part seem to get the latest mod_wsgi (2.x) on ubuntu which still provide 1.x version through apt-get.

Running Plone with Nginx

Spend few hours to get Plone running behind Nginx through proxy_pass. It supposed to be as simple as:-

server {
    listen      80;
    server_name cvt.int-prokab.com;
    root html;

rewrite ^/(.*)$ /cvt last;

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header        Host            $host;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

The plone site are running on port 8080 at 127.0.0.1:8080/cvt. I want it to be exposed to the outside as http://cvt.int-prokab.com/ but it turn out that Plone generate all url on the site as 127.0.0.1:8080/cvt - the address behind and not the exposed public url. Reading explanation on ZMI Virtual Hosting interface:-

The VHM doesn't do anything unless it sees one of the following special path elements in a URL: VirtualHostBase sets the protocol and host, while VirtualHostRoot sets the path root. If the URL path of a request begins with /VirtualHostBase/http/www.buystuff.com, for instance, then URLs generated by Zope will start with http://www.buystuff.com. Since the port number was not specified, it is left unchanged. If your Zope is running on port 8080, and you want generated URLs not to include this port number, you must use /VirtualHostBase/http/www.buystuff.com:80. If the URL contains VirtualHostRoot, then all path elements up to that point are removed from generated URLs. For instance, a request with path /a/b/c/VirtualHostRoot/ will traverse a/b/c/d and then generate a URL with path /d.

Took me sometimes and hair pulling before I could understand how it would work. The rewrite supposed to be like this:-

rewrite ^/(.*)$ /VirtualHostBase/http/cvt.int-prokab.com:80/cvt/VirtualHostRoot/$1 last;

Blog @ localhost

Tags: 

It's fun, really fun. Now I can write anything that I want, jot everything that came to my mind just instantly. No need to think, no need to worry about grammar... and no latency ! As a side note, I used Dokuwiki and it rocks too ;)

Drupal Form API 'value' element

Tags: 

It's common to use hidden value in a form to specify certain variable that was meant to logic which would handle the form processing rather than the user facing the form, hence the name 'hidden'. Drupal Form API provide special element 'value' that can be used for the same task except that the value would not be sent together with the form. Let say you need to main a refference to customer_id when user entering their order in order form, the typical way is to used hidden element like this:-

$form['customer_id'] = array('#type' => 'hidden', '#value' => $customer_id);

This would put the value as a hidden form element and sent along other form value when user submit the form. The alternative is to use the 'value' element:-

$form['customer_id'] = array('#type' => 'value', '#value' => $customer_id);

No value would be sent to the browser but it's still available to the form submit handler just like any other element. Neat idea, I think.

Drupal Form API Gotcha

Tags: 

Doing an upgrade of my app from Drupal 4.7.x to Drupal 5.x. The big difference in Form API in Drupal 5.x vs 4.7.x is on how drupal_get_form (the way form is build) work. In 4.7.x, drupal_get_form work through a push model where we supply the array of form definition to the function call:-

function customer_edit($customer) { $form = array(); $form['customer_name'] = array( '#type' => 'textfield', '#title' => 'Name', '#default_value' => $customer->name ); return drupal_get_form($form, 'customer_edit'); }

The second argument is the form_id. In 5.x however, this has changed to pull model where the form definition need to be build in a dedicated builder function.

function customer_build_form() { $form = array(); $form['customer_name'] = array( '#type' => 'textfield', '#title' => 'Name', '#default_value' => $customer->name ); return $form; }

function customer_edit($customer) { return drupal_get_form('customer_build_form'); }

Now the first argument to drupal_get_form function call is the name of the form builder function. Another way is to implement the new hook introduced hook_forms to do some complex mapping of form_id to builder function.

function customer_forms() { $forms = array(); $forms['customer_edit'] = array( 'callback' => 'customer_build_form' ); $forms['customer_add'] = array( 'callback' => 'customer_build_form' ); }

function customer_edit($customer) { return drupal_get_form('customer_edit'); }

I got some problems when using the hook_forms approach. No errors from PHP process and Nginx just gave an '500 server error'. Further checking revealed that the problem was with the form_id which is similar to already defined function name. My guess was some recursive calling occured when drupal_get_form first try to call a function named customer_edit as being used in the form_id. Unfortunately, the calling function was also a customer_edit. So the fix is, never used form_id that is similar to already defined function since drupal_get_form will first look for it instead of relying on the hook_forms if it is implemented.