<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="http://www.luxagen.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.luxagen.com/" rel="alternate" type="text/html" /><updated>2025-07-14T08:26:41+01:00</updated><id>http://www.luxagen.com/feed.xml</id><title type="html">luxagen.com</title><subtitle>Hustling ideas at the corner of high-performance street and signal-processing boulevard</subtitle><entry><title type="html">RotKraken v2.0 released!</title><link href="http://www.luxagen.com/blog/rotkraken-v2-released" rel="alternate" type="text/html" title="RotKraken v2.0 released!" /><published>2023-01-25T00:00:00+00:00</published><updated>2023-01-25T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/rotkraken-v2-released</id><content type="html" xml:base="http://www.luxagen.com/blog/rotkraken-v2-released"><![CDATA[<p><a href="/product/rotkraken">RotKraken</a> version 2.0 is now released! I can finally say that it’s the product I always wanted it to be, and covers all my data-integrity scenarios, including many automated ones.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[RotKraken version 2.0 is now released! I can finally say that it’s the product I always wanted it to be, and covers all my data-integrity scenarios, including many automated ones.]]></summary></entry><entry><title type="html">BTRam v0.9 released!</title><link href="http://www.luxagen.com/blog/btram-v0-released" rel="alternate" type="text/html" title="BTRam v0.9 released!" /><published>2023-01-23T00:00:00+00:00</published><updated>2023-01-23T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/btram-v0-released</id><content type="html" xml:base="http://www.luxagen.com/blog/btram-v0-released"><![CDATA[<p><a href="/product/btram">BTRam</a> automates a simple solution to the problem of mirroring filesystem trees or subvolumes across multiple machines.<!--more--></p>

<p>Traditionally, one would make all the copies writeable and rely on either conventions of use or tree-diffing to periodically reintegrate the changes. Good tools for comparing filesystem trees are hard to come by, though, especially when your definition of “good” includes both readability and content analysis; identifying moves and copies would be nice too.</p>

<p>An improvement on this is to use a <a href="http://en.wikipedia.org/wiki/Btrfs">BTRFS</a> <a href="http://en.wikipedia.org/wiki/Snapshot_(computer_storage)">snapshot</a> as a common starting point, and then instantiate the two sides from there. Via the excellent <a href="http://digint.ch/btrbk"><code class="language-plaintext highlighter-rouge">btrbk</code></a>, one can then keep reciprocal incremental snapshots of the two instances that both share the initial data and independently record changes on both sides. This workflow doesn’t remove the reintegration problem though.</p>

<p>Instead, I’ve recently simplified by having a writeable <a href="http://en.wikipedia.org/wiki/Btrfs#Subvolumes_and_snapshots">subvolume</a> on machine A (the master copy) and subvolume-<a href="http://linux.die.net/man/8/mount">mount</a>ing a snapshot thereof on machine B (the read-only copy). This reduces complexity by giving me only one snapshot lineage to manage, but it does mean periodically remounting to switch machine B to use a more up-to-date snapshot.</p>

<p>This is where <code class="language-plaintext highlighter-rouge">btram</code> comes in: when run, it will spot subvolume mounts of outdated snapshots (i.e. where later-dated snapshots of the same set are available) and remount automatically. You can also dry-run to see what would happen without doing any actual remounting.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[BTRam automates a simple solution to the problem of mirroring filesystem trees or subvolumes across multiple machines.]]></summary></entry><entry><title type="html">RotKraken bugfix</title><link href="http://www.luxagen.com/blog/rotkraken-bugfix" rel="alternate" type="text/html" title="RotKraken bugfix" /><published>2023-01-12T00:00:00+00:00</published><updated>2023-01-12T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/rotkraken-bugfix</id><content type="html" xml:base="http://www.luxagen.com/blog/rotkraken-bugfix"><![CDATA[<p>I’ve been dealing with a <a href="http://en.wikipedia.org/wiki/MacOS">macOS</a> backup disk containing both creative work and system backups.<!--more--> The disk’s contents total 1.6 TB, which would eat quite a few <a href="http://en.wikipedia.org/wiki/Linear_Tape-Open">LTO-3</a> tapes (especially redundant copies), but the degree of data duplication allowed me to get the whole dataset down to only 330 GB by using <a href="http://rdfind.pauldreik.se/">rdfind</a> in hardlink mode. <a href="http://en.wikipedia.org/wiki/Btrfs">BTRFS</a> snapshots made it easy to do this in a controlled way and verify the results.</p>

<p>Since developing <a href="http://github.com/luxagen/RotKraken">RotKraken</a>, I’ve found it to have some fairly life-changing use cases, not least that it makes the job of organising my filesystem ridiculously fast and easy; suspected duplicate trees can be compared instantly with the included <code class="language-plaintext highlighter-rouge">rkdiff</code> tool before removal.</p>

<p>In this case, though, RK’s standard functionality was a lifesaver for checking both layout and content of rdfind’s dedupe results, except for one thing: there was a subtle library bug in the way.</p>

<p>Some time after running <code class="language-plaintext highlighter-rouge">rk -i</code> on the initial snapshot containing the raw filesystem copy, I discovered that some of the files had never initialised and, in fact, wouldn’t do so. They were all <code class="language-plaintext highlighter-rouge">Icon^M</code> files, which macOS software seems to use frequently.</p>

<p>After 60-90 minutes of investigation, I traced the problem to the <a href="http://metacpan.org/pod/Digest::MD5::File">Digest::MD5::File</a> <a href="http://www.perl.org">Perl</a> library; it barfs on filenames containing <em>trailing</em> <a href="http://en.wikipedia.org/wiki/Carriage_return">carriage returns</a>, returning <code class="language-plaintext highlighter-rouge">undef</code> from its <code class="language-plaintext highlighter-rouge">-&gt;file_md5_hex</code> method. The bug quickly turned out actually to be in the <a href="http://metacpan.org/pod/Digest::MD5">Digest::MD5</a><code class="language-plaintext highlighter-rouge">-&gt;addfile</code> API to which it delegates the hashing under the hood.</p>

<p>This hadn’t shown up in RK’s (fairly comprehensive) tests because, although they deliberately use pathological filenames containing all kinds of unusual characters, the names don’t <em>end</em> with a CR, which is where the problem actually shows up.</p>

<p>I worked around the problem by replacing the <code class="language-plaintext highlighter-rouge">Digest::MD5::File-&gt;file_md5_hex</code> call with an explicit open/slurp/close pattern that uses an instance of <code class="language-plaintext highlighter-rouge">Digest::MD5</code> and calls <code class="language-plaintext highlighter-rouge">-&gt;md5_hex</code> at the end. I verified correctness via the (now amended to use filenames with trailing CRs) tests.</p>

<p>After all this, RK worked perfectly on the dataset, allowing me (via the bundled <code class="language-plaintext highlighter-rouge">rkdiff-stdin</code> tool) to verify no difference between the copied files and the initial hash log I took from the source disk with <a href="http://en.wikipedia.org/wiki/Md5deep">md5deep</a>. As a side benefit, RotKraken is now more practical to use on Mac system backups.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[I’ve been dealing with a macOS backup disk containing both creative work and system backups.]]></summary></entry><entry><title type="html">Website update</title><link href="http://www.luxagen.com/blog/website-update" rel="alternate" type="text/html" title="Website update" /><published>2022-12-04T00:00:00+00:00</published><updated>2022-12-04T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/website-update</id><content type="html" xml:base="http://www.luxagen.com/blog/website-update"><![CDATA[<p>The harder, better, faster, stronger <a href="http://jekyllrb.com/">Jekyll</a>-based website is now online! I actually like this layout better than the old one, it’s super-fast and easy to update, and it’s an actual website instead of just a blog.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[The harder, better, faster, stronger Jekyll-based website is now online! I actually like this layout better than the old one, it’s super-fast and easy to update, and it’s an actual website instead of just a blog.]]></summary></entry><entry><title type="html">Migrating datasets to RotKraken on BTRFS</title><link href="http://www.luxagen.com/blog/migrating-datasets-rotkraken-btrfs" rel="alternate" type="text/html" title="Migrating datasets to RotKraken on BTRFS" /><published>2021-12-17T00:00:00+00:00</published><updated>2021-12-17T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/migrating-datasets-rotkraken-btrfs</id><content type="html" xml:base="http://www.luxagen.com/blog/migrating-datasets-rotkraken-btrfs"><![CDATA[<p>After final testing of <a href="http://github.com/luxagen/RotKraken.git">RotKraken</a> with some smaller datasets (<a href="http://en.wikipedia.org/wiki/Copy-on-write">COW-copied</a> for safety), I recently used it on my ~16 TiB data collection as part of a new long-term data-management strategy designed to simplify everything without compromising on data integrity.<!--more--></p>

<p>They say that “engineering works because it assumes it doesn’t”, thus the existence of <a href="http://en.wikipedia.org/wiki/Test-driven_development">test-driven development</a>, which I used from the start for RotKraken. Despite also designing it never to touch file content, I wasn’t going to assume anything, so here’s how I carefully applied it to my data.</p>

<p>Note: You’ll see a couple of instances of the <code class="language-plaintext highlighter-rouge">sync</code> command below. This is because, at least on Ubuntu 20.04, <a href="http://en.wikipedia.org/wiki/Btrfs">BTRFS</a> has minor bugs (presumably to do with making generation-number updates visible before they’re committed to disk) that sometimes make it act as if nothing on a subvolume has changed. While this doesn’t necessarily mean that snapshotting will use the out-of-date state, I prophylactically <code class="language-plaintext highlighter-rouge">sync</code> before snapshotting subvolumes I’ve just changed to be sure.</p>

<h2 id="1-take-a-before-snapshot">1. Take a “before” snapshot</h2>

<p>We first snapshot the data to provide a safety copy should anything bad happen:</p>

<p><code class="language-plaintext highlighter-rouge">sync</code></p>

<p><code class="language-plaintext highlighter-rouge">btrfs subvolume snapshot -r $PATH $SNAP_PRE</code></p>

<h2 id="2-do-the-job">2. Do the job</h2>

<p>We run RotKraken on the data to do the initial hashing run:</p>

<p><code class="language-plaintext highlighter-rouge">rk -i $PATH</code></p>

<h2 id="3-take-an-after-snapshot">3. Take an “after” snapshot</h2>

<p>To guard against <a href="http://en.wikipedia.org/wiki/rsync">rsync</a> mishaps, we take another read-only snapshot:</p>

<p><code class="language-plaintext highlighter-rouge">sync</code></p>

<p><code class="language-plaintext highlighter-rouge">btrfs subvolume snapshot -r $PATH $SNAP_POST</code></p>

<h2 id="4-check-that-only-the-extended-attributes-have-changed">4. Check that only the extended attributes have changed</h2>

<p>Finally, we use rsync’s handy tree-diffing mode to check that the only changes were to <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">extended attributes</a>:</p>

<p><code class="language-plaintext highlighter-rouge">rsync --delete -aHAXni $SNAP_PRE/ $SNAP_POST/ | grep -v '^.f........x'</code></p>

<p>Et voilà! We can now dispose of the snapshots (if we wish) and get on with more interesting things — with the assurance that any future change to the content of these files will be detectable.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[After final testing of RotKraken with some smaller datasets (COW-copied for safety), I recently used it on my ~16 TiB data collection as part of a new long-term data-management strategy designed to simplify everything without compromising on data integrity.]]></summary></entry><entry><title type="html">RotKraken v1.0 released!</title><link href="http://www.luxagen.com/blog/rotkraken-v1-released" rel="alternate" type="text/html" title="RotKraken v1.0 released!" /><published>2021-10-28T00:00:00+01:00</published><updated>2021-10-28T00:00:00+01:00</updated><id>http://www.luxagen.com/blog/rotkraken-v1-released</id><content type="html" xml:base="http://www.luxagen.com/blog/rotkraken-v1-released"><![CDATA[<p><a href="http://github.com/luxagen/rotkraken">RotKraken</a> version 1.0 is now available. It doesn’t quite have all the features I want, but it’s a highly useful tool in its present form so have at it!</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[RotKraken version 1.0 is now available. It doesn’t quite have all the features I want, but it’s a highly useful tool in its present form so have at it!]]></summary></entry><entry><title type="html">Data integrity: the Prime Directive</title><link href="http://www.luxagen.com/blog/data-integrity-the-prime-directive" rel="alternate" type="text/html" title="Data integrity: the Prime Directive" /><published>2021-06-10T00:00:00+01:00</published><updated>2021-06-10T00:00:00+01:00</updated><id>http://www.luxagen.com/blog/data-integrity-the-prime-directive</id><content type="html" xml:base="http://www.luxagen.com/blog/data-integrity-the-prime-directive"><![CDATA[<p>While I’d love to debate the moral implications of a certain android’s adherence to Starfleet’s highest law, what I want to talk about today has to do with my 15-plus-year mission to never again lose data unintentionally — specifically a principle whose importance I only <em>fully</em> grasped quite recently.<!--more--></p>

<h2 id="yule-regret-this">Yule regret this</h2>

<p>Christmas is traditionally a time for family and friends, but it’s also a convenient lull in the year’s pace, and I’ve developed a habit of using the period to get around to some of the more mundane IT-infrastructure tasks that are rarely urgent enough to do during the rest of the year, especially data management. This last Christmas, therefore, I took the step of trying <a href="http://en.wikipedia.org/wiki/Btrfs">BTRFS</a> for the first time.</p>

<p>In retrospect, I made two mistakes. For one thing, the BTRFS version shipped with <a href="http://ubuntu.com">Ubuntu</a> 16 was in the last stage of relative immaturity and I really should have upgraded to Ubuntu 20 before trying it. The far greater mistake, though, was to use its <a href="http://en.wikipedia.org/wiki/Non-RAID_drive_architectures#Concatenation_(SPAN,_BIG)">spanning</a> feature to join two 8 TB <a href="http://en.wikipedia.org/wiki/Shingled_magnetic_recording">SMR</a> hard drives into a single 16 TB volume. In retrospect, this was a <a href="http://www.youtube.com/watch?v=7aUGBT1DZDI">Very Bad Idea™</a> because some of the things you need to be able to do in a spanning/RAID setup — e.g. <code class="language-plaintext highlighter-rouge">btrfs balance</code> — cannot be rate-controlled. Not only do SMR drives tend to slow down after a few hundred gigabytes of continuous writes, some of them eventually grind to a halt, potentially leaving you with no way to get the filesystem into a consistent state.</p>

<p>These mistakes led to a nightmarish episode in which I got multiple levels down a stack of <a href="http://www.hanselman.com/blog/YakShavingDefinedIllGetThatDoneAsSoonAsIShaveThisYak.aspx">yaks</a>, convinced myself that I had at least one faulty drive, and ended up with a broken filesystem that I didn’t have enough free space to run <code class="language-plaintext highlighter-rouge">btrfs restore</code> on. In the end I solved the last by splurging on server-grade <a href="http://www.westerndigital.com/products/internal-drives/wd-gold-sata-hdd">WD Gold</a> drives and managed to emerge from the chaos traumatised but without any actual data loss.</p>

<p>I threw BTRFS firmly in the sea, got everything shipshape again, and got on with my life.</p>

<p>Recently, the trauma faded enough for me to learn the correct lesson from all this.</p>

<h2 id="a-simple-philosophy">A simple philosophy</h2>

<p>When it comes to data management, there’s a whole category of concerns that seem both diverse and complex — until you realise that they all have the same answer.</p>

<p>Q: What filesystem should I use to minimise the chances of data loss?<br />
Q: Should I get a UPS to guard against filesystem corruption during blackouts?<br />
Q: Should I buy server-grade drives and/or make sure they’re CMR rather than SMR to preserve my data?</p>

<h5 id="a-if-youre-worried-about-this-you-dont-have-enough-redundancy">A: If you’re worried about this, you don’t have enough redundancy!</h5>

<p>To illustrate:</p>

<p>“I want to use the most trustworthy filesystem possible.”<br />
<strong>No you don’t; you want to use one that isn’t obviously hazardous, and think of it as disposable because you have enough redundancy.</strong></p>

<p>“I want to make sure my power supply is as reliable as possible to minimise uncontrolled shutdowns.”<br />
<strong>No you don’t; you want to evaluate the danger of acute data loss versus the real-world frequency of power-loss events, and assess the cost-to-benefit ratio of buying a UPS based on that.</strong></p>

<p>“I want to use the most trustworthy drives money can buy.”<br />
<strong>No you don’t; you want to make that decision based on rational considerations like (a) the amortised time cost of replacing faulty drives, (b) the financial cost, over time, of replacing cheap drives that might not last as long versus that cost for drives with a decent warranty.</strong></p>

<p>“I really must get around to organising my <a href="http://xkcd.com/1360">decade-plus</a> of <a href="http://xkcd.com/1459">accumulated digital junk</a> so I can find things again and free up some space, and I need to do that first.”<br />
<strong>NO, NO, NO!!</strong> <em>(whacks speaker with rolled-up newspaper)</em> <strong>What you really must do is urgently replicate that digital slagheap and achieve redundancy. Once that’s in place, THEN you can sift through it all knowing that the inevitable missteps won’t lose you anything.</strong></p>

<p>I should make clear that the four above points are not strawmen but actually factor into my thinking. The important thing is that by removing the data-loss penalty for mistaken decisions, I don’t have to agonise so much and can take a “live and learn” approach.</p>

<h2 id="how-much">How much?</h2>

<p>While the irony of repeatedly banging on about redundancy isn’t lost on me, it bears repeating because it’s such an important paradigm shift. As for how much is enough, my current policy is 3 copies. Of course, this can be varied depending on the importance of the data, but I generally only do that for offlining data to <a href="http://en.wikipedia.org/wiki/Linear_Tape-Open">tape</a>. For <a href="http://en.wikipedia.org/wiki/Hard_disk_drive">online</a> <a href="http://en.wikipedia.org/wiki/Solid-state_drive">storage</a>, I find it easier to apply a uniform policy, not least because it avoids incessant <a href="http://en.wikipedia.org/wiki/Knapsack_problem">knapsacking</a> difficulties.</p>

<h2 id="outlook-chilled">Outlook: chilled</h2>

<p>Learning the correct lesson has meant overcoming my fear of BTRFS and other “experimental” filesystems, so I’m trying it again. This time it’s on Ubuntu 22, and definitely without any spanning or RAID that would get in the way of recovering from a single-drive failure. Then again, who cares? I have enough redundancy, so if at some future point spanning offers any value, I can reconsider it.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[While I’d love to debate the moral implications of a certain android’s adherence to Starfleet’s highest law, what I want to talk about today has to do with my 15-plus-year mission to never again lose data unintentionally — specifically a principle whose importance I only fully grasped quite recently.]]></summary></entry><entry><title type="html">Delivering reliability with a UPS</title><link href="http://www.luxagen.com/blog/delivering-reliability-ups" rel="alternate" type="text/html" title="Delivering reliability with a UPS" /><published>2021-05-11T00:00:00+01:00</published><updated>2021-05-11T00:00:00+01:00</updated><id>http://www.luxagen.com/blog/delivering-reliability-ups</id><content type="html" xml:base="http://www.luxagen.com/blog/delivering-reliability-ups"><![CDATA[<p>After 4-5 power failures over the last year, one of which corrupted a development repository, nuked my <a href="https://gitextensions.github.io/">Git Extensions</a> configuration, and interrupted external services, I decided I needed a <a href="http://en.wikipedia.org/wiki/Uninterruptible_power_supply">UPS</a>.<!--more--> I was recommended the <a href="http://www.amazon.co.uk/dp/B00IG2P92I">BX500CI</a> by a friend, but an extended period of Amazon stocklessness gave me time to realise that it wasn’t really right for my needs. Since I’m not always in the house, it wouldn’t help much for long outages because the equipment would merely suffer a <em>delayed</em> power loss if I wasn’t around to notice.</p>

<p>A search yielded the more expensive but connected <a href="http://www.amazon.co.uk/dp/B00T7BYX2Y">BX950UI</a>, with full support on Windows, GNU/Linux, and macOS via the excellent <a href="http://www.apcupsd.org">APC UPS Daemon</a>, so I went for it.</p>

<h2 id="initial-setup">Initial setup</h2>

<p>On its arrival, I charged it and spent a couple of hours doing physical hookup, which involved replacing the plugs on a couple of multiway power strips with <a href="http://en.wikipedia.org/wiki/IEC_60320">IEC C13s</a>. <a href="http://www.cyberciti.biz/faq/debian-ubuntu-centos-rhel-install-apcups/">This article</a> and the <a href="http://linux.die.net/man/5/apcupsd.conf"><code class="language-plaintext highlighter-rouge">apcupsd</code> manpage</a> helped me get the basics going in another couple of hours, and I ran a battery calibration.</p>

<p>My theory is that this step doesn’t directly use power-consumption information, but records the drain curve of the battery with respect to total energy delivered to loads, allowing the UPS to correctly estimate remaining runtime for any load. I suspect that it’s worth recalibrating every 3 months or so, both to keep this profile updated and to give the battery itself enough exercise to avoid degrading too fast; I gather lead-acid batteries don’t like being left charged for too many months on end.</p>

<h2 id="fine-tuning">Fine-tuning</h2>

<p>At first I began editing the <code class="language-plaintext highlighter-rouge">/etc/apcupsd/apccontrol</code> script directly, but this file needs to be replaceable on package upgrade, so the correct approach is to edit the scripts specific to each event, e.g. <code class="language-plaintext highlighter-rouge">/etc/apcupsd/doshutdown</code>. The next gotcha was that, not having read the above article properly, I missed the important step of setting <code class="language-plaintext highlighter-rouge">ISCONFIGURED=yes</code> in <code class="language-plaintext highlighter-rouge">/etc/default/apcupsd</code>, without which <code class="language-plaintext highlighter-rouge">apcaccess</code> would work — but not the daemon itself. I was alerted to this problem by the smoking gun that neither the <code class="language-plaintext highlighter-rouge">/var/log/apcupsd.events</code> nor <code class="language-plaintext highlighter-rouge">/etc/apcupsd/powerfail</code> files were being created during testing.</p>

<p>As well as the Ubuntu server (an Intel <a href="http://en.wikipedia.org/wiki/Next_Unit_of_Computing">NUC</a>), I have a Windows 10 desktop attached to the UPS. Since the latter sucks enough juice to reduce battery runtime to about ten minutes, it was crucial to arrange for it to shut down quickly on power loss in order to maximise the server’s uptime. To this end I put the following command in the <code class="language-plaintext highlighter-rouge">/etc/apcupsd/onbattery</code> script, just before the <code class="language-plaintext highlighter-rouge">exit 0</code> line (to conceal any failure owing to e.g. the machine already being off), and using its IP address to dodge any transient name-resolution problems:</p>

<p><code class="language-plaintext highlighter-rouge">net rpc shutdown -t 45 -f -C "UPS shutdown" -I $IP_ADDRESS -U$USERNAME%$PASSWORD</code></p>

<p>This gives 45 seconds’ warning, just enough to run <code class="language-plaintext highlighter-rouge">shutdown /a</code> if there’s a pressing need to use the machine. Testing revealed a few gotchas: the first was that the remote-shutdown feature <strong>apparently</strong> (see later) requires the Remote Registry service to be running, which got me from one error message to another.</p>

<p>The second problem was the need for <a href="http://en.wikipedia.org/wiki/User_Account_Control">UAC</a> auto-elevation on <a href="http://en.wikipedia.org/wiki/Microsoft_RPC">RPC</a> connections; I fixed that by using <code class="language-plaintext highlighter-rouge">regedit</code> to create the DWORD value <code class="language-plaintext highlighter-rouge">LocalAccountTokenFilterPolicy</code>=1 inside the <code class="language-plaintext highlighter-rouge">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System</code> key.</p>

<p>I later discovered that the Remote Registry service was set to Automatic but not actually started — and thus unnecessary. I left it on Automatic anyway and started it, because there’s a nasty impasse one can trip over during cloning/migration of Windows builds where logon is blocked owing to a faulty <code class="language-plaintext highlighter-rouge">MountedDevices</code> configuration.</p>

<p>Another detail worth noting is that I don’t see the point of blocking remote logins on a GNU/Linux system that’s in the process of shutting down; it seems like a nannying feature to save admins from having their session nuked by the shutdown (not much of a benefit) and it takes away options in a time-critical situation. I therefore set this line in <code class="language-plaintext highlighter-rouge">/etc/apcupsd/apcupsd.conf</code>:</p>

<p><code class="language-plaintext highlighter-rouge">NOLOGON disable</code></p>

<h2 id="final-configuration">Final configuration</h2>

<p>Here’s how it looks, along with my comments.</p>

<h3 id="etcapcupsdgitignore">/etc/apcupsd/.gitignore</h3>

<p>Since I run a Git repository in the Ubuntu server’s <code class="language-plaintext highlighter-rouge">/etc</code> folder, the following gets rid of <code class="language-plaintext highlighter-rouge">diff</code> noise:</p>

<p><code class="language-plaintext highlighter-rouge">apctest.output</code></p>

<p><code class="language-plaintext highlighter-rouge">powerfail</code></p>

<h3 id="etcapcupsdapccontrol">/etc/apcupsd/apccontrol</h3>

<p>Since I don’t check <code class="language-plaintext highlighter-rouge">root</code>’s mailbox on the server, I changed the <code class="language-plaintext highlighter-rouge">export SYSADMIN</code> line to refer to my external e-mail address.</p>

<h3 id="etcapcupsdapcupsdconf">/etc/apcupsd/apcupsd.conf</h3>

<p>Here I altered the following:</p>

<p><code class="language-plaintext highlighter-rouge">UPSNAME $NAME</code></p>

<p><code class="language-plaintext highlighter-rouge">UPSCABLE usb</code></p>

<p><code class="language-plaintext highlighter-rouge">UPSTYPE usb</code></p>

<p><code class="language-plaintext highlighter-rouge">DEVICE</code></p>

<p><code class="language-plaintext highlighter-rouge">POLLTIME 15</code></p>

<p><code class="language-plaintext highlighter-rouge">ONBATTERYDELAY 30</code></p>

<p><code class="language-plaintext highlighter-rouge">MINUTES 2</code></p>

<p><code class="language-plaintext highlighter-rouge">#BATTERYLEVEL 5</code></p>

<p><code class="language-plaintext highlighter-rouge">POLLTIME</code> is the maximum time (in seconds) that <code class="language-plaintext highlighter-rouge">apcupsd</code> will take to notice a UPS event. I believe <code class="language-plaintext highlighter-rouge">ONBATTERYDELAY</code> is the time in seconds between one of <code class="language-plaintext highlighter-rouge">apcupsd</code>’s polls noticing power loss and the <code class="language-plaintext highlighter-rouge">onbattery</code> state being triggered; this is how I prevent very short outages from shutting down the Windows machine. <code class="language-plaintext highlighter-rouge">MINUTES</code> specifies how close to battery exhaustion the UPS can get before a shutdown is triggered; 2 minutes is generous for my server, which usually takes 10-20 seconds to shut down.</p>

<p>Using <code class="language-plaintext highlighter-rouge">MINUTES</code> made <code class="language-plaintext highlighter-rouge">BATTERYLEVEL</code> unnecessary, so I commented it out.</p>

<h3 id="etcapcupsdonbattery">/etc/apcupsd/onbattery</h3>

<p>Here I added the remote-shutdown command for the Windows machine (see above).</p>

<h3 id="etcapcupsddoshutdown">/etc/apcupsd/doshutdown</h3>

<p>I replicated the Windows remote-shutdown command here (again before the <code class="language-plaintext highlighter-rouge">exit 0</code> line to conceal failure) so that, even if I choose to keep the machine on during power loss via <code class="language-plaintext highlighter-rouge">shutdown /a</code>, it will retry on battery exhaustion.</p>

<h3 id="etcdefaultapcupsd">/etc/default/apcupsd</h3>
<p> 
<code class="language-plaintext highlighter-rouge">ISCONFIGURED=yes</code></p>

<h2 id="flaws-and-future-work-or-things-i-didnt-have-time-for">Flaws and future work (or Things I Didn’t Have Time For)</h2>

<p>One thing I must address is what I consider the biggest (only?) flaw in <code class="language-plaintext highlighter-rouge">apcupsd</code>: its failure to provide handling for communications failures, which might sabotage the whole setup by hiding a power loss. In theory, this could be worked around with a bunch of custom scripting, but I think the real solution would lie in <code class="language-plaintext highlighter-rouge">apcupsd</code> itself:</p>

<ul>
  <li>introduce a <code class="language-plaintext highlighter-rouge">COMMFAILUREDELAY</code> setting that allows comms to recover within some timespan without generating an event;</li>
  <li>change the comms-failure behaviour to (optionally?) trigger a shutdown.</li>
</ul>

<p>The other major improvement to my setup would be to hibernate the machines in question instead of shutting them down. I looked into this for a while, but there are two problems:</p>

<ul>
  <li>GNU/Linux requires a swap partition in order to hibernate;</li>
  <li>The Samba <code class="language-plaintext highlighter-rouge">net rpc shutdown</code> command provides only shutdown and restart, not sleep or hibernate.</li>
</ul>

<p>I’ll probably deal with the former via an already-planned server rebuild, and the latter could be fixed by remotely invoking the native Windows <code class="language-plaintext highlighter-rouge">SHUTDOWN</code> command instead of using the Samba one, but since Windows 10 no longer has an inbuilt Telnet server this would require extra software and time.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Thanks to this modest amount of work, and some testing — sometimes using the <code class="language-plaintext highlighter-rouge">TIMEOUT</code> setting in <code class="language-plaintext highlighter-rouge">/etc/apcupsd/apcupsd.conf</code> to avoid running the battery all the way down — I no longer have to worry about power loss in the middle of an intense work session, fiddling around with manual Git-repository repairs, or touring the local construction sites in a blame-seeking rage. Yay!</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[After 4-5 power failures over the last year, one of which corrupted a development repository, nuked my Git Extensions configuration, and interrupted external services, I decided I needed a UPS.]]></summary></entry><entry><title type="html">Info Wars 2: a New Hope</title><link href="http://www.luxagen.com/blog/info-wars-2-a-new-hope" rel="alternate" type="text/html" title="Info Wars 2: a New Hope" /><published>2021-01-28T00:00:00+00:00</published><updated>2021-01-28T00:00:00+00:00</updated><id>http://www.luxagen.com/blog/info-wars-2-a-new-hope</id><content type="html" xml:base="http://www.luxagen.com/blog/info-wars-2-a-new-hope"><![CDATA[<p>There’s an idea that’s been kicking around in the recesses of my mind for a year or two, but I couldn’t see how to make it work. I recently realised that it’s actually possible.<!--more--></p>

<p>First, let me backtrack. For some years now my approach to data integrity (i.e. combatting the surprisingly frequent <a href="http://en.wikipedia.org/wiki/Data_degradation">bit rot</a>) has revolved around <a href="http://md5deep.sourceforge.net">md5deep</a>. I’ve been storing logs from that tool with datasets so that I can check later — via another run and a <a href="http://en.wikipedia.org/wiki/Diff">diff</a> — that no bits have flipped in the intervening time. The big problem with this approach has been that these logs quickly become out of date owing to file/directory moves and other day-to-day management, and this means that, even when <code class="language-plaintext highlighter-rouge">diff</code>ing, there’s the problem of tying everything up across moves in order to check that the content hashes themselves haven’t changed.</p>

<p>“If only”, I thought to myself, “I could store the hash information WITH the files themselves! Then all the moving/renaming in the world won’t matter.” I just didn’t see how to do that.</p>

<p>While trying to research support for the <a href="http://en.wikipedia.org/wiki/Archive_bit">DOS archive attribute</a> via <a href="http://www.samba.org">Samba</a> on <a href="http://en.wikipedia.org/wiki/Linux">Linux™</a> (spoiler alert: there doesn’t seem to be any), I came across the concept of <a href="http://en.wikipedia.org/wiki/Extended_file_attributes">extended file attributes</a>. “Holy mackerel!” I said to myself, “This is it!”</p>

<p>I dusted off my rusty <a href="http://www.perl.org">Perl</a> skills and bashed out a prototype with a friend in an hour or two. A week of work in two stints later, I had an initial stab at a tool with real value.</p>

<p>Apart from the benefits of attaching the metadata to the files — no more hash logs or verification difficulties from moves/renames — I’ve quickly found the tool to be a game-changer in other ways too. Firstly, just by separating the long-running initial hashing job from active decisionmaking, it’s proved to be a killer tool for manually deduplicating and organising my files. By using the <strong>export</strong> feature to generate hash logs for trees, I can use <code class="language-plaintext highlighter-rouge">diff</code> to <em>instantly</em> verify that suspected duplicate directories are actually identical and just delete one.</p>

<p>I look forward to doing more work over the next couple of months to polish this product and make it as useful as possible. Watch this space!</p>

<p>Addendum: The tool is named <a href="http://github.com/luxagen/rotkraken">RotKraken</a>.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[There’s an idea that’s been kicking around in the recesses of my mind for a year or two, but I couldn’t see how to make it work. I recently realised that it’s actually possible.]]></summary></entry><entry><title type="html">Lightning-fast coding</title><link href="http://www.luxagen.com/blog/lightning-fast-coding" rel="alternate" type="text/html" title="Lightning-fast coding" /><published>2018-08-27T00:00:00+01:00</published><updated>2018-08-27T00:00:00+01:00</updated><id>http://www.luxagen.com/blog/lightning-fast-coding</id><content type="html" xml:base="http://www.luxagen.com/blog/lightning-fast-coding"><![CDATA[<p>Modern computers are fast. Unbelieveably, mind-blowingly fast.<!--more--></p>

<p>You might not be very aware of this. I’m a programmer and even I forget it most of the time, usually because a simple website I’m using is stuffed full of unnecessary <a href="http://en.wikipedia.org/wiki/JavaScript">JavaScript</a> frameworks, or the desktop software I’m using is layered on too many heavyweight <a href="http://en.wikipedia.org/wiki/Application_programming_interface">APIs</a>. Deep, inefficient technology stacks are everywhere these days.</p>

<p>Much of the time, though, that isn’t the whole problem; it’s the fundamentals too. When I implemented two features for Drummer - my <a href="http://en.wikipedia.org/wiki/C%2B%2B">C++</a>-based rhythm-programming software for Windows - their sheer blazing speed reminded me how much details matter.</p>

<p>One feature was rendering: the process of taking all the user’s rhythm programming and spitting out either a single mixed audio file or a parallel set of them (one per instrument) for inclusion in your <a href="http://en.wikipedia.org/wiki/Digital_audio_workstation">DAW</a> project. As an example, a rhythm track for a 5-minute song with 10 monophonic instruments involves generating (at a <a href="http://en.wikipedia.org/wiki/Sampling_(signal_processing)#Sampling_rate">sample rate</a> of 48 kHz) about half a gigabyte of data. Drummer can render that to a WAV file, or a set of them, in less than half a second on a modern machine with a typical cheap SATA <a href="http://en.wikipedia.org/wiki/Solid-state_drive">SSD</a>.</p>

<p>The second feature was the full-text query system for finding sound samples in your collection based on their metadata. It builds the indices from scratch for 4,500 sample files in 7 seconds on the same SSD, which is pretty neat, but searching is what really shines: a results list matching a keyword string takes about 100 microseconds to generate - that’s a tenth of a millisecond.</p>

<p>How did I achieve this? Through avoiding a few common habits:</p>

<ul>
  <li>unnecessary in-memory data copying;</li>
  <li>on-the-fly allocation of data buffers in stream-processing loops;</li>
  <li>direct processing of <a href="http://en.wikipedia.org/wiki/Sparse_file">sparse</a> data (i.e. consisting mostly of zeroes);</li>
  <li>too many buffer scans during compound processing of data streams.</li>
</ul>

<h2 id="other-languages">Other languages</h2>

<p>This might all sound very C++-centric, but that’s not true. <a href="http://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a> and many other languages can achieve nearly the same performance, and I’ve used these principles to make similarly hair-raising improvements to speed-critical web-service code in my day-to-day contract development. The biggest pitfall is the <a href="http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)">garbage collector</a>: it frees you from explicit ownership concerns, at least for read-only data, but places some inconvenient constraints on memory layout and allocation strategy that can cause <a href="http://en.wikipedia.org/wiki/Locality_of_reference">locality</a> to suffer. If your buffers aren’t unnecessarily huge and you’re not allocating them on the fly, though, locality matters much less thanks to the <a href="http://en.wikipedia.org/wiki/CPU_cache">CPU cache</a>.</p>

<p>While C# now has <code>unsafe</code> and <a href="http://en.wikipedia.org/wiki/Pointer_(computer_programming)">pointers</a> to mitigate these problems, there’s a good argument that using them can badly compromise your code’s developer-compatibility, always a concern in attrition-prone organisations. The good news is that there’s usually no need to go this low-level unless you’re squeezing the last few percent on performance. With careful design, even vanilla “safe” C# can be much faster than you might think.</p>]]></content><author><name>luxagen</name></author><summary type="html"><![CDATA[Modern computers are fast. Unbelieveably, mind-blowingly fast.]]></summary></entry></feed>