Mikael's blog

A developers sixth time trying to maintain a blog

4 comments

The Tiling Truth

Screenshot of XMonad XMonad running on my desktop

Form, function and flexibility. I find that these three concepts are not always easy to rank. Sometimes I feel that form trumps functionality, sometimes I don't. When it comes to window managers however, function is king!

The Form Junkie

I have to admit, I've been totally ignorant about tiling window managers up until now. I've always wanted my desktop to look nice and I've sometimes gone to great lengths to customize my desktop to be pixel perfect the way I want it. But lately I've come to a realization; mouse pointers suck!

Well most mouse pointers do anyway. I'm a trackball user so I think all regular mice sucks anyway, but now I've started hating trackpads. I've been planning on picking up a new laptop soon (well as soon as the X230 gets released) but I want to be able to use it on the couch without having to rely on a mouse/trackpad/trackball. The Lenovos "nipple-mouse" is about as far as I want to go since it sits smack in the middle of the keyboard.

I need a keyboard centric desktop environment.

Enter xmonad

Tiling window managers to the rescue! More specifically; xmonad to the rescue!

xmonad is an awesome (no pun intended*) tiling window manager written in haskell. It's fast, easy to use, easy to configure and it runs entirely of the keyboard. There are many great guides to configuring xmonad, and it's incredibly stable (well duh, it's written in haskell).

* Not really a "pun", but "awesome" is the name of another tiling window manager.

by Mikael Lofjärd
2 comments

Progress at Last

Sometimes you need more than your operating system gives you.

That's when a text editor comes in handy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
 
EXPECTED_ARGS=2
E_BADARGS=65
E_BADPATH=66
 
if [ $# -ne $EXPECTED_ARGS ]
then
  echo "Usage: `basename $0` {source} {dest}"
  exit $E_BADARGS
fi
 
if [[ ! -f "$1" ]]; then
    echo "Source file does not exist or is not a regular file."
    exit $E_BADPATH
fi
 
DESTSIZE=`du -b "$1" | awk '{print \$1; }'`
 
DESTFILENAME=`basename "$1"`
 
if [[ -d "$2" ]]; then
    DESTPATH="$2/$DESTFILENAME"
else
    DESTDIR=`dirname "$2"`
    if [[ ! -d "$DESTDIR" ]]; then
        echo "Dest dir does not exist."
        exit $E_BADPATH
    fi
    DESTPATH="$2"
fi
 
 
cat "$1" | pv -s $DESTSIZE -p -e -r > "$DESTPATH"
 
exit 0

Copying large files to my NAS becomes so much more fun when I actually KNOW that it's doing what it should. Progress bars FTW!

UPDATE: Now it's actually working for more than one case. :)

by Mikael Lofjärd
2 comments

LESS Is More, More Or Less

A while back I read a blog post somewhere about how the LESS parser/compiler had been remade in Javascript.

"Well awesome", I thought to myself as I had been wanting some more flexibility in CSS but had been to stubborn/proud to install the SASS compiler since it's written in Ruby. Needles to say, I wanted to incorporate it in my blog as soon as possible but I've not had the time to actually do it until now.

LESS you say?

LESS (like SASS) is a CSS derived language that adds a whole lot of long needed features to CSS to ease maintenance of large style sheets. It compiles into regular CSS markup either in realtime (through their nifty Javascript implementation in the browser) or, as in my case, as a bootstrapping task when I start my blog.

For now, it's tacked on in a kind of ugly way in my BundleController, but I might redo the actual code some day since I'm not pleased with it. It works though so for now it will have to suffice.

What does if bring to the table?

It brings variables (!!!). Finally you can stop sprinkling your CSS files with color descriptions and font sizes:

1
2
3
4
5
@white: #fff;
 
.myClass {
    background-color: @white;
}

It's as easy as that.

It also brings something called mixins, which is kind of like multiple inheritance:

1
2
3
4
5
6
7
8
9
10
11
12
.basefont(@size: 12px) {
    font-family: Arimo, sans-serif;
    font-size: @size;
}
 
body {
    .basefont;
}
 
h1 {
    .basefont(24px);
}

Mixins can be quite useful in cutting down on repetitive CSS code, and it has support for parameters and default values.

LESS also lets you nest your rules to reduce repeating your selectors:

1
2
3
4
5
6
7
8
9
10
11
12
13
div.sitewrapper {
    >nav {
        ul {
            margin: 7px 0px 2px 5px;
            li {
                margin: 3px 0 2px 0;
                a {
                    width: 70px;
                }
            }
        }
    }
}
When this is compiled it is turned into:
1
2
3
4
5
6
7
8
9
10
11
div.sitewrapper > nav ul {
    margin: 7px 0px 2px 5px;
}
 
div.sitewrapper > nav ul li {
    margin: 3px 0 2px 0;
}
 
div.sitewrapper > nav ul li a {
    width: 70px;
}

There is also support for a lot more complex nesting rules and a calculation API so that you can calculate colors and distances and make relative offsets and such. I recommend you to read up on http://lesscss.org/ about all the cool features of less.

So does it make your life easier?

It's kind of hard to say since I just got it running and my style sheet probably needs more work done on it, but so far I've been able to cut around 50 lines of CSS out of my ~660 line file, and it has gotten a lot less repetitive and a lot more easier to read I think.

It's not deployed yet but when I deploy it this weekend I will let you be the judges as the source code is made available as usual.

by Mikael Lofjärd
2 comments

Cache Me If You Can

Today at work was "do-anything-but-work-day". It's a bit like Googles 20%, but instead of 20% it's more like .0001% .8% or something like that. It's was our first time and not that many people had a clear idea about what to do at first. I on the other hand had a mission all planned out.

The Performance Degradation

When I put the blog on the new server back in January, I noticed a small decrease in performance. After a few tests I've realized that the CPU is the culprit.

The Atom D525, while dual-core, at 1.6 GHz has roughly half the computational power of the Pentium M at 1.5 GHz, which was what my old server had under the hood.

Node.js can make use to multi-core processors by starting more instances of itself, which made concurrent connections on the new server almost the same speed as on the old server. However, concurrent connections isn't really my problem since I only have around 30 readers on a good day.

What's Taking You So Long?

Well even in the old version of the blog, I do a lot of caching. My node-static instance takes care of all static file handling and it does a really good job of caching them. I also cache all of my mustache templates when I start Node.js so I can read them from memory every time I render a page.

What was taking so long was actually more than one thing.

First there was database access. CouchDB is really fast and caches a lot, but its only way of communicating is REST over HTTP so there's still some overhead getting to those cached results.

And then there was presentation logic. The actual rendering of the data on to the template takes a few milliseconds and all pages with source code on them take a few milliseconds more to render all the syntax highlighting server-side. Sometimes there's a lot of RegExp running to make it all happen.

The Mission

This brings us back to today and my mission; to build an in memory cache for caching web server responses.

My plan was to build a cache that stored the entire HTTP response (headers and content) and that I could clear selectively when needed. This lead me to remove my multi-core code and run Node.js as a single process, since otherwise I would have been in another world of hurt trying to get my processes to sync there cache stores.

When a new post is added I want to clear most of the cache (list pages, archive, atomfeed etc) but not the post pages, and when a comment is added to a post I just want to clear the list pages and the post page for that post. So I added a few different cache stores that I could clear out as I wanted to.

Most of this is handled by the CacheManager.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*****************************************
 *   Cache Manager
 *****************************************
 *   Author:  mikael.lofjard@gmail.com
 *   Website: http://lofjard.se
 *   License: MIT License
 ****************************************/
  
var CacheManager = (function () {
  
    var fs = require('fs');
  
    var env = require('./environmentManager').EnvironmentManager;
    var misc = require('./misc').Misc;
  
    var cacheStore = {};
      
    function getStoreType(url)
    {
        var urlParts = url.split('/');
        var result = 'dynamic';
  
        switch (urlParts[1]) {
            case 'source':
            case 'about':
                result = 'static';
                break;
            case 'archive':
            case 'atomfeed':
                result = 'semiStatic';
                break;
            case 'tags':
            case 'tag':
                result = 'semiDynamic';
                break;
            case 'post':
                result = 'floating';
                break;
        }
  
        return result;
    }
  
    return {
  
        init: function () {
            cacheStore = {};
            cacheStore.static = {};         // static between boots       - /source /about
            cacheStore.semiStatic = {};     // clear on new post          - /archive /atomfeed
            cacheStore.semiDynamic = {};    // clear on edit post         - /tags /tag
            cacheStore.dynamic = {};        // clear on comment (default) - / /page
            cacheStore.floating = {};       // null item on comment       - /post
        },
  
        clearOnNewPost: function () {
            env.info('CacheManager: Clearing cache on new post');
            cacheStore.semiStatic = {};
            cacheStore.semiDynamic = {};
            cacheStore.dynamic = {};
        },
  
        clearOnEditPost: function (url) {
            env.info('CacheManager: Clearing cache on edit for ' + url);
            cacheStore.semiDynamic = {};
            cacheStore.dynamic = {};
  
            delete(cacheStore[getStoreType(url)][url]);
        },
  
        clearOnNewComment: function (url) {
            env.info('CacheManager: Clearing cache on comment for ' + url);
            cacheStore.dynamic = {};
            delete(cacheStore[getStoreType(url)][url]);
        },
  
        cache: function (url, headerData, contentData) {
            env.info('CacheManager: Caching content for ' + url);
            cacheStore[getStoreType(url)][url] = { content: contentData, headers: headerData };
        },
  
        fetch: function (url) {
            var data = cacheStore[getStoreType(url)][url];
  
            if (typeof(data) != 'undefined') {
                env.info('CacheManager: Found cached entry for ' + url);
                return data;
            }
  
            return null;
        }
  
    };
  
}());
  
typeof(exports) != 'undefined' ? exports.CacheManager = CacheManager : null;

Hooking It Up

Previously my main workflow looked something like this; The Router looked at the request, called the assigned Controller which fetched data, formed the data into a model and passed the model to the ViewManager which rendered the result to the response stream.

Hooking up the CacheManager meant that I had to get some parts a little "dirtier" than I wanted, but instead of putting a lot of code into the ViewManager, I created the ResponseManager.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*****************************************
 *   Response Manager
 *****************************************
 *   Author:  mikael.lofjard@gmail.com
 *   Website: http://lofjard.se
 *   License: MIT License
 ****************************************/
  
var ResponseManager = (function () {
      
    var env = require('./environmentManager').EnvironmentManager;
    var cm = require('./cacheManager').CacheManager;
  
    var misc = require('./misc').Misc;
    
    return {
          
        writeCachedResponse : function (response, cachedUrl) {
            env.info('ResponseManager: Writing cached view for ' + cachedUrl);
  
            var data = cm.fetch(cachedUrl);
  
            response.writeHead(200, data.headers);
            response.write(data.content, 'utf-8');
            response.end();
            return;
        },
          
        writeResponse : function (request, response, responseData, doNotCache) {
            var pathName = misc.getPathName(request.url);
  
            if (typeof(doNotCache) == 'undefined') {
                cm.cache(pathName, responseData.headers, responseData.content)
            }
              
            response.writeHead(200, responseData.headers);
            response.write(responseData.content, 'utf-8');
            response.end();
            return;
        }
    };
      
}());
  
typeof(exports) != 'undefined' ? exports.ResponseManager = ResponseManager : null;

The ResponseManager does most of the talking with the CacheManager and I remade the ViewManager so that the renderView() method now returns the rendered response instead of writing it to the response stream. This lets the Controllers do the job of rendering through the ViewManager and then passing the result to the ResponseManager.

The other part of the equation is the Router. I didn't really want to put CacheManager calls into the router but it is the first place that has a good path to use as key, so for now the Router checks for the existence of a cached response and, if found, sends it to the ResponseManager before even starting to look up what Controller to call if no cached response is found.

Show Me The Money

So what kind of a performance boost are we talking about?

Well, using the handy ab (Apache Benchmark) I sent a thousand requests to my different implementations:

Uncached

Requests per second: 5.41 (mean)
Time per request: 184.761 ms (mean)
Transfer rate: 177.92 Kbytes/sec recieved

Cached

Requests per second: 62.86 (mean)
Time per request: 15.910 ms (mean)
Transfer rate: 2097.06 Kbytes/sec recieved

That's quite some increase in performance. So much, in fact, that the transfer rate exceeds my measly 10 Mbit/s outgoing fiber connection. At least now, if my blog was to slow down I know it's not the servers fault.

Just for kicks I benchmarked it running on my laptop with a Core 2 Duo at 2.0 GHz and the results point to some possible areas of improvement for Intel on the Atom line (mainly memory access speed):

Cached (on my workstation)

Requests per second: 213.98 (mean)
Time per request: 4.673 ms (mean)
Transfer rate: 7030.95 Kbytes/sec recieved

Luckily I don't have enough traffic to warrant an upgrade to my fiber connection. 100/100 Mbit/s costs almost twice as much as my 100/10 Mbit/s.

by Mikael Lofjärd
3 comments

Screw You Ubuntu - I'm Going Home

Ignoring a short play date with Red Hat around '95, my first Linux love was Slackware.

Slackware was fast and awesome but it somewhat lacked in the package discovery department. I installed most things from source and after learning about all the bad things that can happen when you install new versions of software on top of the old, I setup a package manager, but Slackware still lacked a central package repository.

All Your Source Are Belong To Us

The central source repository led me to switch to Gentoo. Being able to just install things without having to find the source code online first was great, but again I grew tired. The long compile times eventually wore me out and this time the switch was made to Ubuntu.

Out of The Box Experience

Ubuntu was nice in a everything-just-works sort of way, but now, some 6 years later, it doesn't do what I want and I don't know how to fix it with all the magic going on. I need to get back to my beloved manual tweaking.

The Arch of Truth

Arch Linux is my new pal. I've installed a bare core system that boots to the terminal (retro style), I've installed X and am about to configure it to my hearts content. If this doesn't work out then; LFS, here I come!

by Mikael Lofjärd