lib/sidekiq_unique_jobs/script/scripts.rb
# frozen_string_literal: true
module SidekiqUniqueJobs
module Script
# Interface to dealing with .lua files
#
# @author Mikael Henriksson <mikael@mhenrixon.com>
class Scripts
#
# @return [Concurrent::Map] a map with configured script paths
SCRIPT_PATHS = Concurrent::Map.new
#
# Fetch a scripts configuration for path
#
# @param [Pathname] root_path the path to scripts
#
# @return [Scripts] a collection of scripts
#
def self.fetch(root_path)
if (scripts = SCRIPT_PATHS.get(root_path))
return scripts
end
create(root_path)
end
#
# Create a new scripts collection based on path
#
# @param [Pathname] root_path the path to scripts
#
# @return [Scripts] a collection of scripts
#
def self.create(root_path)
scripts = new(root_path)
store(scripts)
end
#
# Store the scripts collection in memory
#
# @param [Scripts] scripts the path to scripts
#
# @return [Scripts] the scripts instance that was stored
#
def self.store(scripts)
SCRIPT_PATHS.put(scripts.root_path, scripts)
scripts
end
#
# @!attribute [r] scripts
# @return [Concurrent::Map] a collection of loaded scripts
attr_reader :scripts
#
# @!attribute [r] root_path
# @return [Pathname] the path to the directory with lua scripts
attr_reader :root_path
def initialize(path)
raise ArgumentError, "path needs to be a Pathname" unless path.is_a?(Pathname)
@scripts = Concurrent::Map.new
@root_path = path
end
def fetch(name, conn)
if (script = scripts.get(name.to_sym))
return script
end
load(name, conn)
end
def load(name, conn)
script = Script.load(name, root_path, conn)
scripts.put(name.to_sym, script)
script
end
def delete(script)
if script.is_a?(Script)
scripts.delete(script.name)
else
scripts.delete(script.to_sym)
end
end
def kill(conn)
if conn.respond_to?(:namespace)
conn.redis.script(:kill)
else
conn.script(:kill)
end
end
#
# Execute a lua script with given name
#
# @note this method is recursive if we need to load a lua script
# that wasn't previously loaded.
#
# @param [Symbol] name the name of the script to execute
# @param [Redis] conn the redis connection to use for execution
# @param [Array<String>] keys script keys
# @param [Array<Object>] argv script arguments
#
# @return value from script
#
def execute(name, conn, keys: [], argv: [])
script = fetch(name, conn)
conn.evalsha(script.sha, keys, argv)
end
def count
scripts.keys.size
end
end
end
end