Category: Computing

Ugarit 2.0 released (by )

Unless I messed up the release process, Ugarit version 2.0 is now available from the Chicken Scheme egg repository.

What does this mean to you, dear reader? Not a huge amount; you can go and read the release notes at the bottom of the Ugarit documentation page for the fine detail. But, to summarise:

  • The beginnings of archival mode! As well as storing chains of snapshots of a filesystem, as Ugarit has always done (generally to be used as a versioned backup system), Ugarit vaults can now also store "archives", which are groups of files or directory trees identified by arbitary metadata, such as "This is the song 'Ooofarno' by 'Bobby and the Beaters', which is track 11 out of 12 from the 'Fishes In The Sea' album", or "This is a photo of Aunt Mavis taken at 13:58 on the 3rd of August, 2020, at Uncle Bob's 100th birthday party", or "This is a PDF of a paper by Donald Knuth on ternary numbers, called 'Simplified Arithmetic in Base Three'". You can then find things by searching on this metadata, which is much, much, nicer than creating trees of directories to organise all your stuff into. The user interface for getting things in and out of archives is still a bit minimal - but I have plans to fix that.

  • The way tags are stored has changed. Ugarit 2.0 will read vaults created by prior versions happily, but when it writes to a vault, it'll write new-format tags (which have type information as well as a pointer to something), which old versions of Ugarit won't be very pleased to find.

  • We now store a "metadata block" in every vault, pointed to by a hidden tag (we didn't used to be able to hide tags, so old versions of Ugarit will show you a funny tag, and complain if you try and do anything with it, as it's a new-format tag). This stores a vault format version number, so we can better handle incompatible changes to the vault format going forward; and as it's hashed and encrypted like any other block, it means you'll get an instant error if you try and connect to a vault using the wrong hashing and encryption settings, rather than bizarre errors further down the line.

  • We've made it possible to store large logs in the vault. When we do a snapshot or an archive import or something, we keep a log of warnings and recoverable errors that cropped up while doing so. This is stored as a file attached directly to the snapshot or import object, so it can be arbitrarily large.

  • Added log.sexpr and properties.sexpr files in the explore-mode interface, inside every snapshot or archive import object, which let you access the log and the metadata. These are files you can extract, or you can look at them with the new cat command.

  • Added a cat command in the explore-mode user interface to dump a file to the screen for viewing.

  • Added a client-side cache of snapshots and imports, which significantly speeds up the exploration of backup histories and archive metadata. Optionally, you can make the cache persist between sessions, otherwise it's made afresh for every explore session.

  • As well as the existing ability to fork a tag into two tags that share the same history (applicable both to snapshot tags and archive tags), added the ability to merge two tags into one, melding the two histories into one. This includes some exciting logic to combine those histories for display in explore mode!

  • Added a new sqlite backend, which provides a storage inside a single file, managed by sqlite. I wrote it to make testing easier, but it's a useful storage backend in its own right!

  • Tidied up the Ugarit internals, splitting the core up into a load of separate modules. This makes development easier for me, and means nothing to users.

So what's next? I want to improve the usability of archive mode - right now, it's quite easy to import a bunch of files, but you have to hand-edit a text file to provide metadata beyond what it can automatically extract (currently just basic file information, plus whatever it can extract from ID3 tags and Ogg metadata); and then you can explore the history of the archive (as a series of imports) through the explore interface, or use a command-line tool to search for files, and then extract them or stream them to standard output.

What I want is:

  • A shiny (web-based?) UI for searching the archive, seeing thumbnails of images, and the ability to download files with a single click or to perform bulk editing operations on metadata with ease and panache.

  • A music player, that lets me pick music from an archive to queue, or to be given an arbitrary search criteria to find music to random-play, playing direct from the archive.

  • A way to pick photos and assemble them into galleries, which are then publicly visible through a Web interface. Sarah wants to be able to put sequences of photos together, as well as individual photos that don't form parts of sequences, into multiple albums, for her blog publishing stuff (which is often quite image-heavy). The current image publishing framework I threw together for her years ago is a bit limiting now, and quite clunky to use.

  • A mountable filesystem that lets me access archives, either in a generic manner (with auto-generated directories for every property, and every value of that property, containing all files with that value of that property) or with customised directory layouts (such as presenting my music collection as /music/ARTIST/ALBUM/NUMBER:TITLE.EXTENSION, with all the capitalised bits generated from the metadata). I'd like to do this by adding this to the explore mode virtual filesystem, and then having that mountable.

Ugarit performance (by )

Ugarit was once renowned for its poor import performance, and rightly so. However, it's a lot faster these days - not, sadly, due to amazing optimisation work on my part, but because Thomas Hintz made write-u8vector! faster. It's not released yet, but will be in Chicken 4.10.

There's still work to be done, though. In my experiments with archival mode, I imported 9GiB into an archive in:

real    24m14.822s
user    17m51.485s
sys     1m59.920s

Writing an uncompressed tarball of the same 9GiB took:

real    8m49.931s
user    0m1.076s
sys     1m1.315s

That's a factor of 3. Ugarit spends four minutes waiting for I/O while tar spent eight minutes, which is puzzling, but Ugarit spent seventeen minutes of CPU time while tar spent one second; this will be down to the fact that Ugarit still copies each byte of the file several times between reading it in and writing it out, and I know how to fix that!

There will be some unavoidable architectural cost in the fact that Ugarit will always use at least two processes - a frontend and a backend, with the data sent over a pipe between them - but I think there's a lot I can reduce first. Onwards and downwards!

Ugarit archive mode manifest maker (by )

When I last wrote about Ugarit progress, I had developed archive mode to the point where one could import a list of files with metadata from a "manifest file", and then search for files based on the metadata from the manifest and stream out chosen files. I gave an example of using this to play MP3s matching a search pattern:

[alaric@ahusai ugarit]$ for i in `ugarit search test.conf music '(= ($ artist) "UNKLE")' keys`;
do ugarit archive-stream test.conf music $i | mpg123 -;
done

Well, that was all based on hand-written manifest files, which are no fun to produce (our music collection is large). As such, I've been working on a "manifest maker" that takes a list of files and directories and makes a manifest file from them, recursing down through directories to list all the files. And for each, it automatically extracts metadata into the manifest file, which can then be hand-edited if required, and then used to import from.

The idea is that the manifest maker will have support for a number of file types it knows how to extract additional metadata from, and the first one I've implemented is ID3 tag extraction from MP3s. I've implemented the ID3 V2.2 and ID3 V2.3 specs, as those were the two that I found present in the subset of my MP3 collection I'm testing against!

For example, here's the output it produced for one of my MP3s:

(object "./test-data/THE HOLLIES - He Ain't Heavy, He's My Brother.mp3"
  (filename = "THE HOLLIES - He Ain't Heavy, He's My Brother.mp3")
  (mime-type = "audio/mpeg")

   ;; Unknown ID3 tag "COMM"="engiTunNORM\x00 00000402 00000000 00001B59 00000000 00004E65 00000000 000040EC 00000000 00015FD5 00000000"
  (keyword = "Pop")
  (name = "He Ain't Heavy, He's My Brother")
  (creator = "THE HOLLIES")
  (creation-date = "2002")
  #;(featuring = "")
  (collection-name = "Legends CD2")
  #;(collection-volume = "")
  #;(collection-volumes = "")
  (volume-index = 16)
  (volume-size = 18)

  (mtime = 1428948696.0)
  (ctime = 1428948696.0)
  (size = 4063360))

It prints out unknown ID3 tags as comments, in case a human can glean some useful information from them to put into the metadata, and it suggests the names of metadata tags I might be able to provide by hand that it hasn't found (in this case, a tag for other people featured in the music, and two for indicating that this album is part of a set. As it happens, it is, as the "CD2" in the name suggests, but it wasn't indicated in the ID3 so I'll have to hand-edit it; likewise, the date from the MP3 of 2002 is clearly for the production of the album, not that classic track... ID3 metadata is often a bit shabby!). Also included are file mtime, ctime, and size in bytes.

I hope to add Ogg Vorbis metadata next; I'd like to add EXIF support to parse information out of the JPEGs in our vast family photo library, but it looks much harder, and I'm not sure how useful it will actually be!

How I’m managing my life with emacs org-mode (by )

It's no secret that I'm a busy person; often, when I decide to do something, a few years pass before I actually get to do it. So the only way I keep afloat is by the judicious use of computers to track my task lists. I rely on automatic systems to make sure I always know what I need to do today, and what are the most important things I need to do "sometime" that I can do right now. There's no way I could keep all that in my head without forgetting about things and letting people down, or feeling stressed because I'm juggling too much in my mind, or not being able to find the best thing to do when I have a free moment.

As I've mentioned before on here, I want a personal information management system based on predicate logic, so I can express complex relationships between things easily, and tell the system how to infer knowledge automatically. However, "build one of those" has yet to hit the top of my TODO list, so for now I'm using emacs' legendary org-mode. This lacks the rich semantic power of my proposed PIM, but it's already implemented and has a nice editing interface 🙂

A few people have asked about what I've done, so here's my attempt to document it.

Read more »

Goal-based artificial intelligence for home automation (and maybe piloting a network of autonomous killbots) (by )

For a while, I've been mulling the idea of writing zmiku, a daemon that can be programmed to automatically control various kinds of systems. My application is home automation, and maybe automating the management of servers (restarting and failing over services, dealing with overload situations, gracefully handling disks being full, that sort of thing); but it occurs to me that the same basic problem also applies to controlling autonomous robots such as space probes, industrial processes, and that sort of thing. A good solution to all these problems in one would be quite useful!

You might say that this is a non-problem; normally, people would just write programs from scratch to control these kinds of things, sitting in a loop reading inputs and updating state variables and choosing what output actions to generate, but the complexity of the resulting program tends to increase rapidly as the problem complexity rises.

Rather than traditional programming languages, a better notation for such a reactive system is a state machine. The Wikipedia articles on a UML state machine diagram gives a good introduction to one version of this notation, including some discussion of ways to extend the most basic version in ways that increase its expressiveness and modularity.

I'd like to base zmiku on a textual version of the UML statecharts, but today I've had a horrible stomach ache, so been unable to do much more than lie around and think about stuff, and what my mind settled on was the interesting question of how to integrate state machines with goal-based programming, which is also useful for controlling complex systems. In a goal-based system, various goals are known to the system, each with a priority; for instance, a flying robot may have a destination demanded by the user, which the navigation system tries to fly the robot towards; but a collision-avoidance system may sometimes override the navigation system when it detect that a collision will result otherwise, with a higher-priority goal for the steering system. And when the collision has been avoided, that goal will disappear, and the earlier goal of getting to the destination will take over once more. And if the robot's batteries are running low, then flying towards a charging place (or a place where the solar panels are in sunlight) might be a higher priority than the user's chosen destination, but not a higher priority than avoiding collisions. And so on.

So I came up with a model for integrating the two, using the "scoreboard" model from artificial intelligence; giving a system a shared global state between a number of concurrent subsystems. And this blog post is the result of me writing up my scribbled notes. I'm still in a lot of stomach pain, so I'm afraid it's going to be a bit rambly 🙂

Read more »

WordPress Themes

Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales
Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales