<?xml version='1.0' encoding='utf-8' ?>
<!--  If you are running a bot please visit this policy page outlining rules you must respect. https://www.livejournal.com/bots/  -->
<rss version='2.0'  xmlns:lj='http://www.livejournal.org/rss/lj/1.0/' xmlns:atom='http://www.w3.org/2005/Atom'>
<channel>
  <title>dormando</title>
  <link>https://dormando.livejournal.com/</link>
  <description>dormando - LiveJournal.com</description>
  <lastBuildDate>Thu, 30 Dec 2010 10:16:32 GMT</lastBuildDate>
  <generator>LiveJournal / LiveJournal.com</generator>
  <lj:journal>dormando</lj:journal>
  <lj:journalid>3594</lj:journalid>
  <lj:journaltype>personal</lj:journaltype>
  <copyright>NOINDEX</copyright>
  <image>
    <url>https://l-userpic.livejournal.com/66703879/3594</url>
    <title>dormando</title>
    <link>https://dormando.livejournal.com/</link>
    <width>100</width>
    <height>100</height>
  </image>

  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/526282.html</guid>
  <pubDate>Thu, 30 Dec 2010 10:16:32 GMT</pubDate>
  <title>MySQL NUMA allocations under 2.6.32+</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/526282.html</link>
  <description>&lt;a href=&quot;http://jcole.us/blog/archives/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/&quot; target=&quot;_blank&quot;&gt;refering Jeremy Cole&apos;s post on swapstorming under NUMA hardware&lt;/a&gt;, I&apos;ll note something potentially new.&lt;br /&gt;&lt;br /&gt;While I&apos;ve seen this &quot;brick wall swapstorming&quot; a few times before and since the post, I just saw some new OS installs not do this by default, and using the numactl to change the defaults is actually harmful to system interactivity.&lt;br /&gt;&lt;br /&gt;In the brick-wall cases, two NUMA zones of ~30G each, plus a mysqld (or memcached) running with 45G of ram, would equal 30G in memory, and 15G in swap. Ugly.&lt;br /&gt;&lt;br /&gt;In this case, I&apos;m getting a little bit in swap, but a relatively even note dist.&lt;br /&gt;&lt;br /&gt;Here&apos;s a box with no numactl tuning:&lt;br /&gt;&lt;pre&gt;
N0        :      7068733 ( 26.97 GB)
N1        :      7120258 ( 27.16 GB)
active    :     13355529 ( 50.95 GB)
anon      :     14187441 ( 54.12 GB)
dirty     :     14185099 ( 54.11 GB)
mapmax    :          265 (  0.00 GB)
mapped    :         1580 (  0.01 GB)
swapcache :         2350 (  0.01 GB)
&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;similar hardware, same OS/kernel running under numactl --interleave=all:&lt;br /&gt;&lt;pre&gt;
N0        :      6778742 ( 25.86 GB)
N1        :      6313382 ( 24.08 GB)
active    :     12395957 ( 47.29 GB)
anon      :     13090566 ( 49.94 GB)
dirty     :     13090566 ( 49.94 GB)
mapmax    :          255 (  0.00 GB)
mapped    :         1588 (  0.01 GB)
&lt;/pre&gt;&lt;br /&gt;... just a touch in swap on the first guy. Though I&apos;m going to wait a few days to declare victory or defeat, since I did see the first guy dump nearly a whole gig of swap once, but wasn&apos;t able to confirm if the swapped memory was mysql yet.&lt;br /&gt;&lt;br /&gt;The side note here is that my numactl-modified node is exhibiting some extreme latency on interactivity. Appears to be related to anything that needs to fork having a half-second delay. MySQL seems to be running fine though.&lt;br /&gt;&lt;br /&gt;I haven&apos;t investigated at all as to how numa distribution has changed in recent kernels (though I know it&apos;s been steadily improving over the years). Unfortunately every other box I&apos;ve used which *has* the problem, runs on a redhat/centos5 kernel. Which is ancient to an extreme.&lt;br /&gt;&lt;br /&gt;In this case it&apos;s debian squeeze with its default 2.6.32 kernel. Anyone try a recent ubuntu or redhat6 yet and see if the NUMA/swap issues are better on there?</description>
  <comments>https://dormando.livejournal.com/526282.html?view=comments#comments</comments>
  <category>mysql</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/525147.html</guid>
  <pubDate>Tue, 21 Sep 2010 22:13:27 GMT</pubDate>
  <title>Redis VS Memcached (slightly better bench)</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/525147.html</link>
  <description>Hello! First read &lt;a href=&quot;http://antirez.com/post/redis-memcached-benchmark.html&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt; if you haven&apos;t yet.&lt;br /&gt;&lt;br /&gt;I will now continue the back-and-forth obnoxiousness that benchmarking seems to be!&lt;br /&gt;&lt;br /&gt;In my tests, I&apos;ve taken the exact testing method antirez has used here, the same test software, the same versions of daemon software, and tweaked it a bit. Below are graphs of the results, and below that is the discussion of what I did.&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://imgprx.livejournal.net/7c3d573d63062291221c83bcfe6fdcce5bd418227318fe7f7b408af62d69e013/P2WlxyVijxKvg29u881fVEMdsf-ah7h03EGMVL1fit7a_RLV28-gHAUgT0p0UV90sktG0TDbbgBEHloJmVUr_lQOh3vKNKTVoF5VsBBoJlz8HeTbqw:todY4JOvyJKas1J1kYBiAw&quot; fetchpriority=&quot;high&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://imgprx.livejournal.net/a086ce20699243d5b646443083ce7b4d661ccfbf8ee94b0b5126676322dab7cc/P2WlxyVijxKvg29u881fVEMdsf-ah7h03EGMVL1fit7a_RLV28-gHAUgT0p0UV90sktG0TDbbgBEHloJmVUr_lQOh3vKNKTVo0peo1N8:cyjsLIddjbBPV0vB3T-6pQ&quot; loading=&quot;lazy&quot;&gt;&lt;br /&gt;&lt;br /&gt;Wow! That&apos;s pretty different from the first two benchmarks.&lt;br /&gt;&lt;br /&gt;First, &lt;a href=&quot;http://consoleninja.net/f/mc/redis_v_mc_bench.tar.gz&quot; target=&quot;_blank&quot;&gt;here&apos;s a tarball&lt;/a&gt; of the work I did. A small bash script, a small perl script to interpret the results (takes some hand fiddling to get it into gnuplot format), and the raw logs from my runs pre-rollup.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;What I did&lt;/h2&gt;&lt;br /&gt;The &quot;toilet&quot; bench and antirez&apos;s benches both share a common issue; they&apos;re busy-looping a single client process against a single daemon server. The antirez benchmark is written much better than the original one; it tries to be asyncronous and is much more efficient.&lt;br /&gt;&lt;br /&gt;However, it&apos;s still one client. &lt;b&gt;memcached is multi-threaded&lt;/b&gt; and has a very, very high performance ceiling. &lt;b&gt;redis is single-threaded&lt;/b&gt; and is very performant on its own.&lt;br /&gt;&lt;br /&gt;There is a trivial patch I did to the benchmarks to make them just run the GET|SET tests. It is included in the tarball.&lt;br /&gt;&lt;br /&gt;What I did was take the &lt;i&gt;same tests&lt;/i&gt;, but I ran several of them in parallel. This required a slight change in pulling the performance figures and running the test. The tests were changed to run indefinitely, either doing sets, or sets then indefinite gets (I wanted to run some sets before the get tests so they weren&apos;t just hammering air).&lt;br /&gt;&lt;br /&gt;The benchmarks were then fired off in parallel via the bash script, with the daemon fresh started before each run. After a rampup time (to allow the sets to happen, as well as let the daemons settle a bit), a script was used to talk to the daemons and sample the request rate. Since the benchmark is running several times in parallel, it&apos;s now most accurate to &lt;i&gt;directly&lt;/i&gt; ask the daemon for how many requests it&apos;s doing. I did some quick hand verification and the sampling code lines up with the output of a non-parallel benchmark. So far so good.&lt;br /&gt;&lt;br /&gt;I checked in with antirez to ensure I was running the tests correctly, and re-ran them as close to the original as I could get. Same number of clients *per daemon*, but there were 4 daemons in this case, so the actual number of clients is actually 4x what&apos;s listed in the graphs.&lt;br /&gt;&lt;br /&gt;The tests ran on localhost using a dual cpu quadcore xeon machine, clocked at 2.27ghz (with turbooost enabled, I&apos;m pretty sure). The OS is centos5 but with a custom 2.6.27 kernel. I verified the single-process benchmark results on my ubuntu laptop runnin 2.6.35 and a 2.50ghz core2duo and got similarish-but-slightly-lower numbers. I also tried the tests on several slightly differing machines after getting some odd initial results. Memcached was using the default of 4 threads. Performance might suffer in this particular test with more threads, as you&apos;d land with more lock contention.&lt;br /&gt;&lt;br /&gt;So these numbers look correct, for what I was trying to do here.&lt;br /&gt;&lt;br /&gt;Nothing else was changed. I used the same tools.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Why I did it&lt;/h2&gt;&lt;br /&gt;&lt;b&gt;Both tests are busy loops&lt;/b&gt;. All three of these benchmarks are wrong, but this can be slightly closer to reality. In most setups, you have many independent processes contacting your cache servers. In some cases, tens of thousands of apache/perl/ruby/python processes across hundreds of machines, all poking and prodding your cluster in parallel.&lt;br /&gt;&lt;br /&gt;I don&apos;t have the room here to explain the difference between running two processes and one process against the same daemon - So I&apos;ll hand waive with &quot;context switches n&apos; stuff&quot;. There&apos;re plenty of good unix textbooks on this topic :)&lt;br /&gt;&lt;br /&gt;So in this case, four very high speed benchmark programs soaked up CPU and hammered a single instance of redis and a single instance of memcached, which displays the strong point for the &lt;b&gt;scalability of a single instance in each case&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Why the bench is still wrong&lt;/h2&gt;&lt;br /&gt;These are contrived benchmarks. They don&apos;t test memcached incr/decr or append/prepend (mc /does/ have a few more features than pure get/set).&lt;br /&gt;&lt;br /&gt;Real world benchmarks will require a mix of sets, gets, incrs, decrs. Also, it requires testing each in isolation; some users might use their key/value store as a counter and hammer incr/decr hard. Others might hammer set hard, others might be near-purely gets.&lt;br /&gt;&lt;br /&gt;All of these need to be tested. All features should be benchmarked and load tested in isolation, and also when mixed. All features need to be tested under abuse as well.&lt;br /&gt;&lt;br /&gt;The test also doesn&apos;t try very hard to ensure the &apos;get&apos; requests actually match anything. A better benchmark would preload some data across 100,000 keys and then randomly fetch them. I might try this next, but for the sake of argument I&apos;m matching the same testing situation as the original blog post.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The interpretation for memcached&lt;/h2&gt;&lt;br /&gt;Memcached sticks to a constrained featureset and multithreads itself for a &lt;b&gt;highly consistent rate of scale and performance&lt;/b&gt;. When pushed to the extreme, it needs to keep up. We also need to stay highly memory efficient. For a bulk of our users, the more keys they can stuff in, the more bang for the buck. Scalable performance is almost secondary to this. This is why we have features like -C, which disables the 8-byte CAS per object.&lt;br /&gt;&lt;br /&gt;In a single-threaded benchmark against a multi-threaded memcached instance, memcached will lose out a bit due to the extra accounting overhead it must perform. However, when used in a realistic scale, it really shines.&lt;br /&gt;&lt;br /&gt;There are some trivial ways we are able to greatly increase this ceiling. It&apos;s not hard to get memcached to run above 500,000 gets per second via some tweaks on some of its central locks. Sets have a lot of room for improvement due to this as well. We plan to accomplish this. Our timing has been bad for quite a while though :)&lt;br /&gt;&lt;br /&gt;In almost all cases, the network hardware for a memcached server will give out before the daemon itself starts to limit your performance. This is a lot of why we haven&apos;t rushed to improve the lock scale.&lt;br /&gt;&lt;br /&gt;Computers are absolutely trending toward more cores and not toward higher clocks. Threading is how we will scale single instances.&lt;br /&gt;&lt;br /&gt;I really hate drawing conclusions from these sort of things. The entire point of this post is more or less me posturing about how shitty benchmarks tend to be. They are created in myopia and usually lauded with fear or pride.&lt;br /&gt;&lt;br /&gt;You can&apos;t benchmark the fact that Redis has atomic list operations against memcached. They do different things and exist in different spaces, and the real differences are philosophical and perhaps barely technical. I&apos;m merely illustrating the proper scalable performance of issuing bland SETs and GETs against a single instance of both pieces of software.&lt;br /&gt;&lt;br /&gt;Understand what your app needs feature-wise, scale-wise, and performance-wise, then use the right tool for the damn job. Don&apos;t just read benchmarks and flip around ignorantly, please :)&lt;br /&gt;&lt;br /&gt;Finally, here&apos;s one more graph... I noticed that redis seemed to do slightly better in the non-parallel benchmark, so I ran the numbers again with a single parallel benchmark in case anyone wants to look into it. Yes, the memcached numbers were lower for the single benchmark test, but I don&apos;t really care since it&apos;s higher when you actually give it multiple clients :)&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://imgprx.livejournal.net/af0744ca438d9467bc741952602682822babeb504ac5bff633df035be0dac8bc/P2WlxyVijxKvg29u881fVEMdsf-ah7h03EGMVL1fit7a_RLV28-gHAUgT0p0UV90sktG0S7XbQRJGB8Dj1Ur_lQOh3vKNKeR410SuQ:o073UrqHB9ZyGMKe-sk6_w&quot; loading=&quot;lazy&quot;&gt;</description>
  <comments>https://dormando.livejournal.com/525147.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>4</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/522738.html</guid>
  <pubDate>Thu, 14 Jan 2010 05:17:03 GMT</pubDate>
  <title>Rolling back 432 million rows in MySQL 5.0</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/522738.html</link>
  <description>Believe it or not, I haven&apos;t put myself into this position before. The day before yesterday I started a LOAD DATA INFILE for 760 million rows. Didn&apos;t really think too hard about it, figured I&apos;d let it run until it finished.&lt;br /&gt;&lt;br /&gt;Unfortunately MySQL did that thing where the row insertion rate slowed to molasses over time, and the box was hosed. I killed the query. So it started rolling back the transaction. Which was 431 million rows in.&lt;br /&gt;&lt;br /&gt;Using `SHOW ENGINE INNODB STATUS` you can look at the number of undo entries a transaction has left to go:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;---TRANSACTION 0 1161525892, ACTIVE 18598 sec, process no 20139, OS thread id 1131772224
ROLLING BACK , undo log entries 431301691&lt;/pre&gt;&lt;br /&gt;Something like that. I left it to rollback overnight. The next day, it had only moved through a few million entries. It&apos;s going to take a week or more! I could just drop the table but it&apos;s locked from the transaction (Is this always true?)&lt;br /&gt;&lt;br /&gt;So, I made sure replicaton was stopped, flushed logs, ensured nothing was talking to the DB.&lt;br /&gt;Then, tried to shut down mysql. It hung, waiting for the transaction. Waited 15 minutes.&lt;br /&gt;Then, I kill -9&apos;ed mysqld.&lt;br /&gt;Then, I waited a few hours for InnoDB crash recovery to run (large 512M redo logs + &lt;a href=&quot;http://www.percona.com/docs/wiki/percona-xtradb:patch:innodb_recovery_patches&quot; target=&quot;_blank&quot;&gt;no fast recovery patch&lt;/a&gt;).&lt;br /&gt;Then, I see this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;InnoDB: Apply batch completed
[etc]
InnoDB: Starting in background the rollback of uncommitted transactions
100113 20:53:31  InnoDB: Rolling back trx with id 0 1161525892, 431119521 rows to undo&lt;/pre&gt;&lt;br /&gt;Still rolling back, but now in the background!&lt;br /&gt;&lt;br /&gt;Finally, I dropped the table housing the offending transaction.&lt;br /&gt;&lt;br /&gt;Bam, now the undo rows are being chewed through at the rate of 1,000,000 every couple seconds. It&apos;ll be undone in a few minutes and I can go finish the maintenance work on the DB without having to reslave it. (It&apos;s a few TB in size, reslaving is a little painful).&lt;br /&gt;&lt;br /&gt;Is there a better way to do this? I had no idea if this process would work or not, and there is the undesireable step of kill -9&apos;ing mysqld.</description>
  <comments>https://dormando.livejournal.com/522738.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>3</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/522027.html</guid>
  <pubDate>Sat, 19 Dec 2009 04:54:29 GMT</pubDate>
  <title>Intel Nehalem CRC32 hardware instruction and HAProxy</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/522027.html</link>
  <description>Was staring at a wall earlier trying to think of things to optimize in an experimental HAProxy setup. I have the proxy configured to do very little processing (even using splice(2) when useful).&lt;br /&gt;&lt;br /&gt;I&apos;m asking HAProxy to do just a couple things in L7:&lt;br /&gt;- Add an X-Forwarded-For header&lt;br /&gt;- Hash the URI onto a list of backend servers&lt;br /&gt;- Shuffle data between sockets&lt;br /&gt;&lt;br /&gt;... pretty much nothing else. blind copy/forward otherwise.&lt;br /&gt;&lt;br /&gt;But, hey, I have a box here with an Intel Nehalem based xeon core. Nehalem has a new hardware instruction for calculating a CRC32 hash. I wonder if it&apos;s any faster? How does the numeric distribution compare?&lt;br /&gt;&lt;br /&gt;Well getting it working wasn&apos;t too hard at all. I&apos;ll post a patch once I clean it up.&lt;br /&gt;&lt;br /&gt;I threw about 9,000 requests with different URI&apos;s at it to see how well it balanced&lt;br /&gt;&lt;pre&gt;
intel:
   1740 001
   1749 002
   1693 003
   1835 004
   1807 005

haproxy:
   1741 001
   1716 002
   1732 003
   1772 004
   1793 005
&lt;/pre&gt;&lt;br /&gt;... a handful of requests were lost in the transfer, so these numbers are to be a little suspect. I&apos;ll try again to see if I can get a more accurate reading. You can already see that the built-in algorithm for haproxy is notably more even than the intel CRC32. *but*, the CRC32 isn&apos;t terribly far off. If you double or triple up your server list, it could end up being more even. I&apos;ll try this later.&lt;br /&gt;&lt;br /&gt;Now, the speed test. I wrote a seperate bench tool that takes some input and runs the hash algorithms N times. I ran each test 3 times in a row per algorithm to ensure the times were close. The bench is for 10 million loops on the hash algo.&lt;br /&gt;&lt;br /&gt;The results are &quot;uri length: N&quot;, then the middle time, for each algorithm.&lt;br /&gt;&lt;br /&gt;uri length: 6&lt;br /&gt;&lt;br /&gt;haproxy:&lt;br /&gt;0m7.459s&lt;br /&gt;intel:&lt;br /&gt;0m6.528s&lt;br /&gt;&lt;br /&gt;uri length: 13&lt;br /&gt;&lt;br /&gt;haproxy:&lt;br /&gt;0m14.139s&lt;br /&gt;intel:&lt;br /&gt;0m6.138s&lt;br /&gt;&lt;br /&gt;ur length: 39&lt;br /&gt;&lt;br /&gt;haproxy:&lt;br /&gt;0m40.366s&lt;br /&gt;intel:&lt;br /&gt;0m8.483s&lt;br /&gt;&lt;br /&gt;... wow, quite a bit of a speedup! BUT, the hash algos do a little extra processing, looking for &apos;?&apos; or &apos;/&apos; characters to short circuit the hash if necessary. I took these out to compare them more on the raw hash speed. Also, technically the haproxy hash was counting &apos;/&apos; characters where the intel one wasn&apos;t.&lt;br /&gt;&lt;br /&gt;uri length: 39&lt;br /&gt;haproxy:&lt;br /&gt;0m33.486s&lt;br /&gt;intel:&lt;br /&gt;0m7.459s&lt;br /&gt;&lt;br /&gt;... still retains most of the speedup. Neat!&lt;br /&gt;&lt;br /&gt;Unfortunately this speedup is likely marginal at best. In the final case with most of the tests in there, haproxy is running at 246,998 hashes per second. Intel is running at 1340662 per second. 246,998 per second already isn&apos;t slow. I&apos;ll have to run the algorithm in a full scale test to see if the difference is even measureable, or if too much CPU time is soaked up everywhere else in haproxy.&lt;br /&gt;&lt;br /&gt;Also you can tell HAProxy to only consider the first N characters of a URI for hashing. Given the pretty linear decrease in speed given the length of the URI, capping the hash length at 10-15 gives you a speedup comparible to switching to the intel algorithm. If you want to hash arbitrary string lengths, the nehalem CRC32 algo is pretty darn impressive with a very minor increase in time under a wide increase in string length :)&lt;br /&gt;&lt;br /&gt;So I&apos;ll clean up the patch, test it more at full scale, and post again later.</description>
  <comments>https://dormando.livejournal.com/522027.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/521163.html</guid>
  <pubDate>Tue, 27 Oct 2009 07:12:00 GMT</pubDate>
  <title>The &quot;multiget hole&quot; and how none of this is new</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/521163.html</link>
  <description>&lt;a target=&apos;_blank&apos; href=&apos;http://highscalability.com/blog/2009/10/26/facebooks-memcached-multiget-hole-more-machines-more-capacit.html&apos;&gt;http://highscalability.com/blog/2009/10/26/facebooks-memcached-multiget-hole-more-machines-more-capacit.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This has been making the rounds today... In my usual fashion I&apos;m going to write an overly complicated post in response.&lt;br /&gt;&lt;br /&gt;The basic claims of the memcached &quot;multiget hole&quot; are thus:&lt;br /&gt;&lt;br /&gt;- If you are primarily using multigets to batch requests.&lt;br /&gt;- Memcached is out of CPU.&lt;br /&gt;- Adding a second memcached instance will split the batch request across both hosts.&lt;br /&gt;- This will make things slower, since your multiget request gets split in half and hits both servers, instead of just one.&lt;br /&gt;&lt;br /&gt;Lets break down this last claim into some detail, then discuss potential workarounds! Everybody put on your bath robe and thinking cap.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Claim:&lt;/b&gt; A multiget, split into two, will be slower than a single multiget against a single server.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What&apos;s really happening:&lt;/b&gt; A multiget, as &lt;a href=&quot;http://code.google.com/p/memcached/wiki/FAQ#Batch_your_requests_with_get_multi&quot; target=&quot;_blank&quot;&gt;referenced here&lt;/a&gt;, is when you combine a fetch for several keys into a single request. Lets say in this exercise you are trying to fetch keys &apos;foo1&apos; through &apos;foo100&apos;, in one single request. The process for a typical memcached client and server instance is:&lt;br /&gt;&lt;br /&gt;- Take the full list of keys requested.&lt;br /&gt;- Hash each key &lt;i&gt;individually&lt;/i&gt; against the list of memcached servers. If you have one server, they all go to the same place, if you have two, they are split.&lt;br /&gt;- For each server that will get keys, issue a special &lt;i&gt;multiget request&lt;/i&gt; against that server. For the ASCII protocol this looks like: `get foo1 foo2 foo3` to the first instance, and `get foo4 foo5 foo6` sent to the second instance. A single write, will get multiple responses back. This is faster than doing them one at a time, since you would be waiting for a response between each get. &quot;get foo1&quot; (wait for response) &quot;get foo2&quot; (wait for response) etc.&lt;br /&gt;- Wait for each server to respond, collect keys, return to caller.&lt;br /&gt;&lt;br /&gt;Lets break down the steps even &lt;i&gt;more&lt;/i&gt;!&lt;br /&gt;&lt;br /&gt;- For each multiget request issued, a *client* may either use a *blocking* or *non blocking* mode.&lt;br /&gt;- In an optimized case, the client will issue a multiget against *both* servers *in parallel* and then call poll(2) (or similar) and wait for the responses.&lt;br /&gt;- In a non-optimised case, the client will issue multigets to each server in turn and wait for the response. libmemcached did this until recently, so you &lt;b&gt;might be surprised&lt;/b&gt;, if you look!&lt;br /&gt;&lt;br /&gt;On the server end:&lt;br /&gt;&lt;br /&gt;- Read all keys requested.&lt;br /&gt;- For each key, hash the key and look it up against the internal hash table.&lt;br /&gt;- Load any valid items for return and...&lt;br /&gt;- ... write them to the socket.&lt;br /&gt;The binary protocol more closely combines all these steps, but the idea is the same.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What the hell are you getting at?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Well, my point is slicing a multiget actually *shifts a tradeoff* as much as it becomes more or less efficient. There is a certain amount of overhead for a *server* to read from a client and respond, but there is also a particular amount of effort for that server to look up the key in its hash table and build a response. It is a &lt;b&gt;fact&lt;/b&gt; that issuing a smaller multiget against a particular server will take *less* CPU time than a larger one. Adding servers does reduce CPU time on the server.&lt;br /&gt;&lt;br /&gt;However, when the client has to issue separate writes to more servers, it is doing more complex work and will thus &lt;b&gt;take longer&lt;/b&gt; and &lt;b&gt;use more CPU time&lt;/b&gt; than if all of the requests were in a single write.&lt;br /&gt;&lt;br /&gt;Hence, adding servers to a cluster *will* reduce the CPU usage on the cluster. The addition is non linear, but it will not make it *worse*. It could however negatively affect clients, and a bad client can be especially affected, if it has to wait for responses in serial.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Part the next: The subtle issue&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Depending on how large your multigets are, it may take less time to split them an issue them against multiple servers. This is *entirely up to you* on how you want to handle, requires testing, and can be affected by kernel tunables.&lt;br /&gt;&lt;br /&gt;If you are issuing a multiget with many keys, or with very large responses, you will be more likely to run up against the (I hope I&apos;m quoting this right) TCP window scale. After so many bytes TCP needs to roundtrip an ACK packet to confirm that the remote end has received the preceeding data. This window will open at a certain size, and then expand or contract depending on how you&apos;re using the connection.&lt;br /&gt;&lt;br /&gt;This is why some downloads or uploads will start out a little slow, then rapidly speed up. It&apos;s also why connections over a laggy or long link might not go above, say, 40k per second, but you can open multiple connections and run them all at 40k/sec to the same server (see also: download accelerators).&lt;br /&gt;&lt;br /&gt;This last example should illustrate what I mean here. Stuffing too much down the pipe at once will cause more roundtrips to the remote server for the TCP acks. If you split a large list and run the data nonblocking, in parallel, to multiple servers, it might take less &lt;b&gt;time&lt;/b&gt; to issue the request, but will use more &lt;b&gt;CPU&lt;/b&gt; on the client.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Part the next to last: The workarounds&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;With the above in mind, the typical workaround has been in use for ages. A long time ago, in a galaxy far far away, brad fitz (or someone over there, I&apos;m not sure who) realized that when fetching all cache keys for a &lt;a href=&quot;http://dormando.livejournal.com/profile&quot; target=&quot;_blank&quot;&gt;livejournal profile&lt;/a&gt; is a trivial multiget of 10+ keys. It was also stupid to issue this (relatively small) multiget across all of the memcached hosts.&lt;br /&gt;&lt;br /&gt;So he added a &lt;i&gt;set and fetch by master key&lt;/i&gt; mode to the perl client (Cache::Memcached at the time). When you issue a set or a get request in &quot;by key&quot; mode, you give any given key a &lt;i&gt;second&lt;/i&gt; key. That is, the key your client uses to hash your data out to the list of memcached servers, is different from the key you hand to memcached for storage. So:&lt;br /&gt;&lt;br /&gt;- You assign a master key &quot;dormando&quot;, to keys &quot;dormando-birthday&quot;, &quot;dormando-website&quot;, etc. This is bad key naming, but bear with me.&lt;br /&gt;- Your client, instead of using &quot;dormando-birthday&quot; to decide where to store the keys, uses the key &quot;dormando&quot;&lt;br /&gt;- Your cilent then sends *just* &quot;dormando-birthday&quot; and &quot;dormando-website&quot; to whatever server &quot;dormando&quot; hashed to.&lt;br /&gt;- Memcached happily stores those keys, without any idea of what the master key was (you can&apos;t get it back).&lt;br /&gt;&lt;br /&gt;Then you issue a multiget back, with the master key of &quot;dormando&quot;. Both keys resolve to the *same* server, and the multiget hits a single host. With a single write, and ideally a single roundtrip.&lt;br /&gt;&lt;br /&gt;If I had a metric pantload of keys to fetch and don&apos;t want to issue them all to the same server (noting the above subtle issue), I can semi-intelligently split the master keys into &quot;dormando-chunk1&quot;, &quot;dormando-chunk2&quot; - depends on what your app can handle.&lt;br /&gt;&lt;br /&gt;This is a simple and elegant way of avoiding having multigets spread thinly across your memcached cluster.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;You could use UDP!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Yeah I guess you could. What about keys with larger responses? This does have a lot of the same issues, but in a different flavor. Could be faster or slower depending on what you&apos;re doing.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;You could REPLICATE!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;annnnnnnnnd do something really complicated where you have to store all of your keys in two places (halving the effective size of your cache!) and having your client randomly pick where each key goes to or comes from each time they&apos;re fetched? When issuing against a cluster of more than *two* machines, this isn&apos;t going to help nearly as much as cutting 50 separate fetches down into a single request, deterministically, by clustering the keys intelligently.&lt;br /&gt;&lt;br /&gt;Note that replication adds a lot more failure scenarios. Network blips can lead to inconsistent cache data, among other things.&lt;br /&gt;&lt;br /&gt;Sounds simpler to use a feature that already &lt;a href=&quot;http://docs.tangent.org/libmemcached/memcached_get.html&quot; target=&quot;_blank&quot;&gt;exists&lt;/a&gt; (look for &quot;mget_by_key&quot;)?&lt;br /&gt;&lt;br /&gt;But you could make either work.&lt;br /&gt;&lt;br /&gt;Fortunately, there&apos;s also a &lt;a href=&quot;http://twitter.com/dlsspy/status/1652349607&quot; target=&quot;_blank&quot;&gt;really short answer to all of this&lt;/a&gt;.</description>
  <comments>https://dormando.livejournal.com/521163.html?view=comments#comments</comments>
  <category>memcached</category>
  <lj:security>public</lj:security>
  <lj:reply-count>7</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/517988.html</guid>
  <pubDate>Fri, 10 Jul 2009 06:08:02 GMT</pubDate>
  <title>Memcached 1.4.0</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/517988.html</link>
  <description>Everyone&apos;s favorite MySQL load relief system, memcached, has just hit the next major stable release: 1.4.0&lt;br /&gt;&lt;br /&gt;This release sports a new binary protocol, major performance improvements, and many new statistics. Major kudos to the work of other people (Trond, Dustin, Toru) who put most of the effort into this new release.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://code.google.com/p/memcached/wiki/ReleaseNotes140&quot; target=&quot;_blank&quot;&gt;Check out the release notes&lt;/a&gt; and give it a shot on your site. Please let us know if you&apos;ve deployed it and any feedback you might have :)</description>
  <comments>https://dormando.livejournal.com/517988.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/515836.html</guid>
  <pubDate>Sat, 04 Apr 2009 00:48:56 GMT</pubDate>
  <title>Memcached 1.2.7 and 1.3.3</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/515836.html</link>
  <description>&lt;a href=&quot;http://groups.google.com/group/memcached/browse_thread/thread/4d86c6d323c70a80&quot; target=&quot;_blank&quot;&gt;original post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;... and this is my usual plea to those mysql/web/industrial folks to try out the latest code. Help us on our quest to scale the crap out of all of your stuff. :)&lt;br /&gt;&lt;br /&gt;Find us on the &lt;a href=&quot;http://groups.google.com/group/memcached/&quot; target=&quot;_blank&quot;&gt;mailing list&lt;/a&gt;, on &lt;a href=&apos;https://www.livejournal.com/rsearch/?tags=%23memcached&apos;&gt;#memcached&lt;/a&gt; on freenode, or on twitter as &lt;a href=&quot;http://www.twitter.com/dormando&quot; target=&quot;_blank&quot;&gt;dormando&lt;/a&gt;, &lt;a href=&quot;http://www.twitter.com/dlsspy&quot; target=&quot;_blank&quot;&gt;dlsspy&lt;/a&gt;, &lt;a href=&quot;http://www.twitter.com/tmaesaka&quot; target=&quot;_blank&quot;&gt;tmaesaka&lt;/a&gt;, and &lt;a href=&quot;http://twitter.com/trondn&quot; target=&quot;_blank&quot;&gt;trondn&lt;/a&gt;. :) All others are fakers.&lt;br /&gt;&lt;br /&gt;---&lt;br /&gt;&lt;br /&gt;Two new memcached releases are available today.&lt;br /&gt;&lt;br /&gt;Stable 1.2.7&lt;br /&gt;&lt;br /&gt;The new stable release which is a maintenance release of the 1.2&lt;br /&gt;series containing several bugfixes and a few features.&lt;br /&gt;&lt;br /&gt;This version is recommended for any production memcached instances.&lt;br /&gt;&lt;br /&gt;  Release Notes:&lt;br /&gt;  &lt;a target=&apos;_blank&apos; href=&apos;http://code.google.com/p/memcached/wiki/ReleaseNotes127&apos;&gt;http://code.google.com/p/memcached/wiki/ReleaseNotes127&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;  Download:&lt;br /&gt;  &lt;a target=&apos;_blank&apos; href=&apos;http://memcached.googlecode.com/files/memcached-1.2.7.tar.gz&apos;&gt;http://memcached.googlecode.com/files/memcached-1.2.7.tar.gz&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Beta 1.3.3&lt;br /&gt;&lt;br /&gt;The new 1.3 beta brings lots of new features, performance, protocol&lt;br /&gt;support and more to memcached.&lt;br /&gt;&lt;br /&gt;Everyone is encouraged to get this into their labs and abuse it as&lt;br /&gt;much as possible.  This will be the stable tree.  We&apos;ve been testing&lt;br /&gt;it quite thoroughly in the memcached community already and find it to&lt;br /&gt;be quite stable, but we&apos;re always looking for more complaints.&lt;br /&gt;&lt;br /&gt;  Release notes:&lt;br /&gt;  &lt;a target=&apos;_blank&apos; href=&apos;http://code.google.com/p/memcached/wiki/ReleaseNotes133&apos;&gt;http://code.google.com/p/memcached/wiki/ReleaseNotes133&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;  Download:&lt;br /&gt;  &lt;a target=&apos;_blank&apos; href=&apos;http://memcached.googlecode.com/files/memcached-1.3.3.tar.gz&apos;&gt;http://memcached.googlecode.com/files/memcached-1.3.3.tar.gz&lt;/a&gt;</description>
  <comments>https://dormando.livejournal.com/515836.html?view=comments#comments</comments>
  <category>memcached</category>
  <category>mysql</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/514861.html</guid>
  <pubDate>Fri, 20 Mar 2009 23:54:36 GMT</pubDate>
  <title>Memcached 1.3.2 beta</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/514861.html</link>
  <description>Yo,&lt;br /&gt;&lt;br /&gt;I didn&apos;t write most of this code, but most of the new changes are pretty awesome &lt;a href=&quot;http://groups.google.com/group/memcached/browse_thread/thread/fa43a54be4f95ae6#&quot; target=&quot;_blank&quot;&gt;go read about it, grab it, and try it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We&apos;re very careful about getting as much testing as possible before declarnig a new release as stable. Please try it out in your development environments, beat up on it, maybe try it out in staging. Perhaps even be naughty and swap out one production machine with it some late night.&lt;br /&gt;&lt;br /&gt;A lot of the changes in this release are good for those high end mysql/etc backed sites where you might be worried about (or are) hitting performance issues with memcached itself. Others like expanded statistics, optional memory optimisations, should be useful for most folks. Please check the release announcements for dustin&apos;s more thorough notes :)&lt;br /&gt;&lt;br /&gt;A 1.2.7 stable release should follow on its heels shortly, but don&apos;t expect anything but bugfixes and a few minor feature enhancements over there.</description>
  <comments>https://dormando.livejournal.com/514861.html?view=comments#comments</comments>
  <category>memcached</category>
  <category>mysql</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/506377.html</guid>
  <pubDate>Wed, 19 Nov 2008 02:55:10 GMT</pubDate>
  <title>Goodbye again, LJ</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/506377.html</link>
  <description>I said it once in 2002, and now again in 2008. See ya, LJ :)&lt;br /&gt;&lt;br /&gt;Mucho thanks to &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;dwell&quot; lj:user=&quot;dwell&quot; &gt;&lt;a href=&quot;https://dwell.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://dwell.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;dwell&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; and &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;tupshin&quot; lj:user=&quot;tupshin&quot; &gt;&lt;a href=&quot;https://tupshin.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://tupshin.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;tupshin&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; for &lt;a href=&quot;http://community.livejournal.com/lj_maintenance/121482.html&quot; target=&quot;_blank&quot;&gt;their&lt;/a&gt; &lt;a href=&quot;http://community.livejournal.com/lj_releases/40846.html&quot; target=&quot;_blank&quot;&gt;mentions&lt;/a&gt; for the work &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;burr86&quot; lj:user=&quot;burr86&quot; &gt;&lt;a href=&quot;https://burr86.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://burr86.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;burr86&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; and I have put in to help them take over LJ. Especially burr86 - I did some hard stuff but he did most of the work from 6A&apos;s side. Kudos to LJ&apos;s ops/eng teams for diving into one of the more complicated web architectures and actually getting it running.&lt;br /&gt;&lt;br /&gt;We&apos;re there for ya (on whatever personal committment we&apos;re comfortable with :P), but LJ&apos;s in good hands. Enjoy.&lt;br /&gt;&lt;br /&gt;I did some pretty cool hacks for MogileFS to help facilitate their move, which I should be posting to the mailing list as soon as I clean up the commits. What&apos;s good for us is good for everyone :) Please remember LJ&apos;s open source history as you march forward.</description>
  <comments>https://dormando.livejournal.com/506377.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/496639.html</guid>
  <pubDate>Sun, 17 Aug 2008 09:21:42 GMT</pubDate>
  <title>Should you cache?</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/496639.html</link>
  <description>&lt;h3&gt;Should you use memcached? Should you just shard mysql more?&lt;/h3&gt;&lt;br /&gt;Memcached&apos;s popularity is expanding its use into some odd places. It&apos;s becoming an authoritative datastore for some large sites, and almost more importantly it&apos;s sneaking into the lowly web startup. This is &lt;a href=&quot;http://fschiettecatte.wordpress.com/2008/08/07/memcached-again/&quot; target=&quot;_blank&quot;&gt;causing&lt;/a&gt; &lt;a href=&quot;http://glinden.blogspot.com/2008/04/replication-caching-and-partitioning.html&quot; target=&quot;_blank&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;http://optimmysql.blogspot.com/2008/05/memcached-but-do-you-need-it.html&quot; target=&quot;_blank&quot;&gt;discussion&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Most of whom seem to be missing the point. In this post I attempt to explain my point of view for how memcached should really influence your bouncing baby startups, and even give some pointers to the big guys who might have trouble seeing the forest through the trees.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Using memcached does not scale your website!&lt;/i&gt; Entertain me, I&apos;m playing semantics here: This thing is not for scaleout. Mostly. What memcached &lt;i&gt;really&lt;/i&gt; is, is a giant floating magnifying glass. It takes what you have already built and makes stretch ten times further. I insist on not confusing caching with scaleout as when your little stretch-armstrong of a website hits that tenfold limit, you&apos;re still screwed. There&apos;s no magic switch or configuration option in memcached that will save you from dealing with proper optimization and sharding.&lt;br /&gt;&lt;br /&gt;You sure can get away with a hell of a lot though!&lt;br /&gt;&lt;br /&gt;Keep it in the front of your mind; no it will not help you batch your writes, or make them smaller, or really help you deal with them in any useful way. If you want to write data you will need back later, you must shard. If it&apos;s data you don&apos;t care about, maybe write it to memcached and make a note of it in your business plan.&lt;br /&gt;&lt;br /&gt;Also strongly keep in mind; memcached won&apos;t help your cache misses suck less.  If you&apos;re writing awful data warehouse quality queries which you expect to run live on the site, go bust out the failboat and get-a-rowin&apos;. You&apos;re screwed. As your dataset grows you will find new slices of hell in which your queries behave in all new ways. What once scanned &quot;a few extra rows&quot; now might hit tens of thousands. Cache misses will suck. You will have to deal with this.  That&apos;s not something this solves.&lt;br /&gt;&lt;br /&gt;Sometimes memcached &lt;i&gt;does&lt;/i&gt; let you achieve the impossible, or scale the unlikely. Take slightly complex queries, or even template operations, which under the best of conditions might take 15-20 milliseconds each. An obnoxious join, a weird subquery, a tree walk, or fancy HTML templating. Being able to do this live could mean the difference betwen your website standing apart or having to settle with an awful workaround. In these cases, with a high enough hit rate, you can soak those cache misses and make the feature work.&lt;br /&gt;&lt;br /&gt;My example isn&apos;t translating a 5 second query into 0.5ms with memcached, it&apos;s a 15-20ms query.  If you had a dozen of these in a page load, a bad load might take an extra quarter second to render, but it wouldn&apos;t ruin the user experience. The issue memcached solves here is subtle. Tacking on 0.25 seconds per page render might not make the site completely unusable, but realize these queries are using solid resources on your expensive hardware for that extra quarter second. With a quadcore database, it&apos;s possible under the best conditions you would only be able to render 14-16 pages per second off of that machine. Throw in all the other things you have to do on a page load, writes, internal database whoosits and uneven CPU usage and you&apos;d be lucky to get 5 pages per second.&lt;br /&gt;&lt;br /&gt;In this case, it&apos;s still walking the line of scalability, but it turns something mildly impossible into something highly probable. On the cheap.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The cost equation&lt;/h3&gt;&lt;br /&gt;Now the most important factor here has reared its ugly head: Cost.&lt;br /&gt;&lt;br /&gt;Cost. Ugly for startups. Ugly for established companies. Nightmares for venture capital. What is your cost? Why am I talking cash about companies who have millions of dollars in VC or sales? Just buy more servers! Whatever, right?&lt;br /&gt;&lt;br /&gt;Well no. The largest cost is &lt;b&gt;time&lt;/b&gt;. All others pale in comparison.  The best physical goods investments your company can make are more related to your people than your hardware. Hardware has horrific depreciation. Most of the value is lost immediately, the rest over the first year of operation.&lt;br /&gt;&lt;br /&gt;In comparison, buying your employees really fucking nice chairs, desks, and monitors in a swanky comfortable office are much more solid investments for your company. Aeron chairs have great resale value for that inevitable going-bumpkus dot bomb sale. Also anything you do to make your workers happier and more productive will pay out more than any hardware investment. Your product ships on time, you react to the market faster.&lt;br /&gt;&lt;br /&gt;To sidestep into hardware a little... Always max out the RAM in your databases. Everyone should. I didn&apos;t realize people don&apos;t actually do this until I read some of these arguments against memcached. Whenever I add memcached to a website, RAM memcached gets is RAM that didn&apos;t fit into the databases, but easily fits into empty memory slots in webservers or cheaper hardware. A good solid database might cost $5,000, but a beefy memcached box will cost less than half that. Way less than that if you just add memory to existing hardware. So &quot;adding that extra RAM to your databases&quot; isn&apos;t a very fair apples-to-apples comparison unless you&apos;re already doing something wrong.&lt;br /&gt;&lt;br /&gt;So it should be obvious just what the hell I&apos;m getting at now, and what seems to be bothering everyone else about this whole &lt;i&gt;stupid&lt;/i&gt; memcached fad.&lt;br /&gt;&lt;br /&gt;You&apos;re all &lt;b&gt;wasting your goddamn time!&lt;/b&gt; Yeesh!&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;How can a small site or startup benefit from memcached?&lt;/h3&gt;&lt;br /&gt;Simple: The idea.&lt;br /&gt;&lt;br /&gt;Caching really wedges your whole RDBMS worldview. You don&apos;t just CRUD anymore. Your data is a process. A flow between points instead of just the store and display. At any time in this flow an idea may be injected.  Maybe it&apos;s serializing a generated object and caching it, maybe it&apos;s utilizing &lt;a href=&quot;http://www.danga.com/gearman/&quot; target=&quot;_blank&quot;&gt;gearman&lt;/a&gt; to shift off some asyncronous work. There is just more to it now.&lt;br /&gt;&lt;br /&gt;But that&apos;s all messy complicated. What can you do? What should you do?&lt;br /&gt;&lt;br /&gt;Design for having cache, design for change.&lt;br /&gt;... but don&apos;t write all the code yet.&lt;br /&gt;... but certainly design for change.&lt;br /&gt;&lt;br /&gt;Think good object design. A &quot;user&quot; is a class. That user has base properties which you might find in the `user` table. A &quot;user&quot; object might have a profile, which is really another object with another class representing a `profile` table.&lt;br /&gt;&lt;br /&gt;my $user; is an invaluable abstraction.&lt;br /&gt;&lt;br /&gt;That user object must load and store data. When you build this at first it&apos;s all standard CRUD. Straight to a database.&lt;br /&gt;&lt;br /&gt;Where would you think to add caching to this system? I hope I&apos;ve made it too obvious.&lt;br /&gt;&lt;br /&gt;At the query layer! Use a database abstraction class and have it memcache resultset objects and... No no no, that&apos;s a lie. I&apos;m lying. Don&apos;t do that.&lt;br /&gt;&lt;br /&gt;Do it inside that $user object. At the highest level possible. Take the whole object state and shovel it somewhere. That object is its own biggest authority. It knows when it&apos;s been updated, when it needs to load data, and when to write to the database. It might&apos;ve had to read from several tables or load dependent objects based on what you ask it to do.&lt;br /&gt;&lt;br /&gt;Instead of wrangling your best and brightest into figuring out a cache invalidation algorithm which might work &quot;okay&quot; against your schemas, do what&apos;s simple for the object. If adding caching to the $user object means the load() function tries memcached first, and all write operations hit memcached with a delete operation, so be it. You just added basic caching to one of the hottest objects in your website in, oh, half an hour. Maybe a few days if you&apos;re really scraping the bottom of the talent barrel.&lt;br /&gt;&lt;br /&gt;Now we&apos;re back where we started. Reap the time benefits! Abstract your data access methods properly, plan for caching. Actually go write caching into a few objects. Maybe turn it off when you&apos;re done. You don&apos;t need it yet. Write your objects to talk directly to your database and save time.&lt;br /&gt;&lt;br /&gt;Same idea for sharding. Either focus on that now, or realize you can take a $user object and extend its load() magic to find and write to users based on a sharding scheme. You probably don&apos;t have to rewrite all of the code to make this happen. Refactor to win.&lt;br /&gt;&lt;br /&gt;So now you&apos;re ready. You&apos;re building your site fast and abstracting where you can. Brace for change. Be ready to shard, be ready to cache. React and change to what you push out which is actually popular, vs overplanning and wasting valuable time. Keeping it simple is gold here.&lt;br /&gt;&lt;br /&gt;You&apos;re building something new and you&apos;re going to fail at it. Your design will be wrong, you will anticipate the wrong feature to be popular. Dealing with this quickly can set you apart. Being able to slap memcached into a bunch of objects in a few days (or even hours) can mean the difference between riding a load spike or riding the walrus.&lt;br /&gt;&lt;br /&gt;Bullet points for fun! How can your small site benefit from memcached:&lt;br /&gt;&lt;br /&gt;- Design for change! Holy crap I can&apos;t say this enough.&lt;br /&gt;- &lt;a href=&quot;http://dormando.livejournal.com/495593.html&quot; target=&quot;_blank&quot;&gt;Don&apos;t cache in ways that piss off your users.&lt;/a&gt;&lt;br /&gt;- Not keeping it simple is fail.&lt;br /&gt;- Cache and shard at the highest level possible relative to your data.&lt;br /&gt;- Read &lt;a href=&quot;http://www.amazon.com/High-Performance-MySQL-Optimization-Replication/dp/0596101716/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1218963235&amp;amp;sr=8-1&quot; target=&quot;_blank&quot;&gt;High Performance MySQL 2nd ed&lt;/a&gt;. Memcached won&apos;t fix your lack of database knowledge.&lt;br /&gt;- The same ideas which help you prepare for cache, helps you prepare for sharding.&lt;br /&gt;- Don&apos;t waste all your time getting it right now. Get it close, get an idea, try it out, and prepare to be wrong.&lt;br /&gt;&lt;br /&gt;Finally:&lt;br /&gt;&lt;br /&gt;- Keep an open mind. Sites like grazr and fotolog do things differently.  Doesn&apos;t mean they&apos;re right, doesn&apos;t mean they&apos;re wrong. Be inventive where it makes sense for your business.&lt;br /&gt;&lt;br /&gt;There. Sorry this came out so long :)</description>
  <comments>https://dormando.livejournal.com/496639.html?view=comments#comments</comments>
  <category>memcached</category>
  <category>scale</category>
  <category>mysql</category>
  <lj:security>public</lj:security>
  <lj:reply-count>7</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/495593.html</guid>
  <pubDate>Sun, 10 Aug 2008 02:14:58 GMT</pubDate>
  <title>Cache your sessions. Don&apos;t piss off your users</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/495593.html</link>
  <description>I hope you&apos;re all enjoying the &lt;a href=&quot;http://danga.com/memcached/news.bml&quot; target=&quot;_blank&quot;&gt;1.2.6&lt;/a&gt; stable release of memcached. Don&apos;t want to hear no whining about it crashing!&lt;br /&gt;&lt;br /&gt;One of the most common questions in memcached land is the ever obnoxious &quot;how do I put my sessions in memcached?&quot;. The long standing answer is usually &quot;you don&apos;t&quot;, or &quot;carefully&quot;, but people often walk the dark path instead. Many libraries do this as well, although I&apos;ve seen at least one which gets it.&lt;br /&gt;&lt;br /&gt;This isn&apos;t as huge of a deal as people make it out to be. I&apos;ve been asked about this over the mailing list, in IRC, in person, and even in job interviews. What people end up doing gives me the willies! Why! Why why why... Well, I know why.&lt;br /&gt;&lt;br /&gt;So what &lt;i&gt;is&lt;/i&gt; the deal with sessions? Why does everyone want to jettison them from mysql/postgres/disk/whatever? Well, a session is:&lt;br /&gt;&lt;br /&gt;- Almost always larger than 250 bytes, and almost always smaller than 5 kilobytes.&lt;br /&gt;- Read from datastore for every logged in (and often logged out) user for every dynamic page load.&lt;br /&gt;- Written &lt;i&gt;to&lt;/i&gt; the datastore for every dynamic page load.&lt;br /&gt;- Eventually reaped from the database after N minutes of inactivity.&lt;br /&gt;&lt;br /&gt;Ok well that sucks I guess. Every time a user loads a page we read a blob row from mysql, then write a blob row back. This is a lot slower than row without blobs. Alright, so I see it now. Memcached to the rescue!&lt;br /&gt;&lt;br /&gt;Er, except maybe it&apos;s a little complicated to actually memcached these things, since we need a write for every read... Why not &lt;b&gt;just&lt;/b&gt; use memcached for sessions!? It lines up perfectly! Check it out:&lt;br /&gt;&lt;br /&gt;- Set a memcached expire time for the max inactivity for a session. Say 30 minutes...&lt;br /&gt;- Read from memcached.&lt;br /&gt;- Write to memcached.&lt;br /&gt;- A miss from memcached means the user is logged out.&lt;br /&gt;&lt;br /&gt;Voila! ZERO reads or writes to the database, fantastic! Fast. Except I really don&apos;t like the tradeoffs here. This is one example where I believe the experience of both your users and your operations team is cheapened. Users now get logged out when &lt;i&gt;anything&lt;/i&gt; goes wrong with memcached! Operations has to dance on eggshells. Or needles. Painful.&lt;br /&gt;&lt;br /&gt;- Evictions are serious business. Even if you disable them (-M), out of memory errors means no one can log into your site.&lt;br /&gt;- Upgrading memcached, OS kernel, hardware, etc, now means kicking &lt;b&gt;everyone&lt;/b&gt; off your site.&lt;br /&gt;- Adding/removing memcached servers kicks people off your site. Even with consistent hashing, while the miss rate is low it&apos;s not going to be zero.&lt;br /&gt;&lt;br /&gt;So now what? Well we have zero accesses on our database, so it&apos;s fast! But we can&apos;t ever touch memcached again in fear of ticking off users. Progress be damned! Before you all think I&apos;m completely off my rocker, I will admit there are some legitimate reasons to do this. If the way your site works doesn&apos;t really impact users on loss of a session, or impacts few enough users, you can use this design pattern. How many people are actually affected if you get logged out of wikipedia.org? Well, the people writing revisions certainly mind, but the greater userbase is unaffected. They&apos;re a non profit, they understand the tradeoff, etc. So that&apos;s fine. It&apos;s not fine for a lot of the people I see suggesting it or doing it. As developers get more comfy with memcached the session issue will become more of an obvious bottleneck.&lt;br /&gt;&lt;br /&gt;The memcached/mysql hybrid really isn&apos;t that bad at all. You can get rid of over 90% of the database reads, a lot of the writes, and leave your users logged in during rolling upgrades of memcached.&lt;br /&gt;&lt;br /&gt;First, recap the components involved: The page session handler itself, and some batch job which reaps dead sessions. For small websites (like a vbulletin forum) these batch jobs are often run during page loads. For larger sites they will be crons and so forth. This batch job can also be used to save data about sessions for later analysis.&lt;br /&gt;&lt;br /&gt;The pattern is simple. For reads fetch from memcached first, database second. For writes write to memcached, unless you haven&apos;t synced the session to the database in the last N seconds. So if a user is clicking around they will only write to the database once every 120 seconds, and write to memcached every time.&lt;br /&gt;&lt;br /&gt;Now modify the batch job. Crawl all expired sessions, and check memcached for the latest data. If session is not really expired don&apos;t expire it then, if it is use the latest possible data from memcached. Write back to the database. Easy.&lt;br /&gt;&lt;br /&gt;You take the tradeoff of sessions being mildly lossy for recent information, but you gain reliability back in your system. Reads against the database should be almost nonexistent, and write load should drop significantly, but not as much as reads.&lt;br /&gt;&lt;br /&gt;So please, if you run some website I might eventually use, don&apos;t put memcached in a place where restarting individual servers might piss me off. Thanks :)&lt;br /&gt;&lt;br /&gt;I&apos;d like to also challenge maintainers of session libraries for all languages to turn this design pattern into tunable (note all the places where I wrote N) libraries folks can plug in and use.&lt;br /&gt;&lt;br /&gt;The more standard this stuff is the more likely the next fancy startup is going to get it right. Reuse is a great thing. I can&apos;t say enough about how great efforts like &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;krow&quot; lj:user=&quot;krow&quot; &gt;&lt;a href=&quot;https://krow.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://krow.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;krow&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&apos;s &lt;a href=&quot;http://tangent.org/552/libmemcached.html&quot; target=&quot;_blank&quot;&gt;libmemcached&lt;/a&gt; go for standardizing how we use memcached, but it&apos;s also a great help to ship libraries for common design patterns.</description>
  <comments>https://dormando.livejournal.com/495593.html?view=comments#comments</comments>
  <category>memcached mysql</category>
  <lj:security>public</lj:security>
  <lj:reply-count>6</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/488994.html</guid>
  <pubDate>Sun, 11 May 2008 23:03:14 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL R6</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/488994.html</link>
  <description>&lt;a href=&quot;http://dormando.livejournal.com/tag/dpm&quot; target=&quot;_blank&quot;&gt;Previously&lt;/a&gt;.&lt;br /&gt;As usual, hit up &lt;a href=&quot;http://consoleninja.net/code/dpm&quot; target=&quot;_blank&quot;&gt;the homepage&lt;/a&gt; for the latest and greatest downloads. Or simply &apos;git pull&apos; and use the tag release-6 if you&apos;re cool enough.&lt;br /&gt;&lt;br /&gt;I&apos;d like to use this post to explain in a more general fashion about what DPM is and why it&apos;s different from &lt;a href=&quot;http://krow.livejournal.com/595518.html&quot; target=&quot;_blank&quot;&gt;the rest of the proxies&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First, milestones since R5:&lt;br /&gt;- BSD licensed. You are now free to roam about the cabin.&lt;br /&gt;- Several C level bugs fixed.&lt;br /&gt;- Many improvements to the lua library dpml.lua&lt;br /&gt;- All of the demos were rewritten using dpml.lua, and are now far easier to use.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Now, what is DPM?&lt;/h3&gt;&lt;br /&gt;- It&apos;s a proxy for MySQL. It is event driven, embeds lua, and is written in C. It allows you to write plugins in lua, and easily extend the core with extra C.&lt;br /&gt;- It supports a subset of the MySQL protocol&apos;s features, and offers a very flexible view into the protocol.&lt;br /&gt;- Fast. The C part takes very little processing time. The more you do in lua the slower it will get. Identify the hot spots and rewrite them in C: The lua&apos;s there for a good reason.&lt;br /&gt;- Supports MySQL&apos;s authentication for 4.1+ client/servers. Authenticate to it like a real database.&lt;br /&gt;- Supports arbitrary timer objects. Kick off events with millisecond granularity.&lt;br /&gt;- Very flexible. DPM&apos;s API intends to be a swiss army chainsaw for MySQL. You can do things in lua which will require extensive amounts of C in other proxies.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;What DPM is not:&lt;/h3&gt;&lt;br /&gt;- A fork of &lt;a href=&quot;http://forge.mysql.com/wiki/MySQL_Proxy&quot; target=&quot;_blank&quot;&gt;MySQL Proxy&lt;/a&gt;. DPM was started during a short period of time when MySQL Proxy was closed source software. I started it from scratch, and have done most of the development myself. It has since taken a similar but different direction.&lt;br /&gt;- Super easy to use. It is missing lots of documentation and is harder to use than MySQL Proxy. As of R6 this is starting to improve... The demos are much easier to work with.&lt;br /&gt;- Controlled by MySQL AB (now Sun Microsystems). I&apos;ll leave it to the reader on whether this is good, or bad. DPM is a community project. Fork it with &lt;a href=&quot;http://github.com&quot; target=&quot;_blank&quot;&gt;github&lt;/a&gt; and send me patches.&lt;br /&gt;- Brand new. I&apos;ve been hacking on it on and off since May of 2007. Most of the features that exist have been supported for over six months.&lt;br /&gt;- Enterprise ready. Plop it in a non criticial place until we get more of the bugs out of it. Monitoring, benchmarking, etc are great starting places.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Okay, so it uses libevent, lua, written in C... Why is it different?&lt;/h3&gt;&lt;br /&gt;- Flexibility is the big deal. Being able to do as much evil as possible from within lua was the goal off the bat.&lt;br /&gt;- Instead of writing library functions in C, the lua API is low level, and a lua library (dpml.lua) is distributed with it, containing handy functions and wrappers. Quick prototyping of library functions, and hot spots can be rewritten in C.&lt;br /&gt;- Facilitates use of lua libraries with a flexible callback structure.&lt;br /&gt;- Almost every high level concept is a lua object or command. Listeners, logical protocol packets, etc. This takes a little explanation.&lt;br /&gt;- DPM is acts like mod_perl, while MySQL Proxy is more like mod_php. Once the startfile is loaded and running, DPM will not automatically reload anything.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Who are you, anyway?&lt;/h3&gt;&lt;br /&gt;- I&apos;m Dormando! Well not really, but it&apos;s easier to remember than my real name.&lt;br /&gt;- I work for &lt;a href=&quot;http://www.sixapart.com&quot; target=&quot;_blank&quot;&gt;Six Apart&lt;/a&gt; as a MySQL DBA.&lt;br /&gt;- I have done systems, DBA, and scalability work for a bunch of huge sites (livejournal.com, gaiaonline.com, and a few others).&lt;br /&gt;- I ride &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;brad&quot; lj:user=&quot;brad&quot; &gt;&lt;a href=&quot;https://brad.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://brad.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;brad&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&apos;s coattails of success, and am presently the release maintainer for memcached. I also answer mail for, hack on, and help run instances of perlbal, mogilefs, etc. While I&apos;m not a genius by any stretch, it means I have scalability and flexibility absolutely in mind while writing this tool, and plenty of experience to draw on.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Using DPM&lt;/h3&gt;&lt;br /&gt;This means DPM has a concept of a &quot;startup&quot; file:&lt;br /&gt;&lt;code&gt;./dpm --startfile yourproject.lua&lt;/code&gt;&lt;br /&gt;... and this file initializes all of the functionality your running DPM instance will have. This is the only argument you actually need to start up. No specifying listening sockets, admin panels, ports, etc.&lt;br /&gt;&lt;br /&gt;So, how does one specify what port and IP&apos;s to listen on? Lets look:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;listen = dpm.listener(&quot;127.0.0.1&quot;, 5500)&lt;br /&gt;listen:register(dpm.MYC_CONNECT, new_client&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This creates, _at runtime_, a listening socket on localhost, port 5500. When a mysql client connects to it, the function &apos;new_client&apos; will be called. At that point you can either pass the client through to a mysql server (see &lt;a href=&quot;http://consoleninja.net/gitweb/gitweb.cgi?p=dpm.git;a=blob;f=lua/demo-direct.lua;hb=HEAD&quot; target=&quot;_blank&quot;&gt;demo-direct.lua&lt;/a&gt;), or handle the client yourself and do connection pooling, load balancing, or implement your own service (see &lt;a href=&quot;http://consoleninja.net/gitweb/gitweb.cgi?p=dpm.git;a=blob;f=lua/startup.lua;hb=HEAD&quot; target=&quot;_blank&quot;&gt;startup.lua&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Weird, right? Complicated too. You have to set up callbacks and wingwangs just to get clients authenticated, or passed through to a backend server.&lt;br /&gt;&lt;br /&gt;This is where the DPML library comes in. If you check out the bottom of demo-direct.lua and startup.lua you&apos;ll see we pass the listener object into a convenience function.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;dpml.passthrough_new_clients(listen, { new_client = new_client, &lt;br /&gt;                             new_command = new_command,&lt;br /&gt;                             finished_command = finished_command,&lt;br /&gt;                             closing = closing_client, },&lt;br /&gt;                             { &quot;127.0.0.1&quot;, 3306 })&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This takes care of the nitty gritty and allows us to just focus on, say, a &apos;new_command&apos; function. Now we&apos;re getting back to the usability of MySQL Proxy a little. Want to have your scripts automatically reload? Write a dpml library to dynamically load, or have a timed reload of, your new_command function from a file. Then everyone can do it easily, but serious users won&apos;t lose the speed from having to work around script caches.&lt;br /&gt;&lt;br /&gt;Neat. Now, it should be obvious what other sorts of evil we can do here.&lt;br /&gt;&lt;br /&gt;Since listeners are arbitrary, and you specify the exact callback function, you can easily have _one_ DPM instance run completely different code on different ports, IP&apos;s, etc. Make your plugins modular, and load a bunch of them at once for some fun.&lt;br /&gt;&lt;br /&gt;How does DPML manage to do some of the things it does?&lt;br /&gt;&lt;br /&gt;When you have a client or server connection object, you may register your own event callbacks on them. Watch for commands, parse resultsets, etc. Lets say you want parse results of queries in your own special way, but sometimes you just want to grab some easy results. So you have something like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;server:register(dpm.MYS_SENT_RSET = read_rset_packet)&lt;br /&gt;[... etc]&lt;br /&gt;dpml.execute_query_buffered(server, &quot;SELECT 1 + 5&quot;, read_results)&lt;/code&gt;&lt;br /&gt;... execute_query_buffered has to use its _own_ set of callbacks to make this work! What happened to yours?&lt;br /&gt;&lt;br /&gt;They step aside for a minute, actually.&lt;br /&gt;&lt;code&gt;local br_callbacks = dpm.new_callback()&lt;br /&gt;register_callbacks(br_callbacks, {&lt;br /&gt;                   [dpm.MYS_SENT_RSET]      = br_read_rset,&lt;br /&gt;                   [dpm.MYS_SENDING_FIELDS] = br_read_fields,&lt;br /&gt;                   [dpm.MYS_SENT_FIELDS]    = br_read_endfields,&lt;br /&gt;                   [dpm.MYS_SENDING_ROWS]   = br_read_rows,&lt;br /&gt;                   [dpm.MYS_WAIT_CMD]       = br_read_finish,&lt;br /&gt;                   [dpm.MYS_RECV_ERR]       = br_read_error,&lt;br /&gt;                   })&lt;br /&gt;[... etc]&lt;br /&gt;server:package_register(br_callbacks)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Any library function, in any file, may temporarily override the callbacks on a client or server object with its own. package_register is a very fast operation, and you&apos;d want to keep your callback object cached somewhere. Now you can have your own processing, but temporarily pass connections over to completely different callbacks. This is limited by a single depth; a library function cannot call another library function and expect to not have its package callbacks overridden.&lt;br /&gt;&lt;br /&gt;This means you may safely use most of dpml&apos;s functionality from your own scripts to help make your life easier. It&apos;s also trivial for anyone to start their own library and contribute it back, or maintain on their own.&lt;br /&gt;&lt;br /&gt;Timers are also way cool, but they were described in a &lt;a href=&quot;http://dormando.livejournal.com/483244.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The polling multiplexer&lt;/h3&gt;&lt;br /&gt;With R6 all of the included demos were rewritten, and a new plugin has been started. Something more complete and useful: A &lt;a href=&quot;http://consoleninja.net/gitweb/gitweb.cgi?p=dpm.git;a=blob;f=lua/multiplex.lua;hb=HEAD&quot; target=&quot;_blank&quot;&gt;query multiplexer&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I wrote this in bits over the last two days, and it&apos;s not complete, but it is functional, and very cool. The concept of the multiplexer is that DPM can send queries to many servers, asyncronously, in parallel, and aggregate the results for you. With only lua. If the code were made to be more verbose and less readable it could take up a lot less CPU time, but as-is it&apos;s not bad at all.&lt;br /&gt;&lt;br /&gt;First, lets connect and say hello:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;mysql&amp;gt; hello;&lt;br /&gt;ERROR 666 (66666): Multiplexer does not understand.&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Rude. When we talk to the multiplexer, we are never directly talking to a MySQL database. We&apos;re in the rudimentary command parser supplied with it. I also like making up error codes that are clearly wrong.&lt;br /&gt;&lt;br /&gt;Lets do something more useful:&lt;br /&gt;&lt;code&gt;mysql&amp;gt; ADD DB host=127.0.0.1,user=happy,pass=wheefun;&lt;br /&gt;Query OK, 1 row affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; SHOW DB STATUS;&lt;br /&gt;+-----------+--------+-------+-------------+---------+---------+&lt;br /&gt;| DSN       | active | queue | disconnects | retries | queries |&lt;br /&gt;+-----------+--------+-------+-------------+---------+---------+&lt;br /&gt;| 127.0.0.1 | 1      | 0     | 0           | 0       | 0       | &lt;br /&gt;+-----------+--------+-------+-------------+---------+---------+&lt;br /&gt;1 row in set (0.00 sec)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Spiffy! We&apos;ve added a single DB to the service, and it has successfully connected to said backend. We can also remove it again with &quot;REMOVE DB 127.0.0.1&quot; - note that DPM doesn&apos;t presently support asyncronous DNS lookups, so we have to use IP addresses for the time being.&lt;br /&gt;&lt;br /&gt;Now we use a really awful syntax:&lt;br /&gt;&lt;code&gt;mysql&amp;gt; POLL ALL 5 SHOW DATABASES;       &lt;br /&gt;Query OK, 1 row affected (0.00 sec)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&quot;ALL&quot; specifies a collection of servers, which is not currently supported. The &apos;5&apos; would be a timeout, which is also not presently supported. They are reserved for now.&lt;br /&gt;... If we had a bunch of databases (and I have tested it with 10+), that would have sent the command &quot;SHOW DATABASES&quot; to _all_ of them. It will not wait for the response before returning though. While we can, we don&apos;t need to here. Why? Because we can do this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;mysql&amp;gt; POLL ALL 5 SHOW FULL PROCESSLIST;&lt;br /&gt;Query OK, 1 row affected (0.00 sec)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;... now we have two commands in flight. Well, they&apos;re probably finished by now, but you get the point. We can connect, fire off 5+ commands to run on all of our DB&apos;s, and go to sleep for a while.&lt;br /&gt;&lt;br /&gt;Then:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;mysql&amp;gt; FETCH ALL SHOW DATABASES;&lt;br /&gt;+-----------+--------------------+&lt;br /&gt;| dpm_dsn   | Database           |&lt;br /&gt;+-----------+--------------------+&lt;br /&gt;| 127.0.0.1 | information_schema | &lt;br /&gt;| 127.0.0.1 | docpart            | &lt;br /&gt;| 127.0.0.1 | mfs                | &lt;br /&gt;| 127.0.0.1 | mysql              | &lt;br /&gt;| 127.0.0.1 | sakila             | &lt;br /&gt;| 127.0.0.1 | test               | &lt;br /&gt;| 127.0.0.1 | world              | &lt;br /&gt;+-----------+--------------------+&lt;br /&gt;7 rows in set (0.00 sec)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;... we get our responses back. DPM has kindly rewritten the results so we can see which server sent which data. Now we can fetch all of the results from the last round, fire off new POLL queries, and go parse what we just got.&lt;br /&gt;&lt;br /&gt;This is a big help for monitoring the databases at &lt;a href=&quot;http://www.sixapart.com&quot; target=&quot;_blank&quot;&gt;Six Apart&lt;/a&gt;. The script will get better over time, and will be perfect to plug into, say, &lt;a href=&quot;http://innotop.sf.net&quot; target=&quot;_blank&quot;&gt;Innotop&lt;/a&gt;, for its querying of groups of servers.&lt;br /&gt;&lt;br /&gt;Lots of features/fixes possible, but as is it works fine. I&apos;ve not made significant changes to the C or lua API (aside from bugfixes) in order to write this. I&apos;ve used the same API as existed five+ months ago.&lt;br /&gt;&lt;br /&gt;Download it, give it a shot, and let me know what you think!&lt;br /&gt;&lt;br /&gt;If you think it&apos;d be awesome to get to play with this in production, and use other fancy danga tools to help maintain one of the coolest web infrastructures, &lt;a href=&quot;http://www.sixapart.com/about/jobs/2008/04/senior-systems-administrator.html&quot; target=&quot;_blank&quot;&gt;We&apos;re looking for Senior Sysadmins :)&lt;/a&gt; There are also &lt;a href=&quot;http://www.sixapart.com/about/jobs/&quot; target=&quot;_blank&quot;&gt;a bunch of other jobs available&lt;/a&gt; if you want to take a look.</description>
  <comments>https://dormando.livejournal.com/488994.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/487758.html</guid>
  <pubDate>Sat, 19 Apr 2008 10:04:26 GMT</pubDate>
  <title>Silly update to Dormando&apos;s Proxy for MySQL</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/487758.html</link>
  <description>If you &lt;a href=&quot;http://consoleninja.net/code/dpm/&quot; target=&quot;_blank&quot;&gt;fetch the latest snapshot&lt;/a&gt; you&apos;ll find the code has been relicensed under BSD.&lt;br /&gt;&lt;br /&gt;Huh. Wonder what you could do with that :)</description>
  <comments>https://dormando.livejournal.com/487758.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/485610.html</guid>
  <pubDate>Tue, 04 Mar 2008 09:58:47 GMT</pubDate>
  <title>Memcached 1.2.5</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/485610.html</link>
  <description>This is absolutely the &lt;a href=&quot;http://lists.danga.com/pipermail/memcached/2008-March/006532.html&quot; target=&quot;_blank&quot;&gt;most awesome release of memcached ever&lt;/a&gt;, and it has absolutely nothing to do with me being a patchmonkey for it.&lt;br /&gt;&lt;br /&gt;Trees are stabilizing and people are getting to work. Fun and exciting things to come :)&lt;br /&gt;&lt;br /&gt;Kudos to MySQL and Sun (like &lt;span  class=&quot;ljuser  i-ljuser  i-ljuser-type-P     &quot;  data-ljuser=&quot;krow&quot; lj:user=&quot;krow&quot; &gt;&lt;a href=&quot;https://krow.livejournal.com/profile/&quot;  target=&quot;_self&quot;  class=&quot;i-ljuser-profile&quot; &gt;&lt;img  class=&quot;i-ljuser-userhead&quot;  src=&quot;https://l-stat.livejournal.net/img/userinfo_v8.png?v=17080&amp;v=924&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://krow.livejournal.com/&quot; class=&quot;i-ljuser-username&quot;   target=&quot;_self&quot;   &gt;&lt;b&gt;krow&lt;/b&gt;&lt;/a&gt;&lt;a class=&quot;i-ljuser-badge i-ljuser-badge--pro&quot; data-badge-type=&quot;pro&quot; data-placement=&quot;bottom&quot; data-pro-badge data-pro-badge-type=&quot;1&quot; data-is-raw hidden href=&quot;#&quot;&gt;&lt;span class=&quot;i-ljuser-badge__icon&quot;&gt;&lt;svg class=&quot;svgicon&quot; width=&quot;25&quot; height=&quot;16&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 33 24&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M19.326 11.95c0 2.01 1.47 3.45 3.48 3.45 2.02 0 3.49-1.44 3.49-3.45 0-2.01-1.47-3.45-3.49-3.45-2.01 0-3.48 1.44-3.48 3.45Zm5.51 0c0 1.24-.8 2.19-2.03 2.19-1.23 0-2.02-.95-2.02-2.19 0-1.25.79-2.19 2.02-2.19s2.03.94 2.03 2.19ZM7.92 15.28H6.5V8.61h3.12c1.45 0 2.24.98 2.24 2.15 0 1.16-.8 2.15-2.24 2.15h-1.7v2.37Zm1.51-3.62c.56 0 .98-.35.98-.9 0-.56-.42-.9-.98-.9H7.92v1.8h1.51ZM18.3802 15.28h-1.63l-1.31-2.37h-1.04v2.37h-1.42V8.61h3.12c1.39 0 2.24.91 2.24 2.15 0 1.18-.74 1.81-1.46 1.98l1.5 2.54Zm-2.49-3.62c.57 0 1-.34 1-.9s-.43-.9-1-.9h-1.49v1.8h1.49Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2 8c0-2.20914 1.79086-4 4-4h20.5c2.2091 0 4 1.79086 4 4v7.9c0 2.2091-1.7909 4-4 4H6c-2.20914 0-4-1.7909-4-4V8Zm4-2.5h20.5C27.8807 5.5 29 6.61929 29 8v7.9c0 1.3807-1.1193 2.5-2.5 2.5H6c-1.38071 0-2.5-1.1193-2.5-2.5V8c0-1.38071 1.11929-2.5 2.5-2.5Z&quot; clip-rule=&quot;evenodd&quot;/&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;) for jumping in on the fun.&lt;br /&gt;&lt;br /&gt;Next up: 1.3.0-rc. Time to start hammering down the binary protocol before it wanders off even more.&lt;br /&gt;&lt;br /&gt;I&apos;m up way too late :) Praise insomnia!</description>
  <comments>https://dormando.livejournal.com/485610.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/484577.html</guid>
  <pubDate>Mon, 04 Feb 2008 06:23:23 GMT</pubDate>
  <title>Dormando&apos;s [crappy] Operations Mantras</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/484577.html</link>
  <description>Ops Mantras (as made popular by Dormando). &lt;br /&gt;I&apos;ve been doing this shit for a while now. I&apos;m presently acting as a MySQL DBA for &lt;a href=&quot;http://www.sixapart.com&quot; target=&quot;_blank&quot;&gt;SixApart&lt;/a&gt;, but these views are mine and not of my employer. This is an omega post of all of the generalized one off mantras I find valuable when approaching operations management. Even if these end up being idealistic, my humble view is to shoot for these and you&apos;ll be better off with what you end up with.&lt;br /&gt;&lt;br /&gt;It&apos;s uh, long, sorry. This _was_ inspired by another post which I&apos;ll not be direct linking. Aside from the list-style, I&apos;ve not stolen anything else. The Mantras are broken up into major sections:&lt;br /&gt;- The Technical Element&lt;br /&gt;- The Human Element&lt;br /&gt;- The Practice&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;The Technical Element&lt;/h1&gt;&lt;br /&gt;&lt;h2&gt;Design for change&lt;/h2&gt;&lt;br /&gt;- The old google mantra is right. Design for change. Change is having to deploy new software, upgrade existing software, scaling, equipment breaking, and people shifting around.&lt;br /&gt;- Everything in this mantra is about finding balance. You might think it&apos;s a good idea to tightly marry your system to a particular OS or Linux distro.  It&apos;s just as bad of an idea to separately them entirely. Use layers and a _little_ indirection if you must.&lt;br /&gt;- This does not mean complete and total platform agnostics. It&apos;s about making one system two, two systems twenty. Dealing if a sysadmin gets hit by a bus, if that dangling harddrive dies, if someone runs rm -rf /. It&apos;s for the incremental changes. Security updates, pushing new corporate content.&lt;br /&gt;&lt;h2&gt;Use automatic, repeatable builds&lt;/h2&gt;&lt;br /&gt;- Don&apos;t build anything by hand. If you do, do it twice, and grab every single command the second time around.&lt;br /&gt;- I cannot stress how important this is. It should take no more than 15 minutes from bare metal to production for new hardware. There does not need to be a human element to screw it up, or get punished when a server goes down and no one knows how to replace it.&lt;br /&gt;- This is true for anything. There is _no_ such thing as a &quot;one off&quot; server build. If you&apos;ve built it once, and it only needs to exist once, it will exist twice. The second time around is when it breaks, or if you need to do a major upgrade or consolidation two years down the road and have no frickin&apos; clue how it was put together.&lt;br /&gt;- Test, vet new builds. This should be easy because your builds are all automatic, correct?&lt;br /&gt;- Scripted builds means that upgrade from Linux Distro Version 3 to Version 4 is absolutely clear cut. Install Version 4 and test the scripts. Read documentation and fix until it works again. This should be a week&apos;s worth of work at most, not a yearlong project. (to finish just in time for Version 5 to come out!).&lt;br /&gt;&lt;h2&gt;Use redundancy&lt;/h2&gt;&lt;br /&gt;- Just beacuse something might be easy to rebuild, doesn&apos;t mean you can ignore redundancy. Jump boxes, mail servers, billing gateways, whatever. Wouldn&apos;t it be a hell of a lot easier if you could swap out one half of the equation without causing downtime for your customers?&lt;br /&gt;- ... and along those lines, you get to &quot;deal with it later!&quot; when a box goes down at 3am, and the redundant machine kicks in.&lt;br /&gt;- Even if it&apos;s not ideal, go for it anyway. Rsync&apos;ing configs to a second box is a step above nothing. &lt;a href=&quot;http://www.drbd.org&quot; target=&quot;_blank&quot;&gt;DRBD&lt;/a&gt; might not be perfect but it can provide an amazing service.&lt;br /&gt;&lt;h2&gt;Use backups&lt;/h2&gt;&lt;br /&gt;- We shouldn&apos;t even joke about this. Use harddrives, burn the tapes. Compress them, move them, run them in parallel. Backup EVERYTHING!&lt;br /&gt;- If your builds are automatic, the entire process can be backed up. If you&apos;re following along past this point a *real* Disaster Recovery plan might not seem so far fetched.&lt;br /&gt;&lt;h2&gt;Keep monitoring specific&lt;/h2&gt;&lt;br /&gt;- Monitor every damn thing you can, but do it right. Don&apos;t get a thousand alerts if your NFS server craps its pants. Don&apos;t alert on timeouts if it doesn&apos;t make sense for your system. Test for success at the most specific level; sure the service might allow a new TCP connection, and it might even say hello, but does it remember how to do its job?&lt;br /&gt;- If you have 500 webservers, you probably don&apos;t need to know immediately if one goes down. You _should_ know if the load balancer decided to not take it out of rotation and real human people are seeing its uglyriffic error messages.&lt;br /&gt;&lt;h2&gt;Graph data, keep exact historical data&lt;/h2&gt;&lt;br /&gt;- Graphs are for visualizing trends. Historical data is for crunching numbers.  Don&apos;t mix the two! It&apos;s too easy to get wrong numbers from eyeballing graphs. Many sites use rrd&apos;s or other aggregating data systems which will average and smooth out data over time to save on storage space. This means it&apos;s not only hard to read, it&apos;s wrong.&lt;br /&gt;- Don&apos;t get trapped having to skim through hundreds of graphs just to pinpoint an issue. If you&apos;re trying to find outliers in the graphs, you can pull those out via scripts as well.&lt;br /&gt;- If you must use graphs for troubleshooting, try to aggregate high level concepts into a single page, which link into drill-down pages from there. If you can see a spike in the database load, you&apos;ll know to click to the page overviewing the databases, then you&apos;d see the one or two iffy machines in question. The idea is to narrow something down fast. Remove as much guesswork as possible.&lt;br /&gt;&lt;h2&gt;Log useful information, use multiple streams of data&lt;/h2&gt;&lt;br /&gt;- Work on your own, and with development, to log as much useful information as you can. Doesn&apos;t matter if you live analyze it and store the data somewhere, or lump it into a database and run reports. Information is useful.&lt;br /&gt;- Useful examples: Page rendering time (what page, what box, etc), user-facing errors, database and internal service errors, bandwidth usage, etc. &lt;br /&gt;- Establish graphs, reports, and do historical comparisons from generalized data.&lt;br /&gt;- Reports are really important. Get digested data week-to-week or day-to-day about changes in your infrastructure.&lt;br /&gt;&lt;h2&gt;Understand your data storage, databases&lt;/h2&gt;&lt;br /&gt;- There&apos;s an entirely separate set of undrestanding about operating databases, but sometimes you can&apos;t leave all of this up to your DBA.&lt;br /&gt;- Having multiple, redundant databases affords you many luxuries. Operations that were once many hours of downtime can be done &quot;online&quot; without shelling out for a huge Oracle instance. MySQL and replication is a fantastic thing.&lt;br /&gt;- Work with the DBAs to get the best possible hardware for the database in question. RAID10, gobs of RAM, many fast spindles, and potentially RAM disks and SSD&apos;s. Ops has access to the vendors, DBA&apos;s can beat the pants off the hardware. Find out what works best and save tons of cash in the long run.&lt;br /&gt;- Database configurations are changing. Software like &lt;a href=&quot;http://www.hivedb.org&quot; target=&quot;_blank&quot;&gt;HiveDB&lt;/a&gt;, MySQL Proxy, &lt;a href=&quot;http://consoleninja.net/code/dpm&quot; target=&quot;_blank&quot;&gt;DPM&lt;/a&gt; exist now. We&apos;re absolutely doing partitioned data for huge datasets. We&apos;re also thinking outside of the box with software like &lt;a href=&quot;http://rubyforge.org/projects/starling/&quot; target=&quot;_blank&quot;&gt;starling&lt;/a&gt; and &lt;a href=&quot;http://danga.com/gearman&quot; target=&quot;_blank&quot;&gt;Gearman&lt;/a&gt;. Learn what these are, and understand that not everything will be in a database.&lt;br /&gt;- Get a good grip on your filers! If the data&apos;s important, back it up! Snapshots on monolithic NFS servers are fantastic, wonderful, and NOT a backup!&lt;br /&gt;- Consider alternatives. &lt;a href=&quot;http://danga.com/mogilefs&quot; target=&quot;_blank&quot;&gt;MogileFS&lt;/a&gt; gets better year after year. There&apos;re likely other projects for freely and cheaply maintaining massive stores of files. Similar systems were developed for youtube.com, archive.org, etc. We&apos;re finally free of expensive NFS filers being the standard!&lt;br /&gt;&lt;h2&gt;Scale out a lot, up a little&lt;/h2&gt;&lt;br /&gt;- You&apos;ve seen all of the papers. Scale out is really the way to go. Get commodity (read: available, affordable, standard, NOT super cheap) hardware and work with everyone to ensure all aspects possible can scale out.&lt;br /&gt;- Scaling out starts at two, work from there. This also happens to encompass redundancy.&lt;br /&gt;- Scale out as far as you can without being idiotic about it. The example of MySQL replication with single master, many slaves, is a fantastic example of one form of scale-out sucking. All slaves must do all writes, so as the number of writes scale up with the reads (if they do for your app, which I bet they certainly do), you get less capacity per slave you add.&lt;br /&gt;- Keep alternatives in mind. User or range partitioning onto many databases, avoiding production slaves where possible, etc. Good ideas, many ways of implement.&lt;br /&gt;- Everything can scale if you give it a chance! Routers, switches, load balancers, webservers, databases.&lt;br /&gt;- Remember scale up? Big evil machines with many slow cores, lots of IO boards, and very expensive storage equipment? They&apos;re coming back. Well, the CPU part is.&lt;br /&gt;- RAM is cheap.&lt;br /&gt;- Combine the two, and you just may end up combining services again. A load balancer here, a webserver there... If an application can use many CPUs (apache) this is perfect. If it can&apos;t (memcached doesn&apos;t get much benefit from it, usually) you can end up wasting tons of available resources by segregating services too much.&lt;br /&gt;- Job systems could potentially fill in gaps here. Where there&apos;re extra cores, slap up more workers.&lt;br /&gt;&lt;h2&gt;Cache&lt;/h2&gt;&lt;br /&gt;- Caching is good. Developers, sysops, etc. Get on this! Yes, it&apos;s weird. It&apos;s different. Sometimes you may even need to, gasp, make a tradeoff for it.  Effective use of caching can have as much as a ten times increase in overall system performance. That&apos;s a giant magnifying glass over the systems you have already and a fraction of the overall cost.&lt;br /&gt;- Memcached. Service cache, denormalize DB structures (where it makes performance sense!), squid cache, or even make better usage of OS caches.&lt;br /&gt;- Test it, toy with it, and break it. There will be new and different problems with caching. Be prepared for it.&lt;br /&gt;&lt;h2&gt;Asyncronous jobs&lt;/h2&gt;&lt;br /&gt;- Starling, Gearman, The Schwartz, whatever. Job systems allow much more application flexibility. Workers can be spawned one-off, be persistent (load cached data, prepare data, etc), be on different hardware, different locations, and be syncronous or asyncronous.&lt;br /&gt;- Maintaining these things is an ops issue. Using them is both a developer and an ops issue.&lt;br /&gt;- User clicks &quot;send all my friends an e-mail&quot;. Schedule a job, immediately say &quot;okay done! Your friends will receive your spam shortly!&quot; - let the job service multiplex and deal with the issue.&lt;br /&gt;- Job systems are great places to bridge services. Blog post -&amp;gt; IM notification, billing cron -&amp;gt; billing services, authentication gateways, etc.&lt;br /&gt;- Easy to scale. There will be choke points for where requests come in, and all the workers need to do is pull. This is in contrast with the largely push/pull state of HTTP.&lt;br /&gt;&lt;h2&gt;Security and patrols&lt;/h2&gt;&lt;br /&gt;- Install security updates! Seriously! There&apos;s a whole crazy network of people who are dedicated to giving these to you in the shortest period of time possible. Don&apos;t let them sit for _years_ because you&apos;re afraid of change.&lt;br /&gt;- Security is in layers. Accept what you can and cannot secure. Just because mysql has password access doesn&apos;t mean it gets to be directly accessable by the internet.&lt;br /&gt;- Disable passwords over ssh. Use passphrase encrypted key auth. Remote users _cannot_ guess your private key. They _have_ to get it from you. Keep it safe, and there&apos;s no point in firewalling off your ssh port.&lt;br /&gt;- Understand how the application works, exactly what it needs to do, and work that to your advantage. If the only part of your application which needs outbound internet access _at all_ are the billing pages and some twitter-posting service, those can easily become job workers. Put the job workers on specific boxes and allow those access to specific hosts. Keep the rest of your network in the dark.&lt;br /&gt;- The above is especially important for php sites, but probably works great elsewhere. If someone breaks in, it&apos;s most likely going to be through your application. When someone gets in through the front gate, they&apos;ll need to haul in their toolbox to get into the safe. Don&apos;t let them pull in data and get what they need, or upload the contents of your database somewhere!&lt;br /&gt;- These specific suggestions aside, read a lot. Use your best judgement, and test. If you have no understanding of how a security model works, that might not immediately make it worthless, but you certainly don&apos;t know where its limits are or even if it works.&lt;br /&gt;- Secure based on testing, theory, attack trees, don&apos;t stab in the dark. I love it when people dream up obscure security models and ordinary folks like me can smush it to crumbles.&lt;br /&gt;- Patrol what you can! Audit logins, logouts, commands used. All accesses to external facing services, including all arguments given in the request. Find outliers, outright ban input outside of the scope of your application, and do what you can actively and have the data to work retroactively.&lt;br /&gt;- If you suspect something&apos;s been cracked, *take proper precaution* and understand a little computer forensics (or get a company that does). Respond by removing network access, checking the system through serial console or direct terminal, and avoiding using any service, config file, or data on the compromised machine. Too many people &quot;clean up a trojan&apos; and never understand how it got there, or if they&apos;ve _actually cleaned it up_.&lt;br /&gt;- If you do have a security team, forensics expert, or anyone else onhand, you must touch the machine as little as possible and isolate it. This means not rebooting it to &quot;clear out some funky running processes&quot;. They need to be able to get at those. If you need to half ass it, go ahead, but remember to wipe the system completely clean, apply any security updates, and do your best to figure out if they&apos;ve compromised any important data. Do what you can.&lt;br /&gt;- Security is an incredible balancing act. If you do it wrong, developers, users, etc, will revolt and find ways around it. If they _can_ get around it, you&apos;re not doing your job right. If they _can&apos;t_ get around it, they might just give up and leave.&lt;br /&gt;- Keep an iron grip on access control. This means ops must absolutely provide windows for what doors have been locked. Kicking development off of production entirely means they get to stab in the dark on fixing hard problems. Providing logging, debugging tools, etc, without allowing them to directly change the service, will be a win for all aspects of production.&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;The Human Element&lt;/h1&gt;&lt;br /&gt;&lt;h2&gt;Learn from many sources&lt;/h2&gt;&lt;br /&gt;- Fill up some RSS feeds, and read at least a few good articles per week. LWN, kerneltrap, undeadly.org, whatever&apos;s relevant, or even loosely related, to what you do.&lt;br /&gt;- Read blogs from smart people. Sometimes they post interesting topics, and comment streams give us the unique ability to directly converse with the masters.&lt;br /&gt;- Read a few blogs from not so smart people. Get a feel for what stumps them, or what they do that doesn&apos;t work so well.&lt;br /&gt;- Get to know people who can kick your ass, at anything. Stay humble.&lt;br /&gt;- Help find your own strengths by taking in from many sources, and gobbling up what envigorates you.&lt;br /&gt;- Read up on success and failure stories from other companies. Ring up their CTO&apos;s and get them to divulge advise over free lunch.&lt;br /&gt;&lt;h2&gt;Try many things&lt;/h2&gt;&lt;br /&gt;- You&apos;ll be amazed at what you can do if you keep trying. Never seen something before? Give it a shot.&lt;br /&gt;- Try to not be a dangerous newbie. Play in the sandbox until you&apos;re comfortable enough to not burn down the house.&lt;br /&gt;&lt;h2&gt;Understand redundancy&lt;/h2&gt;&lt;br /&gt;- Really understand how redundancy affects things. How it works, how it doesn&apos;t work.&lt;br /&gt;- Break redundant systems in a test lab, sometimes in production. Learn what you can while you&apos;re in control. Unplug the power, yank cards out, kill processes, run the box out of memory, yank a harddrive, yank ethernet.&lt;br /&gt;- Test replacing and upgrading systems in a redundant setup. Maybe you can toss in that brand new hal-o-tron 8000 without taking downtime.&lt;br /&gt;&lt;h2&gt;Understand scalability&lt;/h2&gt;&lt;br /&gt;- There&apos;re tons of papers on making scalable systems. Even if you can&apos;t write one yourself, try to understand the theory.&lt;br /&gt;- Learn with virtualization. Set up a few virtual machines and try tossing up applications to multiple machines. Run multiple instances locally on different ports.&lt;br /&gt;- It&apos;s usually the job of operations to do proper capacity planning. You won&apos;t know what to do add unless you truely understand where resources should be added.&lt;br /&gt;&lt;h2&gt;Become a troubleshooting superstar&lt;/h2&gt;&lt;br /&gt;- The moment something breaks the clock is ticking. You must be able to pull out your arsenal and use them effectively.&lt;br /&gt;- Practice troubleshooting. Pick a perfectly good, working page, and try to track down how it works.&lt;br /&gt;- strace, ltrace, lsof, logs.&lt;br /&gt;- Understand that load != load. Look at all available information as to how a host is performing or behaving.&lt;br /&gt;- Be very familiar with the tools for your IO system. Often &quot;mysterious&quot; performance problems happen beacuse your RAID or SAN setup isn&apos;t happy for some reason.&lt;br /&gt;- Leave documentation. Checklists, troubleshooting tips, build tools.&lt;br /&gt;- Build more tools. For yourself, for other people, or add features to existing ones.&lt;br /&gt;&lt;h2&gt;Work with IT&lt;/h2&gt;&lt;br /&gt;- Believe it or not, there is overlap.&lt;br /&gt;- Ops has to maintain high bandwidth network access for servers. IT has to do the same for people, and is often the bridge ops has *into* the datacenter.  It may make sense to work together on this one.&lt;br /&gt;- Draw the right line. IT should manage mail, but ops should manage development servers. Don&apos;t offload things you don&apos;t need to, and offer to do what you do best if necessary.&lt;br /&gt;- Don&apos;t alienate people. Macs are popular, linux is (slowly) gaining share.  Believe it or not, forcing everyone to use microsoft productivity software can bite you. There are plenty of alternatives, try one. Odds are more people in your company are familiar with google apps than they are with outlook.&lt;br /&gt;- Don&apos;t make it more difficult than you have to do for people to run a unix system natively. Unless your backend is a windows shop, wouldn&apos;t you want people to have more familiarity with the OS they&apos;re supposed to support?&lt;br /&gt;&lt;h2&gt;Work with developers&lt;/h2&gt;&lt;br /&gt;- You both work on the same product, for the same purpose. Try working together a little more.&lt;br /&gt;- Having strategy meetings is not working together.&lt;br /&gt;- Development understands the code resources the best, and operations understands the hardware and deployment the best. You can design something more efficient by taking all of this into mind.&lt;br /&gt;- Cross training. Disseminating information can show how tools and designs on both sides can be improved to be more manageable and resilient.&lt;br /&gt;- Be careful of being too demanding on either side. It&apos;s not an Us vs Them.  Everyone&apos;s human. Everyone should be doing as much as they can for the company, not for themselves.&lt;br /&gt;- It&apos;s more pleasent to handle crunch times and emergencies when everyone gets along.&lt;br /&gt;&lt;h2&gt;Work with ops&lt;/h2&gt;&lt;br /&gt;- Ops folks have their specialties. Networking, databases, OS. Don&apos;t forget to talk to each other!&lt;br /&gt;- Getting stuck in a rut is demotivating, boring, and a good way to lose people. Even if your systems ops guy has the ability to look over the shoulder of the network guy, they have the opportunity to learn.&lt;br /&gt;- Always give people an opportunity to try, learn, and grow.&lt;br /&gt;- Be careful of rewarding your best with too much work. If there&apos;re people who can pick up slack, you use them.&lt;br /&gt;- Bad eggs. It happens. Be tough enough to deal with them. Most people can be turned around with a little help, but they need to be able to be independent.&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;The Practice&lt;/h1&gt;&lt;br /&gt;&lt;h2&gt;Fix it now, not later&lt;/h2&gt;&lt;br /&gt;- If a webserver goes offline, don&apos;t care about it. You have ten spare, right?&lt;br /&gt;- Pick a day during the week to sweep up broken crap. Replace any broken hardware, ensure everything&apos;s 100% before swinging into the weekend.&lt;br /&gt;- If small, annoying problems crop up, fix them permanently first thing in the morning. Logs fill up the disk twice last week? Come in fresh the next day, and fix it for good. These stack up, and suck.&lt;br /&gt;- If you have automated builds, use this to your advantage to fix what you can right away, or in bulk.&lt;br /&gt;&lt;h2&gt;Automate everything&lt;/h2&gt; &lt;br /&gt;- Humans can&apos;t screw up scripted tasks (as easily).&lt;br /&gt;- Do it twice. Once by hand if you must, then roll up what you did into a script.&lt;br /&gt;- Commented scripts make fantastic documentation. Instead of writing twenty pages detailing how to install something (which is up to interpretation of the reader!), write a script which explains what it does.&lt;br /&gt;- Scripts can be rolled up into automated builds. The more often something is done, the closer it should get to becoming a zero time task.&lt;br /&gt;&lt;h2&gt;Change what&apos;s necessary&lt;/h2&gt;&lt;br /&gt;- Make small, isolated changes.&lt;br /&gt;- If you don&apos;t have to change it, leave it.&lt;br /&gt;- This also means you must understand _when_ to change. Find what&apos;s necessary and upgrade it, switch it out, make it standard.&lt;br /&gt;&lt;h2&gt;Design for change&lt;/h2&gt;&lt;br /&gt;- If you can&apos;t do it right immediately, get on the road to it being right.&lt;br /&gt;- This means if you don&apos;t have time to do something right, get the basics going with a clear migration roadmap to the right thing. While your new mail system might not be the crazy cool redundant bounce-processing spam monster you dream of, installing postfix and setting up two hosts with a clean configuration gets you closer than you might think.&lt;br /&gt;- This does have a tendency to leave unfinished projects everywhere, but you were going to do that anyway. :)&lt;br /&gt;&lt;h2&gt;Practice updating content, fast&lt;/h2&gt;&lt;br /&gt;- It&apos;s usually the job of operations to push out code. Don&apos;t suck at it. Push in parallel, apply rolling restarts, be an efficient machine.&lt;br /&gt;- This includes software updates, security patches, and configuration changes.&lt;br /&gt;- Use puppet, cfengine, whatever you need to control the configuration. Keep it clean, simple, and easy.&lt;br /&gt;- The fewer files one must change to make a necessary adjustment the better.  If you&apos;re adding one line to 20 files just to push out a new database, you&apos;re doing it wrong. Build simple templates, build outward, and don&apos;t repeat data which needs to be edited by hand.&lt;br /&gt;&lt;h2&gt;Standardize, stick to the standard&lt;/h2&gt;&lt;br /&gt;- Pick one or two standard OS&apos;s, httpd&apos;s, databases, package systems.&lt;br /&gt;- Stick with them. Adjust and upgrade methods as it makes sense.&lt;br /&gt;- Don&apos;t stick with that major version forever. Unless your product is going to be feature frozen forever, you&apos;ll need to keep the standard rolling forward, and everthing behind it.&lt;br /&gt;- The _more_ is standard, the more places your tools will work. The more packages for other parts of the operation will &quot;just work&quot; everywhere else too.&lt;br /&gt;&lt;h2&gt;Document well&lt;/h2&gt;&lt;br /&gt;- Document process&lt;br /&gt;- Document product&lt;br /&gt;- Categorize into shallow trees.&lt;br /&gt;- Don&apos;t redundantly document. If a script has a long help, ask the reader to refer to that. The closer the documentation is to the program being discussed, the more likely it is to stay accurate.&lt;br /&gt;- Marry documentation into code. perldoc, pydoc, etc.&lt;br /&gt;- Out of date documentation is poisonous. Reserve time to keep things up to date. Sit down with new employees and update documentation as they run into problems.&lt;br /&gt;- Use ticketing systems, with moderation. Documentation of history is important as well. Forcing people to create detailed process tickets for a DNS is just pissing in other people&apos;s cheerios.&lt;br /&gt;&lt;h2&gt;Use source control&lt;/h2&gt;&lt;br /&gt;- Use git, or mercurial. Avoid SVN like the black plague.&lt;br /&gt;- Put all of your configurations, scripts hacks, whatever, into source control.&lt;br /&gt;- Keep checkouts everywhere...&lt;br /&gt;- Keep strict, clean, master checkouts. No one should be able to push changes that aren&apos;t comitted, but it should also be easy to test changes (in a VM, directly on a single test machine) without having to wrestle with the source control. &lt;br /&gt;&lt;h2&gt;Hire well&lt;/h2&gt;&lt;br /&gt;- Discern between stubborn and smart&lt;br /&gt;- Don&apos;t avoid hiring senior. Some people really know their shit. Some _seem_ like they do. Others are &quot;senior&quot; in a particular area and will fall behind as technology changes. While you might want to avoid some, there are definitely rockstars out there.&lt;br /&gt;- Don&apos;t avoid hiring junior. I know so many people who&apos;ve started really junior (including myself! I still view myself as junior), who&apos;ve shot up through the ranks and are now have firm established careers. I&apos;d believe most of us have. Except there are ones who don&apos;t learn, don&apos;t have the motivation, or are in the wrong field.&lt;br /&gt;&lt;h2&gt;Avoid vendor lock in, and keep a good relationship with the vendors you do use&lt;/h2&gt;&lt;br /&gt;- Buying propreitary hardward has the major downside of potentially locking you into always using it. It might be a particular SAN, NAS, special-case direct attached storage, backup systems, etc. Avoid getting sucked in. If you follow all of the above design advise, one should be able to build test environments on different platforms quickly. You&apos;re then able to keep on top of hardware evaluations and keep choices open.&lt;br /&gt;- If everything&apos;s deep, dark, gnarled, undocumented, and directly dependent on your fancy proprietary load balancer, you&apos;ll never wriggle free of it.&lt;br /&gt;- Be nice to the vendors you do end up using. If you &quot;push them _hard_ on price!&quot; for every single purchase, expect some shit hardware to show up.&lt;br /&gt;- Datacenters these days have a lot of potentially useful resources. Try to throw some free remote hands service into your contract and abusing that to get harddrives replaced, vendor items shipped/RMA&apos;ed, and some basic hardware installs. I&apos;ve had entire racks of equipment delivered and installed with barely a visit from an employee... and damn, it&apos;s nice.&lt;br /&gt;&lt;h2&gt;Give Open Source a serious try&lt;/h2&gt;&lt;br /&gt;- nginx, mongrel, lighttpd, apache, perlbal, mogilefs, memcached, squid, OpenBGPD, PF, IPTables, LVS, MySQL, Postgres, blah, blah, blah. Before you hop back on that trusty, reliable, expensive proprietary setup, give open source a shot. You might find yourself adding plugins, extensions, code fixes or contracting help to bring features you&apos;d never be able to do otherwise. In my own experience OSS is just as reliable, often moreso, than big expensive hardware when put under significant load.&lt;br /&gt;- The idea of &quot;you get what you pay for&quot; is a complete lie. If you can&apos;t make OSS work for you and need the hand holding, you _can_ still go with a vendor. If you have a smart, motivated team, who really want to learn and understand how their infrastructure runs, you just can&apos;t beat some hardy GPL&apos;ed or BSD&apos;ed systems.&lt;br /&gt;- MySQL and Postgres are fine. Call them tradeoffs if you will; nothing&apos;s going to crawl out of your closet and night and eat your data. Sure, it does happen, but you&apos;re much more likely to be screwed over with monolithic oracle instances going offline (it happens!) than you are with a well tested and stable MySQL instance, in a redundant master&amp;lt;-&amp;gt;master cluster pair.&lt;br /&gt;- I&apos;d say &apos;cite references&apos; - but go look around. Check out any number of articles on the LAMP stack. Most major dot coms, ISP&apos;s, and even corporations now are adopting. Give it a shot. The worst you&apos;ll have is some lost time, and another product to scare your vendor into dropping price with.</description>
  <comments>https://dormando.livejournal.com/484577.html?view=comments#comments</comments>
  <lj:security>public</lj:security>
  <lj:reply-count>14</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/483244.html</guid>
  <pubDate>Tue, 15 Jan 2008 18:49:48 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL Release 5</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/483244.html</link>
  <description>&lt;a href=&quot;http://dormando.livejournal.com/482703.html&quot; target=&quot;_blank&quot;&gt;previously&lt;/a&gt;&lt;br /&gt;Stroll on over to the &lt;a href=&quot;http://consoleninja.net/code/dpm&quot; target=&quot;_blank&quot;&gt;DPM&apos;s minimalist homepage&lt;/a&gt; and grab the latest release tarball, export tarball, clone the git repo, or peruse gitweb.&lt;br /&gt;&lt;br /&gt;While I did some porting work, this release has not been explicitly tested on all of the platforms yet. If there are bugs with a particular platform, please report.&lt;br /&gt;&lt;br /&gt;This release fixes a lot of outstanding complaints I had with the power of the API, and many known obnoxious bugs and restrictions. Like the previous inability to listen on INADDR_ANY, or use unix domain sockets, etc. There are still a number of usability/troubleshooting gotchas when writing programs using DPM, but aside from the learning curve most of it should work now. There are no known crash bugs or memory leaks (aside from a &quot;leak&quot; in the dpml library under specific  conditions).&lt;br /&gt;&lt;br /&gt;Changes since last post:&lt;br /&gt;&lt;br /&gt;- Backend authentication bug fixed.&lt;br /&gt;- Fix for crashes if a listener socket object gets garbage collected (now gracefully closes the socket and cleans up after itself).&lt;br /&gt;- All sockets being written to now get properly flushed. This _also_ means &lt;a href=&quot;http://dormando.livejournal.com/482825.html&quot; target=&quot;_blank&quot;&gt;multiplexing commands between sockets&lt;/a&gt; now works. You can take a command from one client and send, in parallel, to multiple servers, then retrieve the resultsets in parallel. Works in either direction, like sending a single resultset back to multiple clients (for coalescing similar requests).&lt;br /&gt;- Lua connections can actually show the IP address now via conn:socket_address().&lt;br /&gt;- Unix domain socket support works for both clients and servers. Might be shakey, but tested okay.&lt;br /&gt;- New dpm commands for retrieving time:&lt;br /&gt;gettimeofday time time_hires&lt;br /&gt;&lt;br /&gt;dpm.gettimeofday() returns two values: seconds and microseconds&lt;br /&gt;dpm.time() returns seconds&lt;br /&gt;dpm.time_hires() returns milliseconds&lt;br /&gt;... would be fun to add some dpml functions to make time diffing easier!&lt;br /&gt;- New callback timer API:&lt;br /&gt;timer = dpm.new_timer()&lt;br /&gt;timer:schedule(seconds, milliseconds, callback_function, argument)&lt;br /&gt;Schedule a repeating timer to run the lua function &apos;callback_function&apos;&lt;br /&gt;For subsecond resolution set seconds to zero, and milliseconds to nonzero.&lt;br /&gt;&lt;br /&gt;Callbacks look like:&lt;code&gt;&lt;br /&gt;-- &apos;self&apos; is the &apos;timer&apos; object.&lt;br /&gt;function callback_function(self, arg)&lt;br /&gt;    print(&quot;Hello! I have ran &quot; .. arg[&quot;count&quot;] .. &quot; queries.&quot;)&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Timers are cancelled by running timer:cancel()&lt;br /&gt;... if you want callbacks to be ran exactly once, they must unschedule themselves via self:cancel()&lt;br /&gt;- DPML function dpml.execute_query_buffered(server, query, callback)&lt;br /&gt;Runs callback with a buffered resultset. Same table structure as accepted by dpml.send_resultset&lt;br /&gt;- Silly barely-tested dpml.dump_table() utility to recusively pretty-print lua tables for debugging.&lt;br /&gt;&lt;br /&gt;Fun, huh? :)&lt;br /&gt;&lt;br /&gt;For R6 I&apos;m still trying to switch the focus to writing more lua infrastructure than C, but I&apos;d like to support a few more packet types first.&lt;br /&gt;R6 should see the start of a test suite, which is a prerequisite to going back and cleaning up the codebase.&lt;br /&gt;&lt;br /&gt;R7 will start the focus on more testing, and refactoring the code.&lt;br /&gt;It&apos;s pretty ugly; I want to abstract out into more files, and work towards BSD licensing, support C plugins, etc. Function, variable, define naming all needs to be cleaned. Error handling for the lua libs needs to be ironed out... Lots to refactor, but that work is quick when you have a test library.&lt;br /&gt;&lt;br /&gt;BSD licensing is important for me, and this project. It&apos;ll happen.&lt;br /&gt;&lt;br /&gt;Long announcement, sorry. Wrote part of this on my way into work, and decided to release one feature early. Early, often.</description>
  <comments>https://dormando.livejournal.com/483244.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/482825.html</guid>
  <pubDate>Sat, 12 Jan 2008 09:43:48 GMT</pubDate>
  <title>Magic tricks</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/482825.html</link>
  <description>&lt;code&gt;mysql&amp;gt; CREATE TABLE `blah2` ( `hello` int(11) default NULL );&lt;br /&gt;Query OK, 0 rows affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; INSERT INTO blah2 VALUES (1);   &lt;br /&gt;Query OK, 1 row affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; select * from blah2;&lt;br /&gt;+-------+&lt;br /&gt;| hello |&lt;br /&gt;+-------+&lt;br /&gt;|     1 | &lt;br /&gt;|     1 | &lt;br /&gt;+-------+&lt;br /&gt;2 rows in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; show full processlist;&lt;br /&gt;+--------+-------+-----------------+------+---------+------+----------------+-----------------------+&lt;br /&gt;| Id     | User  | Host            | db   | Command | Time | State          | Info                  |&lt;br /&gt;+--------+-------+-----------------+------+---------+------+----------------+-----------------------+&lt;br /&gt;| 503235 | root  | localhost       | NULL | Sleep   |  742 |                | NULL                  | &lt;br /&gt;| 503238 | happy | localhost:36013 | test | Query   |    0 | NULL           | show full processlist | &lt;br /&gt;| 503239 | happy | localhost:36014 | test | Query   |    0 | Writing to net | show full processlist | &lt;br /&gt;+--------+-------+-----------------+------+---------+------+----------------+-----------------------+&lt;br /&gt;3 rows in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; use sakila;&lt;br /&gt;Reading table information for completion of table and column names&lt;br /&gt;You can turn off this feature to get a quicker startup with -A&lt;br /&gt;&lt;br /&gt;Database changed&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; show full processlist;&lt;br /&gt;+--------+-------+-----------------+--------+---------+------+-------+-----------------------+&lt;br /&gt;| Id     | User  | Host            | db     | Command | Time | State | Info                  |&lt;br /&gt;+--------+-------+-----------------+--------+---------+------+-------+-----------------------+&lt;br /&gt;| 503235 | root  | localhost       | NULL   | Sleep   |  801 |       | NULL                  | &lt;br /&gt;| 503238 | happy | localhost:36013 | sakila | Query   |    0 | NULL  | show full processlist | &lt;br /&gt;| 503239 | happy | localhost:36014 | sakila | Sleep   |   17 |       | NULL                  | &lt;br /&gt;+--------+-------+-----------------+--------+---------+------+-------+-----------------------+&lt;br /&gt;3 rows in set (0.00 sec)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Easy.</description>
  <comments>https://dormando.livejournal.com/482825.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/482703.html</guid>
  <pubDate>Wed, 02 Jan 2008 10:50:25 GMT</pubDate>
  <title>No DPM -r5 tonight</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/482703.html</link>
  <description>Out of time, finally getting tired. Tomorrow, for sure :) One last bug to hunt down.&lt;br /&gt;&lt;br /&gt;For now, HEAD&apos;s looking pretty good. &lt;a href=&quot;http://consoleninja.net/code/dpm&quot; target=&quot;_blank&quot;&gt;grab the export tarball, check out the git repo, etc&lt;/a&gt;. If you&apos;re bored.&lt;br /&gt;&lt;br /&gt; The most interesting addition is the CMake build and the fact that DPM&apos;s now portable to at least four operating systems. I&apos;ve read Jan&apos;s &lt;a href=&quot;http://jan.kneschke.de/2007/12/25/replacing-autotools&quot; target=&quot;_blank&quot;&gt;recent&lt;/a&gt; &lt;a href=&quot;http://jan.kneschke.de/2007/12/22/how-to-write-a-good-build-system&quot; target=&quot;_blank&quot;&gt;posts&lt;/a&gt; on the subjecft of build systems, and I&apos;ll admit up front that I haven&apos;t touched DPM recently due to my reluctance to use autotools.&lt;br /&gt;&lt;br /&gt;I&apos;ve decided CMake isn&apos;t evil enough to warrant avoiding it. It&apos;s an extra dependency, so we&apos;ll see how it goes. There&apos;s even been recent discussion on using lua as CMake&apos;s build language :)&lt;br /&gt;&lt;br /&gt;Also, I&apos;ve finally done a few tests with mysql&apos;s sql-bench &quot;run-all-tests&quot; against DPM. So far, it&apos;s passed with no memory leaks or crashes! My one oustanding crash bug was a simple fix noted below.&lt;br /&gt;&lt;br /&gt;Updates for R5 (slight recap from yesterday):&lt;br /&gt;&lt;br /&gt;- Uses CMake as a build system. File tries really hard to satisfy dependency checks and to build it correctly.&lt;br /&gt;- Ported to multiple operating systems (not win32, not really interested). OS X, OpenBSD, FreeBSD.&lt;br /&gt;- Will now optionally build with -g, instead of always.&lt;br /&gt;- With addition of CMake, &apos;make install&apos; now exists and works. No more path hacking to get it loading the default libraries.&lt;br /&gt;- Bugfixes: auth packets can now specify default databases, close open connections if the associated lua object is garbage collected.&lt;br /&gt;- Specify under --verbose if closed connections are listeners&lt;br /&gt;- Finally (argh!) able to set a listener to INADDR_ANY by specifying nil for an IP address: dpm.listener(nil, 3306) would emulate mysql&apos;s default network behavior.&lt;br /&gt;- Some documentation updates.&lt;br /&gt;&lt;br /&gt;Still not done for R5:&lt;br /&gt;&lt;br /&gt;- Bug: Backend authentication does not always work, due to the random scramble generating invalid characters.&lt;br /&gt;- Unix domain socket support (easy, but not five minutes of work).&lt;br /&gt;- Allow lua connection objects to retrieve the connected IP address. (for authentication, etc).&lt;br /&gt;- Potentially a fix for ensuring network flushes happen if packets have been written to the buffer, as presently it will only flush connections which directly receive packets, and proxied connections. Dumb.&lt;br /&gt;- Potentially a few simple dpml additions to help ease new users in.&lt;br /&gt;&lt;br /&gt;For R6:&lt;br /&gt;&lt;br /&gt;TBD ;) Really needs a test suite.</description>
  <comments>https://dormando.livejournal.com/482703.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/482494.html</guid>
  <pubDate>Tue, 01 Jan 2008 07:24:24 GMT</pubDate>
  <title>Happy new year! DPM, memcached, etc</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/482494.html</link>
  <description>r5 of &lt;a href=&quot;http://consoleninja.net/code/dpm&quot; target=&quot;_blank&quot;&gt;DPM&lt;/a&gt; will appear ... tomorrow! It&apos;s close now, but I&apos;ma go party a bit. There&apos;s new code in HEAD if you&apos;re bored.&lt;br /&gt;&lt;br /&gt;New stuff:&lt;br /&gt;&lt;br /&gt;- Bugfixes (crash bugs, silly things)&lt;br /&gt;- &lt;a href=&quot;http://cmake.org&quot; target=&quot;_blank&quot;&gt;CMake&lt;/a&gt; build file.&lt;br /&gt;- Code ported from Linux to OS X (leopard) PPC, OpenBSD 4.2, FreeBSD 6.2&lt;br /&gt;- (not done yet) support INADDR_ANY, unix domain sockets, few more things.&lt;br /&gt;&lt;br /&gt;Also, new releases of memcached coming up as soon as possible, along with nice clear useful documentation.&lt;br /&gt;&lt;br /&gt;- 1.3.0, with binary protocol!&lt;br /&gt;- 1.2.5, with portability fixes!&lt;br /&gt;- Overview of all outstanding projects/ideas worth doing, now that memcached&apos;s development has been jumpstarted.&lt;br /&gt;&lt;br /&gt;Then later;&lt;br /&gt;&lt;br /&gt;- 1.3.1, probably with all of the binary protocol bugs fixed!&lt;br /&gt;&lt;br /&gt;Happy new year!</description>
  <comments>https://dormando.livejournal.com/482494.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>3</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/479182.html</guid>
  <pubDate>Sat, 10 Nov 2007 07:16:01 GMT</pubDate>
  <title>DPM Release-4</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/479182.html</link>
  <description>&lt;a href=&quot;http://consoleninja.net/code/dpm/&quot; target=&quot;_blank&quot;&gt;DPM homepage&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/rel/dpm-r4.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/rel/dpm-r4.tar.gz&lt;/a&gt; - tarball of r4&lt;br /&gt;git clone &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; - to get the latest code, always&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm-export.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/dpm-export.tar.gz&lt;/a&gt; - a tarball of the latest code, for those unwilling to git it.&lt;br /&gt;&lt;br /&gt;It&apos;s been a long time. I suck. One of these days I&apos;ll be productive enough to pop out releases.&lt;br /&gt;&lt;br /&gt;Unfortunately I&apos;ve been sitting on this one for a long time. There are significant API updates in this one, but I&apos;ve cut it just short of being really good. Impatient :)&lt;br /&gt;&lt;br /&gt;Notable API changes:&lt;br /&gt;&lt;br /&gt;- All instances of &apos;myp&apos; have been renamed to &apos;dpm&apos; for coherency.&lt;br /&gt;- New &apos;dpml&apos; lua-based library. Most demos updated to use it. While the low level API offers a huge amount of flexibility, it&apos;s barely usable by a novice. The goal of &apos;dpml&apos; is to provide pure lua wrappers around the low level API to emulate functions you might normally expect.&lt;br /&gt;- Start of a programming manual in doc/API.txt&lt;br /&gt;- &apos;server_status&apos; flags are now accessable in lua, off of eof packets. This means you may detect whether a transaction is in use, if autocommit mode is enabled, if an index was missed, etc. All becoming easier to use with advances in the dpml library.&lt;br /&gt;- New demo &quot;demo-lib.lua&quot; which shows off some of the dpml library functions.&lt;br /&gt;- startup.lua, etc, updated for API changes.&lt;br /&gt;&lt;br /&gt;- Callback system has been optimized. This is one case of a 5x+ speedup while keeping the API almost completely the same. The hacky proxy_until functions have been removed since it&apos;s always fast now.&lt;br /&gt;&lt;br /&gt;- Package level callbacks have been added. All connections have callbacks wired directly into them, but there now exists floating callback structures. You may use this convention to temporarily take over a connection&apos;s callbacks from within a library. The dpml command &apos;connect_mysql_server&apos; uses this, and demo-autoexplain.lua has a more clear example of its use.&lt;br /&gt;I love the way this works :) It&apos;s now easy to build standard packages to build resultsets, handle resultsets, authentication, error conditions, etc.&lt;br /&gt;&lt;br /&gt;- dpm.close() command for forcefully closing a connection. ;)&lt;br /&gt;&lt;br /&gt;Other changes:&lt;br /&gt;&lt;br /&gt;- Small number of bugfixes, mostly related to testing the API. It&apos;s now possible to actually set username/passwords when connecting to a server, to read/change server_status flags, etc.&lt;br /&gt;- Fix from John Loehrer to allow buillding on OS X (intel, I think?)&lt;br /&gt;&lt;br /&gt;Sweet :)&lt;br /&gt;&lt;br /&gt;Now, release 5 will be a ways off. Will be pushing smaller changes into the head of git more often, but I will be dedicating more of my spare time over the next two weeks to other things. That doesn&apos;t mean work stops, so if you&apos;re going to use it and contribute back, still please do! :)</description>
  <comments>https://dormando.livejournal.com/479182.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/477205.html</guid>
  <pubDate>Sun, 14 Oct 2007 00:47:23 GMT</pubDate>
  <title>Proxy for MySQL quickie</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/477205.html</link>
  <description>Some small (but meaningful) updates in HEAD at &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; :&lt;br /&gt;&lt;br /&gt;- You can now actually specify a username/password when connecting to a server. A few other similar spots were fixed.&lt;br /&gt;- You can get the remote connection ID from a connection object.&lt;br /&gt;- New myp.close() command for closing connections ;)&lt;br /&gt;&lt;br /&gt;I&apos;ve tested kill -9&apos;ing clients, servers, in various states of transit with no obvious bugs. It&apos;s probably still unstable but these changes make a big difference.&lt;br /&gt;&lt;br /&gt;I&apos;m not an API designer by any stretch. I&apos;m stuck wondering:&lt;br /&gt;&lt;br /&gt;Should connobj:remote_id() (or just remote()) return the object id, or the full object?&lt;br /&gt;&lt;br /&gt;On that same line, all callbacks have the affected connection ID as an argument. Should that just be a reference to the actual connection object, or continue to be the ID?&lt;br /&gt;&lt;br /&gt;A huge annoyance with the API (and biggest performance issue) is the way the callback API is handled internally. This is changing significantly. I have the new approach but the API details elude me for the moment.</description>
  <comments>https://dormando.livejournal.com/477205.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/477023.html</guid>
  <pubDate>Thu, 11 Oct 2007 09:34:34 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL, release 3</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/477023.html</link>
  <description>&lt;a href=&quot;http://dormando.livejournal.com/474153.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/rel/dpm-r3.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/rel/dpm-r3.tar.gz&lt;/a&gt; - tarball of r3&lt;br /&gt;git clone &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; - to get the latest code, always&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm-export.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/dpm-export.tar.gz&lt;/a&gt; - a tarball of the latest code, for those unwilling to git it.&lt;br /&gt;&lt;br /&gt;It&apos;s been way, way too long. Next release will be within a week and a half, and will try to stick to the once-a-week schedule from then on. There&apos;ve been over 210 commits to the repo at this point. The speed has been picking up.&lt;br /&gt;&lt;br /&gt;Notable changes:&lt;br /&gt;&lt;br /&gt;- Resultsets are fully parseable and writeable! This lacks a decent demo, but the new one makes use of it. -r4 will have many new demos. This was the big missing feature. It&apos;s certainly able to do a lot of work now.&lt;br /&gt;- It&apos;s possible to inject and modify packets being streamed between servers. Inject rows, or modify fields _and_ rewrite rows on the way through :)&lt;br /&gt;- Combine authentication support with resultset management, and you can connect to and query the proxy without a backend mysqld at all. (demo coming soon)&lt;br /&gt;- Many many bugfixes and some code cleanup. Lots of bugfixes.&lt;br /&gt;- New simplistic &quot;auto explain&quot; demo to show off non sucky resultset handling on the raw api.&lt;br /&gt;- Better detection of closed sockets.&lt;br /&gt;- Internal defines pushed up into lua, so no more external lua defines files.&lt;br /&gt;&lt;br /&gt;Upcoming:&lt;br /&gt;&lt;br /&gt;- Callback handling will be restructured. Should be much faster and more flexible. Worst case, faster :) The &quot;proxy_until&quot; hack will be going away.&lt;br /&gt;- Better detection of connection death during internal proxying.&lt;br /&gt;- Large packet detection, if not support.&lt;br /&gt;- Start of basic &quot;easy to use&quot; lua libraries on top of the base API.&lt;br /&gt;- Code cleanups. So many code cleanups.&lt;br /&gt;- Test suite&lt;br /&gt;&lt;br /&gt;Fun API tricks:&lt;br /&gt;- [auto\reloading modules, along with static support&lt;br /&gt;- using multiple module features against a single connection via routing&lt;br /&gt;- different modules living on different listening sockets (easy)&lt;br /&gt;&lt;br /&gt;IF someone wants to help out with autoconf/automake/make, or at least let me know how much magic sauce I should apply, I&apos;d be pretty thrilled. I hate this crap and the project needs an installer sometime soon.&lt;br /&gt;&lt;br /&gt;Sleep now. Taking a day off from the proxy then resuming.</description>
  <comments>https://dormando.livejournal.com/477023.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/474153.html</guid>
  <pubDate>Sat, 25 Aug 2007 23:37:08 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL, release 2</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/474153.html</link>
  <description>&lt;a href=&quot;http://dormando.livejournal.com/473954.html&quot; target=&quot;_blank&quot;&gt;Previous post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/rel/dpm-r2.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/rel/dpm-r2.tar.gz&lt;/a&gt; - tarball of r2&lt;br /&gt;git clone &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; - to get the latest code, always&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm-export.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/dpm-export.tar.gz&lt;/a&gt; - a tarball of the latest code, for those unwilling to git it.&lt;br /&gt;&lt;br /&gt;I&apos;ll keep this short. Also, just noticed my tags aren&apos;t getting uploaded to the remote git. Sorry.&lt;br /&gt;&lt;br /&gt;Bunch of commits worthy of a new release:&lt;br /&gt;- new included pass-through demo.&lt;br /&gt;- cleaned the included connection pooling demo a little.&lt;br /&gt;- &apos;proxy_until&apos; demo. Disables packet processing temporarily. Huge speedup if you&apos;re only doing partial inspection. On an 8.4 million row select (small rows, memory cached):&lt;br /&gt; - no proxy: 5.80s  avg&lt;br /&gt; - default proxy: 19.4s avg&lt;br /&gt; - with proxy_until: 6.2s avg&lt;br /&gt;Nice?&lt;br /&gt;- \s works with mysql CLI now.&lt;br /&gt;- Packet sequencer is less sucky.&lt;br /&gt;- Misc bug fixes.&lt;br /&gt;&lt;br /&gt;Big goal for r3 is to have field and row packet parsing working. This will enable query handling from all within the proxy. Then things get a little more interesting!</description>
  <comments>https://dormando.livejournal.com/474153.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/473954.html</guid>
  <pubDate>Tue, 21 Aug 2007 08:15:46 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL, release 1</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/473954.html</link>
  <description>&lt;a href=&quot;http://dormando.livejournal.com/468396.html&quot; target=&quot;_blank&quot;&gt;original post&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://dormando.livejournal.com/468850.html&quot; target=&quot;_blank&quot;&gt;second post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/rel/dpm-r1.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/rel/dpm-r1.tar.gz&lt;/a&gt; - release 1 of DPM, tarballed sources. Also available via cloning git and tracking the &apos;release-1&apos; tag&lt;br /&gt;&lt;br /&gt;git clone &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; - to get the latest code, always&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm-export.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/dpm-export.tar.gz&lt;/a&gt; - a tarball of the latest code, for those unwilling to git it.&lt;br /&gt;&lt;br /&gt;I&apos;d still call this a preview release, but it&apos;s too juicy to not release in some form. Early and often, right?&lt;br /&gt;&lt;br /&gt;This is the first real release, as it actually does something. It crashes a lot less, has a few options, and is pretty hackable. Many many bugfixes since the last preview release. At this point I can do selects with 8.5 million rows of results through the proxy without much trouble, although it will still segfault if you try too hard.&lt;br /&gt;&lt;br /&gt;The included demo lua file demonstrates very basic query rewriting. Send &quot;HELLO;&quot; in your mysql client and it should return the results of &quot;SELECT 1 + 1&quot; - you may edit this and have some fun.&lt;br /&gt;&lt;br /&gt;Now that life is settling a bit I can get back into this. My goal is one release per week at least.&lt;br /&gt;&lt;br /&gt;I&apos;ve been slowed since I changed jobs (I now work at the parent company of this site as a DBA!), and Dell has &lt;a href=&quot;http://direct2dell.com/one2one/archive/2007/08/03/23340.aspx&quot; target=&quot;_blank&quot;&gt;delayed my new hacking machine into oblivion&lt;/a&gt;. I&apos;m making do with suboptimal wired-to-the-wall hacking provisions for now.&lt;br /&gt;&lt;br /&gt;So, submit patches, questions, requests, hate mail, etc! Or do nothing until I get five more releases out and it actually does something you might want.&lt;br /&gt;&lt;br /&gt;Sorry for the delay! Happy hacking :)</description>
  <comments>https://dormando.livejournal.com/473954.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>4</lj:reply-count>
  </item>
  <item>
  <guid isPermaLink='true'>https://dormando.livejournal.com/468850.html</guid>
  <pubDate>Sat, 16 Jun 2007 09:16:16 GMT</pubDate>
  <title>Dormando&apos;s Proxy for MySQL, preview 2</title>
  <author>dormando</author>
  <link>https://dormando.livejournal.com/468850.html</link>
  <description>Made some commits to my proxy for MySQL tonight. These are significant enough that they warrant another preview post.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://dormando.livejournal.com/468396.html&quot; target=&quot;_blank&quot;&gt;original post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;git clone &lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm.git&apos;&gt;http://consoleninja.net/code/dpm/dpm.git&lt;/a&gt; - to get the latest code, always&lt;br /&gt;&lt;a target=&apos;_blank&apos; href=&apos;http://consoleninja.net/code/dpm/dpm-export.tar.gz&apos;&gt;http://consoleninja.net/code/dpm/dpm-export.tar.gz&lt;/a&gt; - a tarball of the latest code, for those unwilling to git.&lt;br /&gt;&lt;br /&gt;This feels more like a &quot;real&quot; preview now. Aside from one silly bug, it looks like I fixed most of the really bad stability issues. There&apos;re still a handful left, but I was pretty abusive just now and wasn&apos;t able to break it.&lt;br /&gt;&lt;br /&gt;Grab it, build it, read the README.&lt;br /&gt;&lt;br /&gt;The demo allows you to do some fun things:&lt;br /&gt;&lt;br /&gt;- Connect to the proxy four times.&lt;br /&gt;- Run &apos;show processlist&apos; - wow, only shows one connection!&lt;br /&gt;- &apos;use database&apos; in one window, run &apos;show tables&apos; in another. Weird :P&lt;br /&gt;- Disconnect from the server, and the lua end (poorly) intercepts the COM_QUIT query and lets the client die on its own.&lt;br /&gt;&lt;br /&gt;I got stuck for a day on one of these bugs I just fixed. Releases should be hard and fast for a while.</description>
  <comments>https://dormando.livejournal.com/468850.html?view=comments#comments</comments>
  <category>mysql</category>
  <category>dpm</category>
  <lj:security>public</lj:security>
  <lj:reply-count>4</lj:reply-count>
  </item>
</channel>
</rss>
