├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── CHANGES.md ├── LICENSE ├── MIGRATION.md ├── README.md ├── benchmarks ├── add-remove-hashring.js ├── bench_ringpop_stat_cached_keys.js ├── bench_ringpop_stat_new_keys.js ├── compute-checksum.js ├── convergence-time │ ├── scenario-runner.js │ ├── scenarios │ │ ├── half-cluster-failure.js │ │ └── single-node-failure.js │ └── worker.js ├── find-member-by-address.js ├── index.js ├── join-response-merge.js ├── large-membership-update.js ├── large-membership.json ├── lib │ └── runner.js └── run.js ├── client.js ├── client_errors.js ├── client_request.js ├── client_request_errors.js ├── client_request_states.js ├── config.js ├── discover-providers.js ├── examples ├── README.md ├── cluster.js ├── handle_or_forward.js ├── key_value.js ├── leader_election.js ├── parse_args.js ├── safe_parse.js └── tchannel-forwarding.js ├── index.js ├── lib ├── backoff.js ├── errors.js ├── gossip │ ├── damper.js │ ├── dissemination.js │ ├── events.js │ ├── index.js │ ├── join-response-merge.js │ ├── joiner.js │ ├── ping-req-sender.js │ ├── ping-sender.js │ └── state_transitions.js ├── lag_sampler.js ├── logging │ ├── levels.js │ ├── logger_factory.js │ └── module_logger.js ├── membership │ ├── events.js │ ├── index.js │ ├── iterator.js │ ├── member.js │ ├── member_damp_score.js │ ├── merge.js │ ├── rollup.js │ └── update.js ├── middleware │ ├── index.js │ ├── stack.js │ ├── tombstone.js │ └── transport.js ├── nulls.js ├── on_config_event.js ├── on_membership_event.js ├── on_ring_event.js ├── on_ringpop_event.js ├── partition_healing │ ├── discover_provider_healer.js │ ├── healer.js │ └── index.js ├── request-proxy │ ├── index.js │ ├── send.js │ └── util.js ├── ring │ ├── events.js │ ├── index.js │ └── rbtree.js ├── self-evict.js ├── stats-periodic.js ├── trace │ ├── config.js │ ├── core.js │ ├── index.js │ ├── log.js │ ├── store.js │ ├── tchannel.js │ └── types.js └── util.js ├── main.js ├── package.json ├── request_response.js ├── ringpop-handler.js ├── ringpop_errors.js ├── server ├── admin │ ├── config.js │ ├── debug-clear.js │ ├── debug-set.js │ ├── gossip.js │ ├── index.js │ ├── lookup.js │ ├── member.js │ ├── partition-healing.js │ ├── reload.js │ └── stats.js ├── health.js ├── index.js ├── protocol │ ├── damp_req.js │ ├── index.js │ ├── join.js │ ├── ping-req.js │ └── ping.js ├── proxy-req.js └── trace.js └── test ├── .gitignore ├── fixtures.js ├── index.js ├── integration ├── admin_test.js ├── bidirectional-full-sync-test.js ├── discover-provider-retry-test.js ├── gossip_test.js ├── index.js ├── join-test.js ├── not-ready-test.js ├── partition_healing_test.js ├── piggyback-test.js ├── proxy_test.js ├── ring-test.js └── tchannel-proxy-test.js ├── lib ├── alloc-cluster.js ├── alloc-request.js ├── alloc-response.js ├── alloc-ringpop.js ├── bootstrap.js ├── gossip-utils.js ├── int-comparator.js ├── partition-cluster.js ├── tchannel-proxy-cluster.js ├── test-ringpop-cluster.js ├── test-ringpop.js ├── timers-mock.js └── wait-for-convergence.js ├── mock ├── channel.js ├── index.js ├── logger.js ├── membership.js ├── noop.js ├── request-proxy.js └── tchannel.js ├── run-shared-integration-tests ├── travis └── unit ├── backoff_test.js ├── client_request_test.js ├── client_test.js ├── config_test.js ├── damper_test.js ├── discover-providers-example-invalid.json ├── discover-providers-example.json ├── discover-providers-test.js ├── discover_provider_healer_test.js ├── dissemination-test.js ├── gossip_test.js ├── hashring_test.js ├── index-test.js ├── index.js ├── join-response-merge-test.js ├── joiner_test.js ├── member_status_test.js ├── member_test.js ├── membership-changeset-merge-test.js ├── membership-iterator-test.js ├── membership-set-listener-test.js ├── membership-update-rollup-test.js ├── membership_test.js ├── middleware_test.js ├── module_logger_test.js ├── proxy_req_test.js ├── rbiterator_test.js ├── rbtree_test.js ├── request_proxy_test.js ├── ring-test.js ├── ringnode_test.js ├── ringpop_alloc_error_test.js ├── self-evict-test.js ├── server ├── admin │ ├── config_test.js │ ├── gossip_test.js │ └── partition_healing_test.js └── protocol │ ├── damp_req_test.js │ └── join_test.js ├── state_transistion_test.js ├── stats-test.js ├── trace-test.js └── transport_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | *.swp 3 | coverage/* 4 | node_modules/* 5 | npm-debug.log 6 | hosts.json 7 | notes.txt 8 | notes.txt~ 9 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | test/ringpop-common 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "camelcase": true, 4 | "curly": false, 5 | "eqeqeq": true, 6 | "forin": true, 7 | "immed": true, 8 | "indent": 4, 9 | "latedef": "nofunc", 10 | "noarg": true, 11 | "nonew": true, 12 | "plusplus": false, 13 | "quotmark": false, 14 | "regexp": false, 15 | "undef": true, 16 | "unused": true, 17 | "strict": false, 18 | "trailing": true, 19 | "node": true, 20 | "noempty": true, 21 | "maxdepth": 4, 22 | "maxparams": 4, 23 | "newcap": false, 24 | "globalstrict": true 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "4.4.7" 5 | before_install: npm i npm@2 -g 6 | 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - g++-4.8 13 | 14 | env: 15 | - CXX=g++-4.8 RUN="npm run travis" 16 | - CXX=g++-4.8 RUN="npm run test-shared-integration-tests" 17 | 18 | matrix: 19 | fast_finish: true 20 | 21 | script: 22 | - test/travis 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Uber Technologies, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration 2 | Some changes to Ringpop require an additional note about how developer's 3 | should upgrade. Below is where you'll find instructions on how to upgrade 4 | gracefully from one version of Ringpop to another. 5 | 6 | ## Upgrading to v10.13.5 7 | In v10.13.5, we hardened Ringpop to be resilient against wedged nodes. Nodes 8 | may become wedged as a result of a potential TChannel bug -- when pings, 9 | ping-reqs or joins are sent. Ringpop will timeout a wedged request and resume 10 | gracefully. We have observed Ringpop to behave correctly while in this state, 11 | but you may observe otherwise. We will let you come to your own conclusions. 12 | Ringpop will emit an `error` event (when listeners have subscribed) providing a 13 | `PotentiallyWedgedRequestsError` object as an argument. Registering for this event 14 | will give you the ability to restart your process, if you so choose. 15 | 16 | ## Upgrading from v10.9.6 to v10.9.7 17 | v10.9.7 includes an `event` arg as part of the `ringChanged` event that it 18 | emits. The event is of type `RingChangedEvent` and carries with it 3 properties: 19 | `name`, `added` and `removed`. The `name` property is equivalent to the name 20 | of the constructor function. The `added` and `removed` properties are Arrays 21 | that include the addresses of the servers that were either added to or removed 22 | from the hash ring. 23 | 24 | ## Upgrading from v10.4.0 to v10.5.0 25 | v10.5.0 requires the use of TChannel v2.7.1 and above due to TChannel API 26 | requirements of the ringpop-handler.js module. If one does not use this 27 | module, versions of TChannel < v2.7.1 may still work. Use at your own 28 | risk. 29 | -------------------------------------------------------------------------------- /benchmarks/add-remove-hashring.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var largeMembership = require('./large-membership.json'); 24 | var Ringpop = require('../index.js'); 25 | var Suite = require('benchmark').Suite; 26 | 27 | var members = largeMembership.slice(0, 1000); 28 | var servers = members.map(function mapMember(member) { 29 | return member.address; 30 | }); 31 | 32 | function reportPerformance(event) { 33 | console.log(event.target.toString()); 34 | } 35 | 36 | function addRemoveIndividual() { 37 | var ring; 38 | 39 | var suite = new Suite(); 40 | suite.add('add/remove 1000 servers individually', benchThis); 41 | suite.on('start', init); 42 | suite.on('cycle', reportPerformance); 43 | suite.run(); 44 | 45 | function benchThis() { 46 | for (var i = 0; i < 1000; i++) { 47 | ring.addServer(servers[i]); 48 | } 49 | 50 | for (var j = 0; j < 1000; j++) { 51 | ring.removeServer(servers[j]); 52 | } 53 | } 54 | 55 | function init() { 56 | var ringpop = new Ringpop({ 57 | app: 'ringpop-bench', 58 | hostPort: '127.0.0.1:3000' 59 | }); 60 | 61 | ring = ringpop.ring; 62 | } 63 | } 64 | 65 | function addRemoveBulk() { 66 | var ring; 67 | 68 | var suite = new Suite(); 69 | suite.add('add/remove 1000 servers in bulk', benchThis); 70 | suite.on('start', init); 71 | suite.on('cycle', reportPerformance); 72 | suite.run(); 73 | 74 | function benchThis() { 75 | ring.addRemoveServers(servers, servers); 76 | } 77 | 78 | function init() { 79 | var ringpop = new Ringpop({ 80 | app: 'ringpop-bench', 81 | hostPort: '127.0.0.1:3000' 82 | }); 83 | 84 | ring = ringpop.ring; 85 | } 86 | } 87 | 88 | addRemoveIndividual(); 89 | addRemoveBulk(); 90 | -------------------------------------------------------------------------------- /benchmarks/bench_ringpop_stat_cached_keys.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var RingPop = require('../index.js'); 22 | var Suite = require('benchmark').Suite; 23 | 24 | var ringpop; 25 | 26 | function init() { 27 | ringpop = new RingPop({ 28 | app: 'ringpop-bench', 29 | hostPort: '127.0.0.1:3000' 30 | }); 31 | ringpop.stat('gauge', 'num-members', 10); 32 | } 33 | 34 | function reportPerformance(event) { 35 | console.log(event.target.toString()); 36 | } 37 | 38 | function benchThis() { 39 | ringpop.stat('gauge', 'num-members', 10); 40 | } 41 | 42 | var benchmark = new Suite(); 43 | benchmark.add('stat() with caching', benchThis) 44 | .on('start', init) 45 | .on('cycle', reportPerformance) 46 | .run(); 47 | -------------------------------------------------------------------------------- /benchmarks/bench_ringpop_stat_new_keys.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var RingPop = require('../index.js'); 22 | var Suite = require('benchmark').Suite; 23 | 24 | var ringpop; 25 | 26 | function init() { 27 | ringpop = new RingPop({ 28 | app: 'ringpop-bench', 29 | hostPort: '127.0.0.1:3000' 30 | }); 31 | } 32 | 33 | function reportPerformance(event) { 34 | console.log(event.target.toString()); 35 | } 36 | 37 | function benchThis() { 38 | ringpop.stat('gauge', 'num-members', 10); 39 | ringpop.statKeys = {}; // benchmark does not support setup/teardown per sample 40 | } 41 | 42 | var benchmark = new Suite(); 43 | benchmark.add('stat() without caching', benchThis) 44 | .on('start', init) 45 | .on('cycle', reportPerformance) 46 | .run(); 47 | -------------------------------------------------------------------------------- /benchmarks/compute-checksum.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var largeMembership = require('./large-membership.json'); 22 | var Ringpop = require('../index.js'); 23 | var Suite = require('benchmark').Suite; 24 | 25 | function init(size) { 26 | var ringpop = new Ringpop({ 27 | app: 'ringpop-bench', 28 | hostPort: '127.0.0.1:3000' 29 | }); 30 | 31 | ringpop.membership.update(largeMembership.slice(0, size)); 32 | 33 | return ringpop; 34 | } 35 | 36 | function reportPerformance(event) { 37 | console.log(event.target.toString()); 38 | } 39 | 40 | function runBenchmark(title, benchmark) { 41 | var suite = new Suite(); 42 | suite.add(title, benchmark); 43 | suite.on('cycle', reportPerformance); 44 | suite.run(); 45 | } 46 | 47 | function run100MemberBenchmark() { 48 | var ringpop = init(100); 49 | 50 | runBenchmark('compute checksum for 100 members', function benchmark() { 51 | ringpop.membership.computeChecksum(); 52 | }); 53 | } 54 | 55 | function run1kMemberBenchmark() { 56 | var ringpop = init(1000); 57 | 58 | runBenchmark('compute checksum for 1000 members', function benchmark() { 59 | ringpop.membership.computeChecksum(); 60 | }); 61 | } 62 | 63 | run1kMemberBenchmark(); 64 | -------------------------------------------------------------------------------- /benchmarks/convergence-time/scenarios/half-cluster-failure.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function halfClusterFailure(context, callback) { 24 | var hosts = Object.keys(context.hostToAliveWorker); 25 | var selected = []; 26 | var index; 27 | var i; 28 | 29 | for (i = 0; i < hosts.length / 2; i++) { 30 | index = i + Math.floor(Math.random() * (hosts.length - i)); 31 | selected.push(hosts[index]); 32 | hosts[index] = hosts[i]; 33 | } 34 | 35 | selected.forEach(function leave(host) { 36 | context.hostToFaultyWorker[host] = context.hostToAliveWorker[host]; 37 | delete context.hostToAliveWorker[host]; 38 | context.hostToFaultyWorker[host].send({ 39 | cmd: 'leave' 40 | }); 41 | }); 42 | 43 | process.nextTick(callback); 44 | } 45 | 46 | module.exports = { 47 | name: 'half cluster failure', 48 | fn: halfClusterFailure 49 | }; 50 | -------------------------------------------------------------------------------- /benchmarks/convergence-time/scenarios/single-node-failure.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function singleNodeFailure(context, callback) { 24 | var hosts = Object.keys(context.hostToAliveWorker); 25 | var host = hosts[Math.floor(Math.random() * hosts.length)]; 26 | 27 | if (host) { 28 | context.hostToFaultyWorker[host] = context.hostToAliveWorker[host]; 29 | delete context.hostToAliveWorker[host]; 30 | context.hostToFaultyWorker[host].send({ 31 | cmd: 'leave' 32 | }); 33 | } 34 | 35 | process.nextTick(callback); 36 | } 37 | 38 | module.exports = { 39 | name: 'single node failure', 40 | fn: singleNodeFailure 41 | }; 42 | -------------------------------------------------------------------------------- /benchmarks/find-member-by-address.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var largeMembership = require('./large-membership.json'); 24 | var Ringpop = require('../index.js'); 25 | var Suite = require('benchmark').Suite; 26 | 27 | function reportPerformance(event) { 28 | console.log(event.target.toString()); 29 | } 30 | 31 | function benchFindOutOf1000() { 32 | var ringpop; 33 | 34 | var suite = new Suite(); 35 | suite.add('find one member out of 1000', benchThis); 36 | suite.on('start', init); 37 | suite.on('cycle', reportPerformance); 38 | suite.run(); 39 | 40 | function benchThis() { 41 | ringpop.membership.findMemberByAddress(largeMembership[0].address); 42 | } 43 | 44 | function init() { 45 | ringpop = new Ringpop({ 46 | app: 'ringpop-bench', 47 | hostPort: '127.0.0.1:3000' 48 | }); 49 | 50 | ringpop.membership.update(largeMembership.slice(0, 1000)); 51 | } 52 | } 53 | 54 | benchFindOutOf1000(); 55 | -------------------------------------------------------------------------------- /benchmarks/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | require('glob').sync(__dirname + '/**/bench_*.js').forEach(require); 22 | -------------------------------------------------------------------------------- /benchmarks/join-response-merge.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var largeMembership = require('./large-membership.json'); 24 | var mergeJoinResponses = require('../lib/swim/join-response-merge.js'); 25 | var Suite = require('benchmark').Suite; 26 | 27 | function reportPerformance(event) { 28 | console.log(event.target.toString()); 29 | } 30 | 31 | function benchMerge(title, checksum) { 32 | var responses = []; 33 | 34 | var suite = new Suite(); 35 | suite.add(title, benchThis); 36 | suite.on('start', init); 37 | suite.on('cycle', reportPerformance); 38 | suite.run(); 39 | 40 | function benchThis() { 41 | mergeJoinResponses(responses); 42 | } 43 | 44 | function init() { 45 | var members1k = largeMembership.slice(0, 1000); 46 | 47 | for (var i = 0; i < 3; i++) { 48 | responses.push({ 49 | members: members1k, 50 | checksum: checksum 51 | }); 52 | } 53 | } 54 | } 55 | 56 | function benchMergeNoChecksum() { 57 | benchMerge('merge 3 responses of 1000 members with no checksum'); 58 | } 59 | 60 | function benchMergeSameChecksum() { 61 | benchMerge('merge 3 responses of 1000 members with same checksum', 123456789); 62 | } 63 | 64 | benchMergeNoChecksum(); 65 | benchMergeSameChecksum(); 66 | -------------------------------------------------------------------------------- /benchmarks/large-membership-update.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var largeMembership = require('./large-membership.json'); 22 | var Ringpop = require('../index.js'); 23 | var Suite = require('benchmark').Suite; 24 | 25 | var ringpop; 26 | 27 | function init() { 28 | ringpop = new Ringpop({ 29 | app: 'ringpop-bench', 30 | hostPort: '127.0.0.1:3000' 31 | }); 32 | } 33 | 34 | function reportPerformance(event) { 35 | console.log(event.target.toString()); 36 | } 37 | 38 | function benchThis() { 39 | ringpop.membership.members = []; 40 | ringpop.membership.membersByAddress = {}; 41 | ringpop.membership.update(largeMembership); 42 | } 43 | 44 | var benchmark = new Suite(); 45 | benchmark.add('large membership update', benchThis) 46 | .on('start', init) 47 | .on('cycle', reportPerformance) 48 | .run(); 49 | -------------------------------------------------------------------------------- /benchmarks/lib/runner.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | var async = require('async'); 23 | var events = require('events'); 24 | var util = require('util'); 25 | 26 | function Runner(opts) { 27 | this.cycles = opts.cycles || 0; 28 | this.setup = opts.setup || noop; 29 | this.teardown = opts.teardown || noop; 30 | this.before = opts.suite.before || noop; 31 | this.fn = opts.suite.fn || noop; 32 | this.after = opts.suite.after || noop; 33 | } 34 | 35 | util.inherits(Runner, events.EventEmitter); 36 | 37 | Runner.prototype.run = function run(callback) { 38 | var self = this; 39 | 40 | async.series([ 41 | function setup(callback) { 42 | self.emit(Runner.EventType.Setup); 43 | self.setup(callback); 44 | }, 45 | function run(callback) { 46 | async.timesSeries(self.cycles, function wrappedRun(i, callback) { 47 | async.series([ 48 | function before(callback) { 49 | self.emit(Runner.EventType.Before); 50 | self.before(callback); 51 | }, 52 | function fn(callback) { 53 | self.emit(Runner.EventType.Fn); 54 | self.fn(callback); 55 | }, 56 | function after(callback) { 57 | self.emit(Runner.EventType.After); 58 | self.after(callback); 59 | } 60 | ], callback); 61 | }, callback); 62 | }, 63 | function teardown(callback) { 64 | self.emit(Runner.EventType.Teardown); 65 | self.teardown(callback); 66 | } 67 | ], callback); 68 | }; 69 | 70 | Runner.EventType = { 71 | After: 'after', 72 | Before: 'before', 73 | Fn: 'fn', 74 | Setup: 'setup', 75 | Teardown: 'teardown' 76 | }; 77 | 78 | module.exports = Runner; 79 | 80 | function noop(callback) { 81 | if (typeof callback === 'function') { 82 | process.nextTick(callback); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /client_errors.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var TypedError = require('error/typed'); 24 | 25 | module.exports = { 26 | ClientRequestsLimitError: TypedError({ 27 | type: 'ringpop.client.limit-exceeded', 28 | message: 'Client request limit reached', 29 | inflightCurrent: null, 30 | inflightMax: null, 31 | endpoint: null 32 | }) 33 | }; 34 | -------------------------------------------------------------------------------- /client_request_errors.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var TypedError = require('error/typed'); 24 | 25 | module.exports = { 26 | ChannelDestroyedError: TypedError({ 27 | type: 'ringpop.client.channel-destroyed', 28 | message: 'Channel is already destroyed', 29 | endpoint: null, 30 | channelType: null 31 | }), 32 | InvalidHostPortError: TypedError({ 33 | type: 'ringpop.client.invalid-hostport', 34 | message: 'Request made with invalid host port combination ({hostPort})', 35 | hostPort: null 36 | }), 37 | RequestCanceledError: TypedError({ 38 | type: 'ringpop.client.request-cancled', 39 | message: 'Request was canceled while waiting for TChannel response' 40 | }), 41 | SubChannelRequestAfterCancelError: TypedError({ 42 | type: 'ringpop.client-request.sub-channel-request-after-cancel', 43 | message: 'TChannel sub-channel request completed after request was canceled' 44 | }), 45 | WaitForIdentifiedAfterCancelError: TypedError({ 46 | type: 'ringpop.client-request.wait-for-identified-after-cancel', 47 | message: 'TChannel waitForIdentified completed after request was canceled' 48 | }) 49 | }; 50 | -------------------------------------------------------------------------------- /client_request_states.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = { 24 | initialized: 0, 25 | waitForIdentifiedPre: 1, 26 | waitForIdentifiedPost: 2, 27 | subChannelRequestPre: 3, 28 | subChannelRequestPost: 4, // This also represents TChannel completeness 29 | completed: 4 30 | }; 31 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | * `leader_election.js` - Use Ringpop to choose a (sloppy) leader and then 3 | do something interesting. 4 | * `key_value.js` - Use Ringpop to build an in-memory key-value store. 5 | 6 | # Installation 7 | Though each example may have its own dependencies it's safest to run the 8 | below from the root directory: 9 | 10 | `npm install` 11 | 12 | # General Usage 13 | Running any of the examples is the same. Replace `example.js` with any of the 14 | example source files mentioned above: 15 | 16 | ```sh 17 | Usage: example [options] 18 | 19 | Run an example 20 | 21 | Options: 22 | 23 | -h, --help output usage information 24 | -n, --clusterName Name of cluster 25 | -s, --size Size of cluster 26 | -b, --bootstrap JSON compatible array of hosts 27 | -p, --port Base port for cluster 28 | -h, --host Address of host 29 | [options] example.js 30 | ``` 31 | 32 | # Leader Election 33 | Here is a sample of how to start a 5-node cluster and get them to elect a 34 | leader: 35 | 36 | ```sh 37 | node leader_election.js -n le -s 5 38 | ``` 39 | 40 | And here is how you get 5 more nodes to join that cluster and get them to 41 | elect a new leader: 42 | 43 | ```sh 44 | node leader_election.js -n le -s 5 -p 3005 -b '["127.0.0.1:3000"]' 45 | ``` 46 | 47 | # Key Value 48 | Start your Ringpop cluster: 49 | 50 | ``` 51 | node key_value.js -n kv 52 | ``` 53 | 54 | Put a value into the store: 55 | 56 | ```sh 57 | tcurl -p 127.0.0.1:3000 kv put -3 '{"key":"foo","value":"bar"}' 58 | ``` 59 | 60 | And read it back (from a different node): 61 | 62 | ```sh 63 | tcurl -p 127.0.0.1:3002 kv get -3 '{"key":"foo"}' 64 | ``` 65 | -------------------------------------------------------------------------------- /examples/handle_or_forward.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var logger = require('winston'); 24 | var RelayRequest = require('tchannel/relay_handler').RelayRequest; 25 | 26 | /* jshint maxparams: 7 */ 27 | module.exports = function handleOrForward(ringpop, key, req, res, arg2, arg3, handle) { 28 | var dest = ringpop.lookup(key); 29 | if (dest === ringpop.whoami()) { 30 | logger.info(ringpop.whoami() + ' handling request...'); 31 | handle(ringpop, key, res, arg2, arg3); 32 | return; 33 | } 34 | 35 | logger.info(ringpop.whoami() + ' forwarding request to ' + dest + '...'); 36 | var channel = ringpop.channel; 37 | var peer = channel.peers.add(dest); 38 | var outreq = new RelayRequest(channel, peer, req, function buildRes(options) { 39 | res.headers = options.headers; 40 | res.code = options.code; 41 | res.ok = options.ok; 42 | return res; 43 | }); 44 | outreq.createOutRequest(); 45 | }; 46 | -------------------------------------------------------------------------------- /examples/key_value.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Cluster = require('./cluster.js'); 24 | var logger = require('winston'); 25 | var handleOrForward = require('./handle_or_forward'); 26 | var parseArgs = require('./parse_args.js'); 27 | 28 | function App(ringpop) { 29 | var self = this; 30 | self.ringpop = ringpop; 31 | self.appChannel = self.ringpop.appChannel; 32 | self.appChannel.register('health', self.healthCheck.bind(self)); 33 | self.appChannel.register('get', self.get.bind(self)); 34 | self.appChannel.register('put', self.put.bind(self)); 35 | self.data = Object.create(null); 36 | } 37 | 38 | App.prototype.healthCheck = function healthCheck(req, res/*, arg2, arg3*/) { 39 | logger.info('health check'); 40 | res.headers.as = 'raw'; 41 | res.sendOk('', '"OK"'); 42 | }; 43 | 44 | App.prototype.get = function get(req, res, arg2, arg3) { 45 | var self = this; 46 | var key = JSON.parse(arg3).key; 47 | 48 | handleOrForward(self.ringpop, key, req, res, arg2, arg3, function handleGet() { 49 | logger.info(self.ringpop.whoami() + ' get:', arg3.toString()); 50 | res.headers.as = 'raw'; 51 | 52 | res.sendOk('', '"' + self.data[key] + '"'); 53 | }); 54 | }; 55 | 56 | App.prototype.put = function put(req, res, arg2, arg3) { 57 | var self = this; 58 | var requestBody = JSON.parse(arg3); 59 | 60 | handleOrForward(self.ringpop, requestBody.key, req, res, arg2, arg3, function handlePut() { 61 | logger.info(self.ringpop.whoami() + ' put:', arg3.toString()); 62 | 63 | self.data[requestBody.key] = requestBody.value; 64 | res.headers.as = 'raw'; 65 | res.sendOk('', '"OK"'); 66 | }); 67 | }; 68 | 69 | if (require.main === module) { 70 | var cluster = new Cluster(parseArgs()); 71 | cluster.launch(function onLaunch(err, ringpops) { 72 | if (err) { 73 | logger.error('error: ' + err.message); 74 | process.exit(1); 75 | } 76 | 77 | ringpops.forEach(function each(ringpop) { 78 | /* jshint nonew: false */ 79 | new App(ringpop); 80 | }); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /examples/leader_election.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Cluster = require('./cluster.js'); 24 | var logger = require('winston'); 25 | var parseArgs = require('./parse_args.js'); 26 | 27 | var LEADER_KEY = 'LEADER'; 28 | 29 | function App(ringpop) { 30 | this.ringpop = ringpop; 31 | this.currentLeader = null; 32 | this.isLeader = false; 33 | } 34 | 35 | App.prototype.chooseLeader = function chooseLeader() { 36 | var newLeader = this.ringpop.lookup(LEADER_KEY); 37 | if (!this.currentLeader || newLeader !== this.currentLeader) { 38 | logger.info(this.ringpop.whoami() + ' says new leader is ' + newLeader); 39 | 40 | // This app was not a leader, but now is. 41 | if (!this.isLeader && newLeader === this.ringpop.whoami()) { 42 | // Subscribe 43 | this.isLeader = true; 44 | } 45 | 46 | // This app was a leader, but now is not. 47 | if (this.isLeader && newLeader !== this.ringpop.whoami()) { 48 | // Unsubscribe 49 | this.isLeader = false; 50 | } 51 | 52 | this.currentLeader = newLeader; 53 | } 54 | }; 55 | 56 | if (require.main === module) { 57 | var cluster = new Cluster(parseArgs()); 58 | 59 | // Launch cluster. If successful, get individual Ringpop instances (nodes) 60 | // back. 61 | cluster.launch(function onLaunch(err, ringpops) { 62 | if (err) { 63 | logger.error('error:' + err.message); 64 | process.exit(1); 65 | } 66 | 67 | // Create an App wrapper around each Ringpop instance. Choose a 68 | // leader upon instantiation and when a ring change is detected. 69 | ringpops.forEach(function each(ringpop) { 70 | var app = new App(ringpop); 71 | app.chooseLeader(); 72 | ringpop.on('ringChanged', app.chooseLeader.bind(app)); 73 | }); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /examples/parse_args.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var safeParse = require('./safe_parse.js'); 24 | 25 | module.exports = function parseArgs() { 26 | var program = require('commander'); 27 | program.description('Run an example') 28 | .option('-n, --clusterName ', 'Name of cluster') 29 | .option('-s, --size ', 'Size of cluster') 30 | .option('-b, --bootstrap ', 'JSON compatible array of hosts') 31 | .option('-p, --port ', 'Base port for cluster') 32 | .option('-h, --host ', 'Address of host') 33 | .option('[options] example.js') 34 | program.parse(process.argv); 35 | return { 36 | name: program.clusterName, 37 | size: Number(program.size), 38 | bootstrapNodes: safeParse(program.bootstrap), 39 | basePort: Number(program.port), 40 | host: program.host 41 | }; 42 | }; 43 | -------------------------------------------------------------------------------- /examples/safe_parse.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function safeParse(str) { 24 | try { 25 | return JSON.parse(str); 26 | } catch (e) { 27 | return null; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /examples/tchannel-forwarding.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var TChannel = require('tchannel'); 24 | var Ringpop = require('../index.js'); 25 | var RingpopHandler = require('../ringpop-handler.js'); 26 | 27 | function App(options) { 28 | if (!(this instanceof App)) { 29 | return new App(options); 30 | } 31 | 32 | var self = this; 33 | 34 | self.name = options.name; 35 | self.port = options.port; 36 | self.ringpopHosts = options.ringpopHosts; 37 | 38 | self.channel = TChannel(); 39 | self.ringpop = Ringpop({ 40 | app: 'app', 41 | hostPort: '127.0.0.1:' + self.port, 42 | channel: self.channel.makeSubChannel({ 43 | serviceName: 'ringpop', 44 | trace: false 45 | }) 46 | }); 47 | 48 | self.appChannel = self.channel.makeSubChannel({ 49 | serviceName: 'app' 50 | }); 51 | self.appChannel.handler = RingpopHandler({ 52 | channel: self.appChannel, 53 | ringpop: self.ringpop, 54 | logger: self.channel.logger, 55 | realHandler: self.appChannel.handler 56 | }); 57 | 58 | self.appChannel.register('hello', function hello(req, res, arg2, arg3) { 59 | res.headers.as = 'raw'; 60 | res.sendOk('', 'hello from ' + self.name + ' for ' + req.headers.sk); 61 | }); 62 | 63 | self.ringpop.setupChannel(); 64 | } 65 | 66 | App.prototype.bootstrap = function bootstrap(cb) { 67 | var self = this; 68 | 69 | self.channel.listen(self.port, '127.0.0.1', onListen); 70 | 71 | function onListen() { 72 | self.ringpop.bootstrap(self.ringpopHosts, onBootstrap); 73 | } 74 | 75 | function onBootstrap() { 76 | console.log('app: ' + self.name + ' listening on ' + 77 | self.channel.address().port); 78 | } 79 | }; 80 | 81 | if (require.main === module) { 82 | var app1 = App({ 83 | name: 'app1', 84 | port: 4040, 85 | ringpopHosts: ['127.0.0.1:4040', '127.0.0.1:4041'] 86 | }); 87 | var app2 = App({ 88 | name: 'app2', 89 | port: 4041, 90 | ringpopHosts: ['127.0.0.1:4040', '127.0.0.1:4041'] 91 | }); 92 | 93 | app1.bootstrap(); 94 | app2.bootstrap(); 95 | } 96 | -------------------------------------------------------------------------------- /lib/gossip/events.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function ChangesExhaustedEvent() { 24 | this.name = this.constructor.name; 25 | } 26 | 27 | function DampReqFailedEvent(err) { 28 | this.name = this.constructor.name; 29 | this.err = err; 30 | } 31 | 32 | function DampReqUnsatisfiedEvent() { 33 | this.name = this.constructor.name; 34 | } 35 | 36 | function DampedEvent() { 37 | this.name = this.constructor.name; 38 | } 39 | 40 | function DampedMemberExpirationEvent(undampedMembers) { 41 | this.name = this.constructor.name; 42 | this.undampedMembers = undampedMembers; 43 | } 44 | 45 | function DamperStartedEvent() { 46 | this.name = this.constructor.name; 47 | } 48 | 49 | function DampedLimitExceededEvent() { 50 | this.name = this.constructor.name; 51 | } 52 | 53 | function DampingInconclusiveEvent() { 54 | this.name = this.constructor.name; 55 | } 56 | 57 | function SendingPingReqsEvent() { 58 | this.name = this.constructor.name; 59 | } 60 | 61 | module.exports = { 62 | ChangesExhaustedEvent: ChangesExhaustedEvent, 63 | DampReqFailedEvent: DampReqFailedEvent, 64 | DampReqUnsatisfiedEvent: DampReqUnsatisfiedEvent, 65 | DampedEvent: DampedEvent, 66 | DampedLimitExceededEvent: DampedLimitExceededEvent, 67 | DampedMemberExpirationEvent: DampedMemberExpirationEvent, 68 | DamperStartedEvent: DamperStartedEvent, 69 | DampingInconclusiveEvent: DampingInconclusiveEvent, 70 | SendingPingReqsEvent: SendingPingReqsEvent 71 | }; 72 | -------------------------------------------------------------------------------- /lib/gossip/join-response-merge.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var mergeMembershipChangesets = require('../membership/merge.js'); 24 | 25 | function hasSameChecksums(joinResponses) { 26 | var lastChecksum = -1; 27 | 28 | for (var i = 0; i < joinResponses.length; i++) { 29 | var response = joinResponses[i]; 30 | 31 | if (!response.checksum || (lastChecksum !== -1 && lastChecksum !== response.checksum)) { 32 | return false; 33 | } 34 | 35 | lastChecksum = response.checksum; 36 | } 37 | 38 | return true; 39 | } 40 | 41 | function mergeJoinResponses(ringpop, joinResponses) { 42 | if (!Array.isArray(joinResponses) || joinResponses.length === 0) { 43 | return []; 44 | } 45 | 46 | if (hasSameChecksums(joinResponses)) { 47 | return joinResponses[0].members; 48 | } 49 | 50 | return mergeMembershipChangesets(ringpop, mapResponses()); 51 | 52 | function mapResponses() { 53 | return joinResponses.map(function mapResponse(response) { 54 | return response.members; 55 | }); 56 | } 57 | } 58 | 59 | module.exports = mergeJoinResponses; 60 | -------------------------------------------------------------------------------- /lib/lag_sampler.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var globalTimers = require('timers'); 24 | 25 | function LagSampler(opts) { 26 | this.ringpop = opts.ringpop; 27 | this.timers = opts.timers || globalTimers; 28 | this.lagTimer = null; 29 | this.currentLag = 0; 30 | 31 | // toobusy auto-starts sampling. lazily-require until started. 32 | this.toobusy = null; 33 | } 34 | 35 | LagSampler.prototype.start = function start() { 36 | var self = this; 37 | 38 | if (this.lagTimer) { 39 | this.ringpop.logger.debug('ringpop lag sampler lag timer already started', { 40 | local: this.ringpop.whoami() 41 | }); 42 | return; 43 | } 44 | 45 | if (!this.toobusy) { 46 | this.toobusy = require('toobusy-js'); 47 | } 48 | 49 | schedule(); 50 | this.ringpop.logger.debug('ringpop lag sampler started', { 51 | local: this.ringpop.whoami() 52 | }); 53 | 54 | function schedule() { 55 | self.lagTimer = self.timers.setTimeout(function onTimeout() { 56 | self.currentLag = self.toobusy.lag(); 57 | schedule(); 58 | }, self.ringpop.config.get('lagSamplerInterval')); 59 | } 60 | }; 61 | 62 | LagSampler.prototype.stop = function stop() { 63 | if (!this.lagTimer) { 64 | this.ringpop.logger.debug('ringpop lag sampler lag timer already stopped', { 65 | local: this.ringpop.whoami() 66 | }); 67 | return; 68 | } 69 | 70 | this.timers.clearTimeout(this.lagTimer); 71 | this.lagTimer = null; 72 | this.ringpop.logger.debug('ringpop lag sampler stopped', { 73 | local: this.ringpop.whoami() 74 | }); 75 | 76 | if (this.toobusy) { 77 | this.toobusy.shutdown(); 78 | this.toobusy = null; 79 | } 80 | }; 81 | 82 | module.exports = LagSampler; 83 | -------------------------------------------------------------------------------- /lib/logging/levels.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var levels = ['trace', 'debug', 'info', 'warn', 'error', 'off']; 24 | 25 | // e.g. trace => 'trace', debug => 'debug', etc. 26 | var namedLevels = levels.reduce(function each(res, level) { 27 | res[level] = level; 28 | return res; 29 | }, {}); 30 | 31 | // e.g. trace => 0, debug => 1, etc. 32 | var levelToInt = {}; 33 | for (var i = 0; i < levels.length; i++) { 34 | levelToInt[levels[i]] = i; 35 | } 36 | 37 | function convertIntToStr(intVal) { 38 | return levels[intVal]; 39 | } 40 | 41 | function convertStrToInt(str) { 42 | return levelToInt[str]; 43 | } 44 | 45 | module.exports = { 46 | all: namedLevels, 47 | trace: namedLevels.trace, 48 | debug: namedLevels.debug, 49 | info: namedLevels.info, 50 | warn: namedLevels.warn, 51 | error: namedLevels.error, 52 | off: namedLevels.off, 53 | convertIntToStr: convertIntToStr, 54 | convertStrToInt: convertStrToInt, 55 | minLevelInt: levelToInt[namedLevels.trace], 56 | maxLevelInt: levelToInt[namedLevels.error] 57 | }; 58 | -------------------------------------------------------------------------------- /lib/logging/logger_factory.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var ModuleLogger = require('./module_logger.js'); 24 | 25 | // TODO This mapping should eventually go away. It seems that log 26 | // level names by convention (logger name + "LogLevel") is the way 27 | // to go. 28 | var loggerNamesToLevels = { 29 | client: 'clientLogLevel', 30 | damping: 'dampingLogLevel', 31 | dissemination: 'disseminationLogLevel', 32 | gossip: 'gossipLogLevel', 33 | join: 'joinLogLevel', 34 | membership: 'membershipLogLevel', 35 | ring: 'ringLogLevel', 36 | statetransitions: 'stateTransitionsLogLevel', 37 | }; 38 | 39 | function LoggerFactory(opts) { 40 | this.ringpop = opts.ringpop; 41 | this.loggers = {}; // indexed by name 42 | this.defaultLogger = new ModuleLogger(this.ringpop, 'defaultLogLevel'); 43 | } 44 | 45 | LoggerFactory.prototype.getLogger = function getLogger(name) { 46 | if (!name) { 47 | return this.defaultLogger; 48 | } 49 | 50 | if (this.loggers[name]) { 51 | return this.loggers[name]; 52 | } 53 | 54 | return this._createLogger(name); 55 | }; 56 | 57 | LoggerFactory.prototype._createLogger = function _createLogger(name) { 58 | var level = loggerNamesToLevels[name]; 59 | if (!level) return this.defaultLogger; 60 | this.loggers[name] = new ModuleLogger(this.ringpop, level); 61 | return this.loggers[name]; 62 | }; 63 | 64 | module.exports = LoggerFactory; 65 | -------------------------------------------------------------------------------- /lib/logging/module_logger.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Levels = require('./levels.js'); 24 | 25 | function ModuleLogger(ringpop, configName) { 26 | this.ringpop = ringpop; 27 | this.configName = configName; 28 | this.offInt = Levels.convertStrToInt(Levels.off); 29 | } 30 | 31 | ModuleLogger.prototype.debug = function debug(msg, meta) { 32 | // Logging at info level for "debug" logs is intentional. 33 | // The level is controlled by config, not the logger itself. 34 | this._log('debug', msg, meta, 'info'); 35 | }; 36 | 37 | ModuleLogger.prototype.info = function info(msg, meta) { 38 | this._log('info', msg, meta); 39 | }; 40 | 41 | ModuleLogger.prototype.warn = function warn(msg, meta) { 42 | this._log('warn', msg, meta); 43 | }; 44 | 45 | ModuleLogger.prototype.error = function error(msg, meta) { 46 | this._log('error', msg, meta); 47 | }; 48 | 49 | ModuleLogger.prototype.trace = function trace(msg, meta) { 50 | this._log('trace', msg, meta); 51 | }; 52 | 53 | // This function answers the question, "can I log at the desired log level 54 | // given the level configured for this logger?" 55 | ModuleLogger.prototype.canLogAt = function canLogAt(desiredLevel) { 56 | var configLevel = this.ringpop.config.get(this.configName); 57 | var configInt = Levels.convertStrToInt(configLevel); 58 | var desiredInt = Levels.convertStrToInt(desiredLevel); 59 | return configLevel !== Levels.off && configInt <= desiredInt; 60 | }; 61 | 62 | ModuleLogger.prototype._log = function _log(msgLevel, msg, meta, loggerMethod) { 63 | loggerMethod = loggerMethod || msgLevel; 64 | var configLevel = this.ringpop.config.get(this.configName); 65 | var configInt = Levels.convertStrToInt(configLevel); 66 | var msgInt = Levels.convertStrToInt(msgLevel); 67 | if (typeof configInt === 'number' && 68 | !isNaN(configInt) && 69 | typeof msgInt === 'number' && 70 | !isNaN(msgInt) && 71 | msgInt >= configInt && 72 | msgInt < this.offInt) { 73 | this.ringpop.logger[loggerMethod](msg, meta); 74 | } 75 | }; 76 | 77 | module.exports = ModuleLogger; 78 | -------------------------------------------------------------------------------- /lib/membership/events.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function ChecksumComputedEvent(checksum, oldChecksum) { 24 | this.name = this.constructor.name; 25 | this.checksum = checksum; 26 | this.oldChecksum = oldChecksum; 27 | this.timestamp = Date.now(); 28 | } 29 | 30 | // A member becomes reusable when the damp score of a previously damped member 31 | // falls below the reuse limit as specified in config.js by 32 | // dampScoringReuseLimit. 33 | function DampingReusableEvent(member, oldDampScore) { 34 | this.name = this.constructor.name; 35 | this.member = member; 36 | this.oldDampScore = oldDampScore; 37 | } 38 | 39 | function DampingSuppressLimitExceededEvent(member) { 40 | this.name = this.constructor.name; 41 | this.member = member; 42 | } 43 | 44 | function LocalMemberLeaveEvent(member, oldStatus) { 45 | this.name = this.constructor.name; 46 | this.member = member; 47 | this.oldStatus = oldStatus; 48 | } 49 | 50 | module.exports = { 51 | ChecksumComputedEvent: ChecksumComputedEvent, 52 | DampingReusableEvent: DampingReusableEvent, 53 | DampingSuppressLimitExceededEvent: DampingSuppressLimitExceededEvent, 54 | LocalMemberLeaveEvent: LocalMemberLeaveEvent 55 | }; 56 | -------------------------------------------------------------------------------- /lib/membership/iterator.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function MembershipIterator(ring) { 24 | this.ring = ring; 25 | this.currentIndex = -1; 26 | this.currentRound = 0; 27 | } 28 | 29 | MembershipIterator.prototype.next = function next() { 30 | var membersVisited = {}; 31 | var maxMembersToVisit = this.ring.membership.getMemberCount(); 32 | 33 | while (Object.keys(membersVisited).length < maxMembersToVisit) { 34 | this.currentIndex++; 35 | 36 | if (this.currentIndex >= this.ring.membership.getMemberCount()) { 37 | this.currentIndex = 0; 38 | this.currentRound++; 39 | this.ring.membership.shuffle(); 40 | } 41 | 42 | var member = this.ring.membership.getMemberAt(this.currentIndex); 43 | 44 | membersVisited[member.address] = true; 45 | 46 | if (this.ring.membership.isPingable(member)) { 47 | return member; 48 | } 49 | } 50 | 51 | return null; 52 | }; 53 | 54 | module.exports = MembershipIterator; 55 | -------------------------------------------------------------------------------- /lib/membership/member_damp_score.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function MemberDampScore(member, dampScore) { 24 | this.member = member; 25 | this.dampScore = dampScore; 26 | } 27 | 28 | module.exports = MemberDampScore; 29 | -------------------------------------------------------------------------------- /lib/membership/merge.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function mergeMembershipChangesets(ringpop, changesets) { 24 | var mergeIndex = {}; 25 | 26 | for (var i = 0; i < changesets.length; i++) { 27 | var changes = changesets[i]; 28 | 29 | for (var j = 0; j < changes.length; j++) { 30 | var change = changes[j]; 31 | 32 | if (change.address === ringpop.whoami()) { 33 | continue; 34 | } 35 | 36 | var indexVal = mergeIndex[change.address]; 37 | 38 | if (!indexVal || indexVal.incarnationNumber < change.incarnationNumber) { 39 | mergeIndex[change.address] = change; 40 | } 41 | } 42 | } 43 | 44 | var indexKeys = Object.keys(mergeIndex); 45 | var updates = new Array(indexKeys.length); 46 | 47 | for (i = 0; i < indexKeys.length; i++) { 48 | updates[i] = mergeIndex[indexKeys[i]]; 49 | } 50 | 51 | return updates; 52 | }; 53 | -------------------------------------------------------------------------------- /lib/membership/update.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var uuid = require('node-uuid'); 24 | 25 | /** 26 | * Create a new Update 27 | * @param {IMember} subject the subject of the update. 28 | * @param {IMember} [source] the source of the update. 29 | * @constructor 30 | */ 31 | function Update(subject, source) { 32 | if (!(this instanceof Update)) { 33 | return new Update(subject, source); 34 | } 35 | this.id = uuid.v4(); 36 | this.timestamp = Date.now(); 37 | 38 | // Populate subject 39 | subject = subject || {}; 40 | this.address = subject.address; 41 | this.incarnationNumber = subject.incarnationNumber; 42 | this.status = subject.status; 43 | 44 | // Populate source 45 | source = source || {}; 46 | this.source = source.address; 47 | this.sourceIncarnationNumber = source.incarnationNumber; 48 | } 49 | 50 | module.exports = Update; 51 | -------------------------------------------------------------------------------- /lib/middleware/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var _ = require('underscore'); 24 | 25 | module.exports = _.extend( 26 | require('./stack.js'), 27 | require('./tombstone.js'), 28 | require('./transport') 29 | ); 30 | -------------------------------------------------------------------------------- /lib/middleware/transport.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | /* jshint maxparams: 5 */ 23 | 24 | 25 | var safeParse = require('../util.js').safeParse; 26 | 27 | var transportServerMiddleware = { 28 | 'request': function(req, arg2, arg3, callback) { 29 | // this endpoint is special, it treats arg2 as JSON and arg3 as bytes 30 | if (req.endpoint === '/proxy/req') { 31 | var parsedArg2 = safeParse(arg2 && arg2.toString()); 32 | if (parsedArg2 !== null) { 33 | arg2 = parsedArg2; 34 | } 35 | } else { 36 | var parsedArg3 = safeParse(arg3 && arg3.toString()); 37 | if (parsedArg3 !== null) { 38 | arg3 = parsedArg3; 39 | } 40 | } 41 | callback(arg2, arg3); 42 | }, 43 | 'response': function(req, err, res1, res2, callback) { 44 | if (res2 !== undefined && typeof res2 !== 'string' && !Buffer.isBuffer(res2)) { 45 | res2 = JSON.stringify(res2); 46 | } 47 | callback(err, res1, res2); 48 | } 49 | }; 50 | 51 | 52 | module.exports = { 53 | 'transportServerMiddleware': transportServerMiddleware 54 | }; 55 | -------------------------------------------------------------------------------- /lib/nulls.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | function createNullObj() { 22 | function noop() {} 23 | 24 | var obj = {}; 25 | 26 | for (var i = 0; i < arguments.length; i++) { 27 | obj[arguments[i]] = noop; 28 | } 29 | 30 | return obj; 31 | } 32 | 33 | module.exports = { 34 | logger: createNullObj('trace', 'debug', 'info', 'warn', 'error', 'fatal'), 35 | statsd: createNullObj('gauge', 'increment', 'timing') 36 | }; 37 | -------------------------------------------------------------------------------- /lib/on_config_event.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createBackpressureEnabledHandler(ringpop) { 24 | return function onBackpressureEnabled(newValue) { 25 | if (newValue === true) { 26 | ringpop.lagSampler.start(); 27 | } else { 28 | ringpop.lagSampler.stop(); 29 | } 30 | }; 31 | } 32 | 33 | function register(ringpop) { 34 | ringpop.config.on('set.backpressureEnabled', 35 | createBackpressureEnabledHandler(ringpop)); 36 | } 37 | 38 | module.exports = { 39 | register: register 40 | }; 41 | -------------------------------------------------------------------------------- /lib/on_ring_event.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createChecksumComputedHandler(ringpop) { 24 | var ringLogger = ringpop.loggerFactory.getLogger('ring'); 25 | 26 | return function onRingChecksumComputed(event) { 27 | ringpop.stat('increment', 'ring.checksum-computed'); 28 | ringpop.stat('gauge', 'ring.checksum', event.checksum); 29 | ringpop.emit('ringChecksumComputed'); 30 | 31 | if (event.oldChecksum !== event.checksum) { 32 | ringLogger.debug('ringpop ring computed new checksum', { 33 | local: ringpop.whoami(), 34 | checksum: event.checksum, 35 | oldChecksum: event.oldChecksum, 36 | timestamp: event.timestamp 37 | }); 38 | } 39 | }; 40 | } 41 | 42 | function createRingChangedHandler(ringpop) { 43 | return function onRingChanged(event) { 44 | ringpop.stat('increment', 'ring.changed'); 45 | ringpop.emit('ringChanged', event); 46 | }; 47 | } 48 | 49 | function createServerAddedHandler(ringpop) { 50 | return function onRingServerAdded() { 51 | ringpop.stat('increment', 'ring.server-added'); 52 | ringpop.emit('ringServerAdded'); 53 | }; 54 | } 55 | 56 | function createServerRemovedHandler(ringpop) { 57 | return function onRingServerRemoved() { 58 | ringpop.stat('increment', 'ring.server-removed'); 59 | ringpop.emit('ringServerRemoved'); 60 | }; 61 | } 62 | 63 | function register(ringpop) { 64 | var ring = ringpop.ring; 65 | ring.on('added', createServerAddedHandler(ringpop)); 66 | ring.on('checksumComputed', createChecksumComputedHandler(ringpop)); 67 | ring.on('ringChanged', createRingChangedHandler(ringpop)); 68 | ring.on('removed', createServerRemovedHandler(ringpop)); 69 | } 70 | 71 | module.exports = { 72 | createChecksumComputedHandler: createChecksumComputedHandler, 73 | createRingChangedHandler: createRingChangedHandler, 74 | createServerAddedHandler: createServerAddedHandler, 75 | createServerRemovedHandler: createServerRemovedHandler, 76 | register: register 77 | }; 78 | -------------------------------------------------------------------------------- /lib/on_ringpop_event.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createDestroyedHandler(ringpop) { 24 | return function onDestroyed() { 25 | ringpop.lagSampler.stop(); 26 | ringpop.membership.stopDampScoreDecayer(); 27 | ringpop.damper.destroy(); 28 | ringpop.healer.stop(); 29 | }; 30 | } 31 | 32 | function createReadyHandler(ringpop) { 33 | return function onReady() { 34 | if (ringpop.config.get('autoGossip')) { 35 | ringpop.gossip.start(); 36 | } 37 | 38 | if (ringpop.config.get('backpressureEnabled')) { 39 | ringpop.lagSampler.start(); 40 | } 41 | 42 | if (ringpop.config.get('discoverProviderHealerPeriodicEnabled')) { 43 | ringpop.healer.start(); 44 | } 45 | }; 46 | } 47 | 48 | function register(ringpop) { 49 | ringpop.on('destroyed', createDestroyedHandler(ringpop)); 50 | ringpop.on('ready', createReadyHandler(ringpop)); 51 | } 52 | 53 | module.exports = { 54 | createDestroyedHandler: createDestroyedHandler, 55 | createReadyHandler: createReadyHandler, 56 | register: register 57 | }; 58 | -------------------------------------------------------------------------------- /lib/partition_healing/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = { 24 | DiscoverProviderHealer: require('./discover_provider_healer') 25 | }; 26 | -------------------------------------------------------------------------------- /lib/request-proxy/util.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function rawHead(req, ringpopOpts) { 24 | return { 25 | url: req.url, 26 | headers: req.headers, 27 | method: req.method, 28 | httpVersion: req.httpVersion, 29 | ringpopChecksum: ringpopOpts.checksum, 30 | ringpopKeys: ringpopOpts.keys 31 | }; 32 | } 33 | 34 | function strHead(req, ringpopOpts) { 35 | return JSON.stringify(rawHead(req, ringpopOpts)); 36 | } 37 | 38 | module.exports = { 39 | rawHead: rawHead, 40 | strHead: strHead 41 | }; 42 | -------------------------------------------------------------------------------- /lib/ring/events.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function ChecksumComputedEvent(checksum, oldChecksum) { 24 | this.name = this.constructor.name; 25 | this.checksum = checksum; 26 | this.oldChecksum = oldChecksum; 27 | this.timestamp = Date.now(); 28 | } 29 | 30 | function RingChangedEvent(added, removed) { 31 | this.name = this.constructor.name; 32 | this.added = added; 33 | this.removed = removed; 34 | } 35 | 36 | module.exports = { 37 | ChecksumComputedEvent: ChecksumComputedEvent, 38 | RingChangedEvent: RingChangedEvent 39 | }; 40 | -------------------------------------------------------------------------------- /lib/trace/config.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var eventConfigMap = { 24 | 'membership.checksum.update': { 25 | sourcePath: ['membership', 'checksumUpdate'], 26 | sinkOptsDefaults: { 27 | log: { 28 | message: '/trace/membership.checksum.update' 29 | }, 30 | tchannel: { 31 | endpoint: '/trace/membership.checksum.update' 32 | } 33 | } 34 | } 35 | }; 36 | 37 | module.exports = eventConfigMap; 38 | -------------------------------------------------------------------------------- /lib/trace/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = require('./store'); 24 | 25 | -------------------------------------------------------------------------------- /lib/trace/types.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var LogTracer = require('./log'); 24 | var TChannelTracer = require('./tchannel'); 25 | 26 | var tracers = { 27 | tchannel: TChannelTracer, 28 | log: LogTracer, 29 | }; 30 | 31 | module.exports = tracers; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ringpop", 3 | "description": "Scalable, fault-tolerant application-layer sharding", 4 | "contributors": [ 5 | "ben fleis ", 6 | "Alex Hauser ", 7 | "Rui Hu ", 8 | "Bob Nugmanov ", 9 | "Matt Ranney ", 10 | "Jeff Wolski ", 11 | "Jake Verbaten ", 12 | "Mark Yen " 13 | ], 14 | "version": "10.21.0", 15 | "engines": { 16 | "node": "^0.10.32" 17 | }, 18 | "repository": "git://github.com/uber/ringpop.git", 19 | "bin": { 20 | "ringpop": "./main.js" 21 | }, 22 | "scripts": { 23 | "test": "npm run jshint && node test/index.js | faucet", 24 | "test-debug": "node debug test/index.js", 25 | "test-integration": "node test/integration/index.js | faucet", 26 | "test-shared-integration-tests": "test/run-shared-integration-tests", 27 | "test-unit": "node test/unit/index.js | faucet", 28 | "add-licence": "uber-licence", 29 | "check-licence": "uber-licence --dry", 30 | "cover": "istanbul cover --print detail --report html test/index.js | faucet", 31 | "jshint": "jshint --verbose *.js lib/**/*.js server/**/*.js", 32 | "travis": "npm run jshint && npm run cover -s && istanbul report lcov && ((cat coverage/lcov.info | coveralls) || exit 0)", 33 | "view-cover": "opn coverage/index.html" 34 | }, 35 | "dependencies": { 36 | "body": "^5.0.0", 37 | "error": "^5.0.0", 38 | "farmhash": "^1.2.0", 39 | "hammock": "^3.0.1", 40 | "metrics": "^0.1.8", 41 | "node-uuid": "^1.4.3", 42 | "toobusy-js": "^0.5.0", 43 | "underscore": "^1.5.2" 44 | }, 45 | "devDependencies": { 46 | "after": "^0.8.1", 47 | "async": "^0.9.0", 48 | "benchmark": "^1.0.0", 49 | "buffer-equal": "0.0.1", 50 | "cli-color": "^0.3.2", 51 | "commander": "^2.6.0", 52 | "coveralls": "^2.11.2", 53 | "debug-logtron": "^2.1.0", 54 | "express": "^4.13.3", 55 | "faucet": "^0.0.1", 56 | "format-stack": "4.1.0", 57 | "glob": "^4.3.1", 58 | "istanbul": "^0.3.5", 59 | "itape": "^1.5.0", 60 | "jshint": "^2.9.2", 61 | "leaked-handles": "^5.1.0", 62 | "opn": "^1.0.1", 63 | "pre-commit": "^0.0.9", 64 | "tape": "^3.0.3", 65 | "tape-cluster": "2.1.0", 66 | "tchannel": "^3.6.13", 67 | "tcurl": "^4.11.1", 68 | "timer-shim": "^0.3.0", 69 | "tmp": "0.0.31", 70 | "tryit": "^1.0.1", 71 | "uber-licence": "^2.1.3", 72 | "winston": "^1.0.1" 73 | }, 74 | "pre-commit": [ 75 | "check-licence", 76 | "test" 77 | ], 78 | "pre-commit.silent": true, 79 | "itape": { 80 | "trace": { 81 | "debuglog": [ 82 | "ringpop", 83 | "tchannel" 84 | ], 85 | "formatStack": true, 86 | "leakedHandles": { 87 | "fullStack": false 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /request_response.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var util = require('util'); 24 | 25 | function ProtocolRequest(ringpop) { 26 | var localMember = ringpop.membership.localMember || {}; 27 | this.sourceAddr = localMember.address; 28 | this.sourceIncarnationNumber = localMember.incarnationNumber; 29 | this.sourceChecksum = ringpop.membership.checksum; 30 | } 31 | 32 | function ProtocolResponse(/*ringpop*/) { 33 | } 34 | 35 | function DampReqRequest(ringpop, flappers) { 36 | ProtocolRequest.call(this, ringpop); 37 | this.flappers = flappers; // An Array of member's addresses (IDs) 38 | } 39 | 40 | util.inherits(DampReqRequest, ProtocolRequest); 41 | 42 | function DampReqResponse(ringpop, req, scores) { 43 | ProtocolResponse.call(this, ringpop, req); 44 | this.scores = scores; // An Array of MemberDampScore 45 | } 46 | 47 | util.inherits(DampReqResponse, ProtocolResponse); 48 | 49 | function validateRequest(req, otherProps, callback) { 50 | if (!req) { 51 | callback(new Error('Bad request: body is required')); 52 | return false; 53 | } 54 | 55 | var props = ['sourceAddr', 'sourceIncarnationNumber', 56 | 'sourceChecksum'].concat(otherProps); 57 | 58 | for (var i = 0; i < props.length; i++) { 59 | var prop = props[i]; 60 | if (!req[prop]) { 61 | callback(new Error('Bad request: ' + prop + ' is required')); 62 | return false; 63 | } 64 | } 65 | 66 | return true; 67 | } 68 | 69 | module.exports = { 70 | DampReqRequest: DampReqRequest, 71 | DampReqResponse: DampReqResponse, 72 | validateRequest: validateRequest 73 | }; 74 | -------------------------------------------------------------------------------- /ringpop_errors.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var TypedError = require('error/typed'); 24 | 25 | module.exports = { 26 | PotentiallyWedgedRequestsError: TypedError({ 27 | type: 'ringpop.client.wedged-requests', 28 | message: 'Ringpop has timed-out protocol requests that were expected ' + 29 | 'to be timed-out by TChannel. This may be an indication that TChannel ' + 30 | 'has become wedged. Our tests have shown that Ringpop continues ' + 31 | 'to function while in this state, but we advise vigilance and continued ' + 32 | 'monitoring of potentially worsening conditions. You may even want to ' + 33 | 'kill this process and have it restart.' 34 | }) 35 | }; 36 | -------------------------------------------------------------------------------- /server/admin/config.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createConfigGetHandler(ringpop) { 24 | return function handleConfigGet(arg2, arg3, hostInfo, callback) { 25 | var body = arg3; 26 | 27 | if (!body || !Array.isArray(body)) { 28 | callback(null, null, ringpop.config.getAll()); 29 | return; 30 | } 31 | 32 | var configs = body.reduce(function each(memo, key) { 33 | memo[key] = ringpop.config.get(key); 34 | return memo; 35 | }, {}); 36 | 37 | callback(null, null, configs); 38 | }; 39 | } 40 | 41 | function createConfigSetHandler(ringpop) { 42 | return function handleConfigSet(arg2, arg3, hostInfo, callback) { 43 | var body = arg3; 44 | 45 | if (!body) { 46 | callback(new Error('body is required')); 47 | return; 48 | } 49 | 50 | if (typeof body !== 'object' || body === null) { 51 | callback(new Error('body must be an object')); 52 | return; 53 | } 54 | 55 | // Set all configs 56 | Object.keys(body).forEach(function each(key) { 57 | ringpop.config.set(key, body[key]); 58 | }); 59 | 60 | callback(); 61 | }; 62 | } 63 | 64 | module.exports = { 65 | configGet: { 66 | endpoint: '/admin/config/get', 67 | handler: createConfigGetHandler 68 | }, 69 | configSet: { 70 | endpoint: '/admin/config/set', 71 | handler: createConfigSetHandler 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /server/admin/debug-clear.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createDebugClearHandler(ringpop) { 24 | return function handleDebugClear(arg1, arg2, hostInfo, callback) { 25 | ringpop.config.set('gossipLogLevel', 'off'); 26 | callback(null, null, 'ok'); 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /server/admin/debug-set.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createDebugSetHandler(ringpop) { 24 | return function handleDebugSet(arg1, arg2, hostInfo, callback) { 25 | ringpop.config.set('gossipLogLevel', 'info'); 26 | callback(null, null, 'ok'); 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /server/admin/gossip.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createGossipStartHandler(ringpop) { 24 | return function handleGossipStart(arg1, arg2, hostInfo, callback) { 25 | ringpop.gossip.start(); 26 | callback(null, null, 'ok'); 27 | }; 28 | } 29 | 30 | function createGossipStopHandler(ringpop) { 31 | return function handleGossipStop(arg1, arg2, hostInfo, callback) { 32 | ringpop.gossip.stop(); 33 | callback(null, null, 'ok'); 34 | }; 35 | } 36 | 37 | function createGossipTickHandler(ringpop) { 38 | return function handleGossipTick(arg1, arg2, hostInfo, callback) { 39 | ringpop.gossip.tick(function onPing(err) { 40 | if (err) { 41 | callback(err); 42 | return; 43 | } 44 | 45 | callback(null, null, { 46 | checksum: ringpop.membership.checksum 47 | }); 48 | }); 49 | }; 50 | } 51 | 52 | function createGossipStatusHandler(ringpop) { 53 | return function handleGossipStatus(arg1, arg2, hostInfo, callback) { 54 | callback(null, null, { 55 | status: ringpop.gossip.getStatus() 56 | }); 57 | }; 58 | } 59 | 60 | module.exports = { 61 | gossipStart: { 62 | endpoint: '/admin/gossip/start', 63 | handler: createGossipStartHandler 64 | }, 65 | gossipStop: { 66 | endpoint: '/admin/gossip/stop', 67 | handler: createGossipStopHandler 68 | }, 69 | gossipTick: { 70 | endpoint: '/admin/gossip/tick', 71 | handler: createGossipTickHandler 72 | }, 73 | gossipStatus: { 74 | endpoint: '/admin/gossip/status', 75 | handler: createGossipStatusHandler 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /server/admin/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var _ = require('underscore'); 24 | 25 | var baseEndpointHandlers = { 26 | debugClear: { 27 | endpoint: '/admin/debugClear', 28 | handler: require('./debug-clear.js') 29 | }, 30 | debugSet: { 31 | endpoint: '/admin/debugSet', 32 | handler: require('./debug-set.js') 33 | }, 34 | // Deprecated! Use /admin/gossip/start 35 | gossip: { 36 | endpoint: '/admin/gossip', 37 | handler: require('./gossip.js').gossipStart.handler 38 | }, 39 | // Deprecated! Use /admin/member/join 40 | join: { 41 | endpoint: '/admin/join', 42 | handler: require('./member.js').memberJoin.handler 43 | }, 44 | // Deprecated! Use /admin/member/leave 45 | leave: { 46 | endpoint: '/admin/leave', 47 | handler: require('./member.js').memberLeave.handler 48 | }, 49 | lookup: { 50 | endpoint: '/admin/lookup', 51 | handler: require('./lookup.js') 52 | }, 53 | // Deprecated! 54 | reload: { 55 | endpoint: '/admin/reload', 56 | handler: require('./reload.js') 57 | }, 58 | stats: { 59 | endpoint: '/admin/stats', 60 | handler: require('./stats.js') 61 | }, 62 | // Deprecated! Use /admin/gossip/tick. 63 | tick: { 64 | endpoint: '/admin/tick', 65 | handler: require('./gossip.js').gossipTick.handler 66 | } 67 | }; 68 | 69 | module.exports = _.extend({}, baseEndpointHandlers, require('./config.js'), 70 | require('./gossip.js'), require('./member.js'), require('./partition-healing.js')); 71 | -------------------------------------------------------------------------------- /server/admin/lookup.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var errors = require('../../lib/errors.js'); 24 | 25 | module.exports = function createLookupHandler(ringpop) { 26 | return function handleLookup(arg1, arg2, hostInfo, callback) { 27 | var body = arg2; 28 | if (body && body.key) { 29 | return callback(null, null, { 30 | dest: ringpop.lookup(body.key) 31 | }); 32 | } else { 33 | return callback(errors.ArgumentRequiredError({ argument: 'key' })); 34 | } 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /server/admin/partition-healing.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | function createHealPartitionViaDiscoverProviderHandler(ringpop) { 24 | return function handleHealViaDiscoverProvider(arg1, arg2, hostInfo, callback) { 25 | ringpop.healer.heal(function onHeal(err, targets) { 26 | if (err) { 27 | callback(err); 28 | return; 29 | } 30 | callback(null, null, {targets: targets}); 31 | }); 32 | }; 33 | } 34 | 35 | module.exports = { 36 | healViaDiscoverProvider: { 37 | endpoint: '/admin/healpartition/disco', 38 | handler: createHealPartitionViaDiscoverProviderHandler 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /server/admin/reload.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createReloadHandler(ringpop) { 24 | return function handleReload(arg1, arg2, hostInfo, callback) { 25 | ringpop.logger.warn('ringpop endpoint is deprecated: /admin/reload', { 26 | local: ringpop.whoami(), 27 | hostInfo: hostInfo 28 | }); 29 | var body = arg2; 30 | if (body && body.file) { 31 | ringpop.reload(body.file, function(err) { 32 | callback(err); 33 | }); 34 | } 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /server/admin/stats.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createStatsHandler(ringpop) { 24 | return function handleStats(arg1, arg2, hostInfo, callback) { 25 | callback(null, null, ringpop.getStats()); 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /server/health.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createHealthHandler(/*ringpop*/) { 24 | return function handleHealth(arg1, arg2, hostInfo, callback) { 25 | callback(null, null, 'ok'); 26 | }; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var middleware = require('../lib/middleware'); 24 | 25 | function RingpopServer(ringpop, tchannel, middlewares) { 26 | var self = this; 27 | self.ringpop = ringpop; 28 | self.tchannel = tchannel; 29 | // mind the order: request is applied top-down, response in reverse 30 | self.middlewareStack = new middleware.MiddlewareStack(middlewares || []); 31 | 32 | registerEndpointHandlers(require('./admin')); 33 | registerEndpointHandlers(require('./protocol')); 34 | registerEndpointHandlers(require('./trace')); 35 | 36 | // Register stragglers ;) 37 | var createProxyReqHandler = require('./proxy-req.js'); 38 | registerEndpoint('/proxy/req', createProxyReqHandler(self.ringpop)); 39 | 40 | var createHealthHandler = require('./health.js'); 41 | registerEndpoint('/health', createHealthHandler()); 42 | 43 | function registerEndpointHandlers(endpointHandlers) { 44 | Object.keys(endpointHandlers).forEach(function each(key) { 45 | var endpointHandler = endpointHandlers[key]; 46 | registerEndpoint(endpointHandler.endpoint, 47 | endpointHandler.handler(ringpop)); 48 | }); 49 | } 50 | 51 | // Wraps endpoint handler so that it doesn't have to 52 | // know TChannel req/res API. 53 | function registerEndpoint(url, handler) { 54 | tchannel.register(url, function (req, res, arg2, arg3) { 55 | 56 | self.middlewareStack.run(req, arg2, arg3, 57 | function(req, arg2, arg3, callback) { 58 | handler(arg2, arg3, req.remoteAddr, callback); 59 | }, 60 | function(req, err, res1, res2) { 61 | res.headers.as = 'raw'; 62 | if (err) { 63 | res.sendNotOk(null, JSON.stringify(err)); 64 | } else { 65 | if (res2 && !Buffer.isBuffer(res2)) { 66 | res2 = new Buffer(res2); 67 | } 68 | 69 | res.sendOk(res1, res2); 70 | } 71 | }); 72 | 73 | }); 74 | } 75 | } 76 | 77 | module.exports = RingpopServer; 78 | -------------------------------------------------------------------------------- /server/protocol/damp_req.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var DampReqResponse = require('../../request_response.js').DampReqResponse; 24 | var TypedError = require('error/typed'); 25 | 26 | var BadRequestError = TypedError({ 27 | type: 'overrideme', 28 | message: 'Bad request: {reason}', 29 | reason: null 30 | }); 31 | 32 | module.exports = function createDampReqHandler(ringpop) { 33 | return function handleDampReq(arg2, arg3, hostInfo, callback) { 34 | ringpop.stat('increment', 'damp-req.recv'); 35 | 36 | var body = arg3; 37 | if (!body || !body.flappers) { 38 | callback(BadRequestError({ 39 | type: 'ringpop.server.damp-req.bad-request.flappers-required', 40 | reason: 'flappers is required' 41 | })); 42 | return; 43 | } 44 | 45 | var flappers = body.flappers; 46 | if (!Array.isArray(flappers)) { 47 | callback(BadRequestError({ 48 | type: 'ringpop.server.damp-req.bad-request.flappers-array', 49 | reason: 'flappers must be an array' 50 | })); 51 | return; 52 | } 53 | 54 | var dampScores = ringpop.membership.collectDampScores(flappers); 55 | var response = new DampReqResponse(ringpop, body, dampScores); 56 | callback(null, null, response); 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /server/protocol/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = { 24 | dampReq: { 25 | endpoint: '/protocol/damp-req', 26 | handler: require('./damp_req.js') 27 | }, 28 | join: { 29 | endpoint: '/protocol/join', 30 | handler: require('./join.js') 31 | }, 32 | ping: { 33 | endpoint: '/protocol/ping', 34 | handler: require('./ping.js') 35 | }, 36 | pingReq: { 37 | endpoint: '/protocol/ping-req', 38 | handler: require('./ping-req.js') 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /server/protocol/ping-req.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var errors = require('../../lib/errors.js'); 24 | var sendPing = require('../../lib/gossip/ping-sender.js').sendPing; 25 | 26 | module.exports = function createPingReqHandler(ringpop) { 27 | var gossipLogger = ringpop.loggerFactory.getLogger('gossip'); 28 | 29 | return function handlePingReq(arg1, arg2, hostInfo, callback) { 30 | ringpop.stat('increment', 'ping-req.recv'); 31 | 32 | if (!ringpop.isReady) { 33 | ringpop.stat('increment', 'not-ready.ping-req'); 34 | callback(new errors.RingpopIsNotReadyError()); 35 | return; 36 | } 37 | 38 | var body = arg2; 39 | 40 | // NOTE sourceIncarnationNumber is an optional argument. It was not present 41 | // until after the v9.8.12 release. 42 | if (body === null || !body.source || !body.target || !body.changes || !body.checksum) { 43 | return callback(new Error('need req body with source, target, changes, and checksum')); 44 | } 45 | 46 | var source = body.source; 47 | var sourceIncarnationNumber = body.sourceIncarnationNumber; 48 | var target = body.target; 49 | var changes = body.changes; 50 | var checksum = body.checksum; 51 | 52 | ringpop.serverRate.mark(); 53 | ringpop.totalRate.mark(); 54 | ringpop.membership.update(changes); 55 | 56 | gossipLogger.info('ringpop ping-req ping send', { 57 | local: ringpop.whoami(), 58 | source: source, 59 | target: target 60 | }); 61 | 62 | var start = new Date(); 63 | sendPing({ 64 | ringpop: ringpop, 65 | target: target 66 | }, function (err) { 67 | var isOk = !!!err; 68 | 69 | ringpop.stat('timing', 'ping-req-ping', start); 70 | gossipLogger.info('ringpop ping-req ping response', { 71 | local: ringpop.whoami(), 72 | source: source, 73 | target: target, 74 | isOk: isOk 75 | }); 76 | 77 | callback(null, null, { 78 | changes: ringpop.dissemination.issueAsReceiver(source, 79 | sourceIncarnationNumber, checksum).changes, 80 | pingStatus: isOk, 81 | target: target 82 | }); 83 | }); 84 | }; 85 | }; 86 | -------------------------------------------------------------------------------- /server/protocol/ping.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var errors = require('../../lib/errors.js'); 24 | 25 | module.exports = function createPingHandler(ringpop) { 26 | return function handlePing(arg1, arg2, hostInfo, callback) { 27 | ringpop.stat('increment', 'ping.recv'); 28 | 29 | if (!ringpop.isReady) { 30 | ringpop.stat('increment', 'not-ready.ping'); 31 | callback(new errors.RingpopIsNotReadyError()); 32 | return; 33 | } 34 | 35 | var body = arg2; 36 | 37 | // NOTE sourceIncarnationNumber is an optional argument. It was not present 38 | // until after the v9.8.12 release. 39 | if (body === null || !body.source || !body.changes || !body.checksum) { 40 | return callback(new Error('need req body with source, changes, and checksum')); 41 | } 42 | 43 | if (body.hasOwnProperty('app')) { 44 | if (body.app !== ringpop.app) { 45 | ringpop.logger.warn('Rejected ping from wrong ringpop app', body); 46 | return callback(new Error('Pinged ringpop has a different app name')); 47 | } 48 | } else { 49 | if (ringpop.requiresAppInPing) { 50 | ringpop.logger.warn('Rejected ping from unknown ringpop app', body); 51 | return callback(new Error('Pinged ringpop requires app name')); 52 | } 53 | } 54 | 55 | var source = body.source; 56 | var sourceIncarnationNumber = body.sourceIncarnationNumber; 57 | var changes = body.changes; 58 | var checksum = body.checksum; 59 | 60 | ringpop.serverRate.mark(); 61 | ringpop.totalRate.mark(); 62 | 63 | ringpop.membership.update(changes); 64 | 65 | var res = ringpop.dissemination.issueAsReceiver(source, 66 | sourceIncarnationNumber, checksum); 67 | 68 | if (res.fullSync) { 69 | ringpop.dissemination.tryStartReverseFullSync(source, ringpop.maxJoinDuration); 70 | } 71 | callback(null, null, { 72 | changes: res.changes 73 | }); 74 | }; 75 | }; 76 | -------------------------------------------------------------------------------- /server/proxy-req.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = function createProxyReqHandler(ringpop) { 24 | return function handleProxyReq(arg1, arg2, hostInfo, callback) { 25 | var header = arg1; 26 | if (header === null) { 27 | return callback(new Error('need header to exist')); 28 | } 29 | 30 | var body = arg2; 31 | ringpop.requestProxy.handleRequest(header, body, callback); 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /server/trace.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var errors = require('../lib/errors'); 24 | var toCamelCase = require('../lib/util').toCamelCase; 25 | var traceCore = require('../lib/trace/core'); 26 | 27 | module.exports = { 28 | addTrace: { 29 | endpoint: '/trace/add', 30 | handler: createAddTrace, 31 | }, 32 | removeTrace: { 33 | endpoint: '/trace/remove', 34 | handler: createRemoveTrace, 35 | }, 36 | }; 37 | 38 | function createAddTrace(ringpop) { 39 | return function addTrace(head, body, peerInfo, cb) { 40 | _updateTracers(ringpop, 'add', body, cb); 41 | }; 42 | } 43 | 44 | function createRemoveTrace(ringpop) { 45 | return function removeTrace(head, body, peerInfo, cb) { 46 | _updateTracers(ringpop, 'remove', body, cb); 47 | }; 48 | } 49 | 50 | function _updateTracers(ringpop, fnStr, optsStr, cb) { 51 | var opts = toCamelCase(optsStr); 52 | var config = traceCore.resolveEventConfig(ringpop, opts.event); 53 | if (!config) { 54 | var err = errors.InvalidOptionError({ option: 'event', reason: 'it is unknown'}); 55 | ringpop.logger.warn('ringpop: unknown Trace Event specified', { 56 | local: ringpop.whoami(), 57 | err: err 58 | }); 59 | cb(err); 60 | return; 61 | } else { 62 | ringpop.tracers[fnStr](config, opts, cb); 63 | return; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | ringpop-common 2 | -------------------------------------------------------------------------------- /test/fixtures.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Member = require('../lib/membership/member.js'); 24 | 25 | function member1(ringpop, opts) { 26 | opts = opts || {}; 27 | return new Member(ringpop, { 28 | address: opts.address || '127.0.0.1:3001', 29 | incarnationNumber: opts.incarnationNumber || Date.now(), 30 | status: Member.Status.alive 31 | }); 32 | } 33 | 34 | function memberGenerator(ringpop) { 35 | var basePort = 3001; // assumes member with port 3000 is already in membership 36 | var counter = 0; 37 | return function genMember() { 38 | return new Member(ringpop, { 39 | address: '127.0.0.1:' + (basePort + counter++), 40 | incarnationNumber: Date.now() 41 | }); 42 | }; 43 | } 44 | 45 | module.exports = { 46 | member1: member1, 47 | memberGenerator: memberGenerator 48 | }; 49 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | require('glob').sync(__dirname + '/{unit,integration}/**/*{_test,-test}.js').forEach(require); 22 | -------------------------------------------------------------------------------- /test/integration/admin_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var async = require('async'); 24 | var RingpopClient = require('../../client.js'); 25 | var testRingpopCluster = require('../lib/test-ringpop-cluster.js'); 26 | 27 | testRingpopCluster({ 28 | size: 1, 29 | waitForConvergence: false 30 | }, 'config endpoints', function t(bootRes, cluster, assert) { 31 | assert.plan(4); 32 | 33 | var client = new RingpopClient(cluster[0]); 34 | async.series([ 35 | function configSetPart(callback) { 36 | client.adminConfigSet(cluster[0].whoami(), { 37 | testconfig1: 1 38 | }, function onSet(err) { 39 | assert.notok(err, 'no error occurred'); 40 | callback(); 41 | }); 42 | }, 43 | function configGetPart(callback) { 44 | client.adminConfigGet(cluster[0].whoami(), null, 45 | function onGet(err, config) { 46 | assert.notok(err, 'no error occurred'); 47 | assert.equals(config.testconfig1, 1, 'config was set and get'); 48 | callback(); 49 | }); 50 | } 51 | ], function onSeries(err) { 52 | assert.notok(err, 'no error occurred'); 53 | client.destroy(); 54 | }); 55 | }); 56 | 57 | // Test to make sure these endpoints are available. Find tests that test 58 | // behavior of handlers under unit tests. 59 | testRingpopCluster({ 60 | size: 1, 61 | waitForConvergence: false 62 | }, 'gossip endpoints', function t(bootRes, cluster, assert) { 63 | assert.plan(4); 64 | 65 | var client = new RingpopClient(cluster[0]); 66 | async.series([ 67 | adminGossip('adminGossipStart'), 68 | adminGossip('adminGossipStop'), 69 | adminGossip('adminGossipTick') 70 | ], function onSeries(err) { 71 | assert.notok(err, 'no error occurred'); 72 | client.destroy(); 73 | }); 74 | 75 | function adminGossip(method) { 76 | return function doIt(callback) { 77 | client[method](cluster[0].whoami(), function onMethod(err) { 78 | assert.notok(err, 'no error occurred'); 79 | callback(); 80 | }); 81 | }; 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /test/integration/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | require('./admin_test.js'); 24 | require('./join-test.js'); 25 | require('./proxy_test.js'); 26 | require('./ring-test.js'); 27 | require('./gossip_test.js'); 28 | require('./tchannel-proxy-test.js'); 29 | require('./not-ready-test.js'); 30 | require('./piggyback-test.js'); 31 | require('./discover-provider-retry-test.js'); 32 | -------------------------------------------------------------------------------- /test/integration/piggyback-test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var testRingpopCluster = require('../lib/test-ringpop-cluster.js'); 24 | 25 | testRingpopCluster({ 26 | size: 2, 27 | waitForConvergence: false, 28 | autoGossip: false 29 | }, 'raise piggyback counter', function t(bootRes, cluster, assert) { 30 | assert.plan(1); 31 | 32 | var pingSender = cluster[0]; 33 | pingSender.gossip.tick(function() { 34 | var changes = pingSender.dissemination.changes; 35 | var joinChange = changes[pingSender.whoami()]; 36 | var piggybackCount = joinChange.piggybackCount; 37 | assert.equals(piggybackCount, 1, 'piggyback counter raised by one'); 38 | assert.end(); 39 | }); 40 | }); 41 | 42 | function mkBadPingResponder(ringpop) { 43 | ringpop.channel.register('/protocol/ping', function protocolPing(req, res) { 44 | res.headers.as = 'raw'; 45 | res.sendNotOk(null, JSON.stringify('ping failed on purpose')); 46 | }); 47 | } 48 | 49 | testRingpopCluster({ 50 | size: 2, 51 | waitForConvergence: false, 52 | autoGossip: false, 53 | tap: function tap(cluster) { 54 | mkBadPingResponder(cluster[1]); 55 | }, 56 | }, 'don\'t raise piggyback counter when ping fails', function t(bootRes, cluster, assert) { 57 | assert.plan(1); 58 | 59 | var pingSender = cluster[0]; 60 | pingSender.gossip.tick(function() { 61 | var changes = pingSender.dissemination.changes; 62 | var joinChange = changes[pingSender.whoami()]; 63 | var piggybackCount = joinChange.piggybackCount; 64 | assert.equals(piggybackCount, 0, 'piggyback counter not raised'); 65 | assert.end(); 66 | }); 67 | }); -------------------------------------------------------------------------------- /test/integration/ring-test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var test = require('tape'); 24 | 25 | var allocCluster = require('../lib/alloc-cluster.js'); 26 | 27 | test('lookup yourself returns yourself', function t(assert) { 28 | var cluster = allocCluster(function onReady() { 29 | var keys = cluster.keys; 30 | 31 | assert.equal(cluster.one.lookup(keys.one), 32 | cluster.one.whoami()); 33 | assert.equal(cluster.two.lookup(keys.two), 34 | cluster.two.whoami()); 35 | assert.equal(cluster.three.lookup(keys.three), 36 | cluster.three.whoami()); 37 | 38 | cluster.destroy(); 39 | assert.end(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/lib/alloc-request.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var hammock = require('hammock'); 24 | 25 | module.exports = allocRequest; 26 | 27 | function allocRequest(opts) { 28 | var req = hammock.Request(opts); 29 | 30 | if ('json' in opts && opts.json !== true) { 31 | req.headers['content-type'] = 'application/json'; 32 | } 33 | 34 | if ('json' in opts && opts.json !== true) { 35 | req.write(JSON.stringify(opts.json)); 36 | req.end(); 37 | } else { 38 | req.end(); 39 | } 40 | 41 | return req; 42 | } 43 | -------------------------------------------------------------------------------- /test/lib/alloc-response.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var hammock = require('hammock'); 24 | var tryIt = require('tryit'); 25 | 26 | module.exports = allocResponse; 27 | 28 | function allocResponse(opts, cb) { 29 | return hammock.Response(onResponse); 30 | 31 | function onResponse(err, resp) { 32 | if (err) { 33 | return cb(err); 34 | } 35 | 36 | if ('json' in opts) { 37 | tryIt(function parse() { 38 | resp.body = JSON.parse(resp.body); 39 | }); 40 | } 41 | 42 | cb(null, resp); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/lib/alloc-ringpop.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var TChannel = require('tchannel'); 24 | 25 | var makeTimersMock = require('../lib/timers-mock'); 26 | 27 | var globalTimers = { 28 | setTimeout: require('timers').setTimeout, 29 | clearTimeout: require('timers').clearTimeout, 30 | now: Date.now 31 | }; 32 | var DebuglogLogger = require('debug-logtron'); 33 | var RingPop = require('../../index.js'); 34 | 35 | module.exports = allocRingpop; 36 | 37 | function allocRingpop(name, options) { 38 | options = options || {}; 39 | 40 | var host = '127.0.0.1'; 41 | var port = semiRandPort(); 42 | 43 | var tchannel = TChannel({ 44 | timers: (options && options.timers) || globalTimers, 45 | logger: DebuglogLogger((name && name + 'tchannel') || 'tchannel') 46 | }); 47 | 48 | var timers = makeTimersMock(); 49 | var ringpop = new RingPop({ 50 | app: 'test.ringpop.proxy_req_test', 51 | hostPort: host + ':' + String(port), 52 | channel: tchannel.makeSubChannel({ 53 | serviceName: 'ringpop', 54 | trace: false 55 | }), 56 | logger: DebuglogLogger(name || 'ringpop'), 57 | requestProxyMaxRetries: options.requestProxyMaxRetries, 58 | requestProxyRetrySchedule: options.requestProxyRetrySchedule, 59 | enforceConsistency: options.enforceConsistency, 60 | setTimeout: options.useFakeTimers && timers.setTimeout 61 | }); 62 | 63 | ringpop.setupChannel(); 64 | ringpop.timers = timers; 65 | return ringpop; 66 | } 67 | 68 | function semiRandPort() { 69 | var base = 10000; 70 | var rand = Math.floor(Math.random() * 10000); 71 | 72 | return base + rand; 73 | } 74 | -------------------------------------------------------------------------------- /test/lib/bootstrap.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | module.exports = bootstrap; 24 | 25 | function bootstrap(ringpops, done) { 26 | var counter = ringpops.length; 27 | 28 | var hosts = ringpops.map(function who(r) { 29 | return r.whoami(); 30 | }).sort(); 31 | 32 | ringpops.forEach(function boot(r) { 33 | var hostPortParts = r.hostPort.split(':'); 34 | var host = hostPortParts[0]; 35 | var port = Number(hostPortParts[1]); 36 | 37 | r.channel.topChannel.on('listening', function onListening() { 38 | r.channel.topChannel.removeListener('listening', onListening); 39 | r.once('ready', onReady); 40 | r.bootstrap(hosts.slice()); 41 | }); 42 | 43 | r.channel.topChannel.listen(port, host); 44 | }); 45 | 46 | function onReady() { 47 | if (--counter === 0) { 48 | done(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/lib/int-comparator.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | function intComparator(a, b) { 22 | return a-b; 23 | } 24 | 25 | module.exports = intComparator; 26 | -------------------------------------------------------------------------------- /test/lib/test-ringpop.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Ringpop = require('../../index.js'); 24 | var tape = require('tape'); 25 | 26 | function testRingpop(opts, name, test) { 27 | if (typeof opts === 'string' && typeof name === 'function') { 28 | test = name; 29 | name = opts; 30 | opts = {}; 31 | } 32 | 33 | tape(name, function onTest(assert) { 34 | var ringpop = new Ringpop({ 35 | app: opts.app || 'test', 36 | hostPort: opts.hostPort || '127.0.0.1:3000' 37 | }); 38 | 39 | ringpop.isReady = true; 40 | 41 | ringpop.membership.makeLocalAlive(); 42 | 43 | // These are made top-level dependencies as a mere 44 | // convenience to users of the test suite. 45 | var deps = { 46 | config: ringpop.config, 47 | damper: ringpop.damper, 48 | dissemination: ringpop.dissemination, 49 | gossip: ringpop.gossip, 50 | iterator: ringpop.memberIterator, 51 | localMember: ringpop.membership.localMember, 52 | loggerFactory: ringpop.loggerFactory, 53 | membership: ringpop.membership, 54 | requestProxy: ringpop.requestProxy, 55 | ringpop: ringpop, 56 | rollup: ringpop.membershipUpdateRollup, 57 | stateTransitions: ringpop.stateTransitions 58 | }; 59 | 60 | if (opts.async) { 61 | test(deps, assert, cleanup); 62 | } else { 63 | test(deps, assert); 64 | cleanup(); 65 | } 66 | 67 | function cleanup() { 68 | assert.end(); 69 | ringpop.destroy(); 70 | } 71 | }); 72 | } 73 | 74 | module.exports = testRingpop; 75 | -------------------------------------------------------------------------------- /test/lib/timers-mock.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var timerShim = require('timer-shim'); 24 | 25 | // make a simple wrapper to timerShim which is mostly better than time-mock, 26 | // but lacks its .now function. This has everything that require('timers') 27 | // provides, PLUS .now(). Would be better to add .now to timer-shim, but 28 | // that's for another moment. 29 | function makeTimersMock(start) { 30 | start = start || 0; 31 | 32 | var t = new timerShim.Timer(); 33 | t.pause(); 34 | var cur = start; 35 | if (start) { 36 | t.wind(start); 37 | } 38 | return { 39 | setTimeout: t.setTimeout.bind(t), 40 | clearTimeout: t.clearTimeout.bind(t), 41 | setInterval: t.setInterval.bind(t), 42 | clearInterval: t.clearInterval.bind(t), 43 | now: function() { return cur; }, 44 | advance: function(s) { 45 | cur += s; 46 | t.wind(s); 47 | } 48 | } 49 | } 50 | 51 | module.exports = makeTimersMock; 52 | -------------------------------------------------------------------------------- /test/lib/wait-for-convergence.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-node/ringpop-node/c36ee261a33c66c04c816afb9b69dde192837324/test/lib/wait-for-convergence.js -------------------------------------------------------------------------------- /test/mock/channel.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | module.exports = { 22 | waitForIdentified: function (opts, cb) { 23 | cb(null); 24 | }, 25 | request: function(/* options */) { 26 | return { 27 | send: function(url, head, body, cb) { 28 | cb(); 29 | } 30 | }; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /test/mock/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | module.exports = { 22 | channel: require('./channel.js'), 23 | logger: require('./logger.js'), 24 | membership: require('./membership.js'), 25 | noop: require('./noop.js'), 26 | requestProxy: require('./request-proxy.js'), 27 | tchannel: require('./tchannel.js') 28 | }; 29 | -------------------------------------------------------------------------------- /test/mock/logger.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | function noop() {} 22 | 23 | module.exports = { 24 | trace: noop, 25 | debug: noop, 26 | info: noop, 27 | warn: noop, 28 | error: noop, 29 | fatal: noop 30 | }; 31 | -------------------------------------------------------------------------------- /test/mock/membership.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | function noop() {} 22 | 23 | module.exports = { 24 | localMember: { 25 | address: '127.0.0.1:3000', 26 | incarnationNumber: 123456789 27 | }, 28 | remoteMember: { 29 | address: '127.0.0.1:3001', 30 | incarnationNumber: 123456789 31 | }, 32 | remoteMember2: { 33 | address: '127.0.0.1:3002', 34 | incarnationNumber: 123456789 35 | }, 36 | makeFaulty: noop, 37 | shuffle: noop, 38 | update: noop 39 | }; 40 | -------------------------------------------------------------------------------- /test/mock/noop.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | module.exports = function() {}; 22 | -------------------------------------------------------------------------------- /test/mock/request-proxy.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var noop = require('./noop.js'); 24 | 25 | module.exports = { 26 | destroy: noop, 27 | proxyReq: noop 28 | }; 29 | -------------------------------------------------------------------------------- /test/mock/tchannel.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var noop = require('./noop.js'); 24 | 25 | module.exports = { 26 | waitForIdentified: function (opts, callback) { 27 | callback(null) 28 | }, 29 | quit: noop, 30 | close: noop, 31 | request: function request(/* options */) { 32 | return { 33 | send: function(url, head, body, cb) { 34 | cb(null, { 35 | ok: true 36 | }); 37 | } 38 | }; 39 | }, 40 | register: function register() {} 41 | }; 42 | -------------------------------------------------------------------------------- /test/travis: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run Travis CI scripts from an environment variable. 4 | # 5 | # This script evaluates and executes the command string from the RUN 6 | # environment variable. 7 | # 8 | # It allows you to execute different commands in a test matrix on Travis CI, 9 | # based on env vars in your .travis.yml. 10 | # 11 | declare RUN_DEFAULT="" 12 | 13 | # Perform default action if none specified 14 | if [ -z "$RUN" ]; then 15 | RUN=$RUN_DEFAULT 16 | fi 17 | 18 | # Exit with error if there's nothing to do 19 | if [ -z "$RUN" ]; then 20 | echo "ERROR: No actions specified and no default actions. Exiting." >&2 21 | exit 1 22 | fi 23 | 24 | # Execute the command string; echo the commands as they run 25 | eval "set -x; $RUN" 26 | -------------------------------------------------------------------------------- /test/unit/config_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var Config = require('../../config.js'); 24 | var test = require('tape'); 25 | 26 | test('set and get interactions', function t(assert) { 27 | var config = new Config(); 28 | 29 | var configKey = 'testconfig'; 30 | var expectedVal = 100; 31 | config.set(configKey, expectedVal); 32 | 33 | assert.equals(expectedVal, config.get(configKey), 'set and get works'); 34 | assert.end(); 35 | }); 36 | 37 | test('changed events', function t(assert) { 38 | assert.plan(2); 39 | 40 | var config = new Config(); 41 | config.on('set', function onSet() { 42 | assert.pass('set'); 43 | }); 44 | var configKey = 'somekey'; 45 | config.on('set.' + configKey, function onKeySet() { 46 | assert.pass('key set'); 47 | }); 48 | var configVal = 'someval'; 49 | config.set(configKey, configVal); // this triggers change events *fingers crossed* 50 | 51 | assert.end(); 52 | }); 53 | 54 | test('seeds known config', function t(assert) { 55 | var knownKey = 'TEST_KEY'; 56 | var knownVal = 200; 57 | var unknownKey = 'unknown'; 58 | var unknownVal = 100; 59 | var seed = {}; 60 | seed[knownKey] = knownVal; 61 | seed[unknownKey] = unknownVal; 62 | 63 | var config = new Config(null, seed); 64 | 65 | assert.equals(knownVal, config.get(knownKey), 'known config is seeded'); 66 | assert.equals(undefined, config.get(unknownKey), 67 | 'unknown config is not seeded'); 68 | assert.end(); 69 | }); 70 | 71 | test('no seed is OK', function t(assert) { 72 | assert.doesNotThrow(function it() { 73 | /* jshint nonew: false */ 74 | new Config(null); 75 | }); 76 | assert.end(); 77 | }); 78 | 79 | test('validates memberBlacklist seed', function t(assert) { 80 | var config = new Config(null, { 81 | 'memberBlacklist': ['127.0.0.1:3000'] 82 | }); 83 | assert.deepEquals([], config.get('memberBlacklist'), 84 | 'uses default blacklist'); 85 | 86 | var regexList = [/127.0.0.1:*/]; 87 | config = new Config(null, { 88 | 'memberBlacklist': regexList 89 | }); 90 | assert.deepEquals(regexList, config.get('memberBlacklist'), 91 | 'uses seed blacklist'); 92 | assert.end(); 93 | }); 94 | 95 | test('validates num', function t(assert) { 96 | var config = new Config(null, { 97 | 'TEST_KEY': 'notanum' 98 | }); 99 | assert.equals(100, config.get('TEST_KEY'), 100 | 'uses default'); 101 | assert.end(); 102 | }); 103 | -------------------------------------------------------------------------------- /test/unit/discover-providers-example-invalid.json: -------------------------------------------------------------------------------- 1 | this is no json :) 2 | -------------------------------------------------------------------------------- /test/unit/discover-providers-example.json: -------------------------------------------------------------------------------- 1 | ["127.0.0.1:3000", "127.0.0.1:3001"] 2 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // You might ask, what belongs in this unit test directory? The answer: 22 | // anything test that strictly depends one and only one module. It does 23 | // not test the interactions of many modules nor does it require you 24 | // to stand up and bootstrap a single node or multi-node Ringpop cluster. 25 | 'use strict'; 26 | 27 | require('./config_test.js'); 28 | require('./member_test.js'); 29 | require('./membership_test.js'); 30 | require('./server/protocol/join_test.js'); 31 | -------------------------------------------------------------------------------- /test/unit/membership-changeset-merge-test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var mergeMembershipChangesets = require('../../lib/membership/merge.js'); 24 | var testRingpop = require('../lib/test-ringpop.js'); 25 | 26 | testRingpop('merges incarnation numbers', function t(deps, assert) { 27 | var changesets = [firstChangeset(), secondChangeset()]; 28 | 29 | assert.deepEqual(mergeMembershipChangesets(deps.ringpop, changesets), 30 | expectedChangeset(), 'updates are merged'); 31 | 32 | function createUpdate(address, status, incarnationNumber) { 33 | return { 34 | address: address, 35 | status: status, 36 | incarnationNumber: incarnationNumber 37 | }; 38 | } 39 | 40 | function firstChangeset() { 41 | return [ 42 | createUpdate('127.0.0.1:3001', 'suspect', 1), 43 | createUpdate('127.0.0.1:3002', 'alive', 2) 44 | ]; 45 | } 46 | 47 | function secondChangeset() { 48 | return [ 49 | createUpdate('127.0.0.1:3001', 'alive', 2), 50 | createUpdate('127.0.0.1:3002', 'suspect', 1), 51 | createUpdate('127.0.0.1:3003', 'faulty', 1) 52 | ]; 53 | } 54 | 55 | function expectedChangeset() { 56 | return [ 57 | createUpdate('127.0.0.1:3001', 'alive', 2), 58 | createUpdate('127.0.0.1:3002', 'alive', 2), 59 | createUpdate('127.0.0.1:3003', 'faulty', 1), 60 | ]; 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /test/unit/membership-iterator-test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var Member = require('../../lib/membership/member.js'); 22 | 23 | var testRingpop = require('../lib/test-ringpop.js'); 24 | 25 | testRingpop('iterates over two members correctly', function t(deps, assert) { 26 | var membership = deps.membership; 27 | var iterator = deps.iterator; 28 | 29 | membership.makeChange('127.0.0.1:3001', Date.now(), Member.Status.alive); 30 | membership.makeChange('127.0.0.1:3002', Date.now(), Member.Status.alive); 31 | 32 | var iterated = {}; 33 | iterated[iterator.next().address] = true; 34 | iterated[iterator.next().address] = true; 35 | 36 | assert.equals(Object.keys(iterated).length, 2, '2 members iterated over'); 37 | }); 38 | 39 | testRingpop('iterates over three members correctly', function t(deps, assert) { 40 | var membership = deps.membership; 41 | var iterator = deps.iterator; 42 | 43 | membership.makeChange('127.0.0.1:3001', Date.now(), Member.Status.alive); 44 | membership.makeChange('127.0.0.1:3002', Date.now(), Member.Status.alive); 45 | membership.makeChange('127.0.0.1:3003', Date.now(), Member.Status.alive); 46 | 47 | var iterated = {}; 48 | iterated[iterator.next().address] = true; 49 | iterated[iterator.next().address] = true; 50 | iterated[iterator.next().address] = true; 51 | 52 | assert.equals(Object.keys(iterated).length, 3, '3 members iterated over'); 53 | }); 54 | 55 | testRingpop('skips over faulty member and 1 local member', function t(deps, assert) { 56 | var membership = deps.membership; 57 | var iterator = deps.iterator; 58 | 59 | membership.makeChange('127.0.0.1:3001', Date.now(), Member.Status.alive); 60 | membership.makeFaulty('127.0.0.1:3002', Date.now()); 61 | membership.makeChange('127.0.0.1:3003', Date.now(), Member.Status.alive); 62 | 63 | var iterated = {}; 64 | iterated[iterator.next().address] = true; 65 | iterated[iterator.next().address] = true; 66 | iterated[iterator.next().address] = true; 67 | 68 | assert.equals(Object.keys(iterated).length, 2, '2 members iterated over'); 69 | assert.notok(iterated['127.0.0.1:3002'], 'faulty member not iterated over'); 70 | }); 71 | -------------------------------------------------------------------------------- /test/unit/membership-set-listener-test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var membershipEventListeners = require('../../lib/on_membership_event.js'); 24 | var testRingpop = require('../lib/test-ringpop.js'); 25 | 26 | testRingpop('starts state transition period for suspect', function t(deps, assert) { 27 | var ringpop = deps.ringpop; 28 | var stateTransitions = deps.stateTransitions; 29 | var suspect = { 30 | address: '127.0.0.1:3001', 31 | status: 'suspect', 32 | incarnationNumber: Date.now() 33 | }; 34 | 35 | var createSetHandler = membershipEventListeners.createSetHandler; 36 | var listener = createSetHandler(ringpop); 37 | listener([suspect]); 38 | assert.ok(stateTransitions.timers[suspect.address], 'state transition started'); 39 | 40 | stateTransitions.cancel(suspect); 41 | }); 42 | -------------------------------------------------------------------------------- /test/unit/proxy_req_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var _ = require('underscore'); 24 | var test = require('tape'); 25 | 26 | var allocRingpop = require('../lib/alloc-ringpop.js'); 27 | var bootstrap = require('../lib/bootstrap.js'); 28 | var mocks = require('../mock'); 29 | var Ringpop = require('../../index.js'); 30 | 31 | test('proxyReq() proxies the request', function t(assert) { 32 | var left = allocRingpop('left'); 33 | var right = allocRingpop('right'); 34 | 35 | bootstrap([left, right], function onReady() { 36 | var leftKey = left.whoami() + '0'; 37 | var rightKey = right.whoami() + '0'; 38 | 39 | var dest = left.lookup(leftKey); 40 | var dest2 = left.lookup(rightKey); 41 | 42 | assert.notEqual(dest, dest2, 'expected dests to be different'); 43 | 44 | left.destroy(); 45 | right.destroy(); 46 | assert.end(); 47 | }); 48 | }); 49 | 50 | test('proxyReq enforces required args', function t(assert) { 51 | var ringpop = new Ringpop({ 52 | app: 'test', 53 | hostPort: '127.0.0.1:3000' 54 | }); 55 | ringpop.requestProxy = mocks.requestProxy; 56 | 57 | function assertProxyReqThrows(opts) { 58 | try { 59 | ringpop.proxyReq(opts); 60 | assert.fail('no exception thrown'); 61 | } catch (e) { 62 | assert.pass('exception thrown'); 63 | return e; 64 | } 65 | } 66 | 67 | function assertPropertyRequiredError(property, opts, newOpts) { 68 | opts = _.extend(opts || {}, newOpts); 69 | 70 | var exception = assertProxyReqThrows(opts); 71 | assert.equals(exception.type, 'ringpop.options.property-required', 'err type is correct'); 72 | assert.equals(property, exception.property, 'err property is correct'); 73 | 74 | return opts; 75 | } 76 | 77 | var exception = assertProxyReqThrows(); 78 | assert.equals(exception.type, 'ringpop.options.required', 'err type is correct'); 79 | 80 | var opts = assertPropertyRequiredError('keys'); 81 | opts = assertPropertyRequiredError('dest', opts, { keys: ['KEY0'] }); 82 | opts = assertPropertyRequiredError('req', opts, { dest: '127.0.0.1:3000' }); 83 | opts = assertPropertyRequiredError('res', opts, { req: {} }); 84 | 85 | opts = _.extend(opts, { res: {} }); 86 | assert.doesNotThrow(function() { ringpop.proxyReq(opts); }); 87 | assert.end(); 88 | ringpop.destroy(); 89 | }); 90 | -------------------------------------------------------------------------------- /test/unit/ringnode_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | var RingNode = require('../../lib/ring/rbtree').RingNode; 22 | var test = require('tape'); 23 | 24 | test('construct a new red node with supplied values', function t(assert) { 25 | var val = 12345; 26 | var str = 'just in time'; 27 | var node = new RingNode(val, str); 28 | 29 | assert.strictEquals(node.key, val, 'key set to supplied key'); 30 | assert.strictEquals(node.value, str, 'value set to supplied value'); 31 | 32 | assert.strictEquals(node.left, null, 'left is null'); 33 | assert.strictEquals(node.right, null, 'right is null'); 34 | assert.strictEquals(node.red, true, 'color is red'); 35 | assert.end(); 36 | }); 37 | 38 | function makeTree() { 39 | var node1 = new RingNode(1, 'one'); 40 | var node2 = new RingNode(2, 'two'); 41 | var node3 = new RingNode(3, 'three'); 42 | 43 | // 2,B 44 | // / \ 45 | // 1,R 3,R 46 | 47 | node2.red = false; 48 | node2.left = node1; 49 | node2.right = node3; 50 | 51 | return [node1, node2, node3]; 52 | } 53 | 54 | test('getChild', function t(assert) { 55 | var nodes = makeTree(); 56 | 57 | assert.strictEquals(nodes[1].getChild(0), nodes[0], 'root getChild left returns left node'); 58 | assert.strictEquals(nodes[1].getChild(1), nodes[2], 'root getChild right returns right node'); 59 | assert.strictEquals(nodes[0].getChild(0), null, 'leaf 1 getChild left returns null'); 60 | assert.strictEquals(nodes[0].getChild(1), null, 'leaf 1 getChild right returns null'); 61 | assert.strictEquals(nodes[2].getChild(0), null, 'leaf 3 getChild left returns null'); 62 | assert.strictEquals(nodes[2].getChild(1), null, 'leaf 3 getChild right returns null'); 63 | 64 | assert.end(); 65 | }); 66 | 67 | test('setChild', function t(assert) { 68 | var nodes = makeTree(); 69 | nodes[3] = new RingNode(4, 'four'); 70 | 71 | nodes[2].setChild(1, nodes[3]); 72 | assert.strictEquals(nodes[2].right, nodes[3], 'setChild right returns new node'); 73 | 74 | nodes[2].setChild(0, nodes[3]); // not a valid tree, but still a valid test 75 | assert.strictEquals(nodes[2].left, nodes[3], 'setChild left returns new node'); 76 | 77 | assert.end(); 78 | }); 79 | -------------------------------------------------------------------------------- /test/unit/ringpop_alloc_error_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var test = require('tape'); 24 | 25 | var Ringpop = require('../../index.js'); 26 | 27 | test('ringpop without app throws', function t(assert) { 28 | assert.throws(function throwIt() { 29 | Ringpop({}); 30 | }, /Expected `options.app` to be a non-empty string/); 31 | 32 | assert.throws(function throwIt() { 33 | Ringpop({ 34 | app: '' 35 | }); 36 | }, /Expected `options.app` to be a non-empty string/); 37 | 38 | assert.end(); 39 | }); 40 | 41 | test('ringpop without options throws', function t(assert) { 42 | assert.throws(function throwIt() { 43 | Ringpop(); 44 | }, /Expected `options` argument to be passed/); 45 | 46 | assert.end(); 47 | }); 48 | 49 | test('ringpop with invalid hostPort', function t(assert) { 50 | assert.throws(function throwIt() { 51 | Ringpop({ 52 | app: 'foo' 53 | }); 54 | }, /Got\s/); 55 | 56 | assert.throws(function throwIt() { 57 | Ringpop({ 58 | app: 'oh lol silly me', 59 | hostPort: 'silly me' 60 | }); 61 | }, /Got silly me/); 62 | 63 | assert.doesNotThrow(function throwIt() { 64 | Ringpop({ 65 | app: 'foo', 66 | hostPort: 'localhost:4000' 67 | }).destroy(); 68 | }); 69 | 70 | assert.throws(function throwIt() { 71 | Ringpop({ 72 | app: 'foo', 73 | hostPort: 'localhost:not_a_port' 74 | }); 75 | }, /Got localhost:not_a_port/); 76 | 77 | assert.end(); 78 | }); 79 | -------------------------------------------------------------------------------- /test/unit/server/admin/config_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var configHandlers = require('../../../../server/admin/config.js'); 24 | var Ringpop = require('../../../../index.js'); 25 | var test = require('tape'); 26 | 27 | var createConfigGetHandler = configHandlers.configGet.handler; 28 | var createConfigSetHandler = configHandlers.configSet.handler; 29 | 30 | test('config set handler validation', function t(assert) { 31 | var ringpop = new Ringpop({ 32 | app: 'ringpop', 33 | hostPort: '127.0.0.1:3000' 34 | }); 35 | var handleConfigSet = createConfigSetHandler(ringpop); 36 | // Missing body 37 | handleConfigSet(null, null, null, function onHandle(err) { 38 | assert.ok(err, 'an error occurred'); 39 | }); 40 | // Invalid body 41 | handleConfigSet(null, 1, null, function onHandle(err) { 42 | assert.ok(err, 'an error occurred'); 43 | }); 44 | assert.end(); 45 | ringpop.destroy(); 46 | }); 47 | 48 | test('config get/set handlers', function t(assert) { 49 | var config = {}; 50 | config['testconfig1'] = 1; 51 | config['testconfig2'] = 2; 52 | var ringpop = new Ringpop({ 53 | app: 'ringpop', 54 | hostPort: '127.0.0.1:3000' 55 | }); 56 | 57 | var handleConfigSet = createConfigSetHandler(ringpop); 58 | // Test set entire config 59 | handleConfigSet(null, config, null, function onHandle(err) { 60 | assert.notok(err, 'an error did not occur'); 61 | assert.equals(ringpop.config.get('testconfig1'), 1, 62 | 'config was set'); 63 | assert.equals(ringpop.config.get('testconfig2'), 2, 64 | 'config was set'); 65 | }); 66 | 67 | var handleConfigGet = createConfigGetHandler(ringpop); 68 | // Test single config get 69 | handleConfigGet(null, ['testconfig1'], null, 70 | function onHandle(err, res1, res2) { 71 | assert.notok(err, 'an error did not occur'); 72 | assert.deepEquals(res2, { 73 | 'testconfig1': 1 74 | }, 'config was gotten'); 75 | }); 76 | 77 | // Test get all config 78 | handleConfigGet(null, null, null, function onHandle(err, res1, res2) { 79 | assert.notok(err, 'an error did not occur'); 80 | assert.deepEquals(res2, ringpop.config.getAll(), 81 | 'config was gotten'); 82 | }); 83 | assert.end(); 84 | ringpop.destroy(); 85 | }); 86 | -------------------------------------------------------------------------------- /test/unit/server/admin/gossip_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var gossipHandlers = require('../../../../server/admin/gossip.js'); 24 | var Ringpop = require('../../../../index.js'); 25 | var test = require('tape'); 26 | 27 | var createGossipStartHandler = gossipHandlers.gossipStart.handler; 28 | var createGossipStopHandler= gossipHandlers.gossipStop.handler; 29 | 30 | test('start/stop gossip', function t(assert) { 31 | var ringpop = new Ringpop({ 32 | app: 'ringpop', 33 | hostPort: '127.0.0.1:3000', 34 | autoGossip: false 35 | }); 36 | var handleGossipStart = createGossipStartHandler(ringpop); 37 | handleGossipStart(null, null, null, function onHandle(err) { 38 | assert.notok(err, 'no error occurred'); 39 | assert.false(ringpop.gossip.isStopped, 'gossip is started'); 40 | }); 41 | var handleGossipStop = createGossipStopHandler(ringpop); 42 | handleGossipStop(null, null, null, function onHandle(err) { 43 | assert.notok(err, 'no error occurred'); 44 | assert.true(ringpop.gossip.isStopped, 'gossip is started'); 45 | }); 46 | assert.end(); 47 | ringpop.destroy(); 48 | }); 49 | -------------------------------------------------------------------------------- /test/unit/server/admin/partition_healing_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var partitionHealingHandlers = require('../../../../server/admin/partition-healing'); 24 | var Ringpop = require('../../../../index.js'); 25 | var test = require('tape'); 26 | 27 | test('healing via discovery provider', function t(assert) { 28 | assert.plan(2); 29 | 30 | var ringpop = new Ringpop({ 31 | app: 'ringpop', 32 | hostPort: '127.0.0.1:3000' 33 | }); 34 | 35 | ringpop.healer.heal = function heal(cb) { 36 | assert.pass('endpoint calls heal'); 37 | cb(null); 38 | }; 39 | 40 | var handleHeal = partitionHealingHandlers.healViaDiscoverProvider.handler(ringpop); 41 | handleHeal(null, null, null, function onHandle(err) { 42 | assert.notok(err, 'no error occurred'); 43 | }); 44 | 45 | ringpop.destroy(); 46 | }); 47 | 48 | test('healing via discovery provider - error path', function t(assert) { 49 | assert.plan(2); 50 | 51 | var ringpop = new Ringpop({ 52 | app: 'ringpop', 53 | hostPort: '127.0.0.1:3000' 54 | }); 55 | 56 | ringpop.healer.heal = function heal(cb) { 57 | assert.pass('endpoint calls heal'); 58 | cb('error'); 59 | }; 60 | 61 | var handleHeal = partitionHealingHandlers.healViaDiscoverProvider.handler(ringpop); 62 | handleHeal(null, null, null, function onHandle(err) { 63 | assert.ok(err, 'error occurred'); 64 | }); 65 | 66 | ringpop.destroy(); 67 | }); 68 | -------------------------------------------------------------------------------- /test/unit/server/protocol/join_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var createProtocolJoinHandler = require('../../../../server/protocol/join.js'); 24 | var Ringpop = require('../../../../index.js'); 25 | var test = require('tape'); 26 | 27 | test('join fails with blacklist error', function t(assert) { 28 | var ringpop = new Ringpop({ 29 | app: 'ringpop', 30 | hostPort: '127.0.0.1:3000', 31 | memberBlacklist: [/127.0.0.1:*/] 32 | }); 33 | var handleProtocolJoin = createProtocolJoinHandler(ringpop); 34 | handleProtocolJoin(null, { 35 | app: 'ringpop', 36 | source: '127.0.0.1:3001', 37 | incarnationNumber: 1 38 | }, null, function onHandled(err) { 39 | assert.ok(err, 'an error occurred'); 40 | assert.equals(err.type, 'ringpop.invalid-join.blacklist', 41 | 'blacklist error occurred'); 42 | assert.end(); 43 | ringpop.destroy(); 44 | }); 45 | }); 46 | 47 | var badHostPorts = ['127.0.0.1.3000', '127.0.0.1:0:3000', '127.0.0.1.3000', '12.34.56.78:abc']; 48 | 49 | badHostPorts.forEach(function each(hostPort) { 50 | test('join fails with bad hostPort as source', function t(assert) { 51 | var ringpop = new Ringpop({ 52 | app: 'ringpop', 53 | hostPort: '127.0.0.1:3000' 54 | }); 55 | var handleProtocolJoin = createProtocolJoinHandler(ringpop); 56 | handleProtocolJoin(null, { 57 | app: 'ringpop', 58 | source: hostPort, 59 | incarnationNumber: 1 60 | }, null, function onHandled(err) { 61 | assert.ok(err, 'an error occurred'); 62 | assert.equals(err.type, 'ringpop.invalid-join.source'); 63 | assert.end(); 64 | ringpop.destroy(); 65 | }); 66 | }); 67 | }); 68 | 69 | var goodHostPorts = ['127.0.0.1:3001', '12.34.56.78:1234']; 70 | 71 | goodHostPorts.forEach(function each(hostPort) { 72 | test('join succeeds with correctly formatted hostPort as source', function t(assert) { 73 | var ringpop = new Ringpop({ 74 | app: 'ringpop', 75 | hostPort: '127.0.0.1:3000' 76 | }); 77 | var handleProtocolJoin = createProtocolJoinHandler(ringpop); 78 | handleProtocolJoin(null, { 79 | app: 'ringpop', 80 | source: hostPort, 81 | incarnationNumber: 1 82 | }, null, function onHandled(err) { 83 | assert.ifError(err, 'an error occurred'); 84 | assert.end(); 85 | ringpop.destroy(); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/unit/state_transistion_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 'use strict'; 22 | 23 | var test = require('tape'); 24 | 25 | var Member = require('../../lib/membership/member'); 26 | var Ringpop = require('../..'); 27 | 28 | function createRingpop() { 29 | var ringpop = new Ringpop({ 30 | app: 'test', 31 | hostPort: '127.0.0.1:3000' 32 | }); 33 | return ringpop; 34 | } 35 | 36 | handleUpdateSchedulesTimer(Member.Status.alive, false); 37 | handleUpdateSchedulesTimer(Member.Status.leave, false); 38 | handleUpdateSchedulesTimer(Member.Status.faulty, true); 39 | handleUpdateSchedulesTimer(Member.Status.suspect,true); 40 | handleUpdateSchedulesTimer(Member.Status.tombstone,true); 41 | 42 | function handleUpdateSchedulesTimer(state, shouldSchedule) { 43 | test('handleUpdate schedules timers for state: ' + state, function t(assert) { 44 | var ringpop = createRingpop(); 45 | var stateTransitions = ringpop.stateTransitions; 46 | var address = '127.0.0.1:3001'; 47 | 48 | if (!shouldSchedule) { 49 | // register fake timer to test if it's cancelled correctly 50 | stateTransitions.timers[address] = {state: 'fake'} 51 | } 52 | 53 | stateTransitions.handleUpdate({ 54 | status: state, 55 | address: address 56 | }); 57 | if (shouldSchedule) { 58 | assert.ok(stateTransitions.timers[address], 'timer scheduled'); 59 | assert.equal(stateTransitions.timers[address].state, state, 'timer scheduled'); 60 | } else { 61 | assert.notok(stateTransitions.timers[address], 'timer not scheduled'); 62 | } 63 | 64 | assert.end(); 65 | ringpop.destroy(); 66 | }); 67 | } 68 | --------------------------------------------------------------------------------