Monday, September 7, 2020

Chaos


This weekend I was re-reading John Gleick's excellent book Chaos and was reminded of my youth when a team of researchers and Bristol University were working with the Mandelbrot set as an example of a "highly parallel" problem. The focus of their research was the Inmos Transputer and the Occam programming language. In those days, this was a compute-intensive problem.

The modern-day inheritor of that kind of parallel-programming architecture is the GPU. It occurred to me to try and use a mandelbrot generator as a way of experimenting with low-level GPU programming, but in the end I was more interested in how much more powerful my current Macbook Pro is than all that hi-tech architecture of the late 1980s. TL;DR: you can't believe how much.

Implementation

So I set about building a quick-and-dirty implementation of a chaos field generator. I wanted to go a little bit beyond just the Mandelbrot Set and consider other examples too - such as the attractors for the various solutions to n3 -1 = 0 using Newton's Method.

"Obviously" I chose to do all of this in JavaScript. The main reason being that all the code runs in the browser (using a 2d canvas), and that makes it easy to distribute. Moreover, JavaScript has the ability to plug-and-play with different functions, and I used that to control what you see.

The code can be found in the usual place under the directory chaos, and the "results" are up on my website at http://www.gmmapowell.com/chaos/.

Conclusion

You may be able to, but I cannot believe how much more powerful our computers are today than they were 30 years ago. Things that took forever on the latest, most expensive equipment can now be done in an interpreted language on a regular laptop (or even a phone!).

Wednesday, September 2, 2020

A More Functional Database

I have recently been using DynamoDB and, to be frank, its API (at least in Java) is, IMHO, awful. So awful, in fact, that in wrapping the API as described below, I was forced to write a mini-wrapper to call from within the wrapper.

Obviously, I was not going to put up with this. But rather than launching in and doing "something", I sat back and considered what I really wanted. If you know me or read this blog frequently, you would probably fairly rapidly come up with a list something like this:
  • Functional or functional-leaning;
  • Tell-Don't-Ask;
  • Transactional;
  • Asynchronous.
Now, as it happens, these things are not really in conflict in this case, although I ended up with three APIs:
  • A mini-wrapper I had to create just to tidy up the $DynamoDB$ interface;
  • A low-level "tell-don't-ask" API; and
  • A more transactional API with a functional feel.
What I really want to talk about here is the last of these; the other two are really just stepping stones on the way, although, for full disclosure, it was only when I had used the TDA API for a while and experienced the pain of doing it, that the third API occurred to me.

While I’m fully disclosing, this code didn't start here as an experiment, but grew organically out of one of my many “real life” complex and messy projects across a swathe of production code intended to support multiple databases as well as other integrated systems; for simplicity I’ve rewritten history a little and pulled out all the relevant classes for DynamoDB and assembled an example test case that steps through the basic CRUD flow using the functional API.

What am I thinking?

First things first. What do I even mean by a "functional" database? Almost by definition, databases are the opposite of functional. The very way we think about - and describe - databases is CRUD: a sequence of read/update operations with occasional creation and deletion to keep life interesting.

Given my long background in functional programming, a lot of things have been percolating in my brain over the decades and recently have experimented a little with (the somewhat-functional) FaunaDB (which I will eventually get around to writing up here) and in doing that I noticed something of a similarity between functional programs and how I might ideally write database logic.

Here is some functional code:
top = repeat x '*'
x = 5
repeat 0 c = []
repeat n c = c:repeat (n-1) c
Nothing particularly special here. But there is a rhythm to functional programs, which can be described as "define something; use it; repeat". I've often described writing functional programs as "do a little bit of the work now and push the rest off onto another function".

On the other hand, what drives a functional program is the fact that somebody somewhere wants to know a result and expresses this somehow. Generally, this either comes from a "main" method or from some kind of console or REPL. The "top level" expression is broken down into sub-expressions which are evaluated in turn until the basic blocks are reached.

(As an aside, TDA almost exactly inverts this logic: it takes all the basic blocks and says that the results should be sent to a consolidator that combines them and then promotes them to the next level until the top level handler is reached.)

So the question is: how does this relate to databases?

A "pull" model for databases

The normal way of dealing with databases is as an imperative begin-read-write-commit loop. But this transaction can also be viewed as a single operation in a REPL - the model used by functional programs. In this model the transaction becomes:
  • Decide what you want to do
  • Do all the reads and transformations
  • Do all the writes in a single step
How is this different? Most importantly, an important topic in standard database theory isread your writeswhich basically says that there is a choice when you have done a write in a transaction about whether when you use "the value" again - particularly if you choose to read it back from the database - whether you see the version you wrote or the one that existed before your transaction started. By deferring all the writes until the end of the transaction, we avoid this conundrum, which is exactly what you would expect from a functional model, in which values do not change over the course of a function's evaluation.

The other major change is in the way in which we describe the steps of the logic. As with a functional program, we name each value that we read in; as we name it, it becomes available for other steps in the logic. For example, a transaction which reads two values and then writes the result might ordinarily look like this:
begin tx
x = get 'A'
y = get 'B'
z = x + y
put 'C' z
end tx
In a functional notation, we might write something like this:
tx = [Store 'C' z]
z = x + y
x = get db 'A'
y = get db 'B'
Now, this is neither purely functional or really executable; but the idea is that we are describing the transaction rather than actually running it.

Handling the impedance mismatch with imperative languages

Now, while I want to use a "more functional" approach to databases, I in fact still want to do this from within Java - an imperative language. How do I handle that?

It's actually not all that hard. Java has functions, and I can say that each of my "steps" can map to a function. I have an entry point, which kicks off the initial GET operations and identifies the logic operation (z) that I want to be invoked when both of the return values are available. I then define z and annotate its parameter arguments to say where they come from. Basically, the transaction becomes a state that the functions can access through the parameter annotations. Once a value is read or calculated, it is available and any method that depends on that can now be executed.

Handling asynchronicity

It's obviously very important that the database access be asynchronous - the alternatives are to waste threads blocking or just to kill performance - which aren't really alternatives at all.

This is done by having all the functions call into an asynchronous layer in the database and provide a central place to call back. When they do, the current state is updated with the new value and the list of pending logic calls is examined. Any that are now "ready" - i.e. those that were just waiting for this value to arrive - can now execute, possibly launching more GET or LOGIC requests. Any of these that are ready to execute can be run the moment that the current method returns; others will be deferred until all their arguments are present. Every logic method can also add to the pool of "pending writes".

Eventually, all of the logic is complete and it is possible to do the writes - unless an error occurred in the transaction, in which case none of the writes will be done.

An implementation

The implementation (available in the git repository) is called GLS for get-logic-set/subscribe, describing this alternative loop.

Start with the simple test case (SimpleGLSUsage). The fundamental concept here is the UnitOfWork, which is created in the test initializer initTest. This corresponds to the conventional notion of a "transaction" (I have shied away from the word transaction in part because it is overloaded and in part because the semantics of the underlying database are unspecified; in the case of DynamoDB it is not transactional).

Within a unit of work, it is possible to create multiple parallel Relations. A relation represents a thread of work going on with its own namespace - a scope, if you will, within an outer definition in a functional program. This allows multiple lines of logic to coexist while still being part of the same "unit" - the same values are only read once, have the same value across all relations, and the unit succeeds or fails together.

This simple test case is not a series of unit tests. Rather, it is a "script" for executing tests. To make this work, JUnit 5 is used along with its OrderAnnotations. The first test ensures that we can create a trivial object. The enact method on the unit of work says that we have configured everything, and it is ready to go; waitForResult blocks until the unit has completed - success or failure.

The second test simply attempts to read this object back in.

The third test reads the object back in and then prints it. Because Java does not treat functions as "first-class" objects, the method printHello is referenced as a string in the call to logic. This is the name of a method in the RelationHandler class, which in this case is defined to be this, which is the current class.

The fourth to sixth tests check that the greeting is what we would expect it to be, update it, and check that it has changed. The final test cleans up the record by deleting the record (although if anything goes wrong, the whole thing will be deleted on restart).

It is obviously possible to run these tests, but some setup is required: you obviously need an AWS account with a DynamoDB instance configured; you need to create a table and pass its name to the test in the test.table.name property.

Conclusion

The normal database paradigm fits well with imperative languages, but has the normal drawbacks of those languages - most specifically, very slow, synchronous behaviour. It's hard in imperative languages to break out of that because of the complexity of dealing with all the asynchronous state. Changing the metaphor - and making a central agent responsible for the state management - simplifies the code and improves reliability.

Interestingly, in writing this, I can see that my actual implementation does not map perfectly onto my mental model - my implementation has turned out to be more imperative than my mental model. My entry point is fairly close to the "bottom" of the execution stack - as it would be with a TDA implementation. To match the mental model more closely, the "get" operations should not be invoked directly from the entry point function, but should be their own functions, and each of the functions should be named to match the "value" it produces. Maybe I should try again and report on that experiment.

Tuesday, June 30, 2020

A Guest Post By Nelson

I have been very privileged to have had Nelson pairing with me for the past few weeks during lockdown. Here he gives an account of his experiences pairing with me.

I wish I could share Gareth's appreciation for the opportunity to work with me. But the fact is that on the whole, I find Gareth (or "the idiot" as I tend to think of him) very difficult to work with. Apart from just being generally cranky, cantankerous and egotistical, he seems to want to be in control all the time and not very interested in what I can contribute. I sometimes wonder why I'm here at all.

Leaving aside his continual desire to "drive" and control the keyboard (I prefer to think of myself as the more cerebral, anyway), he seems to blame me for everything that goes wrong - even though most of the time it's his fault - and then, whenever we do finally manage to get something working, he takes all the credit - because he was the one doing the typing! As if he's had a single original thought in his life!

More than anything, it's his interaction style that I find difficult to deal with. As I said, I am more a thinker, an observer of reality. He's always chomping at the bit to do something without really stopping to consider if it is a good thing. And then when I try to interrupt his flow, he doesn't even want to listen to me.

Often, I'll find myself contemplating some aspect of the code which seems perplexing or hacked together when suddenly, without warning, he will change the window and flit off onto some completely different task. Then, just when I'm about to point out some obvious flaw, he'll start the debugger and tell me that he's "busy". Eventually, of course, he comes back to the same point I'd originally wanted to let him know about. If only he slowed down for a moment to talk to me.

And then there's his language and communication style. Leaving aside the continual shouting and swearing (well, when you make that many mistakes can you blame him?), whenever he does talk to me it tends to be in a challenging and angry way. Why can't he recognize that I have something to contribute? And even when he is in a good mood - finally having figured out some trivial bug - he says "high five, Nelson", even though he knows I have flippers. Talk about species-ist.

And, finally, since I'm here, I'd like to point out how much nagging and gnashing of teeth it has taken in order to get him to just let me write this little piece on his blog. You'd think I wanted to do a hit job on him or something.

About Nelson

Our guest blogger today was Nelson, a plush shark from Ikea, who sits next to me on a daily basis as my pair partner when I am working from home. He appears to have deep insights into my psyche as a pair partner, not to mention being able to provide truly honest feedback.

Friday, June 26, 2020

Configuring a Custom Domain for API Gateway


Yesterday I was trying to configure a custom domain for one of my sites working in API Gateway. At the end of the day, everything you would expect to have to be there was there, but I found the process very hard to comprehend. While I'm not going to directly recount my experiences - that would be too painful - this is hopefully a somewhat simplified, annotated guide to what I did.

Route 53

Most of the Amazon documentation about this will tell you how to do this, that and the other in Route 53 - Amazon's DNS service. I'm sure that it all works and although I think they say that you don't have to use Route 53 I went down a deep bunnyhole trying to establish the right records on Route 53 that appears to have been completely unnecessary (on a practical level).

It was, however, while I was doing this that I understood the vital piece of information that I was missing. But, no spoilers …

Assumptions

First off, I'm assuming that just by reading this you know what AWS is, have an account and basically know your way around the console. If you don't, then you probably don't want to do any of this …

I'm also assuming that you know a little bit about DNS and have some provider (whether AWS or otherwise) which registers and manages domains for you and will enable you to play with their DNS records, in particular, to create an Alias or CNAME record. If you don't know what I'm talking about, you probably don't want to do this; if it sounded plausible until you reached the CNAME bit, you probably want to look at the help pages or forums for your provider. This is often under the set of "Advanced" or "Do Not Touch" features.

Assuming you are still with me, I'm going to assume that you have configured an API Gateway already and that you know the (so-called 'custom') domain that you want to serve it on. I'm assuming that you have some request that "works" with the default domain. If not, I suggest you get that to work first.

Because API Gateway is secure, you need to provide a certificate through ACM (the Amazon Certificate Manager). This (obviously) needs to be a certificate which names the domain that you wish to use. This process is a little tricky in itself and I should have probably blogged about that when I did it a couple of weeks ago.

Finally (and possibly a bit more tricky), I'm going to assume that you have considered the ramifications of "edge-optimised" vs "regional" API gateways and that you have gone with regional. This is all a bit complicated (too complicated for me?) but basically if you have "edge-optimised" gateways, you will need to set cloudfront up across them in order to do this. I didn't try that, so the rest of this article may or may not be relevant. If you aren't sure, the "Endpoint type" column on the API Gateway screen says if an endpoint is Regional or Edge.

Creating a Custom Domain Name

The first step is to create a "Custom Domain Name" in API Gateway. This does not work in at all the way that I would expect or match any of my mental models, so I will try and explain it from my point of view. I would expect that the option would be called "bind to domain name" or "map domain name to stage" and would be an option on a specific stage of a specific gateway. The functionality is exactly what I would expect, but you reach it through a completely different route.

Note the ID of the gateway you want to bind (that's the string of 10 or so random alphanumerics, not the more user-friendly "Name") but do not click on it. Instead click on the sidebar menu item "Custom domain names".

This will take you to a simple screen which has a search box and a create button. Click the create button to open up a detail window.

In the "Domain name" box, put the domain name you want to bind.

In the "Configuration" section, you will (probably) want to select "Regional" and "TLS 1.2". You then need to choose the appropriate certificate from the ACM.

Add any tags you want and click "Create".

You could easily be forgiven for thinking that you are done at this point but you are not (I have to forgive you since this is where I went wrong).

You should now be on a screen with a label "Domain name details". It all looks pretty reasonable and like what you just specified. But scroll down. Keep on scrolling. Yes, there. The section that says "API mappings". This is what you need to configure in order to bind this domain name to your API Gateway.

Configure API Mappings

Click the button that says "Configure API Mappings". On the screen that appears, click "Add new mapping". You now need to select the correct gateway (API) by comparing the id to the one you remembered earlier (while the name is shown it is not guaranteed to be unique) and the correct stage. It is also possible to bind individual domains to sub-paths of API Gateway, although I personally don't have a use for that.

Click "Save". You should be returned to the "Domain name details" screen.

Configuring DNS

Internally, what has happened here is that API Gateway has cunningly created a new, forwarding web server with a new DNS name. If you look down the list of configuration options you will see an "API Gateway domain name" that looks like nothing you've seen before. All of mine start with "d-" and then have a 10-character name that is most assuredly not one I've seen before.

What's cunning about this server is that if you make an HTTP request to it telling it that you want it as the 'Host', it will return that certificate. If you tell it that the 'Host' name is your custom name, it will return that certificate (when we've finished, you can check that using curl -v, or otherwise).

So now all you need to do is to point your DNS of record at this DNS name. Exactly how you do that depends on your DNS provider. AWS will continually push you towards using Route 53, and it seems that there instructions in that regard are correct, but given that I don't use Route 53 on a daily basis I tried to avoid that and it all worked fine.

Simply set up a CNAME record (AWS, and possibly others, refer to this as an Alias). The value of the CNAME record needs to be the API Gateway domain name quoted above with a final trailing '.'. The purpose of this final dot is to indicate that it is an absolute DNS name. Depending on your DNS provider, it may or may not be necessary for you to actually enter it.

Testing

Given that you have a working API gateway, you should now be able to substitute the API Gateway domain name quoted above in place of the default domain name AND the stage (together with any prefix path you may have chosen to specify in your mapping). This should work immediately - even before you have configured your DNS.

I'm not really sure what to tell you if it doesn't - the whole reason for this blog is that I spent a good hour or more thrashing yesterday because I couldn't figure out the problem (which was that I hadn't set up a mapping from my custom domain name).

Assuming that works, try using your "custom" domain name in place of the API Gateway domain name.

There's a very good probability that this doesn't work. DNS takes a while to propagate for all kinds of reasons. If your DNS provider also offers hosting, try from one of those machines - they are "closer" to the source of the change and will see it sooner. Otherwise your best bet is to wait for a while.

You can use the tool nslookup (where available) to check whether your DNS is reporting the existence of the domain and you can use ping to see if it responds. Or you can just try using curl or your browser until it gives in. But seriously, it can take literal hours for DNS changes to propagate around the world so don't panic.

If you see that the DNS has propagated, and it still doesn't work, then you have probably configured the CNAME incorrectly. The most likely options are that you mistyped (or mis-copied) it and that you either didn't - or did, depending on your provider - provide the final dot.

Conclusion

Configuring a custom domain for AWS API Gateway is actually quite simple but, IMHO, the interface is poorly designed and then poorly explained. If you don't make any mistakes, the process is straightforward, but if you do any one of the steps wrong, you receive very little feedback about where the problem might be.

Thursday, May 7, 2020

Adjusting the Apex Angle

Finally, we want to be able to change the aspect ratio on the cones, that is, how their base compares to their height.  We are going to control this by limiting the scroll wheel action of moving backwards and forwards to the area around the origin and allowing the same gesture further away to cause the radius to expand and contract.

Calculating the Ratio 

The first thing I tried was to simply update the radius on the cone but, predictably enough, once you have created the cone it stays created.  I suppose it would be possible to remove the cones and create new ones, but that seemed excessive.  Instead, what we are going to do is to scale the cone but not symmetrically: we are only going to scale it in the x and z directions.  Because scaling is the first operation to be performed in our update sequence, the base of the cone will always be in the xz plane and so scaling in those directions is sufficient.

In order to make this work we need to keep track of the "current intended radius" and express the scaling as a ratio between that and the "actual radius" of the cone.  We hardcoded that to 20 earlier, so our first step is to extract that; at the same time we'll extract the height of the cone which is used in a number of different places in the code.
var rad = 20;
var baserad = 20;
var ht = 50;

Identifying the cases

Then we need to identify whether we want to continue using the current event handling - moving the cones backwards and forwards - or the new apex angle adjustment based on the mouse location.  The mouse events provided are based on the canvas coordinates, but have been adjusted to reflect the origin.  Thus by saying "close to center" we really do mean something like within a 60-pixel radius of (0, 0).  This function uses Pythagoras' theorem to determine that:
function closeToCenter(e) {
  var r = e.x * e.x + e.y * e.y;
  return r < 3600;
}

The Updated Event Handler

Finally, we can implement the updated event handler.  The existing logic to update the depth is now made conditional on the event being sufficiently close to the center.  The new logic that is applied in the other case updates the "current radius" and then scales both the x and z dimensions based on the ratio of the "current radius" to the original "base radius". 
if (closeToCenter(e)) {
  depth -= e.wheel;
} else {
  rad -= e.wheel;
  top.scale.x = rad / baserad;
  top.scale.z = rad / baserad;
  bottom.scale.x = rad / baserad;
  bottom.scale.z = rad / baserad;
}
Either way, the existing code to actually update the object's matrix will be called once the new values have been calculated.

Summary

That finishes up our conic section viewer.  This post is tagged SHOW_CONES_INTERSECT_PLANE.

The viewer is not as slick or perfect as I would like, but it does work.  More importantly, I now feel I have a handle on what it takes to build WebGL applications with PhiloGL.

Since there is a lot more to WebGL than this, I may well be back to try and dig into additional topics such as custom rendering, blending and the like which seem to require a deeper understanding of the WebGLContext, and possibly even some embedded script programming.

Showing the Intersect Plane

What we really need to be able to do is to visualize the intersect plane, that is, the curve that results by finding all the points on these cones that have the value z = 0.  I've tried and failed a couple of approaches: I was unable to have two WebGL applications showing in different canvases; and, while you weren't looking, I tried adding a plane to the model but the results weren't adequate.

So what we are going to do now is to add another event handler - for click this time - and this will switch between the "3D cone" and "2D conic section" modes.  All the rest of the functionality will work in both modes; you are just somewhat "in the dark" about what you are doing when you are in "intersect" mode.

Showing the Intersect

The basic approach we are going to use for this is to instruct the camera to only show a small amount of the full 3D space; everything else is hidden.  The camera has the feature to ignore everything that is too close or two far away, thus allowing the appearance of X-ray vision.  But by limiting our view to just around the z = 0 plane, we are able to see the intersection of the curve and the plane.  This is exactly what we were originally going to do in the right hand canvas; it's just that we are now doing it "modally" rather than in parallel; time-slicing rather than window-slicing if you like.

Because we have defined the camera to be at z = 99, we would like to define the view to be exactly 99.  However, when we do that, nothing shows up.  Instead, we have to define it to be "a little bit" either side of that exact amount.  The consequence of this is that the lines are not perfect lines but "a little bit fat" but this is just a demonstration, so I'm not that bothered.

We start by introducing a variable to remind us of which mode we are in:
var showIntersect = false;
Then we add an event handler to change between the modes:
onClick: function(e) {
  showIntersect = !showIntersect;
  if (showIntersect) {
    this.camera.near = 98.75;
    this.camera.far = 99.25;
  } else {
    this.camera.near = 10;
    this.camera.far = 200;
  }
  this.camera.update();
}
This first handles the event by inverting the value of showIntersect.  Then, if the value is now true, it limits the camera range to a very small window around the z axis (from z = -0.25 to z = 0.25), causing something approximating the intersection plane to be shown.  If the value is now false, it goes back to the approximate defaults of showing a wide range.

As with everything else, it is not sufficient to update these variables; it is necessary to call update() (on the camera this time), and then the next redraw event will change the image on the screen.

Summary

Although my previous attempts had met with failure, this approach to showing the intersect plane, while not perfect, was remarkably easy to implement.  The code can be checked out at SHOW_CONES_INTERSECT_PLANE.

Moving the Cones Backwards and Forwards

The next step in our plan is to allow the cones to be moved backwards and forwards along the z-axis.

A Quick Refactoring

Although it was perfectly adequate for what we were trying to achieve, the rot method in the previous post in fact bundled two responsibilities: one was the actual rotation, based on mouse position, and the other was to update the matrix based on the current rotation.

In order to incorporate the new event, we need to split those responsibilities.  So let's create a new update function at the top level and call that from within our rot function.
function update(comp) {
  var m = comp.matrix;
  m.id();
  m.$rotateXYZ(comp.rotation.x, comp.rotation.y, comp.rotation.z);
  m.$translate(comp.position.x, comp.position.y, comp.position.z);
  m.$scale(comp.scale.x, comp.scale.y, comp.scale.z);
}

The Event Handler

The next step is to track the scroll events and remember how far in or out of the screen the cones currently are.  We can do this by adding an event handler for onMouseWheel in the same way that the moon example did, and then tracking the current depth and updating both cones.

At the outermost scope within the loadConics() function, we can declare a current depth, i.e. how far the cones are in or out.  Note that it is very important to initialize this variable because otherwise the mathematics that depends upon it will fail and nothing will render.
var depth = 0;
And then in the events section, we can add the handler that updates it:
onMouseWheel: function(e) {
  e.stop();
  depth -= e.wheel;
  update(top);
  update(bottom);
}
Finally, we need to use this value in our update method.  Going back to what we said about order of operations in the last post, it's very important that this translation is the last thing that is done.  How do we manage that while at the same time insisting that the y translation is done before the rotation?  Well, quite simply, we are allowed to call translate (and indeed any of the operations) multiple times.  We invoke translate twice, once for the y translation and once for the z translation.  Of course, these are still written "backwards", so we end up with this:
function update(comp) {
  var m = comp.matrix;
  m.id();
  m.$translate(0, 0, depth);
  m.$rotateXYZ(comp.rotation.x, comp.rotation.y, comp.rotation.z);
  m.$translate(comp.position.x, comp.position.y, comp.position.z);
  m.$scale(comp.scale.x, comp.scale.y, comp.scale.z);
}
Now we can move the cones in and out of the screen.  This is tagged MOVE_CONES_BACKWARDS_FORWARDS.

Summary

With a small refactoring, we have been easily able to move the cones in and out of the screen.