test('call a callback only once', (t) => { const instance = tracker({ hash: farmhash.hash32, allocatedToMe: () => true, whoami: () => 'abcde', on: () => {} }) const peer = { id: 'localhost:12345' } instance.track('hello').on('move', (newPeer) => { t.equal(newPeer, peer, 'peer is set') }) instance.check({ start: farmhash.hash32('hello') - 1, end: farmhash.hash32('hello'), to: peer }) instance.check({ start: farmhash.hash32('hello') - 1, end: farmhash.hash32('hello'), to: peer }) t.end() })
peer.on('up', () => { let key = 'hello' for (let i = 0; i < 10; i++) { t.deepEqual(root.lookup(key), root.mymeta(), 'key is matched by root') key += farmhash.hash32(key) } })
boot(t, (two) => { var i const onePoints = one._hashring.mymeta().points var start = onePoints[0] var end = 0 const twoPoints = two._hashring.mymeta().points for (i = 0; i < twoPoints.length; i++) { if (twoPoints[i] > start) { end = twoPoints[i] for (var k = 1; k < onePoints.length; k++) { if (onePoints[k] > end) { start = onePoints[k - 1] break } } if (start > onePoints[0]) { break } } } var key var hash i = 0 do { key = 'hello' + i++ hash = farmhash.hash32(key) } while (!(start < hash && hash < end)) // now key will be allocated between the two // let's track it one.track(key, { replica: true }) .once('move', function (newPeer) { t.equal(two.whoami(), newPeer.id, 'destination id matches') }) .on('replica', function () { t.fail('no replica event') }) two.track(key, { replica: true }) .on('replica', function (newPeer, oldPeer) { t.equal(one.whoami(), newPeer.id, 'replica id matches') t.notOk(oldPeer, 'no older replica') }) // let's join them in a cluster one.join([two.whoami()], function (err) { t.error(err, 'no error') one.replica(key, function () { // now one is responsible for this key t.pass('one owns key') }) two.close(function () { t.pass('two closed') }) }) })
function genReplicaPoints (id, max) { var points = new Array(max) var last = 0 for (var i = 0; i < max; i++) { last = farmhash.hash32(id + last) points[i] = last } return points }
TChannelFrame.prototype.checksum = function () { var csum = farmhash.hash32(this.arg1); if (this.arg2.length > 0) { csum = farmhash.hash32WithSeed(this.arg2, csum); } if (this.arg3.length > 0) { csum = farmhash.hash32WithSeed(this.arg3, csum); } return csum; };
HashRing.prototype.lookup = function lookup(str) { var hash = farmhash.hash32(str); var iter = this.rbtree.upperBound(hash); var res = iter.str(); if (res === null) { var min = this.rbtree.min(); res = min && min.str; } return res; };
HashRing.prototype.computeChecksum = function computeChecksum() { // If servers is empty, a checksum will still be computed // for the empty string. var serverNames = Object.keys(this.servers); var serverNameStr = serverNames.sort().join(';'); this.checksum = farmhash.hash32(serverNameStr); this.emit('checksumComputed'); };
test('do nothing if the element interval is after', (t) => { const instance = tracker({ hash: farmhash.hash32, allocatedToMe: () => true, whoami: () => 'abcde', on: () => {} }) const peer = { id: 'localhost:12345' } instance.track('hello').on('move', () => { t.fail('this should not be called') }) instance.check({ start: farmhash.hash32('hello') + 10, end: farmhash.hash32('hello') + 20, to: peer }) t.end() })
test('track a key on the ring', (t) => { t.plan(1) const instance = tracker({ hash: farmhash.hash32, allocatedToMe: () => true, whoami: () => 'abcde', on: () => {} }) const peer = { id: 'localhost:12345' } const track = instance.track('hello') track.on('move', (newPeer) => { t.equal(newPeer, peer, 'peer is set') }) instance.check({ start: farmhash.hash32('hello') - 1, end: farmhash.hash32('hello'), to: peer }) })
test('do nothing if the the tracker.end function is called', (t) => { const instance = tracker({ hash: farmhash.hash32, allocatedToMe: () => true, whoami: () => 'abcde', on: () => {} }) const peer = { id: 'localhost:12345' } const track = instance.track('hello').on('move', () => { t.fail('this should not be called') }) track.end() instance.check({ start: farmhash.hash32('hello') - 1, end: farmhash.hash32('hello') + 1, to: peer }) t.end() })
HashRing.prototype.removeServer = function removeServer(name) { if (!this.hasServer(name)) { return; } delete this.servers[name]; for (var i = 0; i < this.replicaPoints; i++) { this.rbtree.remove(farmhash.hash32(name + i), name); } this.computeChecksum(); this.emit('removed', name); };
HashRing.prototype.addServer = function addServer(name) { if (this.hasServer(name)) { return; } this.servers[name] = true; for (var i = 0; i < this.replicaPoints; i++) { this.rbtree.insert(farmhash.hash32(name + i), name); } this.computeChecksum(); this.emit('added', name); };
getTranslation: function (req, modelInstance, callback) { const contentToTranslate = AcTranslationCache.getContentToTranslate(req, modelInstance); if (contentToTranslate && contentToTranslate!=='' && contentToTranslate.length>6 && isNaN(contentToTranslate)) { const contentHash = farmhash.hash32(contentToTranslate).toString(); const textType = req.query.textType; let targetLanguage = req.query.targetLanguage.replace('_','-'); if (targetLanguage!='zh-CN' && targetLanguage!='zh-TW') { targetLanguage = targetLanguage.split("-")[0]; } let indexKey = `${textType}-${modelInstance.id}-${targetLanguage}-${contentHash}`; AcTranslationCache.findOne({ where: { index_key: indexKey } }).then(function (translationModel) { if (translationModel) { callback(null, { content: translationModel.content }); } else { AcTranslationCache.getTranslationFromGoogle(textType, indexKey, contentToTranslate, targetLanguage, modelInstance, callback); } }).catch(function (error) { callback(error); }); } else { log.warn("Empty or short string for translation", { textType: req.query.textType, modelInstance, targetLanguage: req.query.targetLanguage }); if (!modelInstance.language) { modelInstance.update({ language: '??' }).then(function () { callback(null, { content: contentToTranslate }); }).catch( error => { callback(error); }); } else { callback(); } } }
test('track the replica of a value across the ring', (t) => { t.plan(8) const hash = farmhash.hash32('hello') const hashring = new EE() hashring.hash = farmhash.hash32 hashring.allocatedToMe = () => true hashring.whoami = () => 'abcde' const instance = tracker(hashring) const myself = { id: 'a', points: [ // mocked points hash - 10, hash + 50 ] } const peer = { id: 'b', points: [ // mocked points hash + 60, hash + 100 ] } const peer2 = { id: 'b', points: [ // mocked points hash + 55, hash + 200 ] } hashring.next = function (key) { // first go, there is a next peer t.equal(key, hash) hashring.next = function (key) { // second go, there is a next peer t.equal(key, hash) hashring.next = function () { t.fail('next should not be called again') } return peer2 } return peer } hashring.lookup = function (key) { t.equal(key, hash) return myself } const track = instance.track('hello', { replica: true }) track.once('replica', (newPeer, oldPeer) => { t.equal(newPeer, peer, 'peer is set') t.notOk(oldPeer, 'no old peer') track.once('replica', (newPeer2, oldPeer2) => { t.equal(oldPeer2, newPeer, 'peer is set') t.equal(newPeer2, peer2) track.once('replica', () => { t.fail('no more replica') }) }) hashring.emit('peerUp', peer2) instance.check({ start: myself.points[1], end: peer2.points[0], to: peer2 }) }) hashring.emit('peerUp', peer) instance.check({ start: myself.points[1], end: peer.points[0], to: peer }) })
.add('32-farmhash', function () { farmhash.hash32(input); })
internals.hash = function (obj) { const str = JSON.stringify(obj); return Farmhash.hash32(str); };