Permalink
Browse files

Use microsecond timestamps for thrift inserts.

Previously, same-ms inserts were using the same timestamp which
means the first insert might "win" over the second. Using a new
dependency "microtime", default timestamps now have microsecond
precision which makes things a thousand times better.

The Column class now has a timestamp_micro field you can use if
you want to inspect the high resolution timestamp since javascript
Date objects only support millisecond precision.
  • Loading branch information...
1 parent b05fe24 commit 536dee38263bf156355a4d67c14c3ff5db08c39f Muir Manders committed Oct 23, 2013
Showing with 60 additions and 11 deletions.
  1. +18 −6 lib/column.js
  2. +2 −3 lib/column_family.js
  3. +2 −2 lib/row.js
  4. +1 −0 package.json
  5. +37 −0 test/thrift.js
View
@@ -1,11 +1,12 @@
var util = require('util'),
- ttypes = require('./cassandra/cassandra_types');
+ ttypes = require('./cassandra/cassandra_types'),
+ microtime = require('microtime');
/**
* Cassandra Column object representation
* @param {Object} name The name of the column, can be any type, for composites use Array
* @param {Object} value The value of the column
- * @param {Date} timestamp The timestamp of the value
+ * @param {Date} timestamp The timestamp of the value, can be Date or time in microseconds
* @param {Number} ttl The ttl for the column
* @constructor
*/
@@ -20,11 +21,22 @@ var Column = function(name, value, timestamp, ttl){
*/
this.value = value;
+ if (timestamp == null) {
+ /**
+ * The timestamp in microseconds of the value
+ * @default {Number} current time in microseconds
+ */
+ this.timestamp_micro = microtime.now();
+ } else if (this.timestamp instanceof Date) {
+ this.timestamp_micro = timestamp.getTime() * 1000;
+ } else {
+ this.timestamp_micro = timestamp;
+ }
/**
* The timestamp of the value
- * @default {Date} new Date();
+ * @default {Date} current time with ms precision
*/
- this.timestamp = timestamp || new Date();
+ this.timestamp = new Date(this.timestamp_micro / 1000);
/**
* The ttl for the column
@@ -42,8 +54,8 @@ Column.prototype.toThrift = function(nameMarshaller, valueMarshaller){
return new ttypes.Column({
name: nameMarshaller.serialize(this.name),
value: valueMarshaller.serialize(this.value),
- timestamp: this.timestamp.getTime() * 1000,
- ttl:this.ttl
+ timestamp: this.timestamp_micro,
+ ttl: this.ttl
});
};
@@ -120,8 +120,7 @@ function getColumns(columns, options, type){
type = Column;
}
- var keys = Object.keys(columns), len = keys.length, i = 0, key, value, arr = [],
- ts = new Date();
+ var keys = Object.keys(columns), len = keys.length, i = 0, key, value, arr = [];
for(; i < len; i += 1){
key = keys[i];
@@ -131,7 +130,7 @@ function getColumns(columns, options, type){
value = '';
}
- arr.push(new type(key, value, ts, options.ttl));
+ arr.push(new type(key, value, null, options.ttl));
}
return arr;
}
View
@@ -45,9 +45,9 @@ var Row = function(data, schema){
// Only deserialize if specified
if(schema.noDeserialize) {
- this.push(new Column(name,item.value, new Date(item.timestamp / 1000), item.ttl));
+ this.push(new Column(name,item.value, item.timestamp, item.ttl));
} else {
- this.push(new Column(name,deserializeValue(item.value), new Date(item.timestamp / 1000), item.ttl));
+ this.push(new Column(name,deserializeValue(item.value), item.timestamp, item.ttl));
}
this._map[name] = this.length - 1;
View
@@ -15,6 +15,7 @@
, "dependencies": {
"helenus-thrift": "0.7.3"
, "node-uuid": "1.3.3"
+ , "microtime": "0.4.0"
}
, "devDependencies": {
"whiskey": "git://github.com/cloudkick/whiskey.git#b3c5bc23e30c95e46083bc7628c2557c1c15ec95"
View
@@ -206,6 +206,43 @@ module.exports = {
});
},
+ 'test cf.insert default microsecond timestamp':function(test, assert){
+ //try to tease out same-ms collision with 50 attempts
+ var finished = 0, ok = true;
+ var callback = function() {
+ finished++;
+ if (finished % 2 == 0) {
+ cf_standard.get(config.standard_row_key, function(err, row){
+ assert.ifError(err);
+
+ ok = ok && (row.get("one").value === "a");
+
+ if (finished == 100) {
+ assert.ok(ok);
+ assert.ifError(err);
+ test.finish();
+ } else {
+ setTimeout(try_race, 0);
+ }
+ });
+ }
+ };
+
+ var try_race = function() {
+ cf_standard.insert(config.standard_row_key, {"one": "b"}, function(err, results){
+ assert.ifError(err);
+ callback();
+ });
+
+ cf_standard.insert(config.standard_row_key, {"one": "a"}, function(err, results){
+ assert.ifError(err);
+ callback();
+ });
+ };
+
+ try_race();
+ },
+
'test counter cf.incr':function(test, assert){
var key = 'åbcd';

0 comments on commit 536dee3

Please sign in to comment.