Class: Dalli::Ring

Inherits:
Object
  • Object
show all
Defined in:
lib/dalli/ring.rb

Overview

An implementation of a consistent hash ring, designed to minimize the cache miss impact of adding or removing servers from the ring. That is, adding or removing a server from the ring should impact the key -> server mapping of ~ 1/N of the stored keys where N is the number of servers in the ring. This is done by creating a large number of “points” per server, distributed over the space 0x00000000 - 0xFFFFFFFF. For a given key, we calculate the CRC32 hash, and find the nearest “point” that is less than or equal to the the key’s hash. In this implemetation, each “point” is represented by a Dalli::Ring::Entry.

Defined Under Namespace

Classes: Entry

Constant Summary collapse

POINTS_PER_SERVER =

The number of entries on the continuum created per server in an equally weighted scenario.

160

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(servers_arg, protocol_implementation, options) ⇒ Ring

Returns a new instance of Ring.



26
27
28
29
30
31
32
33
34
35
# File 'lib/dalli/ring.rb', line 26

def initialize(servers_arg, protocol_implementation, options)
  @servers = servers_arg.map do |s|
    protocol_implementation.new(s, options)
  end
  @continuum = nil
  @continuum = build_continuum(servers) if servers.size > 1

  threadsafe! unless options[:threadsafe] == false
  @failover = options[:failover] != false
end

Instance Attribute Details

#continuumObject

this is the default in libmemcached



24
25
26
# File 'lib/dalli/ring.rb', line 24

def continuum
  @continuum
end

#serversObject

this is the default in libmemcached



24
25
26
# File 'lib/dalli/ring.rb', line 24

def servers
  @servers
end

Instance Method Details

#closeObject



97
98
99
# File 'lib/dalli/ring.rb', line 97

def close
  @servers.each(&:close)
end

#keys_grouped_by_server(key_arr) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/dalli/ring.rb', line 66

def keys_grouped_by_server(key_arr)
  key_arr.group_by do |key|
    server_for_key(key)
  rescue Dalli::RingError
    Dalli.logger.debug { "unable to get key #{key}" }
    nil
  end
end

#lockObject



75
76
77
78
79
80
81
82
# File 'lib/dalli/ring.rb', line 75

def lock
  @servers.each(&:lock!)
  begin
    yield
  ensure
    @servers.each(&:unlock!)
  end
end

#pipeline_consume_and_ignore_responsesObject



84
85
86
87
88
89
90
91
# File 'lib/dalli/ring.rb', line 84

def pipeline_consume_and_ignore_responses
  @servers.each do |s|
    s.request(:noop)
  rescue Dalli::NetworkError
    # Ignore this error, as it indicates the socket is unavailable
    # and there's no need to flush
  end
end

#server_for_key(key) ⇒ Object

Raises:



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/dalli/ring.rb', line 37

def server_for_key(key)
  server = if @continuum
             server_from_continuum(key)
           else
             @servers.first
           end

  # Note that the call to alive? has the side effect of initializing
  # the socket
  return server if server&.alive?

  raise Dalli::RingError, 'No server available'
end

#server_from_continuum(key) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/dalli/ring.rb', line 51

def server_from_continuum(key)
  hkey = hash_for(key)
  20.times do |try|
    server = server_for_hash_key(hkey)

    # Note that the call to alive? has the side effect of initializing
    # the socket
    return server if server.alive?
    break unless @failover

    hkey = hash_for("#{try}#{key}")
  end
  nil
end

#socket_timeoutObject



93
94
95
# File 'lib/dalli/ring.rb', line 93

def socket_timeout
  @servers.first.socket_timeout
end