redis-script_manager 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +50 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +21 -0
- data/README.md +125 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/redis/script_manager.rb +334 -0
- data/lib/redis/script_manager/version.rb +17 -0
- data/redis-script_manager.gemspec +30 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e5a3988633cfd5315a4591a70edc873c902fe829
|
4
|
+
data.tar.gz: 9e279ed9af52df46d347c69a7f51ce21bf3cfc47
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4408f0dd1575e24f23cd5888d44ce7d539d47955cd9c19100237e886ead48b0b4fd49d3d6f84e79478c307e47d0ee69e5d77ca468029704b18bc30357adddf51
|
7
|
+
data.tar.gz: 754d3250762ec2fbbd0e058b403ba0797aa4d1899a0ba1bc04d79430574ed7d71e882242fbd81f39134a43b1f6b0aa88b58ae7b883e23c175cb3cb87fc54e307
|
data/.gitignore
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
*.bridgesupport
|
21
|
+
build-iPhoneOS/
|
22
|
+
build-iPhoneSimulator/
|
23
|
+
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
25
|
+
#
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
29
|
+
#
|
30
|
+
# vendor/Pods/
|
31
|
+
|
32
|
+
## Documentation cache and generated files:
|
33
|
+
/.yardoc/
|
34
|
+
/_yardoc/
|
35
|
+
/doc/
|
36
|
+
/rdoc/
|
37
|
+
|
38
|
+
## Environment normalization:
|
39
|
+
/.bundle/
|
40
|
+
/vendor/bundle
|
41
|
+
/lib/bundler/man/
|
42
|
+
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
45
|
+
# Gemfile.lock
|
46
|
+
# .ruby-version
|
47
|
+
# .ruby-gemset
|
48
|
+
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
50
|
+
.rvmrc
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redis-script_manager (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.10.3)
|
10
|
+
rake (10.5.0)
|
11
|
+
redis (3.3.3)
|
12
|
+
redis-namespace (1.5.3)
|
13
|
+
redis (~> 3.0, >= 3.0.4)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
bundler (~> 1.14)
|
20
|
+
minitest (~> 5.0)
|
21
|
+
rake (~> 10.0)
|
22
|
+
redis (~> 3.2)
|
23
|
+
redis-namespace (~> 1.5)
|
24
|
+
redis-script_manager!
|
25
|
+
|
26
|
+
BUNDLED WITH
|
27
|
+
1.15.3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 ProsperWorks
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Redis::ScriptManager
|
2
|
+
|
3
|
+
Redis::ScriptManager executes your Lua scripts in your Redis
|
4
|
+
infrastructure while managing trade-offs between bandwidth and
|
5
|
+
statefulness.
|
6
|
+
|
7
|
+
## Other Packages With Related Functionality
|
8
|
+
|
9
|
+
### resque-scheduler
|
10
|
+
|
11
|
+
https://github.com/resque/resque-scheduler
|
12
|
+
|
13
|
+
Resque::Scheduler::Lock::Resiliant manages its scripts with
|
14
|
+
load-on-init and EVALSHA-else-SCRIPT-LOAD-and-re-EVALSHA.
|
15
|
+
|
16
|
+
This is robust if there are no SCRIPT FLUSH or no pipelines, but it
|
17
|
+
will fail if a SCRIPT FLUSH can happen after init, then we run in a
|
18
|
+
pipeline.
|
19
|
+
|
20
|
+
The Lua scripts in resque-scheduler are only a few 100s of bytes in
|
21
|
+
size, so there is little to be gained by avoiding simply using EVAL.
|
22
|
+
|
23
|
+
Warning: resque-scheduler embeds "#{timeout}" in its Lua scripts. One
|
24
|
+
is unlikely to change resque-scheduler lock timeouts frequently, but
|
25
|
+
if so it could be possible to fill the Redis script cache with tons of
|
26
|
+
abandoned scripts.
|
27
|
+
|
28
|
+
### wolverine
|
29
|
+
|
30
|
+
https://github.com/Shopify/wolverine
|
31
|
+
http://shopify.github.io/wolverine/
|
32
|
+
|
33
|
+
Wolverine does load-on-init and EVALSHA-only. Assuming you have only
|
34
|
+
one Redis connection and SCRIPT FLUSH is never called, this is fine
|
35
|
+
even in a pipeline.
|
36
|
+
|
37
|
+
If you juggle multiple Redis connections or are worried about
|
38
|
+
inconsistent script cache contents, Wolverine might have some gaps.
|
39
|
+
|
40
|
+
Wolverine also has nice support for keeping your Lua scripts in a
|
41
|
+
repository of related .lua files, with support for common code
|
42
|
+
folding.
|
43
|
+
|
44
|
+
### redis-lua
|
45
|
+
|
46
|
+
https://github.com/chanks/redis-lua
|
47
|
+
|
48
|
+
Redis-lua does EVALSHA-else-SCRIPT-LOAD-and-re-EVALSHA when not in a
|
49
|
+
pipeline, simple EVAL otherwise.
|
50
|
+
|
51
|
+
This is great: this is always correct regardless of how many Redises
|
52
|
+
you are talking to, and calls outside of a pipeline will tend toward
|
53
|
+
minimal bandwith as the script cache gets warmed up.
|
54
|
+
|
55
|
+
However, if most of your scripting is done within a pipeline,
|
56
|
+
bandwidth use will stay high.
|
57
|
+
|
58
|
+
Early iterations of redis-script_manager were inspired by redis-lua.
|
59
|
+
|
60
|
+
### redis-rb-scripting
|
61
|
+
|
62
|
+
https://github.com/codekitchen/redis-rb-scripting
|
63
|
+
|
64
|
+
redis-rb-scripting does load-on-init plus EVALSHA-else-EVAL. It is
|
65
|
+
correct when the script cache is inconsistent but does not tend to
|
66
|
+
repopulate the cache in these cases.
|
67
|
+
|
68
|
+
redis-rb-scripting has no special provision for pipelines. Called in
|
69
|
+
a pipeline it will fail to recover from a cold cache.
|
70
|
+
|
71
|
+
Like wolverine, redis-rb-scripting also supports a repository of .lua
|
72
|
+
files, but without the common code folding.
|
73
|
+
|
74
|
+
### led
|
75
|
+
|
76
|
+
https://github.com/ciconia/led
|
77
|
+
|
78
|
+
Like redis-rb-scripting, led does load-on-init plus EVALSHA-else-EVAL.
|
79
|
+
It is correct when the script cache is inconsistent but does not tend
|
80
|
+
to repopulate the cache in these cases. It has no special provision
|
81
|
+
for pipelines. Called in a pipeline it will fail to recover from a
|
82
|
+
cold cache.
|
83
|
+
|
84
|
+
Like wolverine, redis-rb-scripting also supports a repository of .lua
|
85
|
+
files with common code folding.
|
86
|
+
|
87
|
+
|
88
|
+
## Installation
|
89
|
+
|
90
|
+
Add this line to your application's Gemfile:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
gem 'redis-script_manager'
|
94
|
+
```
|
95
|
+
|
96
|
+
And then execute:
|
97
|
+
|
98
|
+
$ bundle
|
99
|
+
|
100
|
+
Or install it yourself as:
|
101
|
+
|
102
|
+
$ gem install redis-script_manager
|
103
|
+
|
104
|
+
## Usage
|
105
|
+
|
106
|
+
TODO: Write usage instructions here
|
107
|
+
|
108
|
+
## Development
|
109
|
+
|
110
|
+
After checking out the repo, run `bin/setup` to install
|
111
|
+
dependencies. Then, run `rake test` to run the tests. You can also run
|
112
|
+
`bin/console` for an interactive prompt that will allow you to
|
113
|
+
experiment.
|
114
|
+
|
115
|
+
To install this gem onto your local machine, run `bundle exec rake
|
116
|
+
install`. To release a new version, update the version number in
|
117
|
+
`version.rb`, and then run `bundle exec rake release`, which will
|
118
|
+
create a git tag for the version, push git commits and tags, and push
|
119
|
+
the `.gem` file to [rubygems.org](https://rubygems.org).
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
Bug reports and pull requests are welcome on GitHub at
|
124
|
+
https://github.com/[USERNAME]/redis-script_manager.
|
125
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "redis/script_manager"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
require "redis/script_manager/version"
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
class ScriptManager
|
5
|
+
|
6
|
+
# TODO: rubocop
|
7
|
+
|
8
|
+
# TODO: rdoc
|
9
|
+
|
10
|
+
# Efficiently evaluates a Lua script on Redis.
|
11
|
+
#
|
12
|
+
# Makes a best effort to moderate bandwidth by leveraging EVALSHA
|
13
|
+
# and managing the SCRIPT LOAD state.
|
14
|
+
#
|
15
|
+
# @param redis a Redis to call, must respond to :eval, :evalsha,
|
16
|
+
# and :script
|
17
|
+
#
|
18
|
+
# @param lua a String, the Lua script to execute on redis
|
19
|
+
#
|
20
|
+
# @param keys a list of String, the keys which will bind to the KEYS
|
21
|
+
# list in the lua script.
|
22
|
+
#
|
23
|
+
# @param args a list of arguments which will bind to ARGV list in
|
24
|
+
# the lua script.
|
25
|
+
#
|
26
|
+
# @return the return result of evaluating lua against keys and args
|
27
|
+
# on redis
|
28
|
+
#
|
29
|
+
# TODO: Should this just monkey-patch over Redis.eval?
|
30
|
+
#
|
31
|
+
def self.eval_gently(redis,lua,keys=[],args=[])
|
32
|
+
[:eval,:evalsha,:script,:client].each do |method|
|
33
|
+
if !redis.respond_to?(method) || !redis.respond_to?(:script)
|
34
|
+
raise ArgumentError, "bogus redis #{redis}, no #{method}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
if !lua.is_a?(String)
|
38
|
+
raise ArgumentError, "bogus lua #{lua}"
|
39
|
+
end
|
40
|
+
if !keys.is_a?(Array)
|
41
|
+
raise ArgumentError, "bogus keys #{keys}: non-array"
|
42
|
+
end
|
43
|
+
keys_classes = keys.map(&:class).uniq
|
44
|
+
if [] != keys_classes - [String,Symbol]
|
45
|
+
raise ArgumentError, "bogus keys #{keys}: bad types in #{keys_classes}"
|
46
|
+
end
|
47
|
+
if !args.is_a?(Array)
|
48
|
+
raise ArgumentError, "bogus args #{args}"
|
49
|
+
end
|
50
|
+
if keys.size < 1
|
51
|
+
raise ArgumentError, 'Twemproxy intolerant of 0 keys in EVAL or EVALSHA'
|
52
|
+
end
|
53
|
+
#
|
54
|
+
# Per http://redis.io/commands/eval:
|
55
|
+
#
|
56
|
+
# Redis does not need to recompile the script every time as it
|
57
|
+
# uses an internal caching mechanism, however paying the cost of
|
58
|
+
# the additional bandwidth may not be optimal in many contexts."
|
59
|
+
#
|
60
|
+
# So, where the bandwidth imposed by uploading a short script is
|
61
|
+
# small, we will just use EVAL. Also, in MONITOR streams it can
|
62
|
+
# be nice to see the Lua instead of a bunch of noisy shas.
|
63
|
+
#
|
64
|
+
# However, where the script is long we try to conserve bandwidth
|
65
|
+
# by trying EVALSHA first. In case our script has never been
|
66
|
+
# uploaded before, or if the Redis suffered a FLUSHDB or SCRIPT
|
67
|
+
# FLUSH, we catch NOSCRIPT error, recover with SCRIPT LOAD, and
|
68
|
+
# repeat the EVALSHA.
|
69
|
+
#
|
70
|
+
# Caveat: if all of this is wrapped in a Redis pipeline, then the
|
71
|
+
# first EVALSHA returns a Redis::Future, not a result. We won't
|
72
|
+
# know whether it throws an error until after the pipeline - and
|
73
|
+
# after we've committed to the rest of our stream of commands.
|
74
|
+
# Thus, we can't recover from a NOSCRIPT error.
|
75
|
+
#
|
76
|
+
# To be safe in this pipelined-but-not-in-script-database edge
|
77
|
+
# case, we stick with simple EVAL when we detect we are running
|
78
|
+
# within a pipeline.
|
79
|
+
#
|
80
|
+
if configuration.do_minify_lua
|
81
|
+
lua = self.minify_lua(lua)
|
82
|
+
end
|
83
|
+
if lua.size < configuration.max_tiny_lua
|
84
|
+
_statsd_increment("eval")
|
85
|
+
return redis.eval(lua,keys,args)
|
86
|
+
end
|
87
|
+
if configuration.do_preload
|
88
|
+
#
|
89
|
+
# I tested RLEC in ali-staging with this script:
|
90
|
+
#
|
91
|
+
# sha = Redis.current.script(:load,'return redis.call("get",KEYS[1])')
|
92
|
+
# 100.times.map { |n| "foo-#{n}" }.each do |key|
|
93
|
+
# set = Redis.current.set(key,key)
|
94
|
+
# get = Redis.current.evalsha(sha,[key])
|
95
|
+
# puts "%7s %7s %7s" % [key,get,set]
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Sure enough, all 100 EVALSHA worked. Since ali-staging is a
|
99
|
+
# full-weight sharded RLEC, this tells me that SCRIPT LOAD
|
100
|
+
# propagates to all Redis shards in RLEC.
|
101
|
+
#
|
102
|
+
# Thus, we can trust that any script need only be sent down a
|
103
|
+
# particular Redis connection once. Thereafter we can assume
|
104
|
+
# EVALSHA will work.
|
105
|
+
#
|
106
|
+
# I do not know if the same thing will happen in Redis Cluster,
|
107
|
+
# and I am just about certain that the more primitive
|
108
|
+
# Twemproxy-over-array-of-plain-Redis will *not* propagate
|
109
|
+
# SCRIPT this way.
|
110
|
+
#
|
111
|
+
# Here is an twemproxy issue which tracks this question
|
112
|
+
# https://github.com/twitter/twemproxy/issues/68.
|
113
|
+
#
|
114
|
+
# This implementation is meant to transmit each script to each
|
115
|
+
# Redis no more than once per process, and thereafter be
|
116
|
+
# pure-EVALSHA.
|
117
|
+
#
|
118
|
+
sha = Digest::SHA1.hexdigest(lua)
|
119
|
+
sha_connection_key = [redis.object_id,sha]
|
120
|
+
if !@@preloaded_shas.include?(sha_connection_key)
|
121
|
+
_statsd_increment("preloaded_shas.cache_miss")
|
122
|
+
new_sha = redis.script(:load,lua)
|
123
|
+
if !new_sha.is_a?(Redis::Future)
|
124
|
+
if sha != new_sha
|
125
|
+
raise RuntimeError, "mismatch #{sha} vs #{new_sha} for lua #{lua}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
@@preloaded_shas << sha_connection_key
|
129
|
+
else
|
130
|
+
_statsd_increment("preloaded_shas.cache_hit")
|
131
|
+
end
|
132
|
+
result = redis.evalsha(sha,keys,args)
|
133
|
+
if configuration.preload_cache_size < @@preloaded_shas.size
|
134
|
+
#
|
135
|
+
# To defend against unbound cache size, at a predetermined
|
136
|
+
# limit throw away half of them.
|
137
|
+
#
|
138
|
+
# It is benign to re-load a script, just a performance blip.
|
139
|
+
#
|
140
|
+
# If these caches are running away in size, we have a worse
|
141
|
+
# time: redis.info.used_memory_lua could be growing without
|
142
|
+
# bound.
|
143
|
+
#
|
144
|
+
_statsd_increment("preloaded_shas.cache_purge")
|
145
|
+
num_to_keep = @@preloaded_shas.size / 2
|
146
|
+
@@preloaded_shas = @@preloaded_shas.to_a.sample(num_to_keep)
|
147
|
+
end
|
148
|
+
cache_size = @@preloaded_shas.size
|
149
|
+
_statsd_timing("preloaded_shas.cache_size",cache_size)
|
150
|
+
return result
|
151
|
+
end
|
152
|
+
in_pipeline = redis.client.is_a?(Redis::Pipeline) # thanks @marshall
|
153
|
+
if in_pipeline
|
154
|
+
_statsd_increment("pipeline_eval")
|
155
|
+
return redis.eval(lua,keys,args)
|
156
|
+
end
|
157
|
+
sha = Digest::SHA1.hexdigest(lua)
|
158
|
+
begin
|
159
|
+
_statsd_increment("evalsha1")
|
160
|
+
result = redis.evalsha(sha,keys,args)
|
161
|
+
if result.is_a?(Redis::Future)
|
162
|
+
#
|
163
|
+
# We should have detected this above where we checked whether
|
164
|
+
# redis.client was a pipeline.
|
165
|
+
#
|
166
|
+
raise "internal error: unexpected Redis::Future from evalsha"
|
167
|
+
end
|
168
|
+
result
|
169
|
+
rescue Redis::CommandError => ex
|
170
|
+
if nil != ex.to_s.index('NOSCRIPT') # some vulerability to change in msg
|
171
|
+
_statsd_increment("script_load")
|
172
|
+
new_sha = redis.script(:load,lua)
|
173
|
+
if sha != new_sha
|
174
|
+
raise RuntimeError, "mismatch #{sha} vs #{new_sha} for lua #{lua}"
|
175
|
+
end
|
176
|
+
_statsd_increment("evalsha2")
|
177
|
+
return redis.evalsha(sha,keys,args)
|
178
|
+
end
|
179
|
+
raise ex
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
@@preloaded_shas = Set[] # [redis.object_id,sha(lua)] which have been loaded
|
184
|
+
|
185
|
+
def self.purge_preloaded_shas # for test, clean state
|
186
|
+
@@preloaded_shas = Set[]
|
187
|
+
end
|
188
|
+
|
189
|
+
# To save bandwidth, minify the Lua code.
|
190
|
+
#
|
191
|
+
def self.minify_lua(lua)
|
192
|
+
lua
|
193
|
+
.split("\n")
|
194
|
+
.map { |l| l.gsub(/\s*--[^'"]*$/,'').strip } # rest-of-line comments
|
195
|
+
.reject { |l| /^\s*$/ =~ l } # blank lines
|
196
|
+
.join("\n")
|
197
|
+
end
|
198
|
+
|
199
|
+
# Reports a single count on the requested metric to statsd (if
|
200
|
+
# any).
|
201
|
+
#
|
202
|
+
# @param metric String
|
203
|
+
#
|
204
|
+
def self._statsd_increment(metric)
|
205
|
+
if configuration.statsd
|
206
|
+
configuration.statsd.increment(configuration.stats_prefix+metric)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Reports a timing on the requested metric to statsd (if any).
|
211
|
+
#
|
212
|
+
# @param metric String
|
213
|
+
#
|
214
|
+
def self._statsd_timing(metric,timing)
|
215
|
+
if configuration.statsd
|
216
|
+
configuration.statsd.timing(configuration.stats_prefix+metric,timing)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# @returns the current Redis::ScriptManager::Configuration.
|
221
|
+
#
|
222
|
+
def self.configuration
|
223
|
+
@configuration ||= Configuration.new
|
224
|
+
end
|
225
|
+
|
226
|
+
# Sets the current Redis::ScriptManager::Configuration.
|
227
|
+
#
|
228
|
+
def self.configuration=(configuration)
|
229
|
+
@configuration = configuration
|
230
|
+
end
|
231
|
+
|
232
|
+
# Yields the current Redis::ScriptManager::Configuration, supports
|
233
|
+
# the typical gem configuration pattern:
|
234
|
+
#
|
235
|
+
# Redis::ScriptManager.configure do |config|
|
236
|
+
# config.statsd = $statsd
|
237
|
+
# config.stats_prefix = 'foo'
|
238
|
+
# config.minify_lua = true
|
239
|
+
# config.max_tiny_lua = 1235
|
240
|
+
# config.preload_shas = true
|
241
|
+
# config.preload_cache_size = 10
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
def self.configure
|
245
|
+
yield configuration
|
246
|
+
end
|
247
|
+
|
248
|
+
class Configuration
|
249
|
+
|
250
|
+
# Defaults to nil. If non-nil, lots of stats will be tracked via
|
251
|
+
# statsd.increment and statsd.timing.
|
252
|
+
#
|
253
|
+
def statsd
|
254
|
+
@statsd || nil
|
255
|
+
end
|
256
|
+
def statsd=(statsd)
|
257
|
+
if statsd && !statsd.respond_to?(:increment)
|
258
|
+
raise ArgumentError, "bogus statsd A #{statsd}"
|
259
|
+
end
|
260
|
+
if statsd && !statsd.respond_to?(:timing)
|
261
|
+
raise ArgumentError, "bogus statsd B #{statsd}"
|
262
|
+
end
|
263
|
+
@statsd = statsd
|
264
|
+
end
|
265
|
+
|
266
|
+
# Defaults to ''.
|
267
|
+
#
|
268
|
+
# Prefixed onto all metrics.
|
269
|
+
#
|
270
|
+
def stats_prefix
|
271
|
+
@stats_prefix || ''
|
272
|
+
end
|
273
|
+
def stats_prefix=(stats_prefix)
|
274
|
+
if stats_prefix && !stats_prefix.is_a?(String)
|
275
|
+
raise ArgumentError, "bogus stats_prefix"
|
276
|
+
end
|
277
|
+
@stats_prefix = stats_prefix || ''
|
278
|
+
end
|
279
|
+
|
280
|
+
# Defaults to false.
|
281
|
+
#
|
282
|
+
# If true, all Lua is minified conservatively to save bandwidth
|
283
|
+
# before any other logic or evaluation.
|
284
|
+
#
|
285
|
+
def do_minify_lua
|
286
|
+
@do_minify_lua || false
|
287
|
+
end
|
288
|
+
def do_minify_lua=(do_minify_lua)
|
289
|
+
@do_minify_lua = [true,'true'].include?(do_minify_lua)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Scripts shorter than max_tiny_lua are always EVALed.
|
293
|
+
#
|
294
|
+
# We skip all logic regarding EVALSHA, extra round-trips for
|
295
|
+
# SCRIPT LOAD, etc.
|
296
|
+
#
|
297
|
+
# Defaults to 512. Integers and Strings which convert to Integers OK.
|
298
|
+
#
|
299
|
+
def max_tiny_lua
|
300
|
+
@max_tiny_lua || 512
|
301
|
+
end
|
302
|
+
def max_tiny_lua=(max_tiny_lua)
|
303
|
+
@max_tiny_lua = max_tiny_lua.to_i
|
304
|
+
end
|
305
|
+
|
306
|
+
# Defaults to false.
|
307
|
+
#
|
308
|
+
# If true, shas are preloaded for each Redis connection (which
|
309
|
+
# make safe to use EVALSHA even in pipelines).
|
310
|
+
#
|
311
|
+
def do_preload
|
312
|
+
@do_preload || false
|
313
|
+
end
|
314
|
+
def do_preload=(do_preload)
|
315
|
+
@do_preload = [true,'true'].include?(do_preload)
|
316
|
+
end
|
317
|
+
|
318
|
+
# The cache of shas which have been preloaded is not allowed to
|
319
|
+
# grow larger than this value.
|
320
|
+
#
|
321
|
+
# Defaults to 1000.
|
322
|
+
#
|
323
|
+
def preload_cache_size
|
324
|
+
@preload_cache_size || 1000
|
325
|
+
end
|
326
|
+
def preload_cache_size=(preload_cache_size)
|
327
|
+
@preload_cache_size = preload_cache_size.to_i
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Redis
|
2
|
+
class ScriptManager
|
3
|
+
#
|
4
|
+
# Version plan:
|
5
|
+
#
|
6
|
+
# 0.0.1 - still in Prosperworks/ALI/vendor/gems/redis-script_manager
|
7
|
+
#
|
8
|
+
# 0.0.2 - broke out into Prosperworks/redis-script_manager
|
9
|
+
#
|
10
|
+
# 0.1.0 - big README.md and Rdoc update, open repo
|
11
|
+
#
|
12
|
+
# 0.2.0 - solicit and incorporate initial feedback from select
|
13
|
+
# beta external users
|
14
|
+
#
|
15
|
+
VERSION = "0.0.2" # broken into standalone repo, not polished
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'redis/script_manager/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
|
7
|
+
spec.name = "redis-script_manager"
|
8
|
+
spec.version = Redis::ScriptManager::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
|
11
|
+
spec.authors = ["jhwillett"]
|
12
|
+
spec.email = ["jhw@prosperworks.com"]
|
13
|
+
|
14
|
+
spec.summary = 'Manage Lua script execution in Redis'
|
15
|
+
spec.homepage = 'https://github.com/ProsperWorks/redis-script_manager'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
27
|
+
spec.add_development_dependency 'redis', '~> 3.2'
|
28
|
+
spec.add_development_dependency 'redis-namespace', '~> 1.5'
|
29
|
+
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-script_manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jhwillett
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redis-namespace
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.5'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- jhw@prosperworks.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/console
|
98
|
+
- bin/setup
|
99
|
+
- lib/redis/script_manager.rb
|
100
|
+
- lib/redis/script_manager/version.rb
|
101
|
+
- redis-script_manager.gemspec
|
102
|
+
homepage: https://github.com/ProsperWorks/redis-script_manager
|
103
|
+
licenses: []
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.4.8
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Manage Lua script execution in Redis
|
125
|
+
test_files: []
|