<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>jpcarzolio &#187; memcached</title>
	<atom:link href="http://jpcarzolio.com/tag/memcached/feed/" rel="self" type="application/rss+xml" />
	<link>http://jpcarzolio.com</link>
	<description>Juan&#039;s personal website and blog</description>
	<lastBuildDate>Wed, 25 Mar 2020 18:57:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>Memcached extensions for PHP: some caveats</title>
		<link>http://jpcarzolio.com/2015/memcached-extensions-for-php-some-caveats/</link>
		<comments>http://jpcarzolio.com/2015/memcached-extensions-for-php-some-caveats/#comments</comments>
		<pubDate>Mon, 03 Aug 2015 20:19:32 +0000</pubDate>
		<dc:creator><![CDATA[Juan]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://jpcarzolio.com/?p=143</guid>
		<description><![CDATA[There are two PHP extensions to work with Memcached, which go by the somewhat unfortunate names of Memcache and Memcached (note the missing ending &#8216;d&#8217; in the first one). In this post I&#8217;m going to share my experience of using them together, either to migrate from one to the other or use them simultaneously, and...]]></description>
				<content:encoded><![CDATA[<p>There are two PHP extensions to work with Memcached, which go by the somewhat unfortunate names of <a href="http://php.net/manual/en/book.memcache.php">Memcache</a> and <a href="http://php.net/manual/en/book.memcached.php">Memcached</a> (note the missing ending &#8216;d&#8217; in the first one). In this post I&#8217;m going to share my experience of using them together, either to migrate from one to the other or use them simultaneously, and I&#8217;ll also describe a really strange issue I once ran into, and how to avoid it. As in my <a href="http://jpcarzolio.com/2015/memcached-and-careless-preloading/">previous post</a>, I ran into the problems described below while working on a high traffic Facebook app a few years ago.</p>
<p>One day, after some code changes, our memcached servers started appearing to behave in a strange way. Intermittently, in spans of several minutes, nothing would work. All caching operations &#8212; set, get, add, anything &#8212; would fail. The problem had clearly started after that last code push, but the pushed code looked innocent enough, and the strangest part was that caching operations were failing all around, not just within the new code.</p>
<p>Upon further inspection, I found that the problem was not on the servers&#8217; side: all operations worked fine when talking directly to a server via telnet, and some network monitoring revealed that  the webservers were not actually talking to the memcached servers at all! So it was a client problem, but a weird one: some innocent caching code in one part of the app &#8212; which, by the way, didn&#8217;t involve any configuration or setting changes &#8212; was somehow breaking cache access in the whole app.</p>
<p>At that point, we had always been using the Memcache extension only, and I suspected there might be a bug in it that was somehow triggered by our latest code. So I decided to try the other extension, Memcached, to avoid this hypothetical bug.</p>
<p>The extensions&#8217; APIs are pretty similar, since they expose the same underlaying functionality, but not identical. That means you can&#8217;t just swap <code>new Memcache()</code> with <code>new Memcached()</code> and leave the rest of the code intact. Among the differences are:</p>
<ul>
<li>Each has different <code>addServer()</code> parameters</li>
<li>Memcached has <code>get()</code> and <code>getMulti()</code>, whereas Memcache only has <code>get()</code>, to which you can pass either a single key or an array of keys</li>
<li>Each has different parameters for <code>set()</code>, <code>add()</code>, etc. because Memcache allows setting flags and Memcached doesn&#8217;t</li>
</ul>
<p>Since Memcache had worked perfectly fine up to that point, and I had no proof of an actual bug in it &#8212; nor any guarantees that its quasi-homonymous replacement would be any better &#8212; I wasn&#8217;t willing to ditch it altogether and rewrite all the code to use the other extension. So we wrote an adapter class to wrap the new extension and use it with our existing code. The idea was to be able to use both interchangeably, switching from one to the other to see whether the bug disappeared when switching to Memcached.</p>
<p>But they didn&#8217;t get on well with each other using that simple approach, and new problems came up. Sometimes Memcache would read back a chunk of binary garbage when an object was stored using Memcached, or they would fail to deserialize arrays or numbers when reading objects stored by each other, returning strings instead. I was trying to solve a problem, but was creating new problems instead…</p>
<p>After some struggle, I finally figured things out. The binary garbage was due to compression: by default, Memcached gzips values bigger than 100 bytes. So some objects were compressed and others were not, and that caused Memcache to return some objects as binary garbage. That was easily solved by disabling compression. The serialization issues were due to the fact that both extensions use the flags field (or part of it) to indicate data types, but they use different conventions (storing the type is needed because strings and ints are stored unmodified, but arrays and objects are serialized). The solution was to handle serialization in the adapter (serializing everything except integers) and only pass strings to the extensions&#8217; methods, taking advantage of the fact that both extensions store strings unserialized and with a flags value of zero.</p>
<p>So, with all those fixes in place, we were finally able to use both extensions interchangeably… only to find that the original bug was also present when using Memcached! The same mysterious behaviour! Back to square one…</p>
<p>I had to look somewhere else. What about the latest code? I scrutinized that &#8220;innocent&#8221; code, the one triggering the bug in the first place, one more time. It performed <code>increment</code> and <code>decrement</code> operations, which we had never used before. That got me thinking, and led me to the <a href="https://raw.githubusercontent.com/memcached/memcached/master/doc/protocol.txt">memcached protocol documentation</a>, where it said the <code>incr</code>  and <code>decr</code> commands only worked on decimal representations of <em>unsigned</em> integers. It turned out we were using them on some values initialized with -1. Bingo! There was clearly a problem there, since those operations were guaranteed to fail on -1. But what did those specific failed operations have to do with system-wide malfunction?</p>
<p>Finally, I found the last piece of the puzzle: it appears that both Memcache and Memcached have checks in place to temporarily block a server that is malfunctioning (around 3 and 15 minutes, respectively). And when the server returns an error message on <code>incr</code> /  <code>decr</code> failure, they both seem to misinterpret that as server malfunction, blocking the server for several minutes and causing all operations to fail.</p>
<p>All that trouble caused by a negative number! Well, no. Actually, the real culprit is the unfortunate error handling in Memcached clients, of course.</p>
]]></content:encoded>
			<wfw:commentRss>http://jpcarzolio.com/2015/memcached-extensions-for-php-some-caveats/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcached and careless preloading</title>
		<link>http://jpcarzolio.com/2015/memcached-and-careless-preloading/</link>
		<comments>http://jpcarzolio.com/2015/memcached-and-careless-preloading/#comments</comments>
		<pubDate>Thu, 30 Jul 2015 14:32:51 +0000</pubDate>
		<dc:creator><![CDATA[Juan]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[optimization]]></category>

		<guid isPermaLink="false">https://jpcarzolio.com/?p=139</guid>
		<description><![CDATA[This is a short story about how I became aware of the dangers of &#8220;careless preloading&#8221;, while learning a bit about memcached internals along the way. A few years ago, while working on a high traffic app on the Facebook platform, I ran across a caching bug. All of a sudden, our memcached servers had...]]></description>
				<content:encoded><![CDATA[<p>This is a short story about how I became aware of the dangers of &#8220;careless preloading&#8221;, while learning a bit about memcached internals along the way.</p>
<p>A few years ago, while working on a high traffic app on the Facebook platform, I ran across a caching bug. All of a sudden, our memcached servers had stopped working &#8212; kind of. Stuff could be read, but nothing could be added to the cache: all <code>set</code> operations failed. Actually, it was stranger than that: <em>a few</em> sets did go through.</p>
<p>I had just fixed a broken preload script (which had never actually worked but nobody had ever noticed!) shortly before caching hell broke loose, so that script was my primary suspect. I decided to try the app on a test environment with and without that script, and bingo: without that preload, memcached worked normally, whereas running that preload caused the erratic cache behaviour we were experiencing in production.</p>
<p>So, what&#8217;s a <em>preload</em>, anyway? It&#8217;s a script that fills the cache with data beforehand, in order to avoid having low performance while the cache is gradually fed objects after each miss. I hadn&#8217;t written any preloads, but back then I didn&#8217;t stop to think if they were a good or bad idea, either. And it turned out that in many cases they were a bad idea, because they are always a bad idea if done without proper consideration. Ultimately, it boils down to something that has long been known to be&#8230; <em>the root of all evil</em>. Yep, I&#8217;m talking about <em>premature optimization</em>.</p>
<p>So, when I fixed that preload script (a trivial edit), it…  uhm, started working. And it turned out that its job was to select a whole freakin&#8217; table &#8212; about 1.5M rows &#8212; and load it into memcached. But, hey, that would be, at most, inefficient, right? In the worst case, it <em>might</em> displace useful data replacing it with useless data, but things would fix themselves with usage, right? Wrong! Enter memcached <em>slabs</em>.</p>
<p>For speed and efficiency reasons, Memcached has a custom memory manager, which consists of &#8220;slabs&#8221;, each of which can be assigned any number of 1MB &#8220;pages&#8221;, which are in turn split into a number of equally sized &#8220;chunks&#8221;, each of which may hold an individual object. Slabs hold objects within a specific size range, starting at 88 bytes (I think) and growing exponentially in steps of 1.25x. So for instance there may be a 1280 byte slab, which contains any number of 1MB pages split into many chunks of 1280 bytes, each holding (unless empty) an object whose size will be under 1280 bytes (including key and flags) and above 1024 bytes (the maximum allowed for the previous slab). When you perform a <code>set</code>, Memcached looks at the object size and determines which slab it belongs in, and looks for a free chunk in one of the pages, assigning the slab a new page if needed (i.e. if all its pages are full, or it has no pages at all because no objects of this size were stored before). And once assigned to a slab, a page stays assigned forever and it can&#8217;t be reassigned.</p>
<p>That explains the problem. Our preload scripts were executed after we had to resize our cache pool, or restart servers after security updates, or restart the daemon after a mysterious crash, or migrate to a different EC2  instance type, so they acted on an empty cache (no pages assigned). And this script was storing 1.5M objects with sizes in a rather specific range, causing all or most of the pages to be assigned to a few specific slabs, leaving none or too few available for the rest of the slabs. After a short while, the result was that, unless an incoming object&#8217;s size happened to match one of the few existing slabs, it was discarded. Regardless of the amount of empty space or stale data, those objects didn&#8217;t make it.</p>
<p>So the fix consisted in just removing that preload. The fact that we had never noticed that this particular preload was broken hinted that it wasn&#8217;t really necessary after all. And after some investigation and testing, it turned out that in normal operation &#8212; that is, caching objects on demand &#8212; only around 35k of the 1.5M objects were stored in the cache during the first hour, and there was little to no performance impact during this ramp-up period.</p>
<p>My point is not that preloading itself is inherently bad. Storing those 1.5M objects upfront could have been a good idea in some situation, but it wasn&#8217;t our case. My point is: before preloading data &#8212; before optimizing <em>anything</em> &#8212; make sure it&#8217;s necessary. If it is, make sure you&#8217;re being selective enough to preload useful data. And keep in mind that careless preloading may not only be useless or inefficient, but possibly harmful, as there may be unforeseen side effects.</p>
]]></content:encoded>
			<wfw:commentRss>http://jpcarzolio.com/2015/memcached-and-careless-preloading/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
