dcell 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +4 -1
- data/CHANGES.md +11 -0
- data/Gemfile +6 -3
- data/LICENSE.txt +1 -1
- data/README.md +27 -243
- data/benchmarks/receiver.rb +2 -2
- data/dcell.gemspec +7 -7
- data/explorer/css/bootstrap-responsive.css +686 -0
- data/explorer/css/bootstrap-responsive.min.css +12 -0
- data/explorer/css/bootstrap.css +3990 -0
- data/explorer/css/bootstrap.min.css +689 -0
- data/explorer/css/explorer.css +28 -0
- data/explorer/ico/favicon.ico +0 -0
- data/explorer/img/glyphicons-halflings-white.png +0 -0
- data/explorer/img/glyphicons-halflings.png +0 -0
- data/explorer/img/logo.png +0 -0
- data/explorer/index.html.erb +94 -0
- data/explorer/js/bootstrap.js +1726 -0
- data/explorer/js/bootstrap.min.js +6 -0
- data/lib/dcell.rb +27 -2
- data/lib/dcell/celluloid_ext.rb +14 -3
- data/lib/dcell/directory.rb +15 -3
- data/lib/dcell/explorer.rb +76 -0
- data/lib/dcell/future_proxy.rb +32 -0
- data/lib/dcell/info_service.rb +117 -0
- data/lib/dcell/mailbox_proxy.rb +6 -7
- data/lib/dcell/messages.rb +5 -6
- data/lib/dcell/node.rb +25 -55
- data/lib/dcell/node_manager.rb +81 -0
- data/lib/dcell/registries/cassandra_adapter.rb +86 -0
- data/lib/dcell/registries/gossip/core.rb +235 -0
- data/lib/dcell/registries/gossip_adapter.rb +26 -0
- data/lib/dcell/registries/moneta_adapter.rb +0 -7
- data/lib/dcell/registries/redis_adapter.rb +0 -31
- data/lib/dcell/registries/zk_adapter.rb +1 -39
- data/lib/dcell/router.rb +37 -30
- data/lib/dcell/rpc.rb +23 -23
- data/lib/dcell/server.rb +5 -2
- data/lib/dcell/version.rb +1 -1
- data/logo.png +0 -0
- data/spec/dcell/actor_proxy_spec.rb +4 -0
- data/spec/dcell/celluloid_ext_spec.rb +11 -0
- data/spec/dcell/directory_spec.rb +1 -1
- data/spec/dcell/explorer_spec.rb +17 -0
- data/spec/dcell/global_spec.rb +4 -0
- data/spec/dcell/registries/gossip_adapter_spec.rb +6 -0
- data/spec/spec_helper.rb +14 -7
- data/spec/support/registry_examples.rb +0 -18
- data/tasks/cassandra.task +84 -0
- metadata +55 -35
- data/celluloid-zmq/.gitignore +0 -17
- data/celluloid-zmq/.rspec +0 -4
- data/celluloid-zmq/CHANGES.md +0 -31
- data/celluloid-zmq/Gemfile +0 -7
- data/celluloid-zmq/README.md +0 -56
- data/celluloid-zmq/Rakefile +0 -7
- data/celluloid-zmq/celluloid-zmq.gemspec +0 -28
- data/celluloid-zmq/lib/celluloid/zmq.rb +0 -36
- data/celluloid-zmq/lib/celluloid/zmq/reactor.rb +0 -90
- data/celluloid-zmq/lib/celluloid/zmq/sockets.rb +0 -130
- data/celluloid-zmq/lib/celluloid/zmq/version.rb +0 -5
- data/celluloid-zmq/lib/celluloid/zmq/waker.rb +0 -55
- data/celluloid-zmq/spec/celluloid/zmq/actor_spec.rb +0 -6
- data/celluloid-zmq/spec/spec_helper.rb +0 -2
@@ -0,0 +1,6 @@
|
|
1
|
+
/**
|
2
|
+
* Bootstrap.js by @fat & @mdo
|
3
|
+
* Copyright 2012 Twitter, Inc.
|
4
|
+
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
5
|
+
*/
|
6
|
+
!function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.show?c.show():(c.hoverState="in",setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show))},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.hide?c.hide():(c.hoverState="out",setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide))},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip();a.find(".tooltip-inner").html(this.getTitle()),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a=(a||"").toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,delay:0,selector:!1,placement:"top",trigger:"hover",title:"",template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var b=this.tip(),c=this.getTitle(),d=this.getContent();b.find(".popover-title")[a.type(c)=="object"?"append":"html"](c),b.find(".popover-content > *")[a.type(d)=="object"?"append":"html"](d),b.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){this.targets=this.$body.find(this.selector).map(function(){var b=a(this).attr("href");return/^#\w/.test(b)&&a(b).length?b:null}),this.offsets=a.map(this.targets,function(b){return a(b).position().top})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.offsets,c=this.targets,d=this.activeTarget,e;for(e=b.length;e--;)d!=c[e]&&a>=b[e]&&(!b[e+1]||a<=b[e+1])&&this.activate(c[e])},activate:function(a){var b;this.activeTarget=a,this.$body.find(this.selector).parent(".active").removeClass("active"),b=this.$body.find(this.selector+'[href="'+a+'"]').parent("li").addClass("active"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],b.trigger({type:"show",relatedTarget:e}),f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(a),this.$element.change(),this.hide()},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){if(c.matcher(a))return a}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){return a.replace(new RegExp("("+this.query+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
|
data/lib/dcell.rb
CHANGED
@@ -9,19 +9,27 @@ require 'dcell/directory'
|
|
9
9
|
require 'dcell/mailbox_proxy'
|
10
10
|
require 'dcell/messages'
|
11
11
|
require 'dcell/node'
|
12
|
+
require 'dcell/node_manager'
|
12
13
|
require 'dcell/global'
|
13
14
|
require 'dcell/responses'
|
14
15
|
require 'dcell/router'
|
15
16
|
require 'dcell/rpc'
|
17
|
+
require 'dcell/future_proxy'
|
16
18
|
require 'dcell/server'
|
19
|
+
require 'dcell/info_service'
|
17
20
|
|
18
21
|
require 'dcell/registries/redis_adapter'
|
19
22
|
require 'dcell/registries/moneta_adapter'
|
20
23
|
|
24
|
+
require 'dcell/registries/gossip/core'
|
25
|
+
require 'dcell/registries/gossip_adapter'
|
26
|
+
|
21
27
|
require 'dcell/celluloid_ext'
|
22
28
|
|
23
29
|
# Distributed Celluloid
|
24
30
|
module DCell
|
31
|
+
class NotConfiguredError < RuntimeError; end # Not configured yet
|
32
|
+
|
25
33
|
DEFAULT_PORT = 7777 # Default DCell port
|
26
34
|
@config_lock = Mutex.new
|
27
35
|
|
@@ -46,6 +54,16 @@ module DCell
|
|
46
54
|
|
47
55
|
@me = Node.new @configuration['id'], @configuration['addr']
|
48
56
|
|
57
|
+
# Specify the directory server (defaults to me), and add it
|
58
|
+
# to the local directory.
|
59
|
+
directory = @configuration['directory'] || {}
|
60
|
+
directory = directory.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
|
61
|
+
directory = {
|
62
|
+
'id' => @configuration['id'],
|
63
|
+
'addr' => @configuration['addr']
|
64
|
+
}.merge(directory)
|
65
|
+
DCell::Directory.set directory['id'], directory['addr']
|
66
|
+
|
49
67
|
registry_adapter = @configuration['registry'][:adapter] || @configuration['registry']['adapter']
|
50
68
|
raise ArgumentError, "no registry adapter given in config" unless registry_adapter
|
51
69
|
|
@@ -67,7 +85,10 @@ module DCell
|
|
67
85
|
end
|
68
86
|
|
69
87
|
# Obtain the local node ID
|
70
|
-
def id
|
88
|
+
def id
|
89
|
+
raise NotConfiguredError, "please configure DCell with DCell.setup" unless @configuration
|
90
|
+
@configuration['id']
|
91
|
+
end
|
71
92
|
|
72
93
|
# Obtain the 0MQ address to the local mailbox
|
73
94
|
def addr; @configuration['addr']; end
|
@@ -97,6 +118,10 @@ module DCell
|
|
97
118
|
|
98
119
|
# DCell's actor dependencies
|
99
120
|
class Group < Celluloid::Group
|
100
|
-
supervise
|
121
|
+
supervise NodeManager, :as => :node_manager
|
122
|
+
supervise Server, :as => :dcell_server
|
123
|
+
supervise InfoService, :as => :info
|
101
124
|
end
|
125
|
+
|
126
|
+
Logger = Celluloid::Logger
|
102
127
|
end
|
data/lib/dcell/celluloid_ext.rb
CHANGED
@@ -45,7 +45,7 @@ module Celluloid
|
|
45
45
|
# be reached remotely.
|
46
46
|
def _dump(level)
|
47
47
|
mailbox_id = DCell::Router.register self
|
48
|
-
"#{mailbox_id}@#{DCell.id}"
|
48
|
+
"#{mailbox_id}@#{DCell.id}@#{DCell.addr}"
|
49
49
|
end
|
50
50
|
|
51
51
|
# Create a mailbox proxy object which routes messages over DCell's overlay
|
@@ -57,13 +57,24 @@ module Celluloid
|
|
57
57
|
|
58
58
|
class SyncCall
|
59
59
|
def _dump(level)
|
60
|
-
|
60
|
+
uuid = DCell::RPC::Manager.register self
|
61
61
|
payload = Marshal.dump([@caller,@method,@arguments,@block])
|
62
|
-
"#{
|
62
|
+
"#{uuid}@#{DCell.id}:#{payload}"
|
63
63
|
end
|
64
64
|
|
65
65
|
def self._load(string)
|
66
66
|
DCell::RPC._load(string)
|
67
67
|
end
|
68
68
|
end
|
69
|
+
|
70
|
+
class Future
|
71
|
+
def _dump(level)
|
72
|
+
mailbox_id = DCell::Router.register self
|
73
|
+
"#{mailbox_id}@#{DCell.id}@#{DCell.addr}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self._load(string)
|
77
|
+
DCell::FutureProxy._load(string)
|
78
|
+
end
|
79
|
+
end
|
69
80
|
end
|
data/lib/dcell/directory.rb
CHANGED
@@ -3,21 +3,33 @@ module DCell
|
|
3
3
|
module Directory
|
4
4
|
extend self
|
5
5
|
|
6
|
+
@@directory = {}
|
7
|
+
@@directory_lock = Mutex.new
|
8
|
+
|
6
9
|
# Get the URL for a particular Node ID
|
7
10
|
def get(node_id)
|
8
|
-
|
11
|
+
@@directory_lock.synchronize do
|
12
|
+
@@directory[node_id]
|
13
|
+
end
|
9
14
|
end
|
10
15
|
alias_method :[], :get
|
11
16
|
|
12
17
|
# Set the address of a particular Node ID
|
13
18
|
def set(node_id, addr)
|
14
|
-
|
19
|
+
@@directory_lock.synchronize do
|
20
|
+
@@directory[node_id] = addr
|
21
|
+
end
|
15
22
|
end
|
16
23
|
alias_method :[]=, :set
|
17
24
|
|
18
25
|
# List all of the node IDs in the directory
|
19
26
|
def all
|
20
|
-
|
27
|
+
@@directory_lock.synchronize { @@directory.keys }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Clear the directory.
|
31
|
+
def clear
|
32
|
+
@@directory_lock.synchronize { @@directory.clear }
|
21
33
|
end
|
22
34
|
end
|
23
35
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'dcell'
|
2
|
+
require 'reel'
|
3
|
+
require 'pathname'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module DCell
|
7
|
+
# Web UI for DCell
|
8
|
+
# TODO: rewrite this entire thing with less hax
|
9
|
+
class Explorer < Reel::Server
|
10
|
+
include Celluloid::IO # FIXME: this should really be unnecessary
|
11
|
+
ASSET_ROOT = Pathname.new File.expand_path("../../../explorer", __FILE__)
|
12
|
+
|
13
|
+
def initialize(host = "127.0.0.1", port = 7778)
|
14
|
+
super(host, port, &method(:on_connection))
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_connection(connection)
|
18
|
+
request = connection.request
|
19
|
+
return unless request
|
20
|
+
route connection, request
|
21
|
+
end
|
22
|
+
|
23
|
+
def route(connection, request)
|
24
|
+
if request.url == "/"
|
25
|
+
path = "index.html"
|
26
|
+
else
|
27
|
+
path = request.url[%r{^/([a-z0-9\.\-_]+(/[a-z0-9\.\-_]+)*)$}, 1]
|
28
|
+
end
|
29
|
+
|
30
|
+
if !path or path[".."]
|
31
|
+
Logger.info "404 Not Found: #{request.path}"
|
32
|
+
connection.respond :not_found, "Not found"
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
render_resource connection, path
|
37
|
+
end
|
38
|
+
|
39
|
+
def render_resource(connection, path)
|
40
|
+
if node_id = path[%r{^nodes/(.*)$}, 1]
|
41
|
+
p node_id
|
42
|
+
node = DCell::Node[node_id]
|
43
|
+
path = "index.html"
|
44
|
+
else
|
45
|
+
node = DCell.me
|
46
|
+
end
|
47
|
+
|
48
|
+
asset_path = ASSET_ROOT.join(path)
|
49
|
+
if asset_path.exist?
|
50
|
+
asset_path.open("r") do |file|
|
51
|
+
connection.respond :ok, file
|
52
|
+
end
|
53
|
+
|
54
|
+
Logger.info "200 OK: /#{path}"
|
55
|
+
elsif File.exist?(asset_path.to_s + ".erb") and node
|
56
|
+
connection.respond :ok, render_template(asset_path.to_s + ".erb", node)
|
57
|
+
Logger.info "200 OK: /#{path}"
|
58
|
+
else
|
59
|
+
connection.respond :not_found, "Not found"
|
60
|
+
Logger.info "404 Not Found: /#{path}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def render_template(template, node)
|
65
|
+
@node = node
|
66
|
+
@info = @node[:info].to_hash
|
67
|
+
|
68
|
+
template = ERB.new File.read(template, :mode => 'rb')
|
69
|
+
template.result(binding)
|
70
|
+
end
|
71
|
+
|
72
|
+
def node_path(node)
|
73
|
+
"/nodes/#{node.id}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DCell
|
2
|
+
class FutureProxy
|
3
|
+
def initialize(mailbox_id,node_id,node_addr)
|
4
|
+
@mailbox_id = mailbox_id
|
5
|
+
@node_id = node_id
|
6
|
+
@node_addr = node_addr
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(message)
|
10
|
+
node = Node[@node_id]
|
11
|
+
node = Node.new(@node_id, @node_addr) unless node
|
12
|
+
node.send_message! Message::Relay.new(self, message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def _dump(level)
|
16
|
+
"#{@mailbox_id}@#{@node_id}@#{@node_addr}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loader for custom marshal format
|
20
|
+
def self._load(string)
|
21
|
+
mailbox_id, node_id, node_addr = string.split("@")
|
22
|
+
|
23
|
+
if node_id == DCell.id
|
24
|
+
future = Router.find(mailbox_id)
|
25
|
+
raise "tried to unmarshal dead Celluloid::Future: #{mailbox_id}" unless future
|
26
|
+
future
|
27
|
+
else
|
28
|
+
new(mailbox_id, node_id, node_addr)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module DCell
|
4
|
+
class InfoService
|
5
|
+
include Celluloid
|
6
|
+
attr_reader :os, :os_version, :hostname, :platform, :distribution
|
7
|
+
attr_reader :cpu_arch, :cpu_type, :cpu_vendor, :cpu_speed, :cpu_count
|
8
|
+
attr_reader :ruby_version, :ruby_engine, :ruby_platform
|
9
|
+
|
10
|
+
UPTIME_REGEX = /up ((\d+ days?,)?\s*(\d+:\d+|\d+ \w+)),.*(( \d.\d{2},?){3})/
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@cpu_arch = RbConfig::CONFIG['host_cpu']
|
14
|
+
@os = RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
15
|
+
|
16
|
+
uname = `uname -a`.match(/\w+ (\w[\w+\.\-]*) ([\w+\.\-]+)/)
|
17
|
+
@hostname, @os_version = uname[1], uname[2]
|
18
|
+
|
19
|
+
@platform = RUBY_PLATFORM
|
20
|
+
@ruby_version = RUBY_VERSION
|
21
|
+
@ruby_engine = RUBY_ENGINE
|
22
|
+
|
23
|
+
case os
|
24
|
+
when 'darwin'
|
25
|
+
cpu_info = `sysctl -n machdep.cpu.brand_string`.match(/^((\w+).*) @ (\d+.\d+)GHz/)
|
26
|
+
if cpu_info
|
27
|
+
@cpu_type = cpu_info[1]
|
28
|
+
@cpu_vendor = cpu_info[2].downcase.to_sym
|
29
|
+
@cpu_speed = Float(cpu_info[3])
|
30
|
+
end
|
31
|
+
|
32
|
+
@cpu_count = Integer(`sysctl hw.ncpu`[/\d+/])
|
33
|
+
os, release, build = `sw_vers`.scan(/:\t(.*)$/).flatten
|
34
|
+
@distribution = "#{os} #{release} (#{build})"
|
35
|
+
when 'linux'
|
36
|
+
cpu_info = File.read("/proc/cpuinfo")
|
37
|
+
|
38
|
+
@cpu_vendor = cpu_info[/vendor_id:\s+\s+(Genuine)?(\w+)/, 2]
|
39
|
+
model_name = cpu_info.match(/model name\s+:\s+((\w+).*) @ (\d+.\d+)GHz/)
|
40
|
+
if model_name
|
41
|
+
@cpu_type = model_name[1].gsub(/\s+/, ' ')
|
42
|
+
@cpu_vendor = model_name[2].downcase.to_sym
|
43
|
+
@cpu_speed = Float(model_name[3])
|
44
|
+
end
|
45
|
+
|
46
|
+
cores = cpu_info.scan(/core id\s+: \d+/).uniq.size
|
47
|
+
@cpu_count = cores > 0 ? cores : 1
|
48
|
+
@distribution = `lsb_release -d`[/Description:\s+(.*)\s*$/, 1]
|
49
|
+
else
|
50
|
+
@cpu_type = @cpu_vendor = @cpu_speed = @cpu_count = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
case RUBY_ENGINE
|
54
|
+
when 'ruby'
|
55
|
+
@ruby_platform = "ruby #{RUBY_VERSION}"
|
56
|
+
when 'jruby'
|
57
|
+
@ruby_platform = "jruby #{JRUBY_VERSION}"
|
58
|
+
when 'rbx'
|
59
|
+
@ruby_platform = "rbx #{Rubinius::VERSION}"
|
60
|
+
else
|
61
|
+
@ruby_platform = RUBY_ENGINE
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def platform; RUBY_PLATFORM; end
|
66
|
+
def ruby_engine; RUBY_ENGINE; end
|
67
|
+
def ruby_version; RUBY_VERSION; end
|
68
|
+
|
69
|
+
def load_averages(uptime_string = `uptime`)
|
70
|
+
matches = uptime_string.match(UPTIME_REGEX)
|
71
|
+
unless matches
|
72
|
+
Logger.warn "Couldn't parse uptime output: #{uptime_string}"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
averages = matches[4].strip
|
77
|
+
averages.split(/,? /).map(&:to_f)
|
78
|
+
end
|
79
|
+
alias_method :load_average, :load_averages
|
80
|
+
|
81
|
+
def uptime(uptime_string = `uptime`)
|
82
|
+
matches = uptime_string.match(UPTIME_REGEX)
|
83
|
+
unless matches
|
84
|
+
Logger.warn "Couldn't parse uptime output: #{uptime_string}"
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
uptime = matches[1]
|
89
|
+
days_string = uptime[/^(\d+) days/, 1]
|
90
|
+
days_string ? Integer(days_string) : 0
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_hash
|
94
|
+
uptime_string = `uptime`
|
95
|
+
|
96
|
+
{
|
97
|
+
:os => os,
|
98
|
+
:os_version => os_version,
|
99
|
+
:hostname => hostname,
|
100
|
+
:platform => platform,
|
101
|
+
:distribution => distribution,
|
102
|
+
:ruby_version => ruby_version,
|
103
|
+
:ruby_engine => ruby_engine,
|
104
|
+
:ruby_platform => ruby_platform,
|
105
|
+
:load_averages => load_averages(uptime_string),
|
106
|
+
:uptime => uptime(uptime_string),
|
107
|
+
:cpu => {
|
108
|
+
:arch => cpu_arch,
|
109
|
+
:type => cpu_type,
|
110
|
+
:vendor => cpu_vendor,
|
111
|
+
:speed => cpu_speed,
|
112
|
+
:count => cpu_count
|
113
|
+
}
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/lib/dcell/mailbox_proxy.rb
CHANGED
@@ -4,13 +4,12 @@ module DCell
|
|
4
4
|
class MailboxProxy
|
5
5
|
class InvalidNodeError < StandardError; end
|
6
6
|
|
7
|
-
def initialize(node_id, mailbox_id)
|
7
|
+
def initialize(node_id, node_addr, mailbox_id)
|
8
8
|
raise ArgumentError, "no mailbox_id given" unless mailbox_id
|
9
|
-
|
9
|
+
|
10
10
|
@node_id = node_id
|
11
11
|
@node = Node[node_id]
|
12
|
-
|
13
|
-
|
12
|
+
@node = Node.new(node_id, node_addr) unless @node
|
14
13
|
@mailbox_id = mailbox_id
|
15
14
|
end
|
16
15
|
|
@@ -40,12 +39,12 @@ module DCell
|
|
40
39
|
|
41
40
|
# Custom marshaller for compatibility with Celluloid::Mailbox marshalling
|
42
41
|
def _dump(level)
|
43
|
-
"#{@mailbox_id}@#{@node_id}"
|
42
|
+
"#{@mailbox_id}@#{@node_id}@#{@node_addr}"
|
44
43
|
end
|
45
44
|
|
46
45
|
# Loader for custom marshal format
|
47
46
|
def self._load(string)
|
48
|
-
mailbox_id, node_id = string.split("@")
|
47
|
+
mailbox_id, node_id, node_addr = string.split("@")
|
49
48
|
|
50
49
|
if DCell.id == node_id
|
51
50
|
# If we're on the local node, find the real mailbox
|
@@ -54,7 +53,7 @@ module DCell
|
|
54
53
|
mailbox
|
55
54
|
else
|
56
55
|
# Create a proxy to the mailbox on the remote node
|
57
|
-
DCell::MailboxProxy.new(node_id, mailbox_id)
|
56
|
+
DCell::MailboxProxy.new(node_id, node_addr, mailbox_id)
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
data/lib/dcell/messages.rb
CHANGED
@@ -8,15 +8,14 @@ module DCell
|
|
8
8
|
@id = object_id
|
9
9
|
end
|
10
10
|
|
11
|
-
#
|
12
|
-
class
|
13
|
-
def initialize
|
14
|
-
@
|
11
|
+
# Gossip messages contain health and membership information
|
12
|
+
class Gossip < Message
|
13
|
+
def initialize(peers, data)
|
14
|
+
@peers, @data = peers, data
|
15
15
|
end
|
16
16
|
|
17
17
|
def dispatch
|
18
|
-
|
19
|
-
node.handle_heartbeat if node
|
18
|
+
Node.handle_gossip(@peers, @data)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
data/lib/dcell/node.rb
CHANGED
@@ -3,73 +3,33 @@ module DCell
|
|
3
3
|
class Node
|
4
4
|
include Celluloid
|
5
5
|
include Celluloid::FSM
|
6
|
-
attr_reader :id, :addr
|
6
|
+
attr_reader :id, :addr, :timestamp
|
7
7
|
|
8
8
|
# FSM
|
9
9
|
default_state :disconnected
|
10
10
|
state :shutdown
|
11
11
|
state :disconnected, :to => [:connected, :shutdown]
|
12
12
|
state :connected do
|
13
|
-
send_heartbeat
|
14
13
|
Celluloid::Logger.info "Connected to #{id}"
|
15
14
|
end
|
16
15
|
state :partitioned do
|
17
16
|
Celluloid::Logger.warn "Communication with #{id} interrupted"
|
18
17
|
end
|
19
18
|
|
20
|
-
# Ivars
|
21
|
-
@nodes = {}
|
22
|
-
@lock = Mutex.new
|
23
|
-
|
24
|
-
@heartbeat_rate = 5 # How often to send heartbeats in seconds
|
25
|
-
@heartbeat_timeout = 10 # How soon until a lost heartbeat triggers a node partition
|
26
|
-
|
27
19
|
# Singleton methods
|
28
20
|
class << self
|
29
21
|
include Enumerable
|
30
|
-
|
31
|
-
|
32
|
-
# Return all available nodes in the cluster
|
33
|
-
def all
|
34
|
-
Directory.all.map do |node_id|
|
35
|
-
find node_id
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# Iterate across all available nodes
|
40
|
-
def each
|
41
|
-
Directory.all.each do |node_id|
|
42
|
-
yield find node_id
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Find a node by its node ID
|
47
|
-
def find(id)
|
48
|
-
node = @lock.synchronize { @nodes[id] }
|
49
|
-
return node if node
|
22
|
+
extend Forwardable
|
50
23
|
|
51
|
-
|
52
|
-
|
53
|
-
if addr
|
54
|
-
if id == DCell.id
|
55
|
-
node = DCell.me
|
56
|
-
else
|
57
|
-
node = Node.new(id, addr)
|
58
|
-
end
|
59
|
-
|
60
|
-
@lock.synchronize do
|
61
|
-
@nodes[id] ||= node
|
62
|
-
@nodes[id]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
alias_method :[], :find
|
24
|
+
def_delegators "Celluloid::Actor[:node_manager]", :all, :each, :find, :[], :handle_gossip
|
25
|
+
def_delegators "Celluloid::Actor[:node_manager]", :gossip_rate, :heartbeat_timeout
|
67
26
|
end
|
68
27
|
|
69
28
|
def initialize(id, addr)
|
70
29
|
@id, @addr = id, addr
|
30
|
+
@timestamp = 0
|
71
31
|
@socket = nil
|
72
|
-
@
|
32
|
+
@fresh = true
|
73
33
|
|
74
34
|
# Total hax to accommodate the new Celluloid::FSM API
|
75
35
|
attach self
|
@@ -77,7 +37,8 @@ module DCell
|
|
77
37
|
|
78
38
|
def finalize
|
79
39
|
transition :shutdown
|
80
|
-
@
|
40
|
+
@gossip.cancel if @gossip
|
41
|
+
@socket.close if @socket
|
81
42
|
end
|
82
43
|
|
83
44
|
# Obtain the node's 0MQ socket
|
@@ -137,16 +98,25 @@ module DCell
|
|
137
98
|
end
|
138
99
|
alias_method :<<, :send_message
|
139
100
|
|
140
|
-
|
141
|
-
|
142
|
-
send_message DCell::Message::Heartbeat.new
|
143
|
-
@heartbeat = after(self.class.heartbeat_rate) { send_heartbeat }
|
101
|
+
def tick
|
102
|
+
@timestamp += 1
|
144
103
|
end
|
145
104
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
105
|
+
def fresh?
|
106
|
+
@fresh
|
107
|
+
end
|
108
|
+
|
109
|
+
# Handle an incoming timestamp observation for this node
|
110
|
+
def handle_timestamp(t)
|
111
|
+
@fresh = false if t > 0
|
112
|
+
if @timestamp < t
|
113
|
+
@timestamp = t
|
114
|
+
transition :connected
|
115
|
+
transition :partitioned, :delay => self.class.heartbeat_timeout
|
116
|
+
unless state == :connected
|
117
|
+
Celluloid::Logger.info "Revived node #{id}"
|
118
|
+
end
|
119
|
+
end
|
150
120
|
end
|
151
121
|
|
152
122
|
# Friendlier inspection
|