lib/picky/backends/redis.rb in picky-3.6.7 vs lib/picky/backends/redis.rb in picky-3.6.8
- old
+ new
@@ -4,49 +4,58 @@
#
#
class Redis < Backend
- attr_reader :client
+ attr_reader :client,
+ :immediate
def initialize options = {}
super options
require 'redis'
- @client = options[:client] || ::Redis.new(:db => (options[:db] || 15))
+ @client = options[:client] || ::Redis.new(:db => (options[:db] || 15))
+ @immediate = options[:immediate]
rescue LoadError => e
warn_gem_missing 'redis', 'the Redis client'
end
# Returns an object that on #initial, #load returns an object that responds to:
# [:token] # => [id, id, id, id, id] (an array of ids)
#
def create_inverted bundle
extract_lambda_or(inverted, bundle, client) ||
- List.new(client, "#{bundle.identifier}:inverted")
+ List.new(client, "#{bundle.identifier}:inverted", immediate: immediate)
end
# Returns an object that on #initial, #load returns an object that responds to:
# [:token] # => 1.23 (a weight)
#
def create_weights bundle
extract_lambda_or(weights, bundle, client) ||
- Float.new(client, "#{bundle.identifier}:weights")
+ Float.new(client, "#{bundle.identifier}:weights", immediate: immediate)
end
# Returns an object that on #initial, #load returns an object that responds to:
# [:encoded] # => [:original, :original] (an array of original symbols this similarity encoded thing maps to)
#
def create_similarity bundle
extract_lambda_or(similarity, bundle, client) ||
- List.new(client, "#{bundle.identifier}:similarity")
+ List.new(client, "#{bundle.identifier}:similarity", immediate: immediate)
end
# Returns an object that on #initial, #load returns an object that responds to:
# [:key] # => value (a value for this config key)
#
def create_configuration bundle
extract_lambda_or(configuration, bundle, client) ||
- String.new(client, "#{bundle.identifier}:configuration")
+ String.new(client, "#{bundle.identifier}:configuration", immediate: immediate)
end
+ # Returns an object that on #initial, #load returns an object that responds to:
+ # [id] # => [:sym1, :sym2]
+ #
+ def create_realtime bundle
+ extract_lambda_or(similarity, bundle) ||
+ List.new(client, "#{bundle.identifier}:realtime", immediate: immediate)
+ end
# Does the Redis version already include
# scripting support?
#
def redis_with_scripting?
@@ -86,81 +95,108 @@
# routines, can do so analogue to this in their own
# backend implementations.
#
# Note: We use the amount and offset hints to speed Redis up.
#
+ # TODO What if it hasn't been dumped?
+ # Move this method to the actual backends?
+ #
def ids combinations, amount, offset
- # Just checked once on the first call.
- #
- if redis_with_scripting?
- @@script = "local intersected = redis.call('zinterstore', ARGV[1], #(KEYS), unpack(KEYS)); if intersected == 0 then redis.call('del', ARGV[1]); return {}; end local results = redis.call('zrange', ARGV[1], tonumber(ARGV[2]), tonumber(ARGV[3])); redis.call('del', ARGV[1]); return results;"
-
- require 'digest/sha1'
- @@sent_once = nil
-
- # Scripting version of #ids.
+ if immediate
+ # Just checked once on the first call.
#
- def ids combinations, amount, offset
- identifiers = combinations.inject([]) do |identifiers, combination|
- identifiers << "#{combination.identifier}"
- end
+ if redis_with_scripting?
+ @@script = "local intersected = redis.call('zinterstore', ARGV[1], #(KEYS), unpack(KEYS)); if intersected == 0 then redis.call('del', ARGV[1]); return {}; end local results = redis.call('zrange', ARGV[1], tonumber(ARGV[2]), tonumber(ARGV[3])); redis.call('del', ARGV[1]); return results;"
- # Assume it's using EVALSHA.
+ require 'digest/sha1'
+ @@sent_once = nil
+
+ # Scripting version of #ids.
#
- begin
- client.evalsha @@sent_once,
- identifiers.size,
- *identifiers,
- generate_intermediate_result_id,
- offset,
- (offset + amount)
- rescue RuntimeError => e
- # Make the server have a SHA-1 for the script.
- #
- @@sent_once = Digest::SHA1.hexdigest @@script
- client.eval @@script,
- identifiers.size,
- *identifiers,
- generate_intermediate_result_id,
- offset,
- (offset + amount)
+ class << self
+ def ids combinations, amount, offset
+ identifiers = combinations.inject([]) do |identifiers, combination|
+ identifiers << "#{combination.identifier}"
+ end
+
+ # Assume it's using EVALSHA.
+ #
+ begin
+ client.evalsha @@sent_once,
+ identifiers.size,
+ *identifiers,
+ generate_intermediate_result_id,
+ offset,
+ (offset + amount)
+ rescue RuntimeError => e
+ # Make the server have a SHA-1 for the script.
+ #
+ @@sent_once = Digest::SHA1.hexdigest @@script
+ client.eval @@script,
+ identifiers.size,
+ *identifiers,
+ generate_intermediate_result_id,
+ offset,
+ (offset + amount)
+ end
+ end
end
- end
- else
- # Non-Scripting version of #ids.
- #
- def ids combinations, amount, offset
- identifiers = combinations.inject([]) do |identifiers, combination|
- identifiers << "#{combination.identifier}"
- end
+ else
+ # Non-Scripting version of #ids.
+ #
+ class << self
+ def ids combinations, amount, offset
+ identifiers = combinations.inject([]) do |identifiers, combination|
+ identifiers << "#{combination.identifier}"
+ end
- result_id = generate_intermediate_result_id
+ result_id = generate_intermediate_result_id
- # Intersect and store.
- #
- intersected = client.zinterstore result_id, identifiers
+ # Intersect and store.
+ #
+ intersected = client.zinterstore result_id, identifiers
- # Return clean and early if there has been no intersection.
- #
- if intersected.zero?
- client.del result_id
- return []
- end
+ # Return clean and early if there has been no intersection.
+ #
+ if intersected.zero?
+ client.del result_id
+ return []
+ end
- # Get the stored result.
- #
- results = client.zrange result_id, offset, (offset + amount)
+ # Get the stored result.
+ #
+ results = client.zrange result_id, offset, (offset + amount)
- # Delete the stored result as it was only for temporary purposes.
- #
- # Note: I could also not delete it, but that
- # would not be clean at all.
- #
- client.del result_id
+ # Delete the stored result as it was only for temporary purposes.
+ #
+ # Note: I could also not delete it, but that
+ # would not be clean at all.
+ #
+ client.del result_id
- results
+ results
+ end
+ end
end
+ else
+ # TODO Refactor!
+ #
+ class << self
+ def ids combinations, _, _
+ # Get the ids for each combination.
+ #
+ id_arrays = combinations.inject([]) do |total, combination|
+ total << combination.ids
+ end
+
+ # Call the optimized C algorithm.
+ #
+ # Note: It orders the passed arrays by size.
+ #
+ Performant::Array.memory_efficient_intersect id_arrays
+ end
+ end
end
# Call the newly installed version.
#
ids combinations, amount, offset
@@ -182,10 +218,10 @@
@pid ||= Process.pid
end
# Use the host and pid (generated lazily in child processes) for the result.
#
def generate_intermediate_result_id
- :"#{host}:#{pid}:picky:result"
+ @intermediate_result_id ||= "#{host}:#{pid}:picky:result"
end
end
end
\ No newline at end of file