2014-10-24
In Go I've given up and I'm now using standard packages
In my Go programming, I've come around to an attitude that I'll summarize as 'there's no point in fighting city hall'. What this means is that I'm now consciously using standard packages that I don't particularly like just because they are the standard packages.
I'm on record as disliking the standard flag
package, for example, and while I still believe in my reasons
for this I've decided that it's
simply not worth going out of my way over it. The flag package
works and it's there. Similarly, I don't think that the log
package is necessarily a great solution for emitting messages from
Unix style command line utilities but in my latest Go program I used it anyways. It
was there and it wasn't worth the effort to code warn() and die()
functions and so on.
Besides, using flag and log is standard Go practice so it's going
to be both familiar to and expected by anyone who might look at my code
someday. There's a definite social benefit to doing things the standard
way for anything that I put out in public, much like most everyone uses
gofmt on their code.
In theory I could find and use some alternate getopt package (these days the go to place to find one would be godoc.org). In practice I find using external packages too much of a hassle unless I really need them. This is an odd thing to say about Go, considering that it makes them so easy and accessible, but depending on external packages comes with a whole set of hassles and concerns right now. I've seen a bit too much breakage to want that headache without a good reason.
(This may not be a rational view for Go programming, given that Go deliberately makes using people's packages so easy. Perhaps I should throw myself into using lots of packages just to get acclimatized to it. And in practice I suspect most packages don't break or vanish.)
PS: note that this is different from the people who say you should eg
use the testing package for your testing because you don't really
need anything more than what it provides and stick with the standard
library's HTTP stuff rather than getting a framework. As mentioned, I
still think that flag is not the right answer; it's just not wrong
enough to be worth fighting city hall over.
Sidebar: Doing standard Unix error and warning messages with log
Here's what I do:
log.SetPrefix("<progname>: ")
log.SetFlags(0)
If I was doing this better I would derive the program name from
os.Args[0] instead of hard-coding it, but if I did that I'd have to
worry about various special cases and no, I'm being lazy here.
2014-10-17
My experience doing relatively low level X stuff in Go
Today I wound up needing a program that spoke the current Firefox
remote control protocol instead of
the old -remote based protocol that Firefox Nightly just removed. I had my
choice between either adding a bunch of buffer mangling to a very old
C program that already did basically all of the X stuff necessary or
trying to do low-level X things from a Go program. The latter seemed
much more interesting and so it's what I did.
(The old protocol was pretty simple but the new one involves a bunch of annoying buffer packing.)
Remote controlling Firefox is done through X properties, which is a relatively low level part of the X protocol (well below the usual level of GUIs and toolkits like GTK and Qt). You aren't making windows or drawing anything; instead you're grubbing around in window trees and getting obscure events from other people's windows. Fortunately Go has low level bindings for X in the form of Andrew Gallant's X Go Binding and his xgbutil packages for them (note that the XGB documentation you really want to read is for xgb/xproto). Use of these can be a little bit obscure so it very much helped me to read several examples (for both xgb and xgbutil).
All told the whole experience was pretty painless. Most of the stumbling blocks I ran into were because I don't really know X programming and because I was effectively translating from an older X API (Xlib) that my original C program was using to XCB, which is what XGB's API is based on. This involved a certain amount of working out what old functions that the old code was calling actually did and then figuring out how to translate them into XGB and xgbutil stuff (mostly the latter, because xgbutil puts a nice veneer over a lot of painstaking protocol bits).
(I was especially pleased that my Go code for the annoying buffer packing worked the first time. It was also pretty easy and obvious to write.)
One of the nice little things about using Go for this is that XGB turns out to be a pure Go binding, which means it can be freely cross compiled. So now I can theoretically do Firefox remote control from essentially any machine I remotely log into around here. Someday I may have a use for this, perhaps for some annoying system management program that insists on spawning something to show me links.
(Cross machine remote control matters to me because I read my email on a remote machine with a graphical program, and of course I want to click on links there and have them open in my workstation's main Firefox.)
Interested parties who want either a functional and reasonably commented example of doing this sort of stuff in Go or a program to do lightweight remote control of Unix Firefox can take a look at the ffox-remote repo. As a bonus I have written down in comments what I now know about the actual Firefox remote control protocol itself.
2014-10-11
Thinking about how to create flexible aggregations from causes
Every so often I have programming puzzles that I find frustrating, not so much because I can't solve them as such but because I feel that there must already be a solution for them if I could both formulate the problem right and then use that formulation to search existing collections of algorithms and such. Today's issue is a concrete problem I am running into with NFS activity monitoring.
Suppose that you have a collection of specific event counters, where you know that userid W on machine X working against filesystem Y (in ZFS pool Z) did N NFS operations per second. My goal is to show aggregate information about the top sources of operations on the server, where a source might be one machine, one user, one filesystem, one pool, or some combination of these. This gives me two problems.
The first problem is efficiently going 'upwards' to sum together various specific event counters into more general categories (with the most general one being 'all NFS operations'). This feels like I want some sort of clever tree or inverted tree data structure, but I could just do it by brute force since I will probably not be dealing with too many specific event counters at any one time (from combinations we can see that each 4-element specific initial event maps to 16 categories; this is amenable to brute force on modern machines).
The second problem is going back 'down' from a category sum to the most specific cause possible for it so that we can report only that. The easiest way to explain this is with an example; if we have (user W, machine X, fs Y, pool Z) with 1000 operations and W was the only user to do things from that machine or on that filesystem, we don't want a report that lists every permutation of the machine and filesystem (eg '1000 from X', '1000 against Y', '1000 from X against Y', etc). Instead we want to report only that 1000 events came from user W on machine X doing things to filesystem Y.
If I wind up with a real tree, this smells like a case of replacing nodes that have only one child with their child (with some special cases around the edges). If I wind up with some other data structure, well, I'll have to figure it out then. And a good approach for this might well influence what data structure I want to use for the first problem.
If all of this sounds like I haven't even started trying to write some code to explore this problem, that would be the correct impression. One of my coding hangups is that I like to have at least some idea of how to solve a problem before I start trying to tackle it; this is especially the case if my choice of language isn't settled and I might want to use a different solution depending on the language I wind up in.
(There are at least three candidate languages for what I want to do here, including Go if I need raw speed to make a brute force approach feasible.)