def get_session(env, sid)
unless sid and session = @pool.get(sid)
sid, session = generate_sid, {}
unless @pool.add(sid, session)
raise "Session collision on '#{sid.inspect}'"
end
end
[sid, session]
end
ここでセッションIDを作成してるっぽい。
unless sid and session = @pool.get(sid)
ここでsidの有無を確認していて、存在しなかった場合はgenerate_sid
を呼び出してsidを作成してるっぽい。
def generate_sid
loop do
sid = super
break sid unless @pool.get(sid)
end
end
super
で作成されたsidに対して@pool.get(sid)
でsidをkeyにしてmemcachedに格納してるっぽい。
重複やエラーがなくなるまでループしてる・・・?
ちなみにsuperはこれ。
def generate_sid(secure = @sid_secure)
if secure
SecureRandom.hex(@sid_length)
else
"%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
end
rescue NotImplementedError
generate_sid(false)
end
def get(key, options=nil)
resp = perform(:get, key)
(!resp || resp == 'Not found') ? nil : resp
end
perform(:get, key)
ではkeyの形式をチェックしたり、memcachedに追加する部分。
def perform(op, key, *args)
key = key.to_s
validate_key(key)
key = key_with_namespace(key)
begin
server = ring.server_for_key(key)
server.request(op, key, *args)
rescue NetworkError => e
Dalli.logger.debug { e.message }
Dalli.logger.debug { "retrying request with new server" }
retry
end
end
更にring.server_for_keyを読んでみる
ring.server_for_key(key)
ってなんぞ?
def ring
@ring ||= Dalli::Ring.new(
Array(@servers).map do |s|
Dalli::Server.new(s, @options)
end, @options
)
end
Dalli::Ring.new
も読むとして、まずはArray(@servers).map
が気になる。
@serverは、initializeで作成されてた。
def initialize(servers=nil, options={})
@servers = env_servers || servers || '127.0.0.1:11211'
@options = { :expires_in => 0 }.merge(options)
self.extend(Dalli::Client::MemcacheClientCompatibility) if Dalli::Client.compatibility_mode
@ring = nil
end
大体想像はつくけどenv_servers
も一応見てみる。
def env_servers
ENV['MEMCACHE_SERVERS'] ? ENV['MEMCACHE_SERVERS'].split(',') : nil
end
設定ファイルを見ているみたい。無かったら引数で受け取ったserversを使うしそれも無かった場合はデフォルトの'127.0.0.1:11211'
を使用する、ということみたい。
Dalli::Ring.newみてみる。
def initialize(servers, options)
@servers = servers
@continuum = nil
if servers.size > 1
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
continuum = []
servers.each do |server|
entry_count_for(server, servers.size, total_weight).times do |idx|
hash = Digest::SHA1.hexdigest("#{server.hostname}:#{server.port}:#{idx}")
value = Integer("0x#{hash[0..7]}")
continuum << Dalli::Ring::Entry.new(value, server)
end
end
@continuum = continuum.sort { |a, b| a.value <=> b.value }
end
threadsafe! unless options[:threadsafe] == false
@failover = options[:failover] != false
end
正直読むのきついから次いく
def server_for_key(key)
if @continuum
hkey = hash_for(key)
20.times do |try|
entryidx = self.class.binary_search(@continuum, hkey)
server = @continuum[entryidx].server
return server if server.alive?
break unless @failover
hkey = hash_for("#{try}#{key}")
end
else
server = @servers.first
return server if server && server.alive?
end
raise Dalli::RingError, "No server available"
end
return server if server.alive?
これとreturn server if server && server.alive?
これが返り値になってるけど、結局はserver
とはDalli::Server classのインスタンスなのでDalli::Server#requestを見てみる。
def request(op, *args)
raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}" unless alive?
begin
send(op, *args)
rescue Dalli::NetworkError
raise
rescue Dalli::MarshalError => ex
Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
Dalli.logger.error ex.backtrace.join("\n\t")
false
rescue Dalli::DalliError
raise
rescue => ex
Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
Dalli.logger.error ex.backtrace.join("\n\t")
down!
end
end
rescueしまくってるけど気にしない。要するにsend(op, *args)
これ。
Object#sendを呼び出してるので、op
ってなんだよという話。
ring.server_for_key
で帰ってきたserverに対してserver.request(op, key, *args)
と呼んでいるので、perform(op, key, *args)
このopを見ればいい。
つまり
def generate_sid
loop do
sid = super
break sid unless @pool.get(sid)
end
end
の@pool.get(sid)
が呼び出しているのが、
def get(key, options=nil)
resp = perform(:get, key)
(!resp || resp == 'Not found') ? nil : resp
end
なのでopの正体は:get
。
つまりDalli::Server#getを見ればいいのでは。
def get(key)
req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
write(req)
generic_response(true)
end
writeしてる。
def write(bytes)
begin
@sock.write(bytes)
rescue SystemCallError, Timeout::Error
failure!
retry
end
end
@sockはring.server_for_key
が呼ばれた時に、server.alive?
が呼ばれて、その中で生成されている。
def alive?
return true if @sock
if @last_down_at && @last_down_at + options[:down_retry_delay] >= Time.now
time = @last_down_at + options[:down_retry_delay] - Time.now
Dalli.logger.debug { "down_retry_delay not reached for #{hostname}:#{port} (%.3f seconds left)" % time }
return false
end
connect
!!@sock
rescue Dalli::NetworkError
false
end
このconnectの中
def connect
Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
begin
if @hostname =~ /^\//
@sock = USocket.new(hostname)
elsif options[:async]
raise Dalli::DalliError, "EM support not enabled, as em-synchrony is not installed." if not defined?(AsyncSocket)
@sock = AsyncSocket.open(hostname, port, :timeout => options[:socket_timeout])
else
@sock = KSocket.open(hostname, port, :timeout => options[:socket_timeout])
end
@version = version # trigger actual connect
sasl_authentication if need_auth?
up!
rescue Dalli::DalliError # SASL auth failure
raise
rescue SystemCallError, Timeout::Error, EOFError, SocketError
# SocketError = DNS resolution failure
failure!
retry
end
end
ちょっとこのあたりで読むのが辛くなってきたから一旦終わる。