-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ConfigLoaders::Redis: basic redis-backed configuration
Intended to be wrapped by `Throttled` config loader. n.b. if you use this, you will need to reset the redis configuration in your `after_prefork` hook. (Until #135 handles it automatically.)
- Loading branch information
Showing
3 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
require "resque" | ||
require "resque/pool" | ||
|
||
module Resque | ||
class Pool | ||
module ConfigLoaders | ||
|
||
# Read/write pool config from redis. | ||
# Should be wrapped in +ConfigLoaders::Throttled+. | ||
# | ||
# n.b. The environment needs to be passed in up-front, and will be ignored | ||
# during +call+. | ||
class Redis | ||
attr_reader :redis | ||
attr_reader :app, :pool, :env, :name | ||
|
||
def initialize(app_name: Pool.app_name, | ||
pool_name: Pool.pool_name, | ||
environment: "unknown", | ||
config_name: "config", | ||
redis: Resque.redis) | ||
@app = app_name | ||
@pool = pool_name | ||
@env = environment | ||
@name = config_name | ||
@redis = redis | ||
end | ||
|
||
# n.b. environment must be set up-front and will be ignored here. | ||
def call(_) | ||
redis.hgetall(key).tap do |h| | ||
h.each do |k,v| | ||
h[k] = v.to_i | ||
end | ||
end | ||
end | ||
|
||
# read individual worker config | ||
def [](worker) | ||
redis.hget(key, worker).to_i | ||
end | ||
|
||
# write individual worker config | ||
def []=(worker, count) | ||
redis.hset(key, worker, count.to_i) | ||
end | ||
|
||
# remove worker config | ||
def delete(worker) | ||
redis.multi do | ||
redis.hget(key, worker) | ||
redis.hdel(key, worker) | ||
end.first.to_i | ||
end | ||
|
||
# n.b. this is probably namespaced under +resque+ | ||
def key | ||
@key ||= ["pool", "config", app, pool, env, name].join(":") | ||
end | ||
|
||
end | ||
|
||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
require 'spec_helper' | ||
require 'resque/pool/config_loaders/redis' | ||
|
||
module Resque::Pool::ConfigLoaders | ||
|
||
describe Redis do | ||
before(:each) do | ||
Resque.redis.flushdb | ||
expect(Resque.redis.keys.count).to eq(0) | ||
end | ||
|
||
after(:all) do | ||
Resque.redis.flushdb | ||
end | ||
|
||
subject(:config) { Redis.new(environment: env) } | ||
subject(:env) { "prd" } | ||
|
||
describe "initialization" do | ||
it "uses default app_name and pool_name from Resque::Pool" do | ||
expect(Redis.new.app).to eq(Resque::Pool.app_name) | ||
expect(Redis.new.pool).to eq(Resque::Pool.pool_name) | ||
end | ||
it "uses default 'unknown' environment" do | ||
expect(Redis.new.env).to eq("unknown") | ||
end | ||
it "uses default 'config' name" do | ||
expect(Redis.new.name).to eq("config") | ||
end | ||
it "constructs redis key (expecting to be namespaced under resque)" do | ||
config = Redis.new(app_name: "foo", | ||
pool_name: "bar", | ||
environment: "dev", | ||
config_name: "override") | ||
expect(config.key).to eq("pool:config:foo:bar:dev:override") | ||
end | ||
it "uses resque's redis connection (probably namespaced)" do | ||
expect(Redis.new.redis).to eq(Resque.redis) | ||
expect(Redis.new(redis: :another).redis).to eq(:another) | ||
end | ||
end | ||
|
||
describe "basic API" do | ||
|
||
it "starts out empty" do | ||
expect(config.call(env)).to eq({}) | ||
end | ||
|
||
it "has hash-like index setters" do | ||
config["foo"] = 2 | ||
config["bar"] = 3 | ||
config["numbers_only"] = "elephant" | ||
expect(config.call(env)).to eq({ | ||
"foo" => 2, | ||
"bar" => 3, | ||
"numbers_only" => 0, | ||
}) | ||
end | ||
|
||
it "has indifferent access (but call returns string keys)" do | ||
config[:foo] = 1 | ||
config["foo"] = 2 | ||
expect(config[:foo]).to eq(2) | ||
expect(config.call(env)).to eq("foo" => 2) | ||
end | ||
|
||
it "has hash-like index getters" do | ||
config["foo"] = 86 | ||
config["bar"] = 99 | ||
expect(config["foo"]).to eq(86) | ||
expect(config["bar"]).to eq(99) | ||
expect(config["nonexistent"]).to eq(0) | ||
end | ||
|
||
it "can remove keys (not just set them to zero)" do | ||
config["foo"] = 99 | ||
config["bar"] = 7 | ||
expect(config.delete("foo")).to eq(99) | ||
expect(config.call(env)).to eq("bar" => 7) | ||
end | ||
|
||
end | ||
|
||
describe "persistance" do | ||
|
||
it "can be loaded from another instance" do | ||
config["qA"] = 24 | ||
config["qB"] = 33 | ||
config2 = Redis.new environment: env | ||
expect(config2.call(env)).to eq("qA" => 24, "qB" => 33) | ||
end | ||
|
||
it "won't clash with different configs" do | ||
config[:foo] = 1 | ||
config[:bar] = 2 | ||
config2 = Redis.new app_name: "another" | ||
expect(config2.call(env)).to eq({}) | ||
config3 = Redis.new pool_name: "another" | ||
expect(config3.call(env)).to eq({}) | ||
config4 = Redis.new config_name: "another" | ||
expect(config4.call(env)).to eq({}) | ||
config5 = Redis.new environment: "another" | ||
expect(config5.call(env)).to eq({}) | ||
end | ||
|
||
end | ||
|
||
end | ||
|
||
end |