Archive for the ‘Open Source Software’ Category

Simple Immutable Data in Postgres

Monday, January 14th, 2019

PostgreSQL.jpg

A friend asked me today for the trick I've used in the past to make sure that the data in a database is immutable and versioned. This is nice because it matches what Clojure acts like, internally, and it makes it easy to see who's doing what, when. Assuming we start with a table - for the sake of argument, let's say it's customers, and looks something like:

  CREATE TABLE IF NOT EXISTS customer (
    id             uuid NOT NULL,
    version        INTEGER NOT NULL,
    as_of          TIMESTAMP WITH TIME zone NOT NULL,
    last_name      VARCHAR,
    first_name     VARCHAR,
    PRIMARY KEY (id, version, as_of)
  );

where the first three fields are really the key ones for any table to work with this scheme. You need to have a unique key, and in this case, the easiest I've found is a UUID, and then you need a version and a timestamp when that change was made. What's left in the table is really not important here, but it's something.

You then need to make an Audit table that has the same table structure, but has the string _audit added to the name:

  CREATE TABLE IF NOT EXISTS customer_audit LIKE customer including ALL;

What we then need to create is the following trigger that intercepts the INSERT and UPDATE commands on the customers table, and places the historical data into the Audit table, and the most recent version of the customer data is always kept in the customer table.

The trigger looks like this:

  --
  -- create the trigger on INSERT and UPDATE to update the version if it's
  -- not provided, and to maintain all versions in the audit table, but have
  -- the current version in the non-audit table. Importantly, NOTHING is
  -- deleted.
  --
  CREATE OR REPLACE FUNCTION audit_customer()
  RETURNS TRIGGER AS $body$
  DECLARE
    ver INTEGER;
  BEGIN
    -- get the advisory lock on this id
    perform pg_advisory_xact_lock(('x' || translate(LEFT(NEW.id::text, 18),
                                  '-', ''))::bit(64)::BIGINT);
 
    -- get the max of the existing version for the data now
    SELECT MAX(version) INTO ver
      FROM customer_audit
     WHERE id = NEW.id;
    -- and bump it up one and use that
    IF ver IS NULL THEN
      NEW.version := 1;
    ELSE
      NEW.version := ver + 1;
    END IF;
 
    -- if an update, then we need to insert the new
    IF tg_op = 'UPDATE' THEN
      -- now let's insert the old row into the audit table
      INSERT INTO customer_audit
        VALUES (NEW.*);
    elsif tg_op = 'INSERT' THEN
      -- now let's insert the new row into the audit table
      INSERT INTO customer_audit
        VALUES (NEW.*);
      -- and delete the old one in the customer table
      DELETE FROM customer
        WHERE id = NEW.id
          AND version <= ver;
    END IF;
 
    -- finally, return the row to be inserted to customer
    RETURN NEW;
  END
  $body$ LANGUAGE plpgsql;
 
  CREATE TRIGGER set_version BEFORE INSERT OR UPDATE ON customer
    FOR each ROW EXECUTE PROCEDURE audit_customer();

At this point, we can INSERT or UPDATE on customers and the previous version of that customer will be mmoved to the Audit table, and the the most recent version will be held in the customers table.

I have found this very useful, and I've put it in a gist for easy access.

The point of:

    -- get the advisory lock on this id
    perform pg_advisory_xact_lock(('x' || translate(LEFT(NEW.id::text, 18),
                                  '-', ''))::bit(64)::BIGINT);

is to get an exclusive lock on the data for a given id. This is necessary to make sure that updates from multiple services get serialized on the same data. This scheme can't ensure that there are merged changes - only that there is a sequence of changes to the table, and each one is entirely correct for the time it was entered.

So... what happens if you have a string as the primary key, and not a UUID? Well, use can use the MD5 checksum of the string as the lock indicator:

    -- get the advisory lock on a general string
    perform pg_advisory_xact_lock(('x' || md5(NEW.wiggle::VARCHAR))::bit(64)::BIGINT);

where the field wiggle is a varchar, and here, we are just computing the MD5, and using that as the basis of the lock. Yes, there could be some hash collisions, but that's likely not a huge performance problem, and it's conservative in that we'll over-lock, and not under-lock.

UPDATE: a friend asked about using an int as the primary key, and in that case, the line would be:

    -- get the advisory lock on a general string
    perform pg_advisory_xact_lock(NEW.id::BIGINT);

where the column id is an int. Again, we just need to get it to the bigint for the advisory lock call. After that, Postgres does the rest.

SubEthaEdit 5 is Open Source

Wednesday, November 28th, 2018

subethaedit.jpg

This morning I saw a tweet from SubEthaEdit that they were Open Sourcing the editor - and the current version, SubEthaEdit 5, was still on the Mac App Store, and would be free. This was a real surprise to me. I've paid for several of the versions of this collaborative editor on the Mac - heck, I've written syntax highlighting definition files for Make and Fortran for this editor. It's a big part of my tools in the past.

I have worked with my good friend on Macs for many years, and when this first appeared, as Hydra, I thought that this would be a great tool for working on code with him. But it was commercial, and we were in different states, and we hadn't even started using Git - and GitHub wasn't even an idea at the time. So it just fizzled out.

But at several times in the last 5 years we've both talked about getting something like this going for remote pair coding. It's just an editor, and he's now using Cursive for his Clojure coding, so again, maybe it's not such a great fit... and there are other services that are going for an add-in mode for existing editors, so maybe it needs to be updated to really find it's market. If so, I think that would be great.

I hope it finds a great group of developers now that it's Open Source. I'd love to have a good tool that's really written to handle the collaborative editing from the jump. Then again, I'm not all that sure what we'd need above GitHub... but it's an admirable goal.

Fixing Sublime Text 3 Clojure Markdown Blocks

Wednesday, November 21st, 2018

Sublime Text 2

Today I was looking for a solution to a problem I saw in the syntax highlighting of Sublime Text 3's Markdown files when there were Clojure code blocks in the file. All the other coding blocks I had been using were highlighting with a different background, and the text in the block was highlighted according to that language's rules. But not so with Clojure.

So I asked on the Sublime Text Forums about the issue and to my amazement, I got a response! The response was clear about what I needed to do, and while there was a slight issue with the installation of a package, I solved that with a git clone in the directory, and I was in business.

At the time, I also submitted an issue with the Sublime Text GitHub group, and let them know I was having a problem. Very much like the issue on the Forum post. When I got an answer on the Forums, I updated the GitHub issue, and included the answer I got from the Forum guy. He didn't sound like he was tied into the GitHub group.

Mistake #1.

I then got a response on the Issue kinda poking me to make a PR because I have an answer that worked for me. And me, trying to be helpful to the folks that helped me, wanted to respond. It was just the tone of the request that was a little off. I should have listened to that voice.

Mistake #2.

So I made the Pull Request on the project and put in the comments, etc. and submitted it. At this point, I really want to thank GitHub for making a tool that has the most amazing workflow I've ever used. The ability to fork, and make a PR on a GitHub repo, and have the fork linked to the PR so that updating one updates the other is just amazing. I know it's not impossible - but it is very nice, and that's so nice to see.

The PR, as you can see, is really a very specific YAML file section, and there were, of course, no comments in it. I had no idea that the elements have changed, so when the reviewer chastised me for not looking at the rest of the file, and detecting the changes, I got a little prickly. But I said to myself "Take it easy... this is a simple mistake", and I apologized on the PR, and made the changes.

At the same time, the guy who helped me on the Forum chimed in and threw me under the bus - saying "Yeah, he didn't do the right thing - I gave him something that worked on the release version, but not master". I love it when people do this. It's such a comforting thought to be in an industry of people with such high integrity.

So that was fixed, and I thought "OK... enough of this, moving on..."

Mistake #3.

I then got a note saying that I hadn't read the rules of PR submissions, and that I needed to write rendering tests for this new code block. And believe me, these are not easy, and they are not trivial, and all this for something I should have just said "Sorry, I'm too busy, you can do the PR on your own."

But I read up on the tests. How to write them - and they are stupid. But I did it. And then I wrote the rendering tests and added those to the PR. And they all passed. Which was nice. So now I'm thinking "OK... this is finally over." But when will I learn?

Then a maintainer comes along and says this will have to be put on hold because they don't understand something about what's happening in the code, and that is causing an issue that is unrelated to the PR, but the PR would make things worse, or something.

So... I try to be a nice guy after someone has been nice to me... you know... passing it on... and for that, I get to deal with the Slashdot Kids living in their parent's basement and holding some power trip over poor saps like me that try to help folks out.

But I have a fix. I know what to do for subsequent releases, and I'll never do this again with these folks. Lesson learned.

Published Cap’n Hook to Clojars

Monday, November 19th, 2018

Cap'n Hook

This morning, I wanted to get the first version of Cap'n Hook published on Clojars, but I needed to get a few things cleaned up. First, I needed to get the latest changes to durable-queue up to Clojars, as there were some dependencies I need to clear up - most notably in the nippy and byte-streams packages, so that was fixed up and a simple publish, and I'm up on Clojars.

At this point, I'm a little surprised that I have to type in my GPG passphrase each time I sign the jar for publishing. I can see the logic that I've read online - basically, signing is a fairly infrequent task, and needs to be done with care, so ask for the passphrase on those occasions. OK... I get it, but I'd still like to have it in my Keychain, and then just lock it up there. No need to keep typing it in.

I need to pay closer attention to the passphrase dialog on the next publish, and see if I can't get it to save the passphrase in the Keychain. But it's not horrible at this time - I don't publish all that often.

After I got durable-queue up, I could then clean up the capn-hook code a little and then publish it. Again, not bad - cleared out a few dependencies to streamline things just a touch, and then update the README.md for the folks coming to the GitHub page, and away it goes.

Not a bad morning - got a little more familiar with GPG, and published a few things. Good enough.

First Cut of Cap’n Hook

Wednesday, November 14th, 2018

Cap'n Hook

This morning I've finally put the docs and a few tests on my Clojure library called Cap'n Hook - after the cereal, and the fact that it's all about making web hooks easier to implement in services. When I was looking for my current position, I was thinking what could I write that would be fun and useful at the same time. One of the things that I knew were really useful were the web hooks that allowed one-way, asynchronous, message passing from a service to a set of registered clients.

While it's not really a good replacement for a real messaging system like any of the messaging offerings from AWS, or Tibco, or even Java MQ - it is very easy to implement, and if you're in the world of RESTful services, it's a really handy thing to have, as it's super simple to implement.

Also, if you put in retries, as I have, then it's pretty close to reliable - but only if your receiver is online and taking POST messages on that URL. Sure... it's not perfect, but for dealing with simple messaging between data centers, it's hard to beat. There are a ton of services (GitHub, etc.) that are using this to fire-off events on the client-side of things.

So it's pretty simple to use - the key is that for most cases, you are going to need to have some persistent set of registered URLs, These are the "endpoints" for the HTTP POST calls that this library will be sending in response to the main application saying "Here you go!".

I was a little blocked for several months on this - because I wanted to be able to implement a shared, persistent, reliable storage for the registrations - and then yesterday I realized that was just a bad idea. Each application that uses this will want to implement their own registration their way - and while I implemented a simplified registration that wasn't shared or persistent, the ideas are there, and it's easy to slot in a different registration scheme at any time.

I can see using redis to store the URLs - it has all the primitives to do what's needed. And there's Postgres - or any database, really, and I even cover that in one of the examples in the README.md on the GitHub repo. So there's no need to really solve the registration problem for everyone. Just give the users a simple one to get going, and then they can get as fancy as they need to.

I spent a little time on writing some unit tests, but it's going to be hard to really test the sending as it really needs to hit something, but I'll spend a little time today seeing if there isn't an "echo" test site out there for just this kind of testing, and then I can test sending the POST messages to see if they work.

But it's been a lot of fun to get this done and into testing... The documentation was great fun, and it just was a great little thing to finish.

iTerm2 is Quite Impressive

Tuesday, November 13th, 2018

iTerm2

I've been using iTerm2 since it was forked from the original, as there were things in Terminal.app that I just didn't like at the time: forced scroll bars, difficult selection of words... lots of little usability and chrome things. Now it's true that since then, Terminal.app has gotten a lot better - and on each new macOS upgrade, I always give it a try for a little bit... just to make sure it's still not the tool I'd like to use. But for quite a while, it's been iTerm2.

So why write about it now? Well... with version 3.2, they have used Metal to make the text rendering amazingly fast and the scrolling super smooth. This makes the overall appearance a real treat. Just amazing, really.

Now I'm going to see what the status is for BBEdit and Sublime Text 3 - because this kind of scrolling and updating is really quite impressive!

UPDATE: Sublime Text 3 seems to be using the GPU for rendering! Great! No need to worry about that. I know that there are new MacBook Pros coming out this month with the new GPUs, and now may be the right time to look at upgrading!

Big Move: Shutting Down Google Chrome

Monday, October 8th, 2018

Code Clean Up

This morning, while looking at Google Chrome on my work laptop, and with John Gruber's words ringing in my head:

Man oh man, did Google hit the Mac version of Chrome with the ugly stick or what? Worst looking tabs I’ve ever seen — they look like a rendering bug.

and to be fair, that's what I thought of the changes they put in, yet after working with it for a few weeks I was more accepting of it's new design. But that was just me getting used to it - it wasn't that I was seeing the design factors they were going for.

So this morning, I just decided that on my laptop, I'd stop using it. There was a time that Chrome was a great alternative to IE, yes - on the Mac, and Firefox wasn't really all there, and Safari was still getting going, and if you wanted to have a fast, stable, browser, you went with Chrome. Period.

But those days are no more. Chrome is not a good citizen of the machine - it's got real tracking and security concerns, and in general, I just was getting tired of looking at the app that didn't at all look like a Mac app when I was posting and viewing my blog. So enough. It's over.

I'm using Safari Technology Preview because I like to test what's coming, and while I would not use it for my "main" browser activities, I am more than happy to use it for viewing the blog, and making my football picks with some old friends. Nothing is critical, and everything is OK.

We will see who this goes - I'm betting it'll be just fine, and I'll be happier to not be running a Google app that doesn't look like they wanted to make a native Mac app. That's up to them - and fine if that's what they want to do - I just don't have to like it. Or run it.

Fantastic Postgres Client – Postico

Friday, October 5th, 2018

Postico

I was updating my macOS 10.14 Mojave install to get Apache + PHP + Postgres working, and I realized that I probably never wrote up this great Postgres client for macOS - Postico. When I saw it, and how complete and efficient the UI was, I knew I had to buy it - just to support more development like this.

Normally, I'm a psql guy because I'm far faster on the command line than I am in a GUI - it's just the number of hours and the fingers are always on the keyboard. Plus, let's face it - the feature set of psql is very impressive - I've got it in scripts, and you can pass in variables for insertion into your scripts... it's just pretty amazing. But still... great design needs to be supported, and this is one that really needs the support.

Postico Results

There are so many things I like about this UI. First, it's efficient - you have a query window, and then a table of results. I wrote something like this many years ago, and I had the same UI - but I stopped there. They didn't, and I'm glad they didn't. They then filled in the window title bar with the connection information and the status of the connection. They even let me know if it's local or remote. Lots of good stuff there at a quick glance.

Then we can look at the footer of the window - we can export this data in a variety of forms, and it lets us know the row count, if the query was successful, and the time required. This is all great stuff, and it doesn't chew up a ton of screen real estate, but it's all there - efficient

There is a lot more to this guy - as a Mac app, you'd expect multiple connections to different databases - and you get that as well. I've never really needed it - but again, I'd just open up several terminal windows and then run psql in each. But it's great to know that they spent the time to get all the details right.

Anyway... maybe I'll use it a lot - someday... but I love supporting great designs, and this is a really good one.

Getting Apache 2.4.34 + PHP 7.1.19 Going on macOS 10.14 Mojave

Friday, October 5th, 2018

Yosemite

This morning I thought I'd perform the ritual of getting the old web development tools that I've used in the past going again - this time on macOS 10.14 Mojave. Now I haven't used PHP in ages, but I've still got code and databases for Postgres to use that - so it makes sense to get this all working again, and it's always fun to see how things work out.

Getting PostgreSQL 10.3

Loads of coverage here about Postgres, and it's just so simple to get the latest version from Homebrew:

  $ brew install postgresql

I've even posted how to upgrade from major version differences, so it's easy to get the latest Postgres running on your box, and the tools are just superb.

Activating UserDir in Apache 2.4.34

As in the previous updates, the UserDir extension is not enabled by default, so we need to get that going right away. This enables the code to be run from the development directories, and that's a big time-saver. First, we need to enable the UserDir module in Apache, and then make a specific config file for the user in question. Start by editing /etc/apache2/httpd.conf and line 174 needs to be uncommented to read:

  LoadModule userdir_module libexec/apache2/mod_userdir.so

and then similarly on line 511 uncomment the line to read:

  Include /private/etc/apache2/extra/httpd-userdir.conf

Next, make sure that the file we just included is set up right for including the user
directories. Edit /etc/apache2/extra/httpd-userdir.conf and line 16 needs to be uncommented to read:

  Include /private/etc/apache2/users/*.conf

At this point, you need to make sure you have at least one file in the /etc/apache2/users/ directory for each user, like: drbob.conf:

  <Directory "/Users/drbob/Sites/">
      Options FollowSymLinks Indexes MultiViews ExecCGI
      Require all granted
  </Directory>

where the last line - Require all granted is new as of Apache 2.4, and without it you will get errors like:

  [Thu Dec 18 10:41:32.385093 2014] [authz_core:error] [pid 55994]
    [client fe80::7a31:c1ff:fed2:ca2c:58108] AH01630: client denied by server
    configuration: /Users/drbob/Sites/info.php

Activating PHP in Apache

The mext thing to do is to activate PHP in the supplied Apache 2 with macOS 10.14. This is line 177 in the file - /etc/apache2/httpd.conf and you need to uncomment it to read:

  LoadModule php7_module libexec/apache2/libphp7.so

and then verify a file called /etc/apache2/other/php7.conf exists and contains:

  <IfModule php7_module>
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php-source .phps
 
    <IfModule dir_module>
        DirectoryIndex index.html index.php
    </IfModule>
  </IfModule>

which does all the other PHP configuration in a separate file to make upgrades easy.

Finishing Up

At this point, a simple restart of apache:

  $ sudo apachectl restart

and everything should be in order. Hit a URL that's a static file with the contents:

  <?php
    phpinfo();
  ?>

and you should see all the details about the PHP install - including the PostgreSQL section with the version of Postgres indicated:

Apache PHP on Mojave

What's really great is that Apple has included lots of support in the default PHP install:

  • PHP 7.1.19
  • Postgres 9.3.7
  • MySQL 5.0.12
  • SQLite3 7.1.19

so there's no reason to do anything more to get the kind of support that I used to get. And I get the other databases for free. This is great news! I then run my little test page to make sure the database ocnnection is working:

PHP + Postgres Test

and everything is working exactly as expected!

Updated Homebrew for macOS 10.14 Mojave

Wednesday, October 3rd, 2018

Homebrew

This morning I wanted to make sure that Homebrew was up-to-date on my laptop - now that I'd upgraded it to macOS 10.14 Mojave. In the past, changes to the OS and Xcode almost always meant that I had to update with a simple:

  $ brew update

and off it would go - updating the code for brew as well as the list of new, updated, and deprecated packages. This time, it also pointed out that in the upgrade to Mojave, I think Apple cleared out some empty directories. The update instructed me to:

  $ sudo mkdir -p /usr/local/sbin
  $ sudo chown -R $(whoami) /usr/local/sbin

so that the /usr/local/sbin directory was created, and owned by me. Sounds fair - I don't think I had anything there - and we're good to go, but it's really impressive how they have handled all the package management and versioning. I really like the tools these guys make.