Class: ShardRangeDB

Inherits:
Object show all
Defined in:
lib/adhd/models/shard_range.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nodesv) ⇒ ShardRangeDB

Returns a new instance of ShardRangeDB.



5
6
7
8
9
10
11
12
13
14
# File 'lib/adhd/models/shard_range.rb', line 5

def initialize(nodesv)
  @nodes = nodesv
  
  # Automatically get our shard_db address from our own node name
  @our_node = nodesv.our_node
  @local_shard_db = nodesv.our_node.get_shard_db
  
  puts "Assign default database for shard ranges (#{@local_shard_db})"
  ShardRange.use_database @local_shard_db
end

Instance Attribute Details

#local_shard_dbObject

Returns the value of attribute local_shard_db.



3
4
5
# File 'lib/adhd/models/shard_range.rb', line 3

def local_shard_db
  @local_shard_db
end

#nodesObject

Returns the value of attribute nodes.



3
4
5
# File 'lib/adhd/models/shard_range.rb', line 3

def nodes
  @nodes
end

#our_nodeObject

Returns the value of attribute our_node.



3
4
5
# File 'lib/adhd/models/shard_range.rb', line 3

def our_node
  @our_node
end

Instance Method Details

#build_shards(number) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/adhd/models/shard_range.rb', line 34

def build_shards(number)
  # Make a large list of possible id boundaries
  characters = []
  ("0".."9").each do |c|
    characters << c
  end
  ("a".."f").each do |c|
    characters << c
  end
  
  # Generate 36 x 36 keys to choose boundaries from
  all_keys = []
  characters.each do |c1|
    characters.each do |c2|
      characters.each do |c3|
        all_keys << (c1+c2+c3)
      end
    end
  end
  
  # Now chose our boundaries
  num_range_keys = all_keys.length
  approx_shard_size = (num_range_keys * 1.0) / number
  
  shard_starts = []
  (0...number).each do |n|
    shard_starts << (all_keys[(n * approx_shard_size).floor])
  end
  
  shard_ends = shard_starts.clone
  shard_ends << ("z" * 3)
  shard_ends.delete_at(0)
  
  # Finally build them!
  puts "Build Shards"
  (0...number).each do |n|
    puts "Shard #{n}: from #{shard_starts[n]} to #{shard_ends[n]}"
    shard_name = "sh_#{shard_starts[n]}_to_#{shard_ends[n]}"
    sr = ShardRange.new
    sr.range_start = shard_starts[n]
    sr.range_end = shard_ends[n]
    sr.shard_db_name = shard_name
    sr.save
  end    
end

#get_content_shardsObject



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/adhd/models/shard_range.rb', line 92

def get_content_shards
  # Return the content_shards of our node
  content_shards = {}
  ShardRange.by_node(:key => our_node.name).each do |s|
    
    # Build a content shard object
    content_shards[s.shard_db_name] = ContentShard.new(nodes, s)
  end        
  puts "Content shards #{content_shards.length}"
  content_shards
end

#get_doc_directly(internal_id) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/adhd/models/shard_range.rb', line 132

def get_doc_directly(internal_id)
  # Write a document directly to a nodes content repository
  doc_shard = get_shard(internal_id).first
  
  # TODO: Randomize the order of nodes for load balancing in retrieval!
  docx = []  
  doc_shard.get_nodes.each do |node|
    # Try to write the doc to this node
    begin
      remote_node = Node.by_name(:key => node).first
      remote_ndb = NodeDB.new(remote_node)
      remote_content_shard = ContentShard.new(remote_ndb, doc_shard)
      
      docx = ContentDoc.by_internal_id(:key => internal_id, :database => remote_content_shard.this_shard_db)
      if docx.length > 0
        return {:ok => true, :doc => docx.first, :db => remote_content_shard.this_shard_db }
      end
    rescue
      puts "Could not put doc in node #{node.name}"
      # TODO: change status or chose another management server
      remote_node.status = "UNAVAILABLE"
      remote_node.save      
    end
  end    
return {:ok => false }
end

#get_shard(internal_id) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/adhd/models/shard_range.rb', line 80

def get_shard(internal_id)
  # Finds the list of shards within which this ID lives
  all_shards = ShardRange.by_range_start 
  selected_shards = []
  all_shards.each do |a_shard| # TODO: linear search is inefficient -- create a view 
    if a_shard.range_start <= internal_id && a_shard.range_end > internal_id
      selected_shards << a_shard
    end
  end
  selected_shards
end

#syncObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/adhd/models/shard_range.rb', line 16

def sync
  # We replicate our state from the management node(s)
  # We never push content if we are only storage
  management_nodes = Node.by_is_management.reverse
  
  # NOTE: randomize the order for load balancing here    
  
  management_nodes.each do |mng_node|      
    remote_db = mng_node.get_shard_db      
    bool_from = @our_node.replicate_from(local_shard_db, mng_node, remote_db)
    if our_node.is_management 
      # Push any changes to other management nodes
      bool_to = @our_node.replicate_to(local_shard_db, mng_node, remote_db)
    end      
    break if bool_from && !our_node.is_management
  end
end

#write_doc_directly(content_doc) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/adhd/models/shard_range.rb', line 104

def write_doc_directly(content_doc)
  # Write a document directly to a nodes content repository
  success = {:ok => false , :reason => "No available node found"}
  doc_shard = get_shard(content_doc.internal_id).first
  doc_shard.get_nodes.each do |node|
    # Try to write the doc to this node
    begin
      remote_node = Node.by_name(:key => node).first
      remote_ndb = NodeDB.new(remote_node)
      remote_content_shard = ContentShard.new(remote_ndb, doc_shard)
      remote_content_shard.this_shard_db.save_doc(content_doc)
      success = {:ok => true, :doc => content_doc, :db => remote_content_shard.this_shard_db}
      break
    rescue RestClient::RequestFailed => rf
      if rf.http_code == 409
        puts "Document already there"
        return {:ok => false , :reason => "Document already in database"}
      end
    rescue Exception =>e
      puts "Could not put doc in node #{node} because of #{rf}"
      # TODO: change status or chose another management server
      remote_node.status = "UNAVAILABLE"
      remote_node.save
    end
  end
  return success
end