lark 0.0.1

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/README.markdown ADDED
@@ -0,0 +1,22 @@
1
+ # Lark
2
+
3
+ Lark is a simple system to store local cache in a cluster of redis servers, be
4
+ able to query state about other lark nodes, and notice when one goes offline.
5
+
6
+ Lark.on_expire do |id|
7
+ Log.debug "Lost node #{id}"
8
+ end
9
+
10
+ EM.start do
11
+ lark = Lark.new "bottom_node.1", :expire => 60
12
+ lark.set "ip" => "127.0.0.1", "role" => "worker node"
13
+ EM::PeriodicTimer(5) do
14
+ lark.set "load" => get_load, "mem_usage" => get_mem_usage
15
+ end
16
+
17
+ on_some_event do
18
+ lark.find(/^top_node/).each do |id, data|
19
+ check_on_node(id, data)
20
+ do
21
+ end
22
+ end
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'jeweler'
2
+
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "lark"
5
+ s.description = "A system for nodes to communicate state and presence through a redis cluster"
6
+ s.summary = s.description
7
+ s.author = "Orion Henry"
8
+ s.email = "orion@heroku.com"
9
+ s.homepage = "http://github.com/orionz/lark"
10
+ s.rubyforge_project = "lark"
11
+ s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
12
+ s.add_dependency "redis", [">= 2.0.5"]
13
+ end
14
+
15
+ Jeweler::RubyforgeTasks.new
16
+
17
+ desc 'Run specs'
18
+ task :spec do
19
+ sh 'bacon -s spec/*_spec.rb'
20
+ end
21
+
22
+ task :default => :spec
23
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/lib/lark.rb ADDED
@@ -0,0 +1,142 @@
1
+ require 'redis'
2
+
3
+ class LarkDBOffline < Exception ; end
4
+
5
+ class Lark
6
+ attr_accessor :id
7
+
8
+ def self.urls
9
+ if ENV['LARK_URLS']
10
+ ENV['LARK_URLS'].split(",")
11
+ elsif ENV['LARK_URL']
12
+ [ ENV['LARK_URL'] ]
13
+ else
14
+ [ "redis://127.0.0.1:6379/0" ]
15
+ end
16
+ end
17
+
18
+ def self.dbs
19
+ @dbs ||= urls.map do |url|
20
+ puts "Connecting to #{url}";
21
+ Redis.connect(:url => url)
22
+ end
23
+ end
24
+
25
+ ## all the black magic happens here
26
+ ## pool runs the block against all the db's
27
+ ## throws an error if non of them are online
28
+ ## and returns an array of the block results
29
+ ## from the ones who are online
30
+
31
+ def self.pool(&blk)
32
+ dbs.push dbs.shift
33
+
34
+ num_errors = 0
35
+ results = []
36
+ begin
37
+ dbs.each do |db|
38
+ begin
39
+ results << blk.call(db)
40
+ rescue Errno::ECONNREFUSED, Timeout::Error => e
41
+ puts "#{e.class}: #{e.message}"
42
+ num_errors += 1
43
+ end
44
+ end
45
+ ensure
46
+ raise LarkDBOffline if num_errors == dbs.size
47
+ end
48
+ results
49
+ end
50
+
51
+ def self.on_expired(&blk)
52
+ @expired = blk;
53
+ end
54
+
55
+ def self.expired
56
+ @expired
57
+ end
58
+
59
+ def initialize(_id, options = {})
60
+ @id = _id
61
+ @options = options
62
+ @domain = options[:domain]
63
+ @expire = options[:expire]
64
+ end
65
+
66
+ def load_data
67
+ ## try each db - return when one works without an error
68
+ ## throw an error if all are down
69
+ pool do |db|
70
+ data = db.hgetall(key)
71
+ return data if not data.empty?
72
+ end
73
+ {}
74
+ end
75
+
76
+ def key
77
+ "#{@domain}:#{@id}"
78
+ end
79
+
80
+ def set_key
81
+ "#{@domain}:lark:all"
82
+ end
83
+
84
+ def set(new_data)
85
+ data.merge!(new_data)
86
+ save_data
87
+ end
88
+
89
+ def save_data
90
+ data[:created_on] ||= Time.new.to_i
91
+ data[:domain] ||= @domain
92
+ data[:updated_on] = Time.new.to_i
93
+ pool do |db|
94
+ db.multi do
95
+ db.hmset *[ key, data.to_a ].flatten
96
+ db.expire key, @expire if @expire
97
+ db.sadd set_key, @id
98
+ end
99
+ end
100
+ end
101
+
102
+ def data
103
+ @data ||= load_data
104
+ end
105
+
106
+ def destroy
107
+ pool do |db|
108
+ db.multi do
109
+ db.del key
110
+ db.srem set_key, id
111
+ end
112
+ end
113
+ self.class.expired.call(id, @domain);
114
+ end
115
+
116
+ def all_ids
117
+ pool { |db| db.smembers(set_key) }.flatten.uniq.sort
118
+ end
119
+
120
+ def find_ids(match)
121
+ all_ids.select { |_id| match.match(_id) }
122
+ end
123
+
124
+ def find_valid(match)
125
+ all = find_ids(match).map { |_id| Lark.new(_id, @options) }
126
+ valid = all.reject { |l| l.data.empty? }
127
+ invalid = all - valid
128
+ invalid.each { |i| i.destroy }
129
+ valid
130
+ end
131
+
132
+ def find(match = //)
133
+ result = {}
134
+ find_valid(match).each { |v| result[v.id] = v.data }
135
+ result
136
+ end
137
+
138
+ def pool(&blk)
139
+ self.class.pool(&blk)
140
+ end
141
+
142
+ end
data/spec/init.rb ADDED
@@ -0,0 +1,17 @@
1
+
2
+ require File.dirname(__FILE__) + "/../lib/lark"
3
+ require "bacon"
4
+ require "mocha/api"
5
+ require "mocha/object"
6
+
7
+ class Bacon::Context
8
+ include Mocha::API
9
+ alias_method :old_it,:it
10
+ def it description,&block
11
+ mocha_setup
12
+ old_it(description,&block)
13
+ mocha_verify
14
+ mocha_teardown
15
+ end
16
+ end
17
+
data/spec/lark_spec.rb ADDED
@@ -0,0 +1,31 @@
1
+
2
+ require File.dirname(__FILE__) + "/init.rb"
3
+
4
+ describe 'Lark' do
5
+ before do
6
+ @lark = Lark.new "lark", :domain => "domain"
7
+ @db1 = mock()
8
+ @db2 = mock()
9
+ Lark.stubs(:dbs).returns([@db1, @db2]);
10
+ end
11
+
12
+ it 'should locate the data even if the key and data are on different databases' do
13
+ @db1.expects(:smembers).returns([ "key1" ])
14
+ @db1.expects(:hgetall).with("domain:key1").returns({})
15
+ @db2.expects(:smembers).returns([ ])
16
+ @db2.expects(:hgetall).with("domain:key1").returns({"foo" => "bar"})
17
+
18
+ @lark.find.should.equal({ "key1" => {"foo" => "bar"} })
19
+ end
20
+
21
+ it 'should get the union of the ids on each server' do
22
+ @db1.expects(:smembers).returns([ "a", "b" ])
23
+ @db2.expects(:smembers).returns([ "b", "c" ])
24
+ @lark.all_ids.should.equal ["a","b","c"]
25
+ end
26
+
27
+ # it 'should run delete on old objects'
28
+ # it 'should invoke a handler when deleting old objects'
29
+ # it 'should fetch data properly when one redis is offline'
30
+ # it 'should raise an exception if all redis are offline'
31
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lark
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Orion Henry
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-21 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: redis
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 5
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 5
34
+ version: 2.0.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: A system for nodes to communicate state and presence through a redis cluster
38
+ email: orion@heroku.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README.markdown
45
+ files:
46
+ - README.markdown
47
+ - Rakefile
48
+ - VERSION
49
+ - lib/lark.rb
50
+ - spec/init.rb
51
+ - spec/lark_spec.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/orionz/lark
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project: lark
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A system for nodes to communicate state and presence through a redis cluster
86
+ test_files:
87
+ - spec/init.rb
88
+ - spec/lark_spec.rb