mitchellh-lightcloud 0.6 → 0.7

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.
Files changed (3) hide show
  1. data/lib/lightcloud.rb +99 -76
  2. data/spec/lightcloud_spec.rb +53 -50
  3. metadata +2 -2
data/lib/lightcloud.rb CHANGED
@@ -35,26 +35,30 @@ require File.join(File.dirname(__FILE__), 'tyrant_client')
35
35
  # print LightCloud.get("hello") # => nil
36
36
  #
37
37
  class LightCloud
38
- VERSION = '0.6'
38
+ VERSION = '0.7'
39
39
  DEFAULT_SYSTEM = 'default'
40
+ @@instance = nil
40
41
 
41
- @@systems = {}
42
-
42
+ #--
43
+ # INSTANCE METHODS
44
+ #++
43
45
  # Initialize LightCloud as an instance instead of using the class
44
46
  # methods. Expects the same arguments as LightCloud.init, except
45
47
  # this will return a new instance of LightCloud.
46
48
  #
47
49
  # Any nodes initialized through here will not work one class methods.
48
- def initialize(lookup_nodes, storage_nodes, system=DEFAULT_SYSTEM)
49
- @systems = {}
50
- self.class.init(lookup_nodes, storage_nodes, system, @systems)
50
+ def initialize(lookup_nodes=nil, storage_nodes=nil, system=DEFAULT_SYSTEM)
51
+ add_system(lookup_nodes, storage_nodes, system) if !lookup_nodes.nil? && !storage_nodes.nil?
51
52
  end
52
53
 
53
- def self.init(lookup_nodes, storage_nodes, system=DEFAULT_SYSTEM, systems=@@systems)
54
+ #
55
+ # Create a new LightCloud system within a pre-existing instance.
56
+ def add_system(lookup_nodes, storage_nodes, system=DEFAULT_SYSTEM)
54
57
  lookup_ring, name_to_l_nodes = self.generate_ring(lookup_nodes)
55
58
  storage_ring, name_to_s_nodes = self.generate_ring(storage_nodes)
56
59
 
57
- systems[system] = [lookup_ring, storage_ring, name_to_l_nodes, name_to_s_nodes]
60
+ @systems ||= {}
61
+ @systems[system] = [lookup_ring, storage_ring, name_to_l_nodes, name_to_s_nodes]
58
62
  end
59
63
 
60
64
  #--
@@ -63,63 +67,40 @@ class LightCloud
63
67
  # Sets a value to a key on a LightCloud instance. See
64
68
  # LightCloud.set for more information
65
69
  def set(key, value, system=DEFAULT_SYSTEM)
66
- self.class.set(key, value, system, @systems)
70
+ storage_node = locate_node_or_init(key, system)
71
+ return storage_node.set(key, value)
67
72
  end
68
73
 
69
74
  #
70
75
  # Gets a value to a key on a LightCloud instance. See
71
76
  # LightCloud.get for more information.
72
77
  def get(key, system=DEFAULT_SYSTEM)
73
- self.class.get(key, system, @systems)
74
- end
75
-
76
- #
77
- # Delete a value from a LightCloud instance. See
78
- # LightCloud.delete for more information.
79
- def delete(key, system=DEFAULT_SYSTEM)
80
- self.class.delete(key, system, @systems)
81
- end
82
-
83
- #
84
- # Sets a value to a key in the LightCloud system.
85
- #
86
- # Set first checks to see if the key is already stored. If it is
87
- # it uses that same node to store the new value. Otherwise, it
88
- # determines where to store the value based on the hash_ring
89
- def self.set(key, value, system=DEFAULT_SYSTEM, systems=@@systems)
90
- storage_node = self.locate_node_or_init(key, system, systems)
91
- return storage_node.set(key, value)
92
- end
93
-
94
- #
95
- # Gets a value based on a key.
96
- def self.get(key, system=DEFAULT_SYSTEM, systems=@@systems)
97
78
  result = nil
98
79
 
99
80
  # Try to lookup key directly
100
- storage_node = self.get_storage_ring(system, systems).get_node(key)
81
+ storage_node = get_storage_ring(system).get_node(key)
101
82
  value = storage_node.get(key)
102
83
 
103
84
  result = value unless value.nil?
104
85
 
105
86
  # Else use the lookup ring
106
87
  if result.nil?
107
- storage_node = self.locate_node(key, system, systems)
88
+ storage_node = locate_node(key, system)
108
89
 
109
90
  result = storage_node.get(key) unless storage_node.nil?
110
91
  end
111
92
 
112
- result
93
+ result
113
94
  end
114
95
 
115
96
  #
116
- # Lookup the key and delete it from both the storage ring
117
- # and lookup ring
118
- def self.delete(key, system=DEFAULT_SYSTEM, systems=@@systems)
119
- storage_node = self.locate_node(key, system, systems)
97
+ # Delete a value from a LightCloud instance. See
98
+ # LightCloud.delete for more information.
99
+ def delete(key, system=DEFAULT_SYSTEM)
100
+ storage_node = locate_node(key, system)
120
101
 
121
- storage_node = get_storage_ring(system, systems).get_node(key) if storage_node.nil?
122
- lookup_nodes = get_lookup_ring(system, systems).iterate_nodes(key)
102
+ storage_node = get_storage_ring(system).get_node(key) if storage_node.nil?
103
+ lookup_nodes = get_lookup_ring(system).iterate_nodes(key)
123
104
  lookup_nodes.each_index do |i|
124
105
  break if i > 1
125
106
 
@@ -133,13 +114,13 @@ class LightCloud
133
114
  #--
134
115
  # Lookup Cloud
135
116
  #++
136
- def self.locate_node_or_init(key, system, systems=@@systems)
137
- storage_node = self.locate_node(key, system, systems)
117
+ def locate_node_or_init(key, system)
118
+ storage_node = locate_node(key, system)
138
119
 
139
120
  if storage_node.nil?
140
- storage_node = self.get_storage_ring(system, systems).get_node(key)
121
+ storage_node = get_storage_ring(system).get_node(key)
141
122
 
142
- lookup_node = self.get_lookup_ring(system, systems).get_node(key)
123
+ lookup_node = get_lookup_ring(system).get_node(key)
143
124
  lookup_node.set(key, storage_node.to_s)
144
125
  end
145
126
 
@@ -149,8 +130,8 @@ class LightCloud
149
130
  #
150
131
  # Locates a node in the lookup ring, returning the node if it is found, or
151
132
  # nil otherwise.
152
- def self.locate_node(key, system=DEFAULT_SYSTEM, systems=@@systems)
153
- nodes = self.get_lookup_ring(system, systems).iterate_nodes(key)
133
+ def locate_node(key, system=DEFAULT_SYSTEM)
134
+ nodes = get_lookup_ring(system).iterate_nodes(key)
154
135
 
155
136
  lookups = 0
156
137
  value = nil
@@ -167,14 +148,14 @@ class LightCloud
167
148
  return nil if value.nil?
168
149
 
169
150
  if lookups == 0
170
- return self.get_storage_node(value, system, systems)
151
+ return get_storage_node(value, system)
171
152
  else
172
- return self._clean_up_ring(key, value, system, systems)
153
+ return _clean_up_ring(key, value, system)
173
154
  end
174
155
  end
175
156
 
176
- def self._clean_up_ring(key, value, system, systems=@@systems)
177
- nodes = self.get_lookup_ring(system, systems).iterate_nodes(key)
157
+ def _clean_up_ring(key, value, system)
158
+ nodes = get_lookup_ring(system).iterate_nodes(key)
178
159
 
179
160
  nodes.each_index do |i|
180
161
  break if i > 1
@@ -187,25 +168,84 @@ class LightCloud
187
168
  end
188
169
  end
189
170
 
190
- return self.get_storage_node(value, system, systems)
171
+ return get_storage_node(value, system)
191
172
  end
192
173
 
193
174
  #--
194
175
  # Accessors for rings
195
176
  #++
196
- def self.get_lookup_ring(system=DEFAULT_SYSTEM, systems=@@systems)
197
- systems[system][0]
177
+ def get_lookup_ring(system=DEFAULT_SYSTEM)
178
+ @systems[system][0]
198
179
  end
199
180
 
200
- def self.get_storage_ring(system=DEFAULT_SYSTEM, systems=@@systems)
201
- systems[system][1]
181
+ def get_storage_ring(system=DEFAULT_SYSTEM)
182
+ @systems[system][1]
202
183
  end
203
184
 
204
185
  #--
205
186
  # Accessors for nodes
206
187
  #++
207
- def self.get_storage_node(name, system=DEFAULT_SYSTEM, systems=@@systems)
208
- systems[system][3][name]
188
+ def get_storage_node(name, system=DEFAULT_SYSTEM)
189
+ @systems[system][3][name]
190
+ end
191
+
192
+ #
193
+ # Given a set of nodes it creates the the nodes as Tokyo
194
+ # Tyrant objects and returns a hash ring with them
195
+ def generate_ring(nodes)
196
+ objects = []
197
+ name_to_obj = {}
198
+
199
+ nodes.each do |name, nodelist|
200
+ obj = TyrantNode.new(name, nodelist)
201
+ name_to_obj[name] = obj
202
+
203
+ objects.push(obj)
204
+ end
205
+
206
+ return HashRing.new(objects), name_to_obj
207
+ end
208
+
209
+ #--
210
+ # CLASS METHODS
211
+ #++
212
+ #
213
+ # Initializes LightCloud library with lookup and storage nodes.
214
+ # This only needs to be called with servers with which you intend
215
+ # to use the class methods (set/get/delete)
216
+ def self.init(lookup_nodes, storage_nodes, system=DEFAULT_SYSTEM)
217
+ instance.add_system(lookup_nodes, storage_nodes, system)
218
+ end
219
+
220
+ #
221
+ # Sets a value to a key in the LightCloud system.
222
+ #
223
+ # Set first checks to see if the key is already stored. If it is
224
+ # it uses that same node to store the new value. Otherwise, it
225
+ # determines where to store the value based on the hash_ring
226
+ def self.set(key, value, system=DEFAULT_SYSTEM)
227
+ instance.set(key, value, system)
228
+ end
229
+
230
+ #
231
+ # Gets a value based on a key.
232
+ def self.get(key, system=DEFAULT_SYSTEM)
233
+ instance.get(key, system)
234
+ end
235
+
236
+ #
237
+ # Lookup the key and delete it from both the storage ring
238
+ # and lookup ring
239
+ def self.delete(key, system=DEFAULT_SYSTEM)
240
+ instance.delete(key, system)
241
+ end
242
+
243
+ #--
244
+ # Instance accessor
245
+ #++
246
+ def self.instance
247
+ @@instance ||= self.new
248
+ @@instance
209
249
  end
210
250
 
211
251
  #--
@@ -225,21 +265,4 @@ class LightCloud
225
265
 
226
266
  return lookup_nodes, storage_nodes
227
267
  end
228
-
229
- #
230
- # Given a set of nodes it creates the the nodes as Tokyo
231
- # Tyrant objects and returns a hash ring with them
232
- def self.generate_ring(nodes)
233
- objects = []
234
- name_to_obj = {}
235
-
236
- nodes.each do |name, nodelist|
237
- obj = TyrantNode.new(name, nodelist)
238
- name_to_obj[name] = obj
239
-
240
- objects.push(obj)
241
- end
242
-
243
- return HashRing.new(objects), name_to_obj
244
- end
245
268
  end
@@ -33,9 +33,11 @@ describe LightCloud do
33
33
 
34
34
  @storage_ring = mock(HashRing)
35
35
  @storage_ring.stub!(:get_node).and_return(@storage_valid_node)
36
-
37
- LightCloud.stub!(:get_lookup_ring).and_return(@lookup_ring)
38
- LightCloud.stub!(:get_storage_ring).and_return(@storage_ring)
36
+
37
+ @cloud = LightCloud.new
38
+ @cloud.stub!(:get_lookup_ring).and_return(@lookup_ring)
39
+ @cloud.stub!(:get_storage_ring).and_return(@storage_ring)
40
+ @cloud.stub!(:get_storage_node).and_return(@storage_valid_node)
39
41
  end
40
42
 
41
43
  describe "node generation" do
@@ -65,14 +67,14 @@ describe LightCloud do
65
67
  end
66
68
 
67
69
  it "should return the tyrant nodes as a name to node hash" do
68
- unneeded, name_to_nodes = LightCloud.generate_ring(@valid_lookup_nodes)
70
+ unneeded, name_to_nodes = @cloud.generate_ring(@valid_lookup_nodes)
69
71
 
70
72
  name_to_nodes.should be_has_key('lookup1_A')
71
73
  name_to_nodes['lookup1_A'].should be_kind_of(TyrantNode)
72
74
  end
73
75
 
74
76
  it "should return a hash ring with the nodes" do
75
- ring, unneeded = LightCloud.generate_ring(@valid_lookup_nodes)
77
+ ring, unneeded = @cloud.generate_ring(@valid_lookup_nodes)
76
78
 
77
79
  ring.should be_kind_of(HashRing)
78
80
  end
@@ -86,13 +88,13 @@ describe LightCloud do
86
88
 
87
89
  describe "locating or initting a storage node by key" do
88
90
  after do
89
- LightCloud.should_receive(:locate_node).with(@key, anything, anything).once.and_return(@storage_node)
90
- LightCloud.locate_node_or_init(@key, LightCloud::DEFAULT_SYSTEM, anything)
91
+ @cloud.should_receive(:locate_node).with(@key, anything).once.and_return(@storage_node)
92
+ @cloud.locate_node_or_init(@key, LightCloud::DEFAULT_SYSTEM)
91
93
  end
92
94
 
93
95
  it "should just return the storage node if it was found" do
94
- LightCloud.should_not_receive(:get_storage_ring)
95
- LightCloud.should_not_receive(:get_lookup_ring)
96
+ @cloud.should_not_receive(:get_storage_ring)
97
+ @cloud.should_not_receive(:get_lookup_ring)
96
98
  end
97
99
 
98
100
  it "should set the lookup ring to point to the new storage node if no previous storage node was found" do
@@ -106,27 +108,27 @@ describe LightCloud do
106
108
  describe "locating a storage node by key" do
107
109
  it "should return the storage node if the key is found in the lookup ring" do
108
110
  @nodes[0].should_receive(:get).with(@key).and_return(@storage_node)
109
- LightCloud.should_receive(:get_storage_node).with(@storage_node, anything, anything).once
111
+ @cloud.should_receive(:get_storage_node).with(@storage_node, anything).once
110
112
 
111
- LightCloud.locate_node(@key)
113
+ @cloud.locate_node(@key)
112
114
  end
113
115
 
114
116
  it "should return nil if the key doesn't exist in the lookup ring" do
115
- LightCloud.locate_node(@key).should be_nil
117
+ @cloud.locate_node(@key).should be_nil
116
118
  end
117
119
 
118
120
  it "should attempt to clean up the lookup ring if the value is NOT found in the first node" do
119
121
  @nodes[1].should_receive(:get).with(@key).and_return(@storage_node)
120
- LightCloud.should_not_receive(:get_storage_node)
121
- LightCloud.should_receive(:_clean_up_ring).with(@key, @storage_node, anything, anything).once
122
+ @cloud.should_not_receive(:get_storage_node)
123
+ @cloud.should_receive(:_clean_up_ring).with(@key, @storage_node, anything).once
122
124
 
123
- LightCloud.locate_node(@key)
125
+ @cloud.locate_node(@key)
124
126
  end
125
127
  end
126
128
 
127
129
  describe "cleaning the lookup ring" do
128
130
  after do
129
- LightCloud._clean_up_ring(@key, @storage_node, LightCloud::DEFAULT_SYSTEM)
131
+ @cloud._clean_up_ring(@key, @storage_node, LightCloud::DEFAULT_SYSTEM)
130
132
  end
131
133
 
132
134
  it "should set the key/value onto the first node (index 0)" do
@@ -144,7 +146,7 @@ describe LightCloud do
144
146
  end
145
147
 
146
148
  it "should return the storage node lookup" do
147
- LightCloud.should_receive(:get_storage_node).with(@storage_node, anything, anything).once
149
+ @cloud.should_receive(:get_storage_node).with(@storage_node, anything).once
148
150
  end
149
151
  end
150
152
  end
@@ -154,15 +156,15 @@ describe LightCloud do
154
156
  @key = 'hello'
155
157
  @value = 'world!'
156
158
 
157
- LightCloud.stub!(:locate_node_or_init).and_return(@generic_node)
159
+ @cloud.stub!(:locate_node_or_init).and_return(@generic_node)
158
160
  end
159
161
 
160
162
  after do
161
- LightCloud.set(@key, @value)
163
+ @cloud.set(@key, @value)
162
164
  end
163
165
 
164
166
  it "should lookup the node or init for where to place key" do
165
- LightCloud.should_receive(:locate_node_or_init).with(@key, anything, anything).once.and_return(@generic_node)
167
+ @cloud.should_receive(:locate_node_or_init).with(@key, anything).once.and_return(@generic_node)
166
168
  end
167
169
 
168
170
  it "should set the value on the node returned by locate node or init" do
@@ -178,19 +180,19 @@ describe LightCloud do
178
180
  end
179
181
 
180
182
  after do
181
- LightCloud.get(@key).should eql(@value)
183
+ @cloud.get(@key).should eql(@value)
182
184
  end
183
185
 
184
186
  it "should not resort to the lookup table if it can find the key directly" do
185
187
  @generic_node.should_receive(:get).with(@key).and_return(@value)
186
188
 
187
- LightCloud.should_not_receive(:locate_node)
189
+ @cloud.should_not_receive(:locate_node)
188
190
  end
189
191
 
190
192
  it "should get the storage node from the lookup table if it can't find the key directly" do
191
193
  @generic_node.should_receive(:get).once.and_return(nil)
192
194
 
193
- LightCloud.should_receive(:locate_node).with(@key, anything, anything).and_return(@storage_valid_node)
195
+ @cloud.should_receive(:locate_node).with(@key, anything).and_return(@storage_valid_node)
194
196
  @storage_valid_node.should_receive(:get).with(@key).and_return(@value)
195
197
  end
196
198
  end
@@ -206,7 +208,7 @@ describe LightCloud do
206
208
  # spec in this context, which WOULD raise an error if
207
209
  # it failed
208
210
  lambda do
209
- LightCloud.delete(@key)
211
+ @cloud.delete(@key)
210
212
  end.should_not raise_error
211
213
  end
212
214
 
@@ -217,54 +219,55 @@ describe LightCloud do
217
219
  end
218
220
 
219
221
  it "should first try to get the storage node from lookup ring" do
220
- LightCloud.should_receive(:locate_node).with(@key, anything, anything).once.and_return(@generic_node)
221
- LightCloud.should_not_receive(:get_storage_ring)
222
-
222
+ @cloud.should_receive(:locate_node).with(@key, anything).once.and_return(@generic_node)
223
+ @cloud.should_not_receive(:get_storage_ring)
223
224
  end
224
225
 
225
226
  it "should try to get storage node directly if lookup ring failed" do
226
- LightCloud.should_receive(:locate_node).with(@key, anything, anything).once.and_return(nil)
227
- LightCloud.should_receive(:get_storage_ring).once.and_return(@storage_ring)
227
+ @cloud.should_receive(:locate_node).with(@key, anything).once.and_return(nil)
228
+ @cloud.should_receive(:get_storage_ring).once.and_return(@storage_ring)
228
229
  @storage_ring.should_receive(:get_node).with(@key).once.and_return(@generic_node)
229
230
 
230
231
  @generic_node.should_receive(:delete).with(@key)
231
232
  end
232
233
 
233
234
  it "should only delete from a storage node if one was found" do
234
- LightCloud.should_receive(:locate_node).with(@key, anything, anything).once.and_return(nil)
235
- LightCloud.should_receive(:get_storage_ring).once.and_return(@storage_ring)
235
+ @cloud.should_receive(:locate_node).with(@key, anything).once.and_return(nil)
236
+ @cloud.should_receive(:get_storage_ring).once.and_return(@storage_ring)
236
237
  @storage_ring.should_receive(:get_node).with(@key).once.and_return(nil)
237
238
  end
238
239
  end
239
240
 
240
- describe "lightcloud instances" do
241
+ describe "class methods" do
241
242
  before do
242
- cloud_config = {
243
- 'lookup1_A' => ['127.0.0.1:1234', '127.0.0.2:1234'],
244
- 'storage1_A' => ['127.0.0.1:4567', '127.0.0.2:4567']
245
- }
246
-
247
- @cloud = LightCloud.new(*LightCloud.generate_nodes(cloud_config))
243
+ LightCloud.should_receive(:instance).and_return(@cloud)
244
+
245
+ @key = 'foo'
246
+ @value = 'bar'
248
247
  end
249
248
 
250
- it "should forward arguments to class method init on initialize" do
251
- LightCloud.should_receive(:init).with('a', 'b', 'c', {}).once
252
- LightCloud.new('a','b','c')
249
+ it "should call add_system on singleton when init is called" do
250
+ @cloud.should_receive(:add_system).with(@valid_lookup_nodes, @valid_storage_nodes, anything)
251
+
252
+ LightCloud.init(@valid_lookup_nodes, @valid_storage_nodes)
253
253
  end
254
254
 
255
- it "should forward set arguments to class method" do
256
- LightCloud.should_receive(:set).with('key', 'value', anything, anything).once
257
- @cloud.set('key', 'value')
255
+ it "should call get on instance for get" do
256
+ @cloud.should_receive(:get).with(@key, anything).once
257
+
258
+ LightCloud.get(@key)
258
259
  end
259
260
 
260
- it "should forward get arguments to class method" do
261
- LightCloud.should_receive(:get).with('key', anything, anything).once
262
- @cloud.get('key')
261
+ it "should call set on instance for set" do
262
+ @cloud.should_receive(:set).with(@key, @value, anything).once
263
+
264
+ LightCloud.set(@key, @value)
263
265
  end
264
266
 
265
- it "should forward delete arguments to class method" do
266
- LightCloud.should_receive(:delete).with('key', anything, anything).once
267
- @cloud.delete('key')
267
+ it "should call delete on instance for delete" do
268
+ @cloud.should_receive(:delete).with(@key, anything).once
269
+
270
+ LightCloud.delete(@key)
268
271
  end
269
272
  end
270
273
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mitchellh-lightcloud
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.6"
4
+ version: "0.7"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitchell Hashimoto
@@ -56,7 +56,7 @@ rdoc_options:
56
56
  - --line-numbers
57
57
  - --inline-source
58
58
  - --title
59
- - hash_ring
59
+ - lightcloud
60
60
  - --main
61
61
  - README.rdoc
62
62
  require_paths: