mal.haza.website

Holster 1.0.1

I had one annoying bug that I hadn't got to the bottom of before yesterday's 1.0 release. I even added a bunch of unit tests to make sure Holster was working as I expected. The previous versions of the unit tests looked like this:
 
test("get unknown keys in for loop callbacks null", (t, done) => {
  for (let i = 0; i < 5; i++) {
    holster.get("unknown" + i, data => {
      assert.equal(data, null)
      if (i === 4) done()
    })
  }
})

The idea is that you should be able to make as many requests as you like and Holster will manage the context of each request and map it to the right callback. Can you see the problem with the test though? The loop will finish before the callbacks are called, so the condition will match and done() will be called regardless of if the callbacks were each called correctly. In fact only one was being called but the tests still passed.

They now look like this:
 
test("get unknown keys in for loop callbacks null", (t, done) => {
  let count = 0
  for (let i = 0; i < 5; i++) {
    holster.get("unknown" + i, data => {
      assert.equal(data, null)
      count++
    })
  }
  setTimeout(() => {
    if (count === 5) done()
  }, 200)
})

Which makes sure each was called correctly. Holster wasn't able to pass this version of the test because the context id was being dropped. The id is passed to the API as a parameter that I thought would form part of the closure, but it seems the callback function itself is created before that can happen. The answer was to pass the id to a function that then returns a new callback, which then appears to be called correctly.

This was enough of a problem in the app I'm working on for a 1.0.1 bug fix release.

Holster 1.0

Wow I can't believe it has been over a month since I first tried switching to Holster in an existing application. It's just what it needed though, as it brought up all sorts of edge cases and timing issues that I hadn't thought of. The end result is that Holster now feels usable! This is a 1.0 release because multiple breaking API changes were required.

The biggest change is to the user API, which I mentioned needed fixing in the last update. It actually needed a complete redesign so that calling holster.user() returns a usable API and can also be used to fetch user data on other accounts. It now does this by taking an optional array of two values when making a query, so if you log in and try user.get("key") you will get your own data, and user.get(["other-account", "key"]) will get data on another account and not interfere with your current log in status.

The other API change is to holster.on(), which now takes four parameters. Previously it was only given a lex object and a callback, but I found a lot of situations where I wanted to add a listener and get the current copy of the data at the same time. So the third parameter is a boolean to get the data, and the fourth parameter is the same options object passed to get() to update the wait time if required.

Another API change is that user.recall() previously required a flag to specify if credentials are stored in local storage or session storage, now it just tries both so no flag is required.

As well as this there are a lot of updates that make it feel like a solid 1.0 release.

Verification is now done properly on user nodes, previously it would sign an update which didn't always contain all properties on a node. Now it will fetch the node first if required to make sure it signs everything. Signing and verification also need to be performed on the exact same object, so it sorts the keys to guarantee the same output.

When I started the port I noticed GunDB was doing a lot more wire sends than Holster, I now see how that helps to keep data in sync. It also means the in-memory graph is always up to date, so get requests now don't need to go to disk unless there's been a restart.

This felt like a lot of debugging to get this release out, but looking at the changes being committed it's far from a complete overhaul to how it was originally written! Hopefully this means it's close to being reliable. Next goal is to push the update to rsstream.app and see how it performs over the next few weeks.