Monday, June 24, 2013

First Week.5 in China: Part Two, Refactor PHP get 10% more capacity with one change

The PHP code that I've experienced in China so far is pretty good. I have been in some environments where the Code is horrendous-where variables are set in one file yet used in another file via a require_once.  If that magic variable is not set everything would break with side-effect galore. This is not the case here for the China Team. This team is really good not to imply the other-one wasn't just praising the current one.

The SQL, like many other companies I have been at requires some more extra effort, but the hunger to learn and improve is throughout the culture of the team here. Really that is the first step to improve a system, the willingness by developers and management in getting things done and fixed-fast.

Entering in the environment, first I read all the code. Then created a development environment to play with the code. Next I profiled how the database is being interacted with, and in conjunction with the cache. All looked ok, but with some back of the envelope calculations the server farm is to big for the amount of traffic. Traffic is huge don't get me wrong (5M+ DAU)! But the farm is too big. Digging some more I found that the code spins to search for items on a map by loading all map items, and in a for-loop go through each item until that 1 item is found, then return. This is done two - four times for every api request, especially to trigger an achievement if the item is found on your map. More on the fix later.

Before making any changes, I wanted to get feedback of what the biggest issue was that seem to cause bugs or slow down development. The consensus was the DB Layer was mixed into the Model Layer causing fear of changing said models because there was fear that they would break the DB Layer. It was not quite clear how the code communicated with the DB, thus work was done by the team to use more of the same existing functionality to fulfill feature requests which is sub-optimal if the root functionality was slow or expensive to use in the 1st place at scale.

Thus the 1st recommendation was to separate out the DB Layer in a way where the data being requested is accessed through Data Access Objects (DAO). This concept encapsulates DB logic and for the most part requires only three methods: add, get, delete. Some more complex objects calling DAO had specific SQL to make getting data faster but for the most part three methods per table was all that was needed. Following the new Directory Structure backed by PHP Namespaces all SQL was easy to find and isolated away from the model.

The second recommendation was to remove a bunch of in PHP caches of data, because this was the cause of a vast amount of copies chewing up a ton of memory per request as well as chewing up CPU to build the caches per request. If the cache hit rate is not good don't cache-added complexity sucks to maintain-and can actually slow things down if not needed.

The third recommendation was to make each Model a single instance per distinct entity (singleton-map) throughout the request which reduced the overall amount of database queries by coupling the model creation to database fetches. The database queries are reduced because instead of pulling the same data for object creation in various parts of the code, the single object was referenced.

So here is the new structure for models/database access/utils

v2
v2/classes/DB/   -- DB connection logic
v2/classes/DAO  -- DB Access 
v2/classes/Models  -- New Models
v2/classes/Util  -- common Utility classes
v2/init.php -- everything is setup from this structure

With this new structure, separation of responsibility has been created in the code. More people can work on the same feature. One person can optimize the SQL, while another plugs in the model and yet another handles the access logic (controller). Or a single person can do it all. Most importantly the team loves the new setup.

In my 1st week and 1/2)with the new model format added to the existing code base via editing 242 files for a single model's usage (the largest and one of the most important models that controls the MAP locations of the game) the result has been great, a 10% drop in the number of servers and no user complaints with still more room for improvement. The biggest change was due to removing the spin through all the map locations to find a single item. The fix was changing a O(n) method in PHP getting hit hard to a O(1).

60 more models to go.

The good note about the unoptimized code is its forced the dev ops side of things to mature quickly and the tools that they built are really robust. To deal with features being pushed out that may not be mature enough for the request load the team built this cool dashboard with Jenkins automation, home grown software, realtime server metrics and rules to launch new instances and shrink them automatically throughout the day. It works flawlessly, for the front ends that is. It's pretty cool. Its so good and works so well I am hoping one day that it could be an OpenSource Project on its own.

Tuesday, June 18, 2013

First Week in China: Build a new Dev Environment

I see my role as enabling others. When I was a pure awesome DBA in the early 2000s I enabled developers and customers of a companies product by making mySQL fault-tolerant and fast. As I moved up the stack as an Architect while still holding onto my roots as a DBA-I kept my DBA discipline by enabling my team and company through all the knowledge I garnered.

The first thing I identified in China that can really help my team-members is making a new development environment. The reason, the production and dev environments are wildly different. Dev is on Windows while production runs various flavors of Linux's 2.6 Kernel-mostly Centos-6. Additionally when the code is ready to be push to what I like to call pre-integration servers-meaning the code is not checked in but copied to a test server then checked in if the tests past. As a result developers spend time organizing which test server to use and this server can only be used while in the office.

Generally as a developer you should develop in something simular to your production environment, and the integration server should serve as QA of the product and not as the post development process that by-passes all unit tests (which did not exist).  Also a lot of effort was put into making this Windows to Linux environment to work-just good enough-which really is not. Since PHP behaves slightly differently under Windows, I found that time was being spent on issues that could possibly not show up on Linux's php version. Thus these issues provided enough justification to build an integrated environment, where the end developer can work from home, or from where ever even if  there is no direct network connection to the outside world.


The Setup

Forcing a developer to change their OS of choice, or IDE or what have you is not going to fly in any country-its just too disruptive. Thus I chose to build the environment on virtualbox, a free VM that works on MAC and Windows, the two primary Dev environments. I pre-built the VM and uploaded it to the local fileserver. Now all the team has to do is download the VM.

Here is what is installed on the VM. (These steps follow after installing Centos-6-minimal)

First, I set up a shared directory from the HOST (Mac) machine to the GUEST machine (VM), which contains the code to run the site. This allows the user to use their favorite native IDE app or vim.

Next,  I set up a host virtual network, so even if the HOST does not have a connection to the net, it can always talk to the VM via ssh, httpd or what have you.  I also setup another network interface for the VM to talk to the outside world via the NAT setting so packages can be installed directly on it via yum.


Then I configured the yum repos for Centos-6 epel for core linux utils, 10gen for mongoDB, percona for XtraDB by modifying /etc/yum.repos.d and adding the following repos to my list

Percona.repo
epel-testing.repo
epel.repo
remi.repo
CentOS-Vault.repo
CentOS-Media.repo
CentOS-Debuginfo.repo
CentOS-Base.repo
10gen.repo 


Additionally I installed Percona, MongoDB, php, php-cli, php-frm, nginx, apache, vim-enhanced, etc. via Yum on the VM.


Finally I wrote documentation for the whole process and tested on a few people who have good Spoken English skills. With their feedback the documentation was improved and sent to the rest of the team, who have pretty good written english skills.

Now all the dev team members have to do is download the vm, configured the shared code directory and tada the entire dev environment in a box!

The next step is to resolve Schema Changes, and use Chef to update configurations and packages as if the VM was a real server-this is currently an manual process.


Next Post: Refactor PHP Models and add Unit Tests

Wednesday, June 12, 2013

In China and Spreading mySQL/MariaDB/XtraDB Ganglia, GearmanD, Memcache, MongoDB, HAProxy, Nginx, PHP, Python

I am currently in Beijing for a month as the VP of Technology for Fun+, a US/China based gaming company, spreading the joys of open-source  I have an entire team to do benchmarks, study INNODB flushing, build new technologies, which I hope to open-source  I will also post the results here. Our Stack is mostly on AWS with the following.

HA Proxy Load Balances the Web Tier
Web-Tier runs nginX and php-frm
Data is stored in a new Sharded mySQL layer, Gift platform is on MongoDB
Memcache is used to cache frequently accessed items to give state to our stateless Web-tier and reduce DB load, although we can run without it.

What I am focusing on is

Code-Style
When to cache and not to Cache
How to get the most out of mySQL and MongoDB especially on Index Design
Tools for DevOps by DevOps
Reducing cost

I hope to have a lot of information to share in the next couple of weeks.