<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://blog.pwkf.org/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Personal Workflow Blog</title>
  <link>http://blog.pwkf.org/</link>
  <atom:link href="http://blog.pwkf.org/feed/rss2" rel="self" type="application/rss+xml"/>
  <description>Some thoughts I encounter during my working day in the J2EE land. Originally a blog about PWKF, an easy-to-use workflow solution, but I have less time to work on PWKF that what I had before as real life kicked in !</description>
  <language>en</language>
  <pubDate>Mon, 12 Jul 2010 12:13:08 +0200</pubDate>
  <copyright>(c) 2006-2009 - Steve Schnepp</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Waiting for Munin 2.0 - Native SSH transport</title>
    <link>http://blog.pwkf.org/post/2010/07/Waiting-for-Munin-2.0-Native-SSH-transport</link>
    <guid isPermaLink="false">urn:md5:909601af6402cb869f7d9e2e3b0515ee</guid>
    <pubDate>Mon, 12 Jul 2010 14:11:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>http</category><category>munin</category><category>munin20</category><category>ssh</category><category>sysadmin</category>    
    <description>    &lt;p&gt;In the munin architecture, the munin-master has to connect to the munin-node
via a very simple protocol and plain TCP.&lt;/p&gt;
&lt;p&gt;This has several advantages :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Very&lt;/strong&gt; simple to manage &amp;amp; install&lt;/li&gt;
&lt;li&gt;Optional SSL since 1.4 enabling secure communications&lt;/li&gt;
&lt;li&gt;Quite simple firewall rules.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It has also some disadvantages :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A new listening service means a wider exposure&lt;/li&gt;
&lt;li&gt;The SSL option might add some administrative overhead (certificates
management, ...)&lt;/li&gt;
&lt;li&gt;A native protocol isn't always covered by all firewall solutions&lt;/li&gt;
&lt;li&gt;Some organisations only authorize a few protocols to simplify audits (ex:
only SSH &amp;amp; HTTPS)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Native SSH&lt;/h2&gt;
&lt;p&gt;Theses down points may be solved by &lt;a href=&quot;http://munin-monitoring.org/wiki/MuninSSHTunneling&quot; hreflang=&quot;en&quot;&gt;encapsulation over SSH&lt;/a&gt;, but it can be a tedious task to maintain if
the number of hosts increases.&lt;/p&gt;
&lt;p&gt;Therefore 2.0 introduces the concept of a &lt;strong&gt;native SSH&lt;/strong&gt;
transport. Its usage is dead simple : replace the address with an
&lt;code&gt;ssh://&lt;/code&gt; URL-like one.&lt;/p&gt;
&lt;p&gt;The node still has to be modified to communicate with
&lt;code&gt;stdin&lt;/code&gt;/&lt;code&gt;stdout&lt;/code&gt; instead of a network socket. For now,
only &lt;a href=&quot;http://blog.pwkf.org/post/2008/11/04/A-Poor-Man-s-Munin-Node-to-Monitor-Hostile-UNIX-Servers&quot;&gt;pmmn&lt;/a&gt;
and &lt;a href=&quot;http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Asynchronous-updates&quot;&gt;munin-async&lt;/a&gt;
are able to provide such a node.&lt;/p&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;The URL is quite self-explanatory as shown in the example below :&lt;/p&gt;
&lt;pre&gt;
[old-style-host]
    address host.example.com

[new-style-host]
    address ssh://munin-node-user@host.example.com:/path/to/stdio-enabled-node --params
&lt;/pre&gt;
&lt;h3&gt;Installation notes&lt;/h3&gt;
&lt;p&gt;Authentication should be done without password but via SSH keys. The
connection is from &lt;code&gt;munin-user@host-munin&lt;/code&gt; to
&lt;code&gt;munin-node-user@remote-node&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you use &lt;code&gt;munin-async&lt;/code&gt;, the user on the remote node might only
be a readonly one, since it only needs to read spooled data. This implies that
you use &lt;code&gt;--spoolfetch&lt;/code&gt; and not &lt;code&gt;--vectorfetch&lt;/code&gt; that
updates the spool repository.&lt;/p&gt;
&lt;h2&gt;Upcoming HTTP(S) transport in 3.0&lt;/h2&gt;
&lt;p&gt;And the sweetest part is that since all the work has been done for adding
another transport, adding a CGI-based HTTP transport one is possible (and
therefore done) for 3.0.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/07/Waiting-for-Munin-2.0-Native-SSH-transport#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/07/Waiting-for-Munin-2.0-Native-SSH-transport#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/532312</wfw:commentRss>
      </item>
    
  <item>
    <title>Waiting for Munin 2.0 - Performance - Asynchronous updates</title>
    <link>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Asynchronous-updates</link>
    <guid isPermaLink="false">urn:md5:e1957d01162a0736f5534140c4cbdd4e</guid>
    <pubDate>Sat, 26 Jun 2010 16:07:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>munin</category><category>munin20</category><category>performance</category>    
    <description>    &lt;p&gt;&lt;code&gt;munin-update&lt;/code&gt; is &lt;strong&gt;the&lt;/strong&gt; fragile link in the munin
architecture. A &lt;strong&gt;missed execution&lt;/strong&gt; means that some &lt;strong&gt;data
is lost&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The problem : updates are synchronous&lt;/h2&gt;
&lt;p&gt;In Munin 1.x, updates are synchronous : the value of each
service&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#pnote-526512-1&quot; id=&quot;rev-pnote-526512-1&quot; name=&quot;rev-pnote-526512-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; is the one that &lt;code&gt;munin-update&lt;/code&gt;
retrieves each scheduled run.&lt;/p&gt;
&lt;p&gt;The issue is that &lt;code&gt;munin-update&lt;/code&gt; has to ask every service on
every node for their values. Since the values are only computed when asked,
&lt;code&gt;munin-update&lt;/code&gt; has to wait quite some time for every value.&lt;/p&gt;
&lt;p&gt;This &lt;strong&gt;very simple design&lt;/strong&gt; enables munin to have the
&lt;strong&gt;simplest plugins&lt;/strong&gt; : they are completely
&lt;strong&gt;stateless&lt;/strong&gt;. While being one great strength of munin, it puts a
severe blow on scalability : more plugins/node means obviously a slower
retrieval.&lt;/p&gt;
&lt;h2&gt;Evolving Solutions&lt;/h2&gt;
&lt;h3&gt;1.4 : Parallel Fetching&lt;/h3&gt;
&lt;p&gt;1.4 addresses some of these scalability issues by implementing parallel
fetching. It takes into account that the &lt;strong&gt;most of the execution
time&lt;/strong&gt; of &lt;code&gt;munin-update&lt;/code&gt; is spent &lt;strong&gt;waiting for
replies&lt;/strong&gt;. In 1.4 &lt;code&gt;munin-update&lt;/code&gt; can ask
&lt;code&gt;max_processes&lt;/code&gt; nodes in parallel.&lt;/p&gt;
&lt;p&gt;Now, the I/O part is becoming the next limiting factor, since updating many
RRDs in &lt;strong&gt;parallel&lt;/strong&gt; is the same as &lt;strong&gt;random I/O&lt;/strong&gt;
access for the underlying munin-master OS. Serializing &amp;amp; grouping the
updates will be possible with the new RRDp interface from rrdtool version 1.4
and on-demand graphing. Tomas Zvala even offered &lt;a href=&quot;http://sourceforge.net/mailarchive/message.php?msg_name=4BEDB877.9050004%40zvala.cz&quot; hreflang=&quot;en&quot;&gt;a patch for 1.4 RRDp&lt;/a&gt; on the ML. It is very promising, but
doesn't address the root defect in this design : a hard dependence of regular
&lt;code&gt;munin-update&lt;/code&gt; runs.&lt;/p&gt;
&lt;h3&gt;2.0 : Stateful plugins&lt;/h3&gt;
&lt;p&gt;2.0 provides a way for plugins to be stateful. They might schedule their
polling themselves, and then when &lt;code&gt;munin-update&lt;/code&gt; runs, only emit
collect already computed values. This way, a &lt;strong&gt;missed run&lt;/strong&gt; isn't
as dramatic as it is in the 1.x series, since &lt;strong&gt;data isn't lost&lt;/strong&gt;.
The data collection is also &lt;strong&gt;much faster&lt;/strong&gt; because the real
computing is done &lt;strong&gt;ahead of time&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;2.0 : Asynchronous proxy node&lt;/h3&gt;
&lt;p&gt;But &lt;strong&gt;changing plugins&lt;/strong&gt; to be &lt;strong&gt;stateful&lt;/strong&gt; and
self-polled is &lt;strong&gt;difficult&lt;/strong&gt; and tedious. &lt;strong&gt;It even works
against of one of the real strength of munin&lt;/strong&gt; : having simple &amp;amp;
stateless plugins.&lt;/p&gt;
&lt;p&gt;To address this concern, an experimental proxy node is created. For 2.0 it
takes the form of a couple of processes : &lt;code&gt;munin-async-server&lt;/code&gt; and
&lt;code&gt;munin-sync-client&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The proxy node in detail (&lt;code&gt;munin-async&lt;/code&gt;)&lt;/h2&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;These 2 processes form an asynchronous proxy between
&lt;code&gt;munin-update&lt;/code&gt; and &lt;code&gt;munin-node&lt;/code&gt;. This avoids the need to
change the plugins or upgrade &lt;code&gt;munin-node&lt;/code&gt; on all nodes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;munin-async-server&lt;/code&gt; should be installed on the same host than
the proxied &lt;code&gt;munin-node&lt;/code&gt; in order to avoid any network issue. It is
the process that will poll regularly &lt;code&gt;munin-node&lt;/code&gt;. The I/O issue of
&lt;code&gt;munin-update&lt;/code&gt; is here non-existent, since &lt;code&gt;munin-async&lt;/code&gt;
stores all the values by simply appending them in a text file without any
further processing. This file is later read by the client's
&lt;code&gt;munin-update&lt;/code&gt;, and it will be processed there.&lt;/p&gt;
&lt;h3&gt;Specific update rates&lt;/h3&gt;
&lt;p&gt;Having one proxy per node enables a polling of all the services there with a
specific update rate.&lt;/p&gt;
&lt;p&gt;To achieve this, &lt;code&gt;munin-async-server&lt;/code&gt; forks into multiple
processes, one for each proxied service. This way each service is completely
isolated from the other, and therefore is able to have its own update rate, is
safe from other plugins slowdowns, and it does even completely parallelize the
information gathering.&lt;/p&gt;
&lt;h3&gt;SSH transport&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;munin-async-client&lt;/code&gt; uses the new SSH native transport of 2.0. It
permits a very simple install of the async proxy.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#rev-pnote-526512-1&quot; id=&quot;pnote-526512-1&quot; name=&quot;pnote-526512-1&quot;&gt;1&lt;/a&gt;] in 1.2 it's the same as plugin, but since 1.4 and the
introduction of &lt;ins&gt;multigraph&lt;/ins&gt;, one plugin can provide multiple
&lt;ins&gt;services&lt;/ins&gt;.&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Asynchronous-updates#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Asynchronous-updates#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/526512</wfw:commentRss>
      </item>
    
  <item>
    <title>CGI on steroids with FastCGI, but on a CGI-only server - The FastCGI wrapper</title>
    <link>http://blog.pwkf.org/post/2010/06/CGI-on-steroids-with-FastCGI%2C-but-on-a-CGI-only-server-The-FastCGI-wrapper</link>
    <guid isPermaLink="false">urn:md5:ba5366e894e1d3032bccad18415143b6</guid>
    <pubDate>Mon, 21 Jun 2010 22:22:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>sysadmin</category>
        <category>design</category><category>performance</category><category>perl</category>    
    <description>    &lt;h2&gt;FastCGI is really &lt;em&gt;CGI on steroids&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;FastCGI is very common way to increase performance of a CGI installation. It
is based on the fact that usually the startup of CGI scripts is slow, whereas
the response is quite fast.&lt;/p&gt;
&lt;p&gt;So if you have a persistent process, you only have to take care of the
startup once, and you then experience a real speedup.&lt;/p&gt;
&lt;h3&gt;FastCGI vs mod_perl (or mod_python, ...)&lt;/h3&gt;
&lt;p&gt;Once a big fan of &lt;code&gt;mod_perl&lt;/code&gt;, I'm converted to FastCGI since.
&lt;code&gt;mod_perl&lt;/code&gt; was for a long time &lt;strong&gt;the&lt;/strong&gt; answer for
speeding up Perl CGI scripts. It has a very good track record of stability and
has real hooks deep in the Apache processing requests.&lt;/p&gt;
&lt;p&gt;FastCGI focuses on a different feature set that is more actual than
&lt;code&gt;mod_perl&lt;/code&gt;&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#pnote-527747-1&quot; id=&quot;rev-pnote-527747-1&quot; name=&quot;rev-pnote-527747-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is much simpler to install and configure, especially when having
multiple applications.&lt;/li&gt;
&lt;li&gt;Able to connect to a distant server (running as a different UID, chrooted
or even on a remote host)&lt;/li&gt;
&lt;li&gt;Able to mix scripting languages without any need to compile some other
apache modules.&lt;/li&gt;
&lt;li&gt;Able to be used with several webservers, even closed-source ones : FastCGI
is a protocol, not an API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;But steroids do have some side effects&lt;/h2&gt;
&lt;h3&gt;CGI issues&lt;/h3&gt;
&lt;p&gt;One downside is that your CGI script should be adapted to FastCGI and the
fact that the script doesn't end with the end of the request.&lt;/p&gt;
&lt;p&gt;In the real world that's quite easy. Every language that is commonly used
for CGI offers &lt;a href=&quot;http://www.fastcgi.com/drupal/node/5&quot; hreflang=&quot;en&quot;&gt;CGI-wrapper libraries&lt;/a&gt; that works in a FastCGI context as well as a
plain CGI one.&lt;/p&gt;
&lt;h3&gt;Webserver issues&lt;/h3&gt;
&lt;p&gt;Another issue can also come from the webserver. Since CGI is dead simple to
implement even the micro-webserver &lt;a href=&quot;http://www.acme.com/software/thttpd/&quot; hreflang=&quot;en&quot;&gt;thttpd&lt;/a&gt; implements
it.&lt;/p&gt;
&lt;p&gt;FastCGI on the other hand is a little more difficult to implement, since the
webserver needs to create a container that monitors and calls the
FastCGI-enabled script.&lt;/p&gt;
&lt;h3&gt;A standalone FastCGI container&lt;/h3&gt;
&lt;p&gt;Fortunately, the FastCGI team provided us with a ready-to-use container and
a very simple client that acts a plain CGI script, but proxies it to a
full-blown container.&lt;/p&gt;
&lt;p&gt;Since the plain CGI part is a very small native executable its overhead is
negligible compared to the reply time, even without comparison with the startup
time of the whole script.&lt;/p&gt;
&lt;p&gt;Its installation is also quite straightforward. I just installed the
&lt;code&gt;libfcgi&lt;/code&gt; package on Debian : it provides
&lt;code&gt;/usr/bin/cgi-fcgi&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I created a simple CGI wrapper for my previous &lt;a href=&quot;http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-FastCGI&quot;&gt;munin
benchmarking&lt;/a&gt; needs :&lt;/p&gt;
&lt;pre&gt;
#! /bin/sh

exec /usr/bin/cgi-fcgi -connect /tmp/munin-cgi.sock \
     /usr/lib/cgi-bin/munin-cgi-graph
&lt;/pre&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#rev-pnote-527747-1&quot; id=&quot;pnote-527747-1&quot; name=&quot;pnote-527747-1&quot;&gt;1&lt;/a&gt;] who really need deep apache hooks ?&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/06/CGI-on-steroids-with-FastCGI%2C-but-on-a-CGI-only-server-The-FastCGI-wrapper#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/06/CGI-on-steroids-with-FastCGI%2C-but-on-a-CGI-only-server-The-FastCGI-wrapper#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/527747</wfw:commentRss>
      </item>
    
  <item>
    <title>Waiting for Munin 2.0 - Performance - FastCGI</title>
    <link>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-FastCGI</link>
    <guid isPermaLink="false">urn:md5:68c4f27314364d8247cef0bd2bb372fc</guid>
    <pubDate>Wed, 16 Jun 2010 23:25:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>munin</category><category>munin20</category>    
    <description>    &lt;p&gt;1.2 has CGI, it is slow, unsupported, but &lt;a href=&quot;http://munin-monitoring.org/wiki/CgiHowto&quot; hreflang=&quot;en&quot;&gt;it does
exist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;1.4 has even an &lt;a href=&quot;http://munin-monitoring.org/wiki/CgiHowto#FastCGI&quot; hreflang=&quot;en&quot;&gt;experimental FastCGI&lt;/a&gt; install mode.&lt;/p&gt;
&lt;p&gt;Quoting from this page :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is more a proof of concept than a recommended - it's slow. Also we do
not test it before every release&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In 2.0 lots of work has been done to take this experimental CGI mode into a
supported one. It might even be the primary way of using munin since, when an
install has a certain size, CGI becomes mandatory.&lt;/p&gt;
&lt;p&gt;That's because &lt;code&gt;munin-graph&lt;/code&gt; doesn't have time to finish its job
when the next one is launched, and the new one doesn't run. It is not as
dramatic as a missed &lt;code&gt;munin-update&lt;/code&gt; execution, since the graphs will
still be generated on the later round, but there will be random graph lags and
it will put quite some stress on the CPU &amp;amp; I/O subsystem. This will slow
&lt;code&gt;munin-update&lt;/code&gt; down since it also uses the I/O subsystem much, and
that's to be avoided at all costs.&lt;/p&gt;
&lt;p&gt;Mainstream CGI has some consequences :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Only the FastCGI wrapper remained : the plain CGI one is dropped.
&lt;ul&gt;
&lt;li&gt;The CPAN module &lt;code&gt;CGI::Fast&lt;/code&gt; is compatible when launched as a
normal CGI.&lt;/li&gt;
&lt;li&gt;Almost all HTTP servers support plain CGI, and with the &lt;a href=&quot;http://www.fastcgi.com/devkit/doc/cgi-fcgi.1&quot; hreflang=&quot;en&quot;&gt;cgi-fcgi&lt;/a&gt;
wrapper from the FastCGI devkit (Debian package &lt;code&gt;libfcgi&lt;/code&gt;), you can
have the best of both worlds (a custom HTTP server &amp;amp; FastCGI). I even
posted on how to have a working &lt;a href=&quot;http://blog.pwkf.org/post/2010/06/CGI-on-steroids-with-FastCGI%2C-but-on-a-CGI-only-server-The-FastCGI-wrapper&quot;&gt;
thttpd with FastCGI&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The old process limit mechanism is dropped also. The FastCGI server
configuration is a much better way to control it. The old code was based on
System V semaphores and was not 100% reliable.&lt;/li&gt;
&lt;li&gt;A caching system has to be implemented, in order for each graph to be
generated only once for its lifetime.&lt;/li&gt;
&lt;li&gt;The CGI process is launched with the HTTP server user. Since it doesn't
only read now, but also writes log files and images files, there is an extra
step when installing it. But it's already described in the &lt;a href=&quot;http://munin-monitoring.org/wiki/CgiHowto&quot; hreflang=&quot;en&quot;&gt;Munin CGI&lt;/a&gt; page
given previously.&lt;/li&gt;
&lt;li&gt;Since the process is launched only once, for now it read only once the
config. So if some part of the config change, the FastCGI container MUST be
restarted.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Some benchmarks&lt;/h2&gt;
&lt;p&gt;Now, the sweet part : I'm putting up some micro-benchmarks.&lt;/p&gt;
&lt;p&gt;They should be taken with caution as every benchmark should be, but I think
the general idea is conveyed. For the sake of simplicity I'm only doing 1
request in parallel and disabled IMS caching.&lt;/p&gt;
&lt;h3&gt;Basic 1.2 CGI&lt;/h3&gt;
&lt;pre&gt;
$ httperf --num-conns 10  --add-header='Cache-Control: no-cache\n' \
    --uri  /cgi-bin/munin-cgi-graph/localdomain/localhost.localdomain/cpu-day.png

Total: connections 10 requests 10 replies 10 test-duration 27.939 s

Connection rate: 0.4 conn/s (2793.9 ms/conn, &amp;lt;=1 concurrent connections)
Connection time [ms]: min 1653.9 avg 2793.9 max 5217.0 median 1912.5 stddev 1487.8
Connection time [ms]: connect 0.0
Connection length [replies/conn]: 1.000

Request rate: 0.4 req/s (2793.9 ms/req)
Request size [B]: 131.0
&lt;/pre&gt;
&lt;h3&gt;1.4 FastCGI&lt;/h3&gt;
&lt;p&gt;The munin-fastcgi-graph is only loaded once, but the munin-graph is reloaded
each time.&lt;/p&gt;
&lt;pre&gt;
$ httperf --num-conns 10  --add-header='Cache-Control: no-cache\n' \
    --uri  /cgi-bin/munin-fastcgi-graph/localdomain/localhost.localdomain/cpu-day.png

Total: connections 10 requests 10 replies 10 test-duration 13.807 s

Connection rate: 0.7 conn/s (1380.7 ms/conn, &amp;lt;=1 concurrent connections)
Connection time [ms]: min 1141.3 avg 1380.7 max 1636.1 median 1381.5 stddev 173.7
Connection time [ms]: connect 0.0
Connection length [replies/conn]: 1.000

Request rate: 0.7 req/s (1380.7 ms/req)
&lt;/pre&gt;
&lt;p&gt;The response time is cut almost in half. That's expected, since only the top
half of the processing isn't reloaded.&lt;/p&gt;
&lt;h3&gt;2.0 FastCGI&lt;/h3&gt;
&lt;p&gt;Here everything is loaded once.&lt;/p&gt;
&lt;pre&gt;
$ httperf --num-conns 10  --add-header='Cache-Control: no-cache\n' \
    --uri  /cgi-bin/munin-cgi-graph-2.0/localdomain/localhost.localdomain/cpu-day.png

Total: connections 10 requests 10 replies 10 test-duration 1.668 s

Connection rate: 6.0 conn/s (166.8 ms/conn, &amp;lt;=1 concurrent connections)
Connection time [ms]: min 123.0 avg 166.8 max 513.4 median 127.5 stddev 121.9
Connection time [ms]: connect 0.0
Connection length [replies/conn]: 1.000

Request rate: 6.0 req/s (166.8 ms/req)
&lt;/pre&gt;
&lt;p&gt;Now response time is cut almost by a ten factor ! That's quite good news,
since it goes 20 times faster that the original CGI.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-FastCGI#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-FastCGI#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/525792</wfw:commentRss>
      </item>
    
  <item>
    <title>Waiting for Munin 2.0 - Performance - Architecture</title>
    <link>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Introduction-Architecture</link>
    <guid isPermaLink="false">urn:md5:08010319781f7bf88a099f0de8c8960e</guid>
    <pubDate>Thu, 10 Jun 2010 03:48:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>munin</category><category>munin20</category>    
    <description>    &lt;h2&gt;A little intro/refresh on munin's architecture on the master&lt;/h2&gt;
&lt;p&gt;Munin has a very simple architecture on the master : &lt;code&gt;munin-cron&lt;/code&gt;
is launched via cron every 5 minutes. Its only job is to launch in order
&lt;code&gt;munin-update&lt;/code&gt;, &lt;code&gt;munin-graph&lt;/code&gt;, &lt;code&gt;munin-html&lt;/code&gt;
&amp;amp; &lt;code&gt;munin-limits&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The various processes&lt;/h2&gt;
&lt;h3&gt;munin-update&lt;/h3&gt;
&lt;p&gt;This process retrieves the values from the various nodes and to update the
rrd files. This one should never take more than 5 minutes to run, otherwise
there will be gaps since the next update will not be launched
(lockfile-protected runs).&lt;/p&gt;
&lt;p&gt;This process stresses the I/O on the master, and depends on the plugins
execution time on the various nodes. On 1.4 the retrieval is
multi-threaded&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#pnote-525782-1&quot; id=&quot;rev-pnote-525782-1&quot; name=&quot;rev-pnote-525782-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;, so an slow node doesn't impact too much the
whole process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.0 proposes asynchronous updates and vectorized
updates.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;munin-graph&lt;/h3&gt;
&lt;p&gt;This process generates all the image files from the rrd files.&lt;/p&gt;
&lt;p&gt;It is usually a process that is quite CPU-bound, it generates also a fair
load of I/O. Since 1.4 there might also be a parallel graphing generation in
order to take advantage of multiple CPU / multiple I/O paths.&lt;/p&gt;
&lt;p&gt;A simple optimization is to generate only needed graphs instead of all of
them each time. This leads to CGI-generation of graphs. 1.2 &amp;amp; 1.4 took a
first step in this direction, but it's quite a hack since it's only a very
basic script that calls &lt;code&gt;munin-update&lt;/code&gt; with the correct
parameters.&lt;/p&gt;
&lt;p&gt;A FastCGI port of the wrapper (&lt;code&gt;munin-cgi-graph&lt;/code&gt;) removes the
overhead of starting the wrapper for each call, but in 1.4 the code is quite
experimental and has some serious bugs that would need extensive patching to be
fixed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.0 completes the integration of CGI graphing with removing the
overhead of calling &lt;code&gt;munin-graph&lt;/code&gt; and does this extensive patching
for bugs fixing&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;munin-html&lt;/h3&gt;
&lt;p&gt;This process generates all the html files from the rrd files. This one is
quite fast for now.&lt;/p&gt;
&lt;h3&gt;munin-limits&lt;/h3&gt;
&lt;p&gt;This process checks the limits to see if there is a warning/alert to send
via mail or nagios. This one is also quite fast for now.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/#rev-pnote-525782-1&quot; id=&quot;pnote-525782-1&quot; name=&quot;pnote-525782-1&quot;&gt;1&lt;/a&gt;] more multi-process actually&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Introduction-Architecture#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Introduction-Architecture#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/525782</wfw:commentRss>
      </item>
    
  <item>
    <title>Waiting for Munin 2.0 - Introduction</title>
    <link>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Introduction</link>
    <guid isPermaLink="false">urn:md5:bb2170b02e8c57e4f5118071419e8e6a</guid>
    <pubDate>Tue, 08 Jun 2010 22:30:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>munin</category><category>munin20</category>    
    <description>    &lt;p&gt;This is the first article of a series about the coming version 2.0 of
&lt;a href=&quot;http://munin-monitoring.org/&quot; hreflang=&quot;en&quot;&gt;Munin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea came from the series &lt;a href=&quot;http://www.depesz.com/index.php/2009/07/03/waiting-for-8-5-lets-start/&quot; hreflang=&quot;en&quot;&gt;Waiting from 8.5&lt;/a&gt; about PostgreSQL.&lt;/p&gt;
&lt;p&gt;The ironic part is that their 8.5 release has become a 9.0, just like our
1.5 will be a 2.0.&lt;/p&gt;
&lt;p&gt;I'll post several small articles about new or enhanced-enough features. They
will all be tagged &lt;a href=&quot;http://blog.pwkf.org/tag/munin20&quot;&gt;munin20&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Planned summary :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Introduction-Architecture&quot;&gt;Performance
- Architecture context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/Using-FastCGI-with-a-CGI-only-server-The-FastCGI-wrapper&quot;&gt;Performance
- FastCGI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Performance-Asynchronous-updates&quot;&gt;Performance
- Asynchronous updates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Performance - Misc&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.pwkf.org/post/2010/07/Waiting-for-Munin-2.0-Native-SSH-transport&quot;&gt;Native
SSH transport&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Custom data retention plans (keep more data)&lt;/li&gt;
&lt;li&gt;Dynamic zooming&lt;/li&gt;
&lt;/ol&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Introduction#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/06/Waiting-for-Munin-2.0-Introduction#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/525488</wfw:commentRss>
      </item>
    
  <item>
    <title>Don't use Excerpt... At least with DotClear.</title>
    <link>http://blog.pwkf.org/post/2010/04/Do-not-use-Excerpt-At-least-with-DotClear</link>
    <guid isPermaLink="false">urn:md5:5e310276dfa3d044fb0f767f3527a814</guid>
    <pubDate>Thu, 01 Apr 2010 08:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>general</category>
            
    <description>    &lt;p&gt;DotClear automatically generates a &lt;code&gt;meta description&lt;/code&gt; tag from
the blog entry, but it doesn't take the excerpt into account.&lt;/p&gt;
&lt;p&gt;It just takes the beginning of the article &lt;strong&gt;content&lt;/strong&gt;. Since
the excerpt is also shown at the beginning of the article, I cannot just write
2 times the same content.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;meta description&lt;/code&gt; is quite interesting since it is usually used
for the little snipped under a search result in usual search engines, so having
the beginning of the post in here is very nice.&lt;/p&gt;
&lt;p&gt;This fact annihilates the &lt;a href=&quot;http://blog.pwkf.org/post/2009/05/Are-Excerpts-a-Good-Thing&quot;&gt;good point of having
excerpts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm now falling back to removing progressively all the excerpts on my
posts...&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/04/Do-not-use-Excerpt-At-least-with-DotClear#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/04/Do-not-use-Excerpt-At-least-with-DotClear#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/504880</wfw:commentRss>
      </item>
    
  <item>
    <title>API Design: Avoid hidden costs of simple features</title>
    <link>http://blog.pwkf.org/post/2010/03/API-Design%3A-Hidden-costs-of-simple-features</link>
    <guid isPermaLink="false">urn:md5:8ea72e6386e7410474ce3ece000deda1</guid>
    <pubDate>Wed, 31 Mar 2010 21:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>programming</category>
        <category>api</category><category>design</category><category>performance</category>    
    <description>    &lt;p&gt;&lt;strong&gt;Programmers&lt;/strong&gt; are usually like water : they &lt;strong&gt;always
use&lt;/strong&gt; the &lt;strong&gt;path of least resistance&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let's see how to use this fact to predict the usage of an API when you
design it.&lt;/p&gt;
&lt;h2&gt;Initial API&lt;/h2&gt;
&lt;p&gt;Consider the very simple DB API that consumes a connected ResultSet and
presents a disconnected version of it.&lt;/p&gt;
&lt;pre&gt;
class DisconnectedResultSet{
        public DisconnectedResultSet (ResultSet rs);
        public boolean next();
        public Object getObject(int col_idx);
}
&lt;/pre&gt;
&lt;p&gt;It's usage is quite easy :&lt;/p&gt;
&lt;pre&gt;
while (drs.next()) {
        int col_idx = 1;
        drs.getObject(col_idx++); // Do something w/ 1st col
        drs.getObject(col_idx++); // Do something w/ 2st col
        //...
}
&lt;/pre&gt;
&lt;h2&gt;Just a &lt;em&gt;little&lt;/em&gt; evolution...&lt;/h2&gt;
&lt;p&gt;Since the &lt;code&gt;DisconnectedResultSet&lt;/code&gt; is &lt;em&gt;disconnected&lt;/em&gt;, we
can imagine that it should implement a &lt;code&gt;rewind()&lt;/code&gt; method in order to
use it several times without running the initial query again. We now have an
updated class :&lt;/p&gt;
&lt;pre&gt;
class DisconnectedResultSet{
        public DisconnectedResultSet (ResultSet rs);
        public boolean next();
        public Object getObject(int col_idx);   
        public void rewind(); // Be able to rewind it
}
&lt;/pre&gt;
&lt;p&gt;And its classical usage :&lt;/p&gt;
&lt;pre&gt;
while (drs.next()) {
        // do stuff...
}
// ...
drs.rewind();
while (drs.next()) {
        // do something else with the same data...
}
// ...
drs.rewind();
while (drs.next()) {
        // do something else with the same data...
}
// ...
&lt;/pre&gt;
&lt;h2&gt;A new need comes&lt;/h2&gt;
&lt;p&gt;A new need comes : see if the &lt;code&gt;DisconnectedResultSet&lt;/code&gt; is empty or
not in order to avoid sending header.&lt;/p&gt;
&lt;p&gt;The usual way is to send them once when iterating like :&lt;/p&gt;
&lt;pre&gt;
boolean is_headers_sent = false;
while (drs.next()) {
        if (! is_headers_sent) { 
                send_headers(); 
                is_headers_sent = true;
        }
        // do something else with the same data...
}
&lt;/pre&gt;
&lt;p&gt;But since there is a nice &lt;code&gt;rewind()&lt;/code&gt;method, just waiting to be
used, the code might become :&lt;/p&gt;
&lt;pre&gt;
if (drs.next()) {
        send_headers(); 
}
drs.rewind();
while (drs.next()) {
        // do something else with the same data...
}
&lt;/pre&gt;
&lt;p&gt;Now, this code isn't generic anymore to accommodate a connected
ResultSet.&lt;/p&gt;
&lt;p&gt;So, as John Carmack said :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The cost of adding a feature isn't just the time it takes to code it. The
cost also includes the addition of an obstacle to future expansion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's really true when you design &lt;strong&gt;APIs&lt;/strong&gt; since their purpose
is to &lt;strong&gt;last long&lt;/strong&gt; and to &lt;strong&gt;be extended&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So, &lt;strong&gt;think twice when you propose an extension &amp;quot;just in
case&amp;quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The &lt;em&gt;little&lt;/em&gt; evolution, revisited...&lt;/h2&gt;
&lt;p&gt;To solve this case, don't propose a &lt;code&gt;rewind()&lt;/code&gt; method, but offer
a &lt;code&gt;duplicate()&lt;/code&gt; one. It offers the same functionality, just in a new
object.&lt;/p&gt;
&lt;p&gt;The usage will be almost the same as shown below, but since it feels more
performance-sensitive, it won't be used as lightly : the &lt;code&gt;boolean
is_headers_sent&lt;/code&gt; pattern has now more chances to be used.&lt;/p&gt;
&lt;pre&gt;
while (drs.next()) {
        // do stuff...
}
// ...
drs = drs.duplicate();
while (drs.next()) {
        // do something else with the same data...
}
// ...
drs = drs.duplicate();
while (drs.next()) {
        // do something else with the same data...
}
// ...
&lt;/pre&gt;
&lt;p&gt;It's an other example that &lt;a href=&quot;http://blog.pwkf.org/post/2007/12/03/Use-immutable-objects-to-avoid-synchronisation&quot;&gt;immutable
objects are the way to go&lt;/a&gt;, but for a different reason this time.&lt;/p&gt;
&lt;p&gt;Note: Just finished my March 2010 article, even &lt;strong&gt;on time&lt;/strong&gt;...
I'm still trying to keep at least a one article per month blogging rate. So far
so good for 2010, still 9 months to go !&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/03/API-Design%3A-Hidden-costs-of-simple-features#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/03/API-Design%3A-Hidden-costs-of-simple-features#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/475523</wfw:commentRss>
      </item>
    
  <item>
    <title>Free Exception lunch : Use unchecked exceptions, but still announce which ones you might throw.</title>
    <link>http://blog.pwkf.org/post/2010/02/You-Shall-Declare-The-Unchecked-Exceptions-You-Might-Throw</link>
    <guid isPermaLink="false">urn:md5:03e06c61165fcdfa2d6e0d529dfbeb36</guid>
    <pubDate>Sat, 20 Feb 2010 16:01:00 +0100</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>java</category>
            
    <description>    &lt;p&gt;In a previous article I choosed my side : &lt;a href=&quot;http://blog.pwkf.org/post/2009/07/Checked-or-Unchecked-Exceptions-for-Legacy-Code&quot;&gt;Unchecked
Exceptions are much simpler to use&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But, on the other side of this great division, there is a very valid point :
&lt;strong&gt;You usually declare checked exceptions&lt;/strong&gt;. Sure it's possible to
only declare to throw &lt;code&gt;Exception&lt;/code&gt;, but that would defeat the whole
purpose of using checked exceptions.&lt;/p&gt;
&lt;p&gt;The nicest thing is that you can also have a custom exception hierarchy, but
based on &lt;code&gt;RuntimeException&lt;/code&gt; instead of a plain
&lt;code&gt;Exception&lt;/code&gt;. This way it's like in C++. Everything might be thrown,
and you don't &lt;strong&gt;need&lt;/strong&gt; to handle them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Declaring them&lt;/strong&gt;, on the other side, is very interesting
because you are &lt;strong&gt;documenting your interface&lt;/strong&gt; for almost
&lt;strong&gt;free&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So, use unchecked exceptions to free yourself of the checked catch-slavery,
but still declare the custom ones you might throw.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/02/You-Shall-Declare-The-Unchecked-Exceptions-You-Might-Throw#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/02/You-Shall-Declare-The-Unchecked-Exceptions-You-Might-Throw#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/436982</wfw:commentRss>
      </item>
    
  <item>
    <title>Immutability of an URL</title>
    <link>http://blog.pwkf.org/post/2010/02/Immutability-of-an-URL</link>
    <guid isPermaLink="false">urn:md5:d309c7c0d59a5f7e7cbcd5c896b3f2cd</guid>
    <pubDate>Sat, 20 Feb 2010 15:18:00 +0100</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>general</category>
            
    <description>    &lt;p&gt;In the pure spirit of &lt;a href=&quot;http://www.javapractices.com/topic/TopicAction.do?Id=211&quot; hreflang=&quot;en&quot;&gt;Data
is King&lt;/a&gt; I think that URL should &lt;strong&gt;never&lt;/strong&gt; change. Even the W3C
agrees with their &lt;a href=&quot;http://www.w3.org/Provider/Style/URI.html&quot; hreflang=&quot;en&quot;&gt;Cool URIs don't change&lt;/a&gt; article.&lt;/p&gt;
&lt;p&gt;But we all know that in IT &lt;em&gt;never&lt;/em&gt; is only &lt;em&gt;not in the foreseen
future&lt;/em&gt;. So URL &lt;strong&gt;do&lt;/strong&gt; change, at least after a while, and
usually for technical reasons&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/02/#pnote-397657-1&quot; id=&quot;rev-pnote-397657-1&quot; name=&quot;rev-pnote-397657-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Since you can update your website to update the URLs, but the inbound link
cannot be easily updated. To handle this need, the HTTP protocol has specified
the 301 response code.&lt;/p&gt;
&lt;p&gt;The solution is that the site should remember all the urls that it generated
and redirects accordingly. This way you'll never loose a potential reader to
the infamous 404 (this page does not exist).&lt;/p&gt;
&lt;p&gt;Some sites even try to approximate the page on a custom 404 page. That's
another reason to have user-friendly urls : to be able to hint your reader to
appropriate pages in case you don't find his initial destination.&lt;/p&gt;
&lt;p&gt;Sadly, this redirect behavior isn't supported by my blog engine
(dotclear)... That's for the &lt;a href=&quot;http://en.wikipedia.org/wiki/Eating_one%27s_own_dog_food&quot; hreflang=&quot;en&quot;&gt;eat
your own dog's food&lt;/a&gt;, but I'm looking forward to do it on my current
blogging platform.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2010/02/#rev-pnote-397657-1&quot; id=&quot;pnote-397657-1&quot; name=&quot;pnote-397657-1&quot;&gt;1&lt;/a&gt;] upgrade to another blog engine...&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2010/02/Immutability-of-an-URL#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2010/02/Immutability-of-an-URL#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/397657</wfw:commentRss>
      </item>
    
  <item>
    <title>Avoid the Preprocessor : Use ''Compile-Time Polymorphism'' for Cross-platform Development</title>
    <link>http://blog.pwkf.org/post/2009/12/Avoid-the-Preprocessor-Use-Compile-Time-Polymorphism-for-Cross-platform-Development</link>
    <guid isPermaLink="false">urn:md5:ac68468a97cc5a2146661e506b24a7c2</guid>
    <pubDate>Fri, 11 Dec 2009 23:00:00 +0100</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>c++</category>
            
    <description>&lt;p&gt;When writing portable cross-platform code, don't litter your code with
preprocessor macros, use &lt;em&gt;compile-time polymorphism&lt;/em&gt; instead.&lt;/p&gt;
&lt;p&gt;A flexible build system will enable you to use advanced OOP-like
compile-time polymorphism. That way you can hide all the specifics of the
different platform behind an interface firewall. It is the usual way that most
cross-platform toolkits and frameworks (such as QT, GTK or wxWidgets) are
designed.&lt;/p&gt;    &lt;p&gt;We'll take a very simple file I/O subsystem here as an example
(&lt;code&gt;open&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;). We'll also only take Linux and
Windows, since that's usually the 2 most common platforms people want to
develop for&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/12/#pnote-467153-1&quot; id=&quot;rev-pnote-467153-1&quot; name=&quot;rev-pnote-467153-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;
&lt;h2&gt;The usual abstraction with the preprocessor&lt;/h2&gt;
&lt;p&gt;Usually preprocessor &lt;code&gt;#ifdef&lt;/code&gt;s are used to compile specific parts
for the underlying OS.&lt;/p&gt;
&lt;p&gt;The file &lt;code&gt;fileiosys.cpp&lt;/code&gt; looks&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/12/#pnote-467153-2&quot; id=&quot;rev-pnote-467153-2&quot; name=&quot;rev-pnote-467153-2&quot;&gt;2&lt;/a&gt;]&lt;/sup&gt; like the one below
:&lt;/p&gt;
&lt;pre&gt;
fileiosys_filedesc open(char* filename) {
#ifdefine __WIN32__
    return OpenFile(
        filename, 
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, 
        NULL,
    );  
#else // UNIX
    return open(filename, O_READ | O_WRITE, 0666);
#endif
}

int close (fileiosys_filedesc fd) {
#ifdefine __WIN32__
    return CloseHandle(fd) ? 0 : -1;
#else // UNIX
    return close(fd);
#endif
}
&lt;/pre&gt;
&lt;p&gt;The header &lt;code&gt;fileiosys.h&lt;/code&gt; looks then like :&lt;/p&gt;
&lt;pre&gt;
// Define a custom file descriptor
#ifdefine __WIN32__
    #define fileiosys_filedesc HANDLE
#else // UNIX
    #define fileiosys_filedesc int
#endif

fileiosys_filedesc open(char* filename); 
int close(fileiosys_filedesc fd); 
&lt;/pre&gt;
&lt;h2&gt;Avoid the preprocessor Compile-time polymorphism&lt;/h2&gt;
&lt;p&gt;As the previous little example, the code isn't really easy to read. You have
to always think which environnement you are in. All the specifics are
multiplexed in the same file, and the programmer has to always demultiplex it
in real time each time he reads the code.&lt;/p&gt;
&lt;p&gt;The last part of the interface file is quite simple to read since it's
already abstracted away. Now let's completely demultiplex the implementation in
several implementation files.&lt;/p&gt;
&lt;h3&gt;The header file is the common interface&lt;/h3&gt;
&lt;p&gt;The interface is the most important part of the design. It should be
high-level enough to mask the differences between the plateforms you want to
support, but not too high-level, otherwise you'll end up duplicating to much
code.&lt;/p&gt;
&lt;p&gt;So, for our file I/O subsystem, we'll just abstract the usual syscalls
&lt;code&gt;open&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt; in the same way as before.&lt;/p&gt;
&lt;p&gt;The parameters that are passed through the interface are also very
important. You cannot usually leak a platform-specific structure. So here all
the file descriptors are just an opaque handle, represented by a pointer to a
structure defined only as a forward declaration in the header. This pattern,
sometimes called the &lt;a href=&quot;http://en.wikipedia.org/wiki/Opaque_pointer&quot; hreflang=&quot;en&quot;&gt;pimpl idiom&lt;/a&gt;, enables use to really share the representation
while implementing it differently.&lt;/p&gt;
&lt;p&gt;The header file is preserved as &lt;code&gt;fileiosys.h&lt;/code&gt;, whereas the
implementation is in the files &lt;code&gt;linux/fileiosys.cpp&lt;/code&gt; and
&lt;code&gt;win32/fileiosys.cpp&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;&lt;code&gt;fileiosys.h&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;
// Define the forward declaration
struct fis_filedesc;
fis_filedesc* open(char* filename); 
int close(fileiosys_filedesc* fd); 
&lt;/pre&gt;
&lt;h4&gt;&lt;code&gt;win32/fileiosys.cpp&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;
struct fis_filedesc {
    HANDLE handle;
};

fis_filedesc* open(char* filename) {
    fis_filedesc* fd = new fis_filedesc();
    fd-&amp;gt;handle = OpenFile(
        filename, 
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, 
        NULL,
    );  

    return fd;
}

int close (fis_filedesc* fd) {
    int retval = CloseHandle(fd-&amp;gt;handle) ? 0 : -1;
    delete(fd);
    return retval;
}
&lt;/pre&gt;
&lt;h4&gt;&lt;code&gt;linux/fileiosys.cpp&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;
struct fis_filedesc {
    int file_descriptor;
};

static fis_filedesc[32];

fis_filedesc* open(char* filename) {
    fis_filedesc* fd = new fis_filedesc();
    fd-&amp;gt;file_descriptor = open(filename, O_READ | O_WRITE, 0666);
    return fd;
}

int close (fis_filedesc* fd) {
    int retval = close(fd);
    delete(fd);
    return retval;
}
&lt;/pre&gt;
&lt;h4&gt;The build system : &lt;code&gt;Makefile&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The makefile should take into account the different platforms, and only
compile the needed implementation file. All the gluing magic will then be done
at linking time instead of preprocessor time.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The interest of have multiple implementation files is obvious. It is
&lt;strong&gt;much&lt;/strong&gt; more straightforward to read and only marginally harder
to write. But since most of the time code is read and not written, the choose
is quite a no-brainer.&lt;/p&gt;
&lt;p&gt;The nicest part is that all this is possible even without the expensive
run-time polymorphism and RTTI, since the choose is done at compile-time.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/12/#rev-pnote-467153-1&quot; id=&quot;pnote-467153-1&quot; name=&quot;pnote-467153-1&quot;&gt;1&lt;/a&gt;] actually when targeting Linux, you usually target all
Unix-like systems since they already have POSIX as a common abstraction&lt;/p&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/12/#rev-pnote-467153-2&quot; id=&quot;pnote-467153-2&quot; name=&quot;pnote-467153-2&quot;&gt;2&lt;/a&gt;] The code is not real, it has been sweetened&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/12/Avoid-the-Preprocessor-Use-Compile-Time-Polymorphism-for-Cross-platform-Development#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/12/Avoid-the-Preprocessor-Use-Compile-Time-Polymorphism-for-Cross-platform-Development#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/467153</wfw:commentRss>
      </item>
    
  <item>
    <title>Native SSH transport for Munin</title>
    <link>http://blog.pwkf.org/post/2009/11/Native-SSH-transport-for-Munin</link>
    <guid isPermaLink="false">urn:md5:9850ae8f77d67536a24ea8c5e21b723e</guid>
    <pubDate>Thu, 26 Nov 2009 22:00:00 +0100</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>munin</category>
        <category>munin</category><category>ssh</category>    
    <description>&lt;p&gt;I &lt;a href=&quot;http://blog.pwkf.org/post/2008/11/04/A-Poor-Man-s-Munin-Node-to-Monitor-Hostile-UNIX-Servers&quot;&gt;blogged&lt;/a&gt;
about the &lt;a href=&quot;http://munin.projects.linpro.no/&quot; hreflang=&quot;en&quot;&gt;munin
monitoring system&lt;/a&gt; a while ago.&lt;/p&gt;
&lt;p&gt;The fact the Munin team did quite a remarkable job in cleaning up the 1.2
code for the 1.4 release enabled me to add a native SSH transport for Munin,
and made be able to get rid of all SSH tunnels.&lt;/p&gt;    &lt;p&gt;Actually the tunnel won't disappear, but they will be launched only when
needed and, most importantly configured in &lt;code&gt;munin.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://blog.pwkf.org/public/direct_ssh_transport.diff&quot; hreflang=&quot;en&quot;&gt;native ssh for
munin patch&lt;/a&gt; should applies quite cleanly on revision 3101 of the svn
trunk.&lt;/p&gt;
&lt;p&gt;Its use is quite straightforward : in &lt;code&gt;/etc/munin/munin.conf&lt;/code&gt;,
you just migrate address to the new configuration directive
&lt;code&gt;remote_connection_cmd&lt;/code&gt; that take the whole &lt;code&gt;ssh&lt;/code&gt; command
to launch a stdio munin-node such as pmmn.&lt;/p&gt;
&lt;p&gt;If we take the examples from the previous post, it becomes clear it's
&lt;strong&gt;much&lt;/strong&gt; easier to configure.&lt;/p&gt;
&lt;h3&gt;munin.conf snippet - Inetd version&lt;/h3&gt;
&lt;pre&gt;
[server1]
    address localhost
    port 7001
[server2]
    address localhost
    port 7002
&lt;/pre&gt;
&lt;h3&gt;munin.conf snippet - Native SSH transport version&lt;/h3&gt;
&lt;p&gt;Right now, the &lt;code&gt;address&lt;/code&gt; directive is still mandatory, but
ignored when connecting.&lt;/p&gt;
&lt;pre&gt;
[server1]
    address dummy
    remote_connection_cmd /usr/bin/ssh -- supusr@server1 /home/suprusr/pmmn/pmmn.pl
[server2]
    address dummy
    remote_connection_cmd /usr/bin/ssh -- supusr@server2 /home/suprusr/pmmn/pmmn.pl
&lt;/pre&gt;
&lt;h3&gt;Warning&lt;/h3&gt;
&lt;p&gt;Beware that the &lt;code&gt;ssh&lt;/code&gt; process will now be launched by the
&lt;code&gt;munin&lt;/code&gt; user, so you have to update the key-based SSH authentication
accordingly.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/11/Native-SSH-transport-for-Munin#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/11/Native-SSH-transport-for-Munin#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/462735</wfw:commentRss>
      </item>
    
  <item>
    <title>Sed is much slower than Perl, or not...</title>
    <link>http://blog.pwkf.org/post/2009/11/Sed-is-much-slower-than-Perl-or-not...</link>
    <guid isPermaLink="false">urn:md5:97ad55cfcb78666f554d7bfbd3f3915e</guid>
    <pubDate>Sat, 14 Nov 2009 12:18:00 +0100</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>sysadmin</category>
        <category>locales</category><category>perl</category><category>sql</category>    
    <description>    &lt;p&gt;I wanted to do some text replacement with a &lt;strong&gt;huge&lt;/strong&gt; file
(think ~18GiB), filled with &lt;strong&gt;huge&lt;/strong&gt; lines (think ~2MiB per
ligne)&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/11/#pnote-459477-1&quot; id=&quot;rev-pnote-459477-1&quot; name=&quot;rev-pnote-459477-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I naïvely piped it through &lt;code&gt;sed&lt;/code&gt; and I was quite shocked that it
was CPU bound, and not I/O bound. The average rate was about 5 MiB/s (measured
with &lt;a href=&quot;http://www.ivarch.com/programs/pv.shtml&quot; hreflang=&quot;en&quot;&gt;pv&lt;/a&gt;,
and the CPU was at almost 100%.The text file was gzipped on the filesystem, but
with a 1/100 ratio, so the gzip process just took less than 2% CPU. I replaced
then the &lt;code&gt;sed -e&lt;/code&gt; with the Perl one-liner &lt;code&gt;perl -lnpe&lt;/code&gt;,
and .... &lt;em&gt;tadaa&lt;/em&gt;, it was flying at a rate of 50MiB/s !&lt;/p&gt;
&lt;p&gt;While I'm a big fan of Perl, and know its effectiveness to handle text
streams, I'm was still astonished : being 10x faster than sed was
something.&lt;/p&gt;
&lt;p&gt;But in the good old saying &lt;q&gt;Too good to be true means suspect&lt;/q&gt;, I
remembered something about the character encoding of the regular expression.
Since the system is entirely configured in UTF8, I suspected the
&lt;em&gt;infamous&lt;/em&gt; UTF8 overhead over plain ASCII.&lt;/p&gt;
&lt;p&gt;I was right : a little &lt;code&gt;LANG=C&lt;/code&gt; in front of the sed command line
restored the rate to 50MiB/s.&lt;/p&gt;
&lt;p&gt;So, &lt;strong&gt;beware&lt;/strong&gt; of the &lt;strong&gt;performance impact&lt;/strong&gt; of
&lt;strong&gt;UTF8&lt;/strong&gt; strings, and try to avoid it if you can.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/11/#rev-pnote-459477-1&quot; id=&quot;pnote-459477-1&quot; name=&quot;pnote-459477-1&quot;&gt;1&lt;/a&gt;] For the record, it was a MySQL dump&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/11/Sed-is-much-slower-than-Perl-or-not...#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/11/Sed-is-much-slower-than-Perl-or-not...#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/459477</wfw:commentRss>
      </item>
    
  <item>
    <title>Quickly replicate the clock between remote hosts with SSH</title>
    <link>http://blog.pwkf.org/post/2009/09/Code-snippet-to-quickly-replicate-the-clock-from/to-a-remote-host</link>
    <guid isPermaLink="false">urn:md5:11e72e879c3d3f63b031ab72bb65db9f</guid>
    <pubDate>Fri, 11 Sep 2009 08:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>sysadmin</category>
        <category>ssh</category>    
    <description>    &lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Network_Time_Protocol&quot; hreflang=&quot;en&quot;&gt;NTP&lt;/a&gt; is very handy for server clock synchronisation, but it can be
cumbersome to deploy.&lt;/p&gt;
&lt;p&gt;Sometimes you just need to do a one-shot clock synchronisation, so you use
the standard &lt;code&gt;date&lt;/code&gt; command. But there isn't a flag to easily copy a
setting to another.&lt;/p&gt;
&lt;h2&gt;From a remote host&lt;/h2&gt;
&lt;p&gt;Quite easy :&lt;/p&gt;
&lt;pre&gt;
# date `ssh remoteuser@remotehost date +%m%d%H%M%Y.%S`
&lt;/pre&gt;
&lt;h2&gt;To a remote host&lt;/h2&gt;
&lt;p&gt;It's also very easy&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/Code-snippet-to-quickly-replicate-the-clock-from/#pnote-440747-1&quot; id=&quot;rev-pnote-440747-1&quot; name=&quot;rev-pnote-440747-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; :&lt;/p&gt;
&lt;pre&gt;
# ssh root@remotehost date `date +%m%d%H%M%Y.%S`
&lt;/pre&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/Code-snippet-to-quickly-replicate-the-clock-from/#rev-pnote-440747-1&quot; id=&quot;pnote-440747-1&quot; name=&quot;pnote-440747-1&quot;&gt;1&lt;/a&gt;] Yes, I &lt;strong&gt;do&lt;/strong&gt; know that logging remotely
as root is a security pitfall...&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/09/Code-snippet-to-quickly-replicate-the-clock-from/to-a-remote-host#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/09/Code-snippet-to-quickly-replicate-the-clock-from/to-a-remote-host#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/440747</wfw:commentRss>
      </item>
    
  <item>
    <title>Databases: Efficient Case-insensitive searches with Function-based Indexing</title>
    <link>http://blog.pwkf.org/post/2009/09/Databases%3A-Efficient-Case-insensitive-searches-with-Function-based-Indexing</link>
    <guid isPermaLink="false">urn:md5:0672e1835437418b84886618aa2e1440</guid>
    <pubDate>Tue, 08 Sep 2009 22:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>database</category>
            
    <description>&lt;p&gt;Doing a &lt;strong&gt;case insensitive search&lt;/strong&gt; is a very &lt;strong&gt;common
task&lt;/strong&gt;, but is quite &lt;strong&gt;hard to optimize correctly&lt;/strong&gt;. But
since it's done via a &lt;code&gt;UPPER(MY_COLUMN) = UPPER('MY_DATA')&lt;/code&gt;, it
doesn't use the index that could be on &lt;code&gt;MY_COLUMN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Different RDMS means different approaches.&lt;/p&gt;    &lt;h2&gt;Special case of case-insensitive search&lt;/h2&gt;
&lt;p&gt;In Oracle10g, you might use the new case-insensitive search with a
&lt;code&gt;&lt;a href=&quot;http://www.dba-oracle.com/t_oracle10g_release_2_case_insensitive_searches.htm&quot; hreflang=&quot;en&quot;&gt;NLS_SORT=BINARY_CI&lt;/a&gt;&lt;/code&gt; command.&lt;/p&gt;
&lt;h3&gt;Pro&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Designed for this purpose, so it's very straightforward to use&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Con&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Limited to case-insensitive searching by design&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Native functional indexes&lt;/h2&gt;
&lt;p&gt;Some databases provides native functional indexes.&lt;/p&gt;
&lt;p&gt;On these databases optimization is done simply by creating an index on
&lt;code&gt;UPPER(MY_COLUMN)&lt;/code&gt; and letting the query optimizer
transparently&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#pnote-426279-1&quot; id=&quot;rev-pnote-426279-1&quot; name=&quot;rev-pnote-426279-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; use the newly created index.&lt;/p&gt;
&lt;p&gt;It usually work by applies a function to the data just before handing it to
the index, so the function output doesn't exist in the database.&lt;/p&gt;
&lt;h3&gt;Pro&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Very&lt;/strong&gt; easy to use : it just feels right (you can naïvely
create an index on the WHERE clause)&lt;/li&gt;
&lt;li&gt;Doesn't take any extra space in the database (only the index).&lt;/li&gt;
&lt;li&gt;Generic, can be used for something else than just case-insensitive
searches.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Con&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Since the data isn't stored in the database, a call to the function has to
be made when&lt;/li&gt;
&lt;li&gt;Functions have to be from the immutable category in the &lt;a href=&quot;http://www.postgresql.org/docs/8.3/static/xfunc-volatility.html&quot; hreflang=&quot;en&quot;&gt;function volatility categories&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Generated columns&lt;/h2&gt;
&lt;p&gt;DB2 provides something called &lt;a href=&quot;http://publib.boulder.ibm.com/infocenter/db2luw/v8/topic/com.ibm.db2.udb.doc/ad/c0007004.htm&quot; hreflang=&quot;en&quot;&gt;generated columns&lt;/a&gt;. It's almost the same than the native
indexes, except that the functional column is explicit.&lt;/p&gt;
&lt;h3&gt;Pro&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Quite easy to use, since the column is updated and used transparently.&lt;/li&gt;
&lt;li&gt;Generic, can be used for something else than just case-insensitive
searches. Just make sure the optimizer uses the extra column. You might have to
rewrite the request a little.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Con&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Requires extra space in the table.&lt;/li&gt;
&lt;li&gt;Removing a column can be cumbersome (in DB2 you have to recreate the whole
table for example), whereas removing a simple index is much easier.&lt;/li&gt;
&lt;li&gt;The extra column is returned when doing a &lt;code&gt;SELECT * FROM
...&lt;/code&gt;&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#pnote-426279-2&quot; id=&quot;rev-pnote-426279-2&quot; name=&quot;rev-pnote-426279-2&quot;&gt;2&lt;/a&gt;]&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Trigger-based &lt;em&gt;Generated columns&lt;/em&gt; Emulation&lt;/h2&gt;
&lt;p&gt;If nothing else is provided, you always have the option to emulate. The
solution will be trigger-based since it's one of the few perfect match for
them.&lt;/p&gt;
&lt;p&gt;So, the base idea is derived from the &lt;em&gt;Generated columns&lt;/em&gt; : have a
special extra column that represents the output of the function. An index will
be created on this column and used via a &lt;em&gt;manual&lt;/em&gt; update of the involved
requests (Adding an extra &lt;code&gt;WHERE&lt;/code&gt; clause should be more than enough,
this way you might even benefit from a partial match).&lt;/p&gt;
&lt;h3&gt;Pro&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Universal. Useful if portability is paramount.&lt;/li&gt;
&lt;li&gt;Very simple : there is no need to understand advanced database
features.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Con&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Only a poor man's solution : everything is manual&lt;/li&gt;
&lt;li&gt;The same than &lt;em&gt;Generated columns&lt;/em&gt; since it's the same idea, just
manually implemented.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#rev-pnote-426279-1&quot; id=&quot;pnote-426279-1&quot; name=&quot;pnote-426279-1&quot;&gt;1&lt;/a&gt;] You might have to update some kind of statistics for
the new index&lt;/p&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#rev-pnote-426279-2&quot; id=&quot;pnote-426279-2&quot; name=&quot;pnote-426279-2&quot;&gt;2&lt;/a&gt;] But you don't do that anyway, do you ?&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/09/Databases%3A-Efficient-Case-insensitive-searches-with-Function-based-Indexing#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/09/Databases%3A-Efficient-Case-insensitive-searches-with-Function-based-Indexing#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/426279</wfw:commentRss>
      </item>
    
  <item>
    <title>Overloading a method is hard : a common pitfall</title>
    <link>http://blog.pwkf.org/post/2009/09/Overloading-method-is-hard-%3A-a-common-pitfall</link>
    <guid isPermaLink="false">urn:md5:b89efe0436b185be77c2bd6856338b51</guid>
    <pubDate>Mon, 07 Sep 2009 22:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>java</category>
            
    <description>    &lt;p&gt;As I said in my &lt;a href=&quot;http://blog.pwkf.org/post/2009/06/Equality-in-Java-is-a-Hot-Topic-but-a-Hazardous-one&quot;&gt;equality
article&lt;/a&gt;, overloading in Java&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#pnote-439371-1&quot; id=&quot;rev-pnote-439371-1&quot; name=&quot;rev-pnote-439371-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt; is resolved by the
&lt;strong&gt;static&lt;/strong&gt; type of the argument, not the &lt;strong&gt;run-time&lt;/strong&gt;
type.&lt;/p&gt;
&lt;p&gt;It's a &lt;strong&gt;generic&lt;/strong&gt; problem of most compiled OO languages since
usually &lt;strong&gt;overloading resolution happens at compile-time and not at
runtime&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now, &lt;em&gt;that&lt;/em&gt; militates for the well known idiom :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Never overload a method with one that has the same number of parameters.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Actually, it should be enough to overload a method with one that accept
parameters that are not inheritance-related : &lt;code&gt;String&lt;/code&gt; and
&lt;code&gt;Number&lt;/code&gt; would be OK, but &lt;code&gt;MyClass&lt;/code&gt; and
&lt;code&gt;Object&lt;/code&gt; would not.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/09/#rev-pnote-439371-1&quot; id=&quot;pnote-439371-1&quot; name=&quot;pnote-439371-1&quot;&gt;1&lt;/a&gt;] It's not really a &lt;em&gt;Java&lt;/em&gt;-ism, it's the same in
other languages, such as C++ .&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/09/Overloading-method-is-hard-%3A-a-common-pitfall#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/09/Overloading-method-is-hard-%3A-a-common-pitfall#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/439371</wfw:commentRss>
      </item>
    
  <item>
    <title>A Simple Dns Server for a SOHO Network</title>
    <link>http://blog.pwkf.org/post/2009/08/A-Simple-Dns-Server-for-a-SOHO-Network</link>
    <guid isPermaLink="false">urn:md5:980ba57dcf712852978b5c8496cfeca0</guid>
    <pubDate>Mon, 10 Aug 2009 20:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>sysadmin</category>
        <category>sql</category>    
    <description>    &lt;p&gt;I'm in search of a very simple DNS Server for a small network. It should be
:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;recursive &amp;amp; caching (can be used as a proxy)&lt;/li&gt;
&lt;li&gt;very simple administration (parsing /etc/hosts would be perfect, raw DNS
zones like BIND would be a little bit overkill)&lt;/li&gt;
&lt;li&gt;quite lightweight (aka no dependency on an SQL engine like MySQL, such as
MyDNS)&lt;/li&gt;
&lt;li&gt;Seamless integration to Windows lookups (nmblookup) via proxying functions
(DNS to/from NMB)&lt;/li&gt;
&lt;/ul&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/08/A-Simple-Dns-Server-for-a-SOHO-Network#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/08/A-Simple-Dns-Server-for-a-SOHO-Network#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/340907</wfw:commentRss>
      </item>
    
  <item>
    <title>Databases: Better Defer Constraints than Avoid Them</title>
    <link>http://blog.pwkf.org/post/2009/07/Databases%3A-Better-Defer-Constraints-than-Avoid-Them</link>
    <guid isPermaLink="false">urn:md5:23cfdca534043c8742e5371ebd5a5ede</guid>
    <pubDate>Fri, 31 Jul 2009 08:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>database</category>
            
    <description>&lt;p&gt;Constraints are considered a &lt;q&gt;Good Thing&lt;/q&gt;. They enable to rely more
heavily on the validity of the database. It is quite important to note that
validity is in terms of &lt;strong&gt;modeling&lt;/strong&gt; and not in terms of
&lt;strong&gt;business&lt;/strong&gt; &lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#pnote-415135-1&quot; id=&quot;rev-pnote-415135-1&quot; name=&quot;rev-pnote-415135-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The biggest complains we can have about constraints is that it is sometimes
quite annoying to do some updates while consistently validating the
constraints. You have to care about the order of the operations you do.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#rev-pnote-415135-1&quot; id=&quot;pnote-415135-1&quot; name=&quot;pnote-415135-1&quot;&gt;1&lt;/a&gt;] I'll write an article about this later&lt;/p&gt;
&lt;/div&gt;    &lt;h2&gt;&lt;em&gt;ACID&lt;/em&gt;ity of transactions as rescue...&lt;/h2&gt;
&lt;p&gt;Let's examine what all of the &lt;code&gt;A&lt;/code&gt;, the &lt;code&gt;C&lt;/code&gt;, the
&lt;code&gt;I&lt;/code&gt; and the &lt;code&gt;D&lt;/code&gt; have for implications on our current
subject.&lt;/p&gt;
&lt;h3&gt;Transactions are &lt;strong&gt;atomic&lt;/strong&gt; (A)&lt;/h3&gt;
&lt;p&gt;The database should not really care about validating the constraints all the
time Only the beginning and end state is really important. &lt;strong&gt;Inside a
transaction, the data may be inconsistent&lt;/strong&gt; : &lt;q&gt;Dust hasn't yet
settled&lt;/q&gt;.&lt;/p&gt;
&lt;p&gt;Let's study an example. You have an unique index on an ordering.&lt;/p&gt;
&lt;pre&gt;
TABLE ITEMS (
    ITEM_ID SERIAL,
    GROUP_ID INTEGER,
    ORDER INTEGER,
    LABEL VARCHAR
)

ADD UNIQUE INDEX ON ITEMS(GROUP_ID, ORDER)
&lt;/pre&gt;
&lt;p&gt;Here, if you want to swap 2 items, you have to use an unused temporary
value, otherwise the check won't be valid &lt;strong&gt;at all times&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Transactions are &lt;strong&gt;consistent&lt;/strong&gt; (C)&lt;/h3&gt;
&lt;p&gt;The check &lt;strong&gt;needs&lt;/strong&gt; to be done at least when the transaction
ends. No special need here except that you &lt;strong&gt;need&lt;/strong&gt; to re-enable
before ending the transaction.&lt;/p&gt;
&lt;h3&gt;Transactions are &lt;strong&gt;isolated&lt;/strong&gt; (I)&lt;/h3&gt;
&lt;p&gt;We can also easily imagine that the check only &lt;strong&gt;needs&lt;/strong&gt; to be
done when the transaction ends, just before the commit. Normally no one should
be able to see the changing data meanwhile it's not completed.&lt;/p&gt;
&lt;p&gt;One &lt;strong&gt;very important&lt;/strong&gt; thing to know is that if you are using a
&lt;strong&gt;READ UNCOMMITTED isolation&lt;/strong&gt; for other transactions &lt;strong&gt;you
will see inconsistent data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, if you are doing that, you know what you are doing and are obviously
taking special care about it, don't you ?&lt;/p&gt;
&lt;h3&gt;Transactions are &lt;strong&gt;durable&lt;/strong&gt; (D)&lt;/h3&gt;
&lt;p&gt;This final property has nothing to do with our current issue. Good.&lt;/p&gt;
&lt;h2&gt;... But sometimes early warning is quite nice&lt;/h2&gt;
&lt;p&gt;When interacting with external systems, that don't participate in our
transaction, extra care should be taken in order to cope with the exceptional
case of a constraint failure. Distributed transactions is a very complex
subject, and usually it's not &lt;del&gt;supported&lt;/del&gt; done&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#pnote-415135-1&quot; id=&quot;rev-pnote-415135-1&quot; name=&quot;rev-pnote-415135-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With the principle of &lt;em&gt;least surprise&lt;/em&gt; in mind, we can easily
understand why &lt;code&gt;deferred&lt;/code&gt; is not the default behavior, but it makes
a very nice addition to our toolbox.&lt;/p&gt;
&lt;p&gt;So now there isn't any good reason anymore not to use (and abuse)
constraints in your databases.&lt;/p&gt;
&lt;p&gt;Remember, &lt;strong&gt;your data is you most precious asset&lt;/strong&gt;, protect it
at all cost from evil misbehaved bug-ridden software&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#pnote-415135-2&quot; id=&quot;rev-pnote-415135-2&quot; name=&quot;rev-pnote-415135-2&quot;&gt;2&lt;/a&gt;]&lt;/sup&gt; !&lt;/p&gt;
&lt;p&gt;Wow... Just in time before the end of the month... I would have failed
&lt;a href=&quot;http://blog.pwkf.org/post/2009/04/Should-The-URL-Include-a-Date-or-Not&quot;&gt;my motto on
URLs&lt;/a&gt; ;-)&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#rev-pnote-415135-1&quot; id=&quot;pnote-415135-1&quot; name=&quot;pnote-415135-1&quot;&gt;1&lt;/a&gt;] Do you always implement a double-phase commit on every
SOAP/REST/XML-HTTP/POST interface that you expose ?&lt;/p&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/07/#rev-pnote-415135-2&quot; id=&quot;pnote-415135-2&quot; name=&quot;pnote-415135-2&quot;&gt;2&lt;/a&gt;] Yeah, I have chosen my side in the coming war...&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/07/Databases%3A-Better-Defer-Constraints-than-Avoid-Them#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/07/Databases%3A-Better-Defer-Constraints-than-Avoid-Them#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/415135</wfw:commentRss>
      </item>
    
  <item>
    <title>Checked or Unchecked Exceptions for Legacy Code ?</title>
    <link>http://blog.pwkf.org/post/2009/07/Checked-or-Unchecked-Exceptions-for-Legacy-Code</link>
    <guid isPermaLink="false">urn:md5:4a50674f72aab4eef3e236336183a3c3</guid>
    <pubDate>Wed, 15 Jul 2009 20:55:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>java</category>
            
    <description>&lt;p&gt;This question can almost trigger a &lt;strong&gt;religious war&lt;/strong&gt;. On
Internet &lt;strong&gt;no fixed consensus&lt;/strong&gt; on the matter seems to
&lt;strong&gt;exists&lt;/strong&gt;. But most of the articles there have usually
&lt;strong&gt;something in common&lt;/strong&gt; : they are always mostly
&lt;strong&gt;applicable&lt;/strong&gt; when you start a &lt;strong&gt;new
application&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Is the situation the same when you have a certain amount of &lt;strong&gt;existing
code to maintain&lt;/strong&gt; ?&lt;/p&gt;    &lt;h2&gt;Checked Exceptions are breaking encapsulation&lt;/h2&gt;
&lt;p&gt;In theory it's quite nice since the called code can communicate with it's
caller when something unexpected happened.&lt;/p&gt;
&lt;p&gt;The key word here is &lt;em&gt;&lt;ins&gt;unexpected&lt;/ins&gt;&lt;/em&gt;. If you have to
explicitly know the exceptions that could occur, it's not really unexpected.
And if it's not unexpected, using exception handling just add an
&lt;em&gt;out-of-band&lt;/em&gt; data path. It's on par with transporting data in a private
class field when calling a member instead of using its arguments. This leads to
breaking encapsulation as Alan Griffiths wrote in &lt;a href=&quot;http://www.octopull.demon.co.uk/java/ExceptionalJava.html&quot; hreflang=&quot;en&quot;&gt;Exceptional Java&lt;/a&gt;. This vision is also shared by Bruce Eckel in his
article entitled &lt;a href=&quot;http://www.mindview.net/Etc/Discussions/CheckedExceptions&quot; hreflang=&quot;en&quot;&gt;Does
Java need Checked Exceptions&lt;/a&gt;?.&lt;/p&gt;
&lt;h2&gt;Checked Exceptions are quite painful to use&lt;/h2&gt;
&lt;h3&gt;Local Exception Handling is hard to manage&lt;/h3&gt;
&lt;p&gt;I personally find checked exceptions quite painful to use. By definition,
you have to catch every exception that is thrown by the underlaying code.&lt;/p&gt;
&lt;p&gt;And if the underlaying code doesn't know what to do with the exception,
chances are, that you don't know either, so you just pass the exception to the
caller. And so on...&lt;/p&gt;
&lt;h3&gt;Too much code to change&lt;/h3&gt;
&lt;p&gt;Therefore exceptions are usually caught at the top level with a generic
catch-all structure that logs the error, since no layer could sensibly do
something clever with the exception.&lt;/p&gt;
&lt;p&gt;Then you just have to change all the signature of the whole stack, just to
be able to catch them at the top. Using unchecked exception lets you have this
for free, and conveys the meaning that nothing is caught until the top.&lt;/p&gt;
&lt;h2&gt;Unchecked Exceptions might be dangerous...&lt;/h2&gt;
&lt;p&gt;Obviously, unchecked means not checked. So you might fail to catch them at
the top level and then the whole application crashes. Checked exceptions are a
safeguard against that. Just like strong static typing is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You trade compile-time safety&lt;/strong&gt; (checked) &lt;strong&gt;for
development-time speed and ease&lt;/strong&gt; (unchecked).&lt;/p&gt;
&lt;h2&gt;... but are not really.&lt;/h2&gt;
&lt;p&gt;On the other hand, if you have a good design, you don't have much different
top levels, and then the risk is somewhat limited.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Moreover, since you &lt;em&gt;always&lt;/em&gt; have to take unchecked one into
account, why don't use them ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By the way, in C++, the exceptions are unchecked by default.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/07/Checked-or-Unchecked-Exceptions-for-Legacy-Code#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/07/Checked-or-Unchecked-Exceptions-for-Legacy-Code#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/395563</wfw:commentRss>
      </item>
    
  <item>
    <title>Databases: Efficient Denormalization with Views</title>
    <link>http://blog.pwkf.org/post/2009/05/Efficient-Denormalization-with-Views</link>
    <guid isPermaLink="false">urn:md5:dddd0fc150be07492e5d6229f6e07480</guid>
    <pubDate>Tue, 02 Jun 2009 08:00:00 +0200</pubDate>
    <dc:creator>Steve Schnepp</dc:creator>
        <category>database</category>
            
    <description>&lt;p&gt;Everyone is unanimous that &lt;strong&gt;database normalization&lt;/strong&gt; is
considered a &lt;strong&gt;Good Thing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But it usually comes with a cost : &lt;strong&gt;writing queries can be very
tedious&lt;/strong&gt; since you always have to join many tables together to be able
to retrieve useful human data from all those reference tables.&lt;/p&gt;    &lt;p&gt;On the other side, to &lt;strong&gt;denormalize&lt;/strong&gt; is sometimes seen as a
way to :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;optimize development&lt;/strong&gt; : you do not need to write (and
debug) complex queries since all the data is nicely located in the same
table&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;optimize performance&lt;/strong&gt; : the data has a better locality (no
need to fetch or compute data from elsewhere). You can even pre-compute
&lt;a href=&quot;http://database-programmer.blogspot.com/2008/11/keeping-denormalized-values-correct.html&quot; hreflang=&quot;en&quot;&gt;order totals&lt;/a&gt;&lt;sup&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/05/#pnote-402145-1&quot; id=&quot;rev-pnote-402145-1&quot; name=&quot;rev-pnote-402145-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Denormalize correctly is quite difficult since &lt;strong&gt;one change&lt;/strong&gt;
snowballs in &lt;strong&gt;multiple updates&lt;/strong&gt; to keep the redundant data
coherent. It is therefore usually done on the &lt;strong&gt;application side&lt;/strong&gt;
with ready-to-use ORM frameworks. But managing it at the application level
comes with an ugly cost : it has to be &lt;strong&gt;reimplemented over and
over&lt;/strong&gt; each time the application switches technology, version or when a
new application is connecting to the database.&lt;/p&gt;
&lt;p&gt;In almost every programming language, code reuse is encouraged. Here, at the
database level, we can also apply the same principles, and use views to
transparently present a denormalized API (read &lt;em&gt;tables&lt;/em&gt;) on a more
normalized schema.&lt;/p&gt;
&lt;p&gt;The main points are :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Views are the main code reuse vector at the database level. Current
database usually correctly optimize (by rewrite and recombination) the simple
queries so that performance is on par with a hand crafted query that doesn't
use the views&lt;/li&gt;
&lt;li&gt;Sometime even faster since the data is nicely organized, so there is much
less data to transfer, and I/O bandwidth is a usual suspect.&lt;/li&gt;
&lt;li&gt;The application part doesn't even need to know that normalization happens
under-hood since &lt;a href=&quot;http://www.ibm.com/developerworks/db2/library/techarticle/0210rielau/0210rielau.html&quot; hreflang=&quot;en&quot;&gt;updates to views&lt;/a&gt; are possible in many modern RDMs with the
&lt;a href=&quot;http://publib.boulder.ibm.com/infocenter/ids9help/topic/com.ibm.sqlt.doc/sqltmst333.htm&quot; hreflang=&quot;en&quot;&gt;@@INSTEAD OF@@ trigger&lt;/a&gt; (or something &lt;a href=&quot;http://www.postgresql.org/docs/current/static/rules-update.html&quot; hreflang=&quot;en&quot;&gt;equivalent&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Theses updates can be created with &lt;a href=&quot;http://en.wikipedia.org/wiki/Materialized_view&quot; hreflang=&quot;en&quot;&gt;Materialized
views&lt;/a&gt; are a step even further on the denormalizing road, since it provides
the common benefits of denormalize without the implementation caveats. We can
even hand-craft these &lt;em&gt;Materialized views&lt;/em&gt; directly in an aggregation
table in order to have the space benefits of normalisation and the performance
benefits of denormalization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, nothing stops you from normalizing at will, and denormalizing when
needed. Where to put the cursor is yours to decide, but moving it afterwards is
finally easier that what is commonly admited.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.pwkf.org/post/2009/05/#rev-pnote-402145-1&quot; id=&quot;pnote-402145-1&quot; name=&quot;pnote-402145-1&quot;&gt;1&lt;/a&gt;] That article also explains why &lt;strong&gt;denormalization
maintenance must stay at the database level&lt;/strong&gt; with a very interesting
metaphor&lt;/p&gt;
&lt;/div&gt;</description>
    
    
    
          <comments>http://blog.pwkf.org/post/2009/05/Efficient-Denormalization-with-Views#comment-form</comments>
      <wfw:comment>http://blog.pwkf.org/post/2009/05/Efficient-Denormalization-with-Views#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.pwkf.org/feed/atom/comments/402145</wfw:commentRss>
      </item>
    
</channel>
</rss>