I’ve been looking into building a REST api using the Azure SDK for node (https://github.com/WindowsAzure/azure-sdk-for-node). I have a simple MessageTable in Azure tablestore – the PartitionKey is a device identifier (messages belong to a specific messaging device) and the RowKey is a number that identifies the message.
The problem is that the RowKey is a very big number which usually ends with a load of 9’s (it’s a ‘reverse-timestamp’), and the node-azure library is rounding this number up when data is returned. See example JSON response below:
{
"id":"http://xxx.table.core.windows.net/MessageTable(PartitionKey='12345',RowKey='2520801590159999999')",
"link":"MessageTable(PartitionKey='12345',RowKey='2520801590159999999')",
"updated":"2012-03-13T15:53:34Z",
"etag":"W/\"datetime'2011-11-24T13%3A36%3A41.9555578Z'\"",
"PartitionKey":12345,
"RowKey":2520801590160000000,
"Timestamp":"2011-11-24T13:36:41.955Z",
...other results removed for brevity
}
The “id” and “link” elements show the correct RowKey of 2520801590159999999; the “RowKey” element shows a rounded version of this: 2520801590160000000.
Anyone know what’s going on?
I’ve also logged this question – I’m sure it’s the same root cause:
https://stackoverflow.com/questions/9683515/journey-routing-fails-due-to-number-rounding
Edit @smarx:
Test code reproduced below. When running, going to http://localhost:8080/devices/12345/messages gives the json output sampled above. Azure table is MessageTable, partition key is deviceId (12345), rowkey is messageId (2520801590159999999).
var express = require('express');
var http = require('http');
var azure = require('azure');
var uuid = require('node-uuid');
var tableService = azure.createTableService( '[ACCOUNT_NAME]', '[ACCOUNT_KEY]' );
var app = module.exports = express.createServer();
app.configure(function(){
app.use(app.router);
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
app.get('/devices/:deviceId/messages', function ( req, res ) {
var query = azure.TableQuery
.select()
.top( 30 )
.from( "MessageTable" )
.where( 'PartitionKey eq ?', req.params.deviceId );
tableService.queryEntities( query, function (error, entities) {
if ( null != error ) {
res.end('Could not query MessagesTable: ' + error.code);
return;
}
res.send( entities );
});
});
app.listen(8080);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
[EDIT by @smarx]:
Simpler repro:
tableService.createTableIfNotExists('testtable', function () {
tableService.insertEntity('testtable', {
PartitionKey: 'pkey',
RowKey: '2520801590159999999'
}, function () {
tableService.queryEntity('testtable', 'pkey', '2520801590159999999', function (error, entity) {
console.log(entity.RowKey); // prints 2520801590160000000
});
});
});
This is a bug here: https://github.com/WindowsAzure/azure-sdk-for-node/blob/master/lib/util/atomhandler.js.
The implementation of
parse(by default) tries to guess the type of a property value if none is specified, which I don’t believe is the correct behavior. (The lack of a type means to use the default, which is string.)I’ve filed a bug (https://github.com/WindowsAzure/azure-sdk-for-node/issues/114), including a suggested fix. You can try it yourself by just adding
convertTypes = falseat the top of theparsefunction.Edit: This has now been fixed in the repo. (Click on the GitHub issue link above to see.)