Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Monday, March 4, 2013

Optimizing Phpfox - Tip #2

The feed seems to be the most resource hungry feature so far. If Timeline is enabled , when going to a profile the script needs to find which years have content so it can display the Timeline years block; this can span up to 20 queries to the database in some cases and while it makes proper use of indexes and conditions this is a load we can save in 2 ways:

1) Disable Timeline
2) Disable the Time block, this is perhaps the most feasible option, if you have timeline enabled it must be for a reason. To disable this specific block go to AdminCP -> CMS -> Block Manager, then click on profile.index and disable "Feed Timeline":



 you will keep the timeline look but the year selector wont be there


With this small change your site will be using a lot less resources and it will contribute to keeping it more stable and efficient.

Note: In case you are wondering, the queries that come from this block do in fact get cached, one cache file per user, but the cache file (specific to a user) is deleted after that user posts something that creates a feed.

Friday, February 8, 2013

Optimization of Phpfox: MySQL

Today  Yesterday I landed in a bug report that mentioned full table scans, in my younger years (ok, 5 years ago) I was very interested in databases and even took a workshop in Mexico by MySQL to become a DBA so I dutifully began testing.

The first thing I did was to set up a local copy of Phpfox 5.3.0 RC1 (not yet released) with content (users, blogs, friends,...), then enabled the slow_query_log and set the long_query_time to 2, because this is a local server with no traffic I assumed a safe bet that no query would take longer than 2 seconds. Then I enabled log-queries-not-using-indexes to capture those in the slow query log. Then using the script I was able to find queries that could be optimized (theoretically at least).

There were indeed some queries using union and joins that could be optimized (large sites will likely see a performance increase in 5.3.0 RC1).

I do have to point out a couple of things:
First, some queries are being logged but they are meant to scan the full table, for example when getting all the user groups we want all the records in that table, using indexes would not improve performance because we want everything in that table, intentionally this table is typically very small, it defaults to 5 records and in normal circumstances shouldn't grow beyond 10 rows.
Second, In some cases, MySQL seemed to not want to use a specific index, for example with this query:

SELECT COUNT(*)
FROM phpfox_user AS u
JOIN phpfox_user_field AS ufield
ON (ufield.user_id = u.user_id)
WHERE u.status_id = 0 AND u.view_id = 0
If you run that query with explain you will see that MySQL had many indexes to use but chose none, and in my test it did run a full table scan. So to help in this situation we implemented the function forceIndex() in the DBA library, this allowed us to tell MySQL which index to use and after rewriting the query we saved on fetching rows, (in the one that I have just rewritten in the feed there is a filtering improvement of 45.31%)  which translates in a performance improvement.

Third, we made sure that whenever the developer queries for getSlaveField or getField the database library adds a LIMIT 1 if it was not added by the developer, we saw a tiny tiny performance after this small change.

Fourth, I also found some strange behavior in mysql where it would log a query from a 'derived' table and not use any indexes for that sub-query, but taking the sub-query out and testing it by itself did use the index, in this case force index did not help, for what is worth, this only happened in the main Blog section when logged in as an administrator.

Fifth, in one occasion after optimizing the query (meaning it did not get logged in the slow query log) it took longer for mysql to fetch the results, we opted for 'un-optimizing' the query since the tangible benefits outweighed the theoretical ones.

The improvements added affect sections frequently reached like the Browse Members and Home (after logging in, where the news feed is).

For developers: here is sample code using the forceIndex function:

$iCnt = $this->database()
        ->from($this->_sTable, 'u')
        ->forceIndex('status_id')
        ->join(Phpfox::getT('user_field'), 'ufield', 'ufield.user_id = u.user_id')
        ->where($this->_aConditions)
        ->execute('getSlaveField');
There surely are queries still to fix, if you find them please let us know, we will continue looking for them but must also attend to other bug reports.

Wednesday, April 4, 2012

Why CometChat Improves Performance?

Every now and then we get a client in support whose hosting account has been suspended for 'abuse'. The typical case is that the client was in a shared hosting package with more traffic than it could handle, and the real killer is often the default IM that comes with Phpfox. This does not necessarily mean that the default IM is poorly coded and here's why: When a user sends a message via the default IM this message uses an ajax connection and connects to the web server (which in 100% of the cases we've had with this problem is Apache), the web server loads the PHP engine, the PHP engine loads the Phpfox libraries and Phpfox connects to the database and stores the message in the database, which could be as short as one word ("Yes") or even less. A user can send almost one message per second (depends on typing speed) and in these (un)lucky cases that can mean the entire routine all over again for each and every message sent.
At the same time, every client that is connected to your IM is listening for messages, this process means that the client (web browser) opens an ajax connection, this reaches the web server (again, usually Apache), this loads the PHP engine which then loads the Phpfox libraries and it checks for new messages, so far its only a second, but if there are no new messages or no new users online, it simply waits, checks the database 5 seconds later and if nothing new still, it waits... and so on for like 30 seconds, during this time the connection to the server was open, PHP and Phpfox loaded in memory and database being queried constantly, for every client that is connected. Share hosting plans limit the amount of connections open and with good reason, they have to enforce the "good neighbor" policy, but this means one single site cannot support a certain number of connections open at the same time.

Frankly, CometChat has a lot of cool features, but what really boosts performance in the client's server is the CometService, it literally takes the load off the server. Make no mistake, if you do not get CometService you will still get a better IM than the default one, but it will query your server constantly and the benefits will be limited. The CometService adds true Comet technology, meaning it is the server who contacts the client (web browser) when there is anything new.  And it is not even the client's server who takes the load, its CometChat's server.

If you are a Phpfoxer and your server is suffering from the default IM's load, do not blame the IM, blame the architecture, it was built before Comet technology existed, and give CometService a try, it costs $9/month.