Meteor Velocity: Down the Debugging Rabbit Hole

Written by Pete Corey on Feb 9, 2015.

Meteor Requires Node v0.10.33 or Later

The other day I added Velocity to a Meteor app (meteor add mike:mocha) and I was greeted with a strange error when the server restarted:

W20150130-22:24:55.285(-8)? (STDERR) [velocity-mirror] Meteor requires Node v0.10.33 or later.

Hmm. Doesn’t the current version of Meteor (1.0.3) use its own instance of Node v0.10.33? Let’s check…

% meteor shell
> process.argv[0]
‘/home/pcorey/.meteor/packages/meteor-tool/.1.0.40.moil5k++os.linux.x86_32+web.browser+web.cordova/meteor-tool-os.linux.x86_32/dev_bundle/bin/node’
% /home/pcorey/.meteor/packages/meteor-tool/.1.0.40.moil5k++os.linux.x86_32+web.browser+web.cordova/meteor-tool-os.linux.x86_32/dev_bundle/bin/node --version
v0.10.33

Just like I thought, Meteor is using Node v0.10.33. So where is this error coming from? Let’s do some monkey patching to find out!

Monkey Patching to the Rescue

I started by editing Meteor’s boot.js (/home/pcorey/.meteor/packages/meteor-tool/.1.0.40.moil5k++os.linux.x86_32+web.browser+web.cordova/meteor-tool-os.linux.x86_32/tools/server/boot.js). I overwrote console.error with my own custom function which prints the current call stack and then logs the error as usual:

oldConsoleError = console.error;
console.error = function() {
    var orig = Error.prepareStackTrace;
    Error.prepareStackTrace = function(_, stack){ return stack; };
    var err = new Error;
    Error.captureStackTrace(err, arguments.callee);
    var stack = err.stack;
    Error.prepareStackTrace = orig;

    oldConsoleError(stack.toString().replace(/,/g,'\n'));
    oldConsoleError.apply(this, arguments);
}

So now what happens when we start the Meteor server?

(STDERR) Socket.<anonymous> (/home/pcorey/velocity-test/.meteor/local/build/programs/server/packages/velocity_node-soft-mirror.js:493:17)
(STDERR) Socket.emit (events.js:95:17)
(STDERR) Socket.<anonymous> (_stream_readable.js:764:14)
(STDERR) Socket.emit (events.js:92:17)
(STDERR) emitReadable_ (_stream_readable.js:426:10)
(STDERR) emitReadable (_stream_readable.js:422:5)
(STDERR) readableAddChunk (_stream_readable.js:165:9)
(STDERR) Socket.Readable.push (_stream_readable.js:127:10)
(STDERR) Pipe.onread (net.js:528:21)
(STDERR) [velocity-mirror] Meteor requires Node v0.10.33 or later.

Interesting… It looks like we should take a look at velocity_node-soft-mirror.js:493:

mirrorChild.getChild().stderr.on('data', function (data) {
    console.error('[velocity-mirror]', data.toString());
}); 

So this is where the error is coming from. mirrorChild is a child node process spawned a few lines earlier:

mirrorChild.spawn({
    command: 'node',
    args: [mainJs],
    options: {
        silent: true,
        detached: true,
        cwd: process.env.PWD,
        env: _.defaults(environment, process.env)
    }
});

It looks like it’s simply running node, which will use the version of node on the system’s PATH, not Meteor’s bundled version of node. Let’s check what version of node lives on the PATH:

% node --version
v0.10.25

Mystery solved. Velocity is spawning a “mirror” instance of Meteor but it’s using whatever instance of node is installed on the machine, not the version bundled with Meteor.

Fixing the Problem

Interestingly, if we look through the node-soft-mirror package on Github, we can see that this problem exists in the tagged version 0.2.6 and below, but it was fixed in version 0.2.7. If we take a look at the meteor-mocha-web project on Github, we can see that it’s explicitly depending on version 0.2.4 of node-soft-mirror.

An easy way to fix this is to explicitly add version 0.2.7 or higher of node-soft-mirror to our project.

meteor add velocity:node-soft-mirror@0.3.1

After that, the Meteor server should start without issue and everything should work as expected.

Well, that was definitely an adventure. It was pretty obvious what was going on after I dug into the internals of the error and how Velocity uses its “mirror”, but hindsight is always 20/20. I’ve filed a bug on the meteor-mocha-web project to update their node-soft-mirror package.

TL;DR

The current version of mike:mocha uses the version installed on the system, not the version packaged with Meteor. Explicitly add velocity:node-soft-mirror v0.2.7 or above to fix the problem.

Announcing East5th!

Written by Pete Corey on Feb 4, 2015.

I’ve decided to take an enormous step in my career. At the end of 2014 I left my job working as a contact software developer, and decided to forge my own path by starting a small web consultancy and development shop. After going through the hoops of incorporation, I can happily say that my company, East5th, is now alive, well and accepting clients!

Throughout my career as a software developer, I’ve come to believe in many ideas and ideals related to the software creation process. I believe that software should be tailor made to meet a client’s needs. Discovering what those needs are is fundamentally as important, or more important than implementing a solution. I believe that software should be developed iteratively. A minimum viable product will soothe a vast majority of pains at the lowest cost to the client. I believe in using value based pricing as a means of focusing on quality over commodity. More than anything, I believe that clients are looking for a professional’s expertise to guide them through the process of building a software solution.

I couldn’t be more excited to put these beliefs into practice with East5th.

1pxsolidtomato will live on! I’ll continue blogging on a variety of technical topics, but I hope to mix it up with updates on East5th and my experiences as a business owner. Stay tuned!

Suffixer! Find Meaningful Unregistered Domains

Written by Pete Corey on Feb 2, 2015.

Early last week I released a small web app, Suffixer! The idea behind Suffixer is to compile a database of all words that can be created with registerable Top Level Domains as their suffixes. You can then search through that database and quickly determine if any of the resulting domains are available for registration.

Like most of my recent projects, Suffixer was built with Meteor. You can find the source on github.

Building Suffixer’s Database

Suffixer was built using data from Wiktionary and Namecheap’s API. I wrote a few custom Meteor packages to help initially populate Suffixer’s database. The first, pcorey:namecheap, is simply a wrapper around a modified version of Chad Smith’s node-namecheap library. Another custom package, pcorey:namecheap-tlds, exposes a Meteor collection which is populated with a call to the Namecheap’s getTldList API method. A third package, pcorey:wiktionary, parses a Wiktionary tsv dump file and fills a collection with all relevant words.

None of these packages are currently published. If you would like to use any of them, let me know and I’ll hapily make them available.

Searching and Checking

Searching through the database is fairly straight forward thanks to MongoDB’s text search functionality and the power of Meteor’s Publish & Subscribe. Searching on the client is initiated through a debounced subscription. The server has a matching publish method that takes the search term as an argument (among other things):

Meteor.publish('wiktionary-namecheap', function(suffix, definition, limit, hideRegistered, favorites) {
    var results = Wiktionary.find(
        getSelector(suffix, definition, hideRegistered, favorites),
        getOptions(limit));
    var domainMap = getDomainMap(results);
    checkAndUpdateDomains(domainMap);
    return results;
});

This publish method does a few interesting things. First, it queries Mongo for any results matching the provided search term (definition). Next, it loops through all of the results looking for any domains who’s registration status is either unregistered or unknown. Those domains are passed to Namecheap’s check API, and the results of that call update the status of the corrresponding Mongo documents. The real magic is that while the API callback updating the Mongo documents is asynchronous, those changes are automatically and instantly pushed to the client. How cool is that?

Known Problems and Lessons Learned

My goal with this project was to create a Minimum Viable Product. That means that Suffixer was released with a few issues:

Static Data

Namecheap’s getTldList is only called when the database is first populated, and all words in the Wiktionary tsv not paired up to a Top Level Domain are excluded from the database. This means that if any new TLDs are made available by Namecheap in the future, the Suffixer database would have to be wiped and rebuilt. A much better way to future-proof this functionality would be to store all Wiktionary entries in Mongo, along with the list of available TLDs. Periodically, getTldList could be called, potentially updating the TLD collection and any new Wiktionary word matches. This would most likely require some schema re-working.

Wikitext Is Hard

Wikitext is hard. Specifically, correctly rendering wikitext templates is hard. I looked into a few different options for rendering wikitext into plain text, but all of them fell short when it came to expanding templates. From what I’ve found, the only way to expand a template is to use Wikipedia’s API. Unfortunately, do to the huge number of wikitext entries I needed to parse, this wasn’t a viable option. This version of Suffixer leaves the unexpanded templates in the definition text. For the most part, they’re human readable and add valuable context to the definitions.

Final Thoughts

Overall, I’m happy with how the project turned out. I learned a good deal about Mongo and even more about Meteor. Meteor continues to be a very interesting and exciting platform to work with.

If you have any comments or suggestions about Suffixer, please let me know!