Living in a state of accord.

Moolah Diaries – Upgrading to Hapi 17

Hapi 17 was released a while back with quite a few nice improvement – mostly centered around using promises instead of callbacks and thus fitting in with async/await well.  However, it’s a very significant change right across it’s APIs so it took quite a while for the various plugins that Moolah uses to be updated.

Eventually though they were all ready and then the actual upgrade of Moolah code was pretty straight forward.  Moolah had been using async handlers via a plugin all along so there were minor changes to how they were specified but very few changes to the control flow they then used.

In the end it only took an evening’s work to get it all upgraded and I haven’t encountered any bugs since. That’s a pretty good vote of confidence in the effectiveness of it’s tests.

The next big upgrade will be to move the front-end over to WebPack v4. The improvements there look excellent, removing a lot of the previously required config and simplifying things but that also means it’s a pretty complex migration.

Moolah Diaries – Earmarked Funds

I dropped out of the habit of writing about Moolah but it’s been ticking along doing what I need it to for a fair while with a few additions here and there. Recently I spent a chunk of time adding the ability to ‘earmark’ money for a particular use.

My main motivation is that we’re planning a family trip to Germany at the end of the year and I want to be able to set aside money through the year as we save for it. Various things will need to be paid upfront and those expense need to come out of the earmarked funds. I could track that with a new account for the savings, but since I’m not setting up a new bank account the balances wouldn’t match up and I’d spend a lot of time transferring money in and out of the holiday account.

So I’ve introduced ‘Earmark’ as a new type of entity. In many ways they’re similar to accounts, but a single transaction can have both an account and an earmark, affecting the balance of both. So the transaction recording paying for our flights has the credit card as it’s account and the earmark set to the Germany holiday. It acts as an expense reducing the balance of both the account and the earmark.

Similarly, my wife’s income goes into our bank account and for this year I’m also earmarking it for Germany so it increases the bank balance (we did actually get the cash) and also increases the balance of the earmark.

This sounds like double counting, but the earmark balance is not included in the net worth which is still just the sum of account balances. The UI also displays “available funds” which is the sum of account balances less the sum of earmark balances.

The result of this is that an expense with an account and earmark reduces the balance of the account (we now have less money in total) but also reduces the balance of the earmark so there is less money set aside for that purpose. As a result the net worth is reduced but the available funds remains the same because the cost came out of our reserved funds.

Income with an account and an earmark increases the balance of both, so net worth goes up but available funds stays the same – those funds have been earmarked for that purpose and aren’t available for general use.

But what if we want to earmark a fixed amount per month separately to any actual income? To support that, Moolah allows creating transactions with an earmark but no account. In that case we’re recording the allocation of funds but with no actual cash movement.

The feature set is rounded out by tracking how much has been saved (total of income) and how much has been spent (total of expenses), a savings target and target date and providing some visualisations to show savings progress vs time and expense breakdowns.

The Curious Case of Vuetify Bug 2773

I ran into an issue with Vuetify recently where the 1.0 betas were constantly utilising a CPU core when run in Safari. It turned out, I wasn’t alone and it had been reported as bug 2773.

There wasn’t a lot to go on – the problem only happened in Safari and the timeline showed it was constantly invalidating and recalculating the layout, apparently without any JavaScript executing.

First step was to create a simple reproduction case, which turned out to be a template with just:

  <v-navigation-drawer app></v-navigation-drawer>

So something to do with navigation drawers and whatever effects the app attribute causes. Looking at how the generated DOM changes with and without ‘app’ led me to discover the suspicious looking CSS:

max-height: calc(100% - 0px);

Sure enough, the problem went away if that was changed to a simple max-height: 100%; but the ‘- 0px’ part isn’t as silly as it seems. If you have a toolbar at the top of the page it adjusts to allow for the toolbar height becoming ‘max-height: calc(100% – 64px)’ which also causes high-CPU. But an element with ‘max-height: calc(100% – 0px)’ by itself doesn’t trigger the bug so there must be something else contributing to the problem…

More digging ensued, including deleting large chunks of Vuetify’s css to see when the problem went away which lead to the rule:

.navigation-drawer[data-booted="true"] {
  transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);

That and the max-height was all the CSS required to reproduce the problem when Vuetify created the DOM, but not enough when applied to a static HTML page.

The ‘data-booted’ attribute is added dynamically by Vuetify when the component is loaded to trigger an enter animation on the element but I still couldn’t reproduce the issue without Vuetify even if I wrote JavaScript to add that attribute after a delay.

It turns out the third piece required to trigger the high CPU usage is that the element has to be created with document.createElement. If it’s included in the page HTML or even created with el.outerHTML = ‘…’ there’s no issue.  So it turns out to be a Safari/WebKit bug which can be reproduced with:

  <div id="app">
    const nav = document.createElement('aside'); = 'calc(100% - 0px)';
    const originalEl = document.getElementById('app');
    originalEl.parentElement.replaceChild(nav, originalEl);
    requestAnimationFrame(function() { = '1s';

You can try it out.  With Safari 11.0.2 (and possibly other versions) you can see the Timelines tab in dev tools show the page constantly recalculating layout and Activity Monitor will show the process for that tab using around 100% CPU.

The work around for Vuetify was to ensure that transition-property which was defaulting to ‘all’ (because lots of things about navigation drawers animate) doesn’t include the ‘max-height’ property or anything else that uses ‘calc’. Hence pull request 2958.

The problem appears to have already been fixed in WebKit as I can’t reproduce it in the current Safari Technology Preview build.

Modernising Our JavaScript – Supporting OpenSource

At LMAX, we’ve been using Vue.js for all our new JavaScript work very successfully for a while now. Our existing products used Vue.js for the logic but continued to use bootstrap as the component library to ensure the UI remained consistent.  For anything new though, we started using Vuetify to give us better UIs much more easily.

Vuetify is close to reaching it’s 1.0 release now and we’ve been using it for a while so when we first started with it, the vast majority of development and support was being done by the project founder, John Leider in his spare time. Migrating UI libraries is painful so having an early stage project without a lot of community (at the time) was a pretty big risk for us, but the code was excellent and John was obviously very committed to the project, being very responsive to bug reports and questions.  So LMAX jumped on board as a platinum supporter of Vuetify, making regular monthly payments to support Vuetify development.

It’s been a resounding success.

As a project, Vuetify has continued to grow – mostly because of the excellent work John was doing to build the community – giving more stability and now has multiple regular contributors. With the number of supporters also growing John quit his day job and is now focussed on Vuetify full-time. So our primary aim of supporting the viability and longevity of the project has worked out well.

We’ve also had secondary benefits. As part of being a platinum supporter the LMAX logo appears on the Vuetify site and we’ve set it up to link to our jobs site. It’s driven quite a lot of traffic that way and I think is actually the top source of traffic to our careers site.

I believe we also get some guaranteed support time as part of the package, but frankly John is so responsive I’m not sure if we’d actually notice the difference. We’re also keen to contribute code but the project is now moving along so well that stuff gets fixed for us before we get a chance to do it. It’s possible some of my colleague who are doing more with Vuetify have contributed some stuff, but every bug I’ve found has been fixed almost as soon as I can finish the bug report…

Overall, it’s been a huge success and well worth the money. I’d definitely encourage others to become financial backers of projects they depend on.

IPv6 on EdgeRouter X (ERX) and SkyMesh (EdgeOS 1.9.7)

The internet is full of forum posts with various questions, tips and suggestions for IPv6 on EdgeRouters as people struggle to get it working. The challenge is that the fix you actually need depends on which version of EdgeOS you’re running (ubnt are continuing to flesh out the IPv6 configuration support) and the setup of your ISP.

So here’s the magic steps I needed for an ERX running EdgeOS 1.9.7+hotfix.3 with SkyMesh NBN over HFC (cable).

  1. Use the Basic Setup wizard to get the basic config in place
    • Port eth0
    • Internet connection type PPPoE (and enter your PPPoE login)
    • Enable the default firewall
    • Enable DHCPv6 Prefix Delegation
      • Prefix length /56
      • Enable the default IPv6 firewall
      • IPv6 LANs switch0
    • Only use one LAN
    • Setup the IPv4 LAN address and users as required.
  2. Additional config required either via the command line ‘configure’ or Config Tree tab in the UI.
    • set interfaces ethernet eth0 pppoe 0 ipv6 enable 
    • set interfaces ethernet eth0 pppoe 0 ipv6 address autoconf
    • set interfaces ethernet eth0 pppoe 0 dhcpv6-pd rapid-commit disable
    • set interfaces switch switch0 ipv6 address autoconf

The extra config essentially boils down to three main things:

  1. PPPoE interface needs to have IPv6 enabled if IPv6 is going to work.
  2. The PPPoE and switch0 interfaces need to auto configure their IPv6 address.
  3. SkyMesh do not support rapid-commit for prefix delegation. Almost every ERX IPv6 config I’ve seen has this enabled but with SkyMesh the devices on the LAN and the switch0 interface will fail to get global IPv6 IPs unless it’s disabled.

As an optional extra if you want to avoid ICMP filtering you should duplicate rule ’30’ from firewall ipv6-name WANv6_LOCAL to   (action accept, protocol ipv6-icmp being the key values). This is allowing extra traffic through your firewall to all devices on your network so you should only do that if you’ve done the research and are comfortable with it. You should have working IPv6 connectivity without doing this – don’t just do it as a desperate option to get things working.