Freezing the Kohana layer

Kohana 3.3, on a project that earns money every day. The framework is effectively finished. The repository barely moves, the community left years ago. Nobody will approve a rewrite, and I have stopped asking. This is a normal situation and it deserves a better plan than “someday we migrate”. The plan we settled on is a freeze. Pin the exact framework version and vendor it. Not “3.3.*”, the exact commit. The build must be reproducible in five years, when the original download link is dead. ...

March 4, 2016 · 2 min · Murat Useinov

Middleware groups in Laravel 5.2

An IP whitelist check placed after auth. That was the whole bug. Every scanner bot on the internet was going through session start and a user lookup just to be told to go away. The fix was to move one line up in the Kernel. Laravel 5.2 makes that line easier to see. Middleware groups, and the request pipeline is finally written down in one place: protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \App\Http\Middleware\VerifyCsrfToken::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], ]; Before 5.2, sessions and CSRF were global middleware. They ran for everything, including API routes that have no use for cookies. Now web and api are two separate pipelines, and you can read each one top to bottom. ...

February 8, 2016 · 2 min · Murat Useinov

Upsert in PostgreSQL 9.5

$row = $db->fetchOne('SELECT id FROM counters WHERE name = ?', array($name)); if ($row) { $db->execute('UPDATE counters SET value = value + 1 WHERE name = ?', array($name)); } else { $db->execute('INSERT INTO counters (name, value) VALUES (?, 1)', array($name)); } Works on the laptop. In production two requests arrive in the same millisecond. Both SELECT, both see nothing, both INSERT. One dies with a duplicate key error. Or worse, there is no unique constraint, and now you have two rows and a bug report you cannot reproduce. The window between SELECT and INSERT is tiny, so it fires once a week, always for someone else. ...

January 12, 2016 · 2 min · Murat Useinov

PHP 7 measured on real code

One endpoint. The heaviest catalog page of one project, same data, same opcache settings, PHP 5.6 against 7.0. That is the only benchmark I trust. 7.0 has been out for three weeks, everyone has seen the hello-world numbers, and I do not believe hello-world numbers on principle. Response time dropped about 40 percent. Memory per request, more than half. No code changes. I ran it again because I did not believe the first numbers either. The engine rewrite is real: smaller internal structures, cheaper function calls, and typical framework code is exactly that, thousands of small calls and arrays. ...

December 26, 2015 · 2 min · Murat Useinov

Symfony 3.0 is not a rewrite

Symfony 2.8 and 3.0, released today, both at once, on purpose. The pairing is the whole message. 2.8 is the last of the 2.x line and an LTS. 3.0 is roughly 2.8 with the deprecated code deleted. Same features, cleaner body. This turns a scary major upgrade into two steps. Step one, move to 2.8. Minor upgrade, low risk. Then make the deprecation log empty. The phpunit bridge prints every deprecated call your code and tests touch. Burn the list down during normal sprints, item by item, each fix small and releasable on its own. ...

November 30, 2015 · 2 min · Murat Useinov

JSON column is not a schema

ALTER TABLE users ADD profile JSON; MySQL 5.7 went GA this week, and this line is now legal. Native JSON type: validation on insert, binary storage, functions to read paths. The question is not whether it works. It is what belongs in it. Classic case: user profile with flexible metadata. Marketing wants a new field every other week. The old options were bad in familiar ways. Forty nullable columns and an ALTER for every idea. Or EAV, key-value rows, where every read is a self-join festival and nothing has a type. ...

October 23, 2015 · 2 min · Murat Useinov

Kohana on PHP 7: whose problem is it

The test suite went green on the PHP 7 release candidate on the second try. I expected worse. Then I opened modules/ and the mood changed. The core mostly works. The community is patching compatibility in the 3.3 branch on GitHub right now, and the fixes are small: deprecated PHP 4 style constructors, changed engine behavior in dark corners. Whoever says the framework is dead is not reading the commit log. It is not dead. It is volunteers. ...

September 8, 2015 · 2 min · Murat Useinov

Redis cache and Redis queue are different databases

Two lines from redis.conf on one project: maxmemory 2gb maxmemory-policy allkeys-lru Correct for a cache. Then sessions moved into the same instance, because Redis was already there. Then the job queue, same reason. One process, three tenants. This works until the day it does not. Cache data is disposable by definition, and those two lines embrace that. Memory fills up, Redis evicts the coldest keys, the application rebuilds them on demand. Persistence is optional. After a restart a cold cache is an inconvenience, not an incident. ...

August 21, 2015 · 2 min · Murat Useinov

Run your tests on PHP 7 now

Three lines in .travis.yml. That is the whole cost of knowing in July what will break on PHP 7 in December. php: - 5.6 - nightly matrix: allow_failures: - php: nightly Alpha builds are out, release is planned for this autumn, and production is on 5.6 and will stay there for a while. The build stays green on 5.6. The nightly job fails quietly in the corner and produces a list of future problems while nobody is under pressure to read it. ...

July 8, 2015 · 2 min · Murat Useinov

Laravel 5.1 and the dependency ladder

PHP 5.5.9. That is the minimum for Laravel 5.1, which came out this week, the first Laravel with LTS: two years of bug fixes, three of security fixes. Two weeks ago I wrote the same about Symfony 2.7. Two frameworks arriving at the same idea in one summer is no coincidence. PHP applications got old enough to have a lifecycle. Draw the ladder for your project once. OS at the bottom. Then the PHP version the OS ships, or the one you build. Then the framework. Then the packages on top. Every rung has its own end-of-life, and the rungs are coupled. If production sits on some old distro with 5.4, the 5.1 upgrade is suddenly an ops project. And above you, half the packages will require 5.1 in their next major, so staying behind slowly cuts you off from fixes. ...

June 14, 2015 · 2 min · Murat Useinov