XCache made me lose my nerves!

Another year, another trial... But let's start from the beginning, shall we?

Introduction

Since my last post a lot has changed in way of development environment. This is due to my new job @ Motorpresse Stuttgart where I started as lead developer in november, last year. Since the server provider is quite fond of Ubuntu and prefers the "good old apache" stuff, I had to create new virtual machines, ansible roles and tons of configuration to match the server's state. Most annoying: Apache2 (2.2 and 2.4) with either mod_php5 and php-fpm. So I decidede to set up the upcoming refactoring/redesign of serveral projects with a symfony2 skeleton and API application. One more constraint for the environment is XCache, which I know to be slower than APC and less reliable. But whatever floats the admin's boat...

The application

Main goal of the new application design is to decouple an old CMS with messy data structure and even worse templating from a - yet to be finished - modern symfony2/gulp/sass driven frontend. The data access is asynchronous with triggered CRUD events, ending in a message queue where they are used to manage the separate frontend data structure. This results in me having two instances of symfony2 running on one machine.

XCache introduces itself

By default the new Zend Opcode Cache module is disabled in php.ini, so no crossfire between XCache and php. So I thought. Having the frontend runnig, I started work on the API and when I tried to call both frontends at once, a "AppKernel cannot be redeclared" error occured. Ok, strange... I only have one AppKernel per project and since the classes are loaded via absolute path...

Ways to solve the problem

An hour later, I had divided my two apps into two php-fpm pools before realizing that all caching takes place in the master process. At that point I deactivated XCache -> everyting works fine. OK, XCache variable cache on... Everything works fine. Conclusion: I need another opcode cache. XCache is able to run with disabled opcode cache - thank god - and the Zend opcode cache is not crashing when being enabled at the same time.

The whole process of figuring out why my app crashed in the first place and therefore fixing it, took me the better part of one day. Great stuff. Really pissed by XCache.

Tags: php, symfony2, software architecture, caching, XCache

How to Bundle!

This post ist partly a recap of the symfony life 2014 in Berlin where Toni (@havvg) and I did a "Lightning-Rant" on this topic. For some time now, we've been sporadically discussing the placement of models in the "VC-style-framework" symfony2. SFUGSTR did a whole meeting about that back in 2013 with great and controversial discussion :-) And since the new "Best Practices" book for development with symfony has emerged, I think it's time to write a little something myself.

Basics

As of now we've witnessed a lot of developers in projects using bundles to structure their applications, which - in itself - is no bad thing per se. Especially when starting to develop with symfony2 for the first time, it is not that easy to fully grasp the abstractness of the architecture and structure code accordingly. The basic idea of a bundle is having a container to put alle framework-related or -coupled stuff in a dedicated place where it can be found and interpreted. That's it! Framework stuff! A lot of people start packing "utilities", models, ... into a "CoreBundle" or "AppBundle" and that's not right (from an architectural point of view). To solve this misery there are a few approaches and I am going to discuss two of them.

Scenario

I assume following scenario, which I think is most common:

  • we have an application we know to most likely stay with symfony2 as framework
  • we are likely to have a lot of business logic
  • the application uses a database and therefore entities and repositories

The "one bundle to rule them all" approach

This is my preferred approach ;-) We're using exactly one bundle, no vendor namespace - I love calling it the "CoreBundle" - and place a second folder into the <project>src/ folder, called "Core" or whatever you want to name your business logic container. Within this container, I usually place every part of my business logic I do not get as an external library via packagist. To access this stuff, I create service definitions within the CoreBundle. Also I tend to place all controllers within the bundle, since they are also tightly coupled to the framework.

Structure:

project_root
 |- app
 |  |- ...
 |
 |- src
 |  |- CoreBundle
 |  |- Core
 |    |- subnamespace1
 |    |- subnamespace2
 |    |- ...
 | 
 ...

In my opinion this looks quite concise and also I love the extra part of the namespace, containing all my business logic (Core).

Benefits:

  • The overall structuring directory-wise is easy to grasp for people working with sf2
  • No changes concerning class-loading is needed, everything works out of the box

The "no bundle" approach

Toni (@havvg) is really fond of getting rid of all subfolders and bundles, so he suggests to pack all code flat into the src/ directory. You can find a sample project skeleton on GitHub.

Structure:

project_root
 |- app
 |  |- ...
 |
 |- src
 |  |- Controller
 |  |- Entity
 |  |- Repository
 |  |- ....
 | 
 ...

As you can see, this flattens the project structure in an immense way and also allows for nice DDD. Downside is: You have to overwrite e.g. the ApplicationKernel to be able to find Controllers and locate all the other stuff. For the people asking themselves "where are the templates and other files?!": They need to be placed in app/Resources/.

What does this mean for my OSS bundles?

I know, there are loads of bundles around in symfony-world. And lots of them have business logic within their directory structure. If you want to de-clutter this mess, try following approach: Pack all the relevant business-logic into a framework-decoupled package (library!), promote it on packagist and then write a bundle to make the library easily accessible via frameworks like symfony. Read this article for a nice approach to make your bundle framework-interoperable!

More to read on this topic:

Tags: php, symfony2, software architecture

Profiling for bottlenecks

On our way to fully understand how our software works and where there may be bottlenecks in our code, it's quite handy to have at least one server instance ready with XDebug installed. Since we've had some performance issues yesterday on fahrrad.de and the other shops, we've decided to profile the overview page. It's the page with the highest database/cache usage and a lot of nested controller calls (sf2 subrequests) and even small bottlenecks on that page normally kill the page load time. When we create profiles for later analysis with KCachegrind (KDE), we're usually profiling the page without cache enabled once and then another profile with cache enabled. So we're able to see where the initial page load might be further optimized and where the differences are to a fully cached page.

Loading the profile file with KCachegrind, I normally check the "Callee Map" first. I click on the main node from the Caller List on the left hand side: Then you get a nice image with loads of nested, colored rectangles, varying in size. Each of those rectangles represents one function call and its percentual amount of the processing time (in percent). If they are nested, it's a nested call and the size correlates to the processing time percentage. As you can see here: callee map without cache there's a lot of calls and three blocks in particular stick out of the mass: The're massive. Like we-are-40-percent-of-the-processing-time-massive. They represent our config manager and its loading of Yaml files via symfony2 Yaml component. It's a one-time thing, since the whole config stuff is cached in APC for one week, if there's no deployment in between. So nothing really obvious here... Next I check the Callgraph for any red colored method calls. Since there's nothing really bad besides our ConfigManager, we'll check the cached version of the page.

As expected, the whole config stuff is gone, lots of small rectangles: But one thing that catches the eye is the large green block in the upper left, representing a pdo object with a query. That's a real bummer... uncached call to the database. callee map with cache Switching to the Callgraph reveals: It's our realtime product availability check. callgraph

So here's at least something we can check if it's really necessary to make a database call everytime. Maybe store this in redis or...

Besides looking at and interpeting nice images, there's always The "Caller List" with all method calls and there you can check which ones use most of the processing time, how often are they called, ...

As you can see: Using the XDebug profiler really could help you identify bottlenecks!

Tags: php, debugging, performance