redcord 0.0.3 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/redcord.rb +1 -0
- data/lib/redcord/actions.rb +78 -6
- data/lib/redcord/attribute.rb +110 -13
- data/lib/redcord/base.rb +13 -3
- data/lib/redcord/connection_pool.rb +28 -0
- data/lib/redcord/migration.rb +2 -0
- data/lib/redcord/migration/index.rb +57 -0
- data/lib/redcord/migration/ttl.rb +9 -4
- data/lib/redcord/railtie.rb +0 -1
- data/lib/redcord/redis.rb +200 -0
- data/lib/redcord/redis_connection.rb +29 -23
- data/lib/redcord/relation.rb +112 -14
- data/lib/redcord/serializer.rb +84 -33
- data/lib/redcord/server_scripts/create_hash.erb.lua +81 -0
- data/lib/redcord/server_scripts/delete_hash.erb.lua +17 -8
- data/lib/redcord/server_scripts/find_by_attr.erb.lua +51 -16
- data/lib/redcord/server_scripts/find_by_attr_count.erb.lua +45 -14
- data/lib/redcord/server_scripts/shared/index_helper_methods.erb.lua +45 -16
- data/lib/redcord/server_scripts/shared/lua_helper_methods.erb.lua +20 -4
- data/lib/redcord/server_scripts/shared/query_helper_methods.erb.lua +81 -14
- data/lib/redcord/server_scripts/update_hash.erb.lua +40 -26
- data/lib/redcord/tasks/redis.rake +15 -0
- data/lib/redcord/vacuum_helper.rb +90 -0
- metadata +21 -5
- data/lib/redcord/prepared_redis.rb +0 -147
- data/lib/redcord/server_scripts/create_hash_returning_id.erb.lua +0 -69
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redcord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chan Zuckerberg Initiative
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: connection_pool
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.2.3
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.2.3
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: sorbet
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,19 +176,21 @@ files:
|
|
162
176
|
- lib/redcord/attribute.rb
|
163
177
|
- lib/redcord/base.rb
|
164
178
|
- lib/redcord/configurations.rb
|
179
|
+
- lib/redcord/connection_pool.rb
|
165
180
|
- lib/redcord/logger.rb
|
166
181
|
- lib/redcord/lua_script_reader.rb
|
167
182
|
- lib/redcord/migration.rb
|
183
|
+
- lib/redcord/migration/index.rb
|
168
184
|
- lib/redcord/migration/migrator.rb
|
169
185
|
- lib/redcord/migration/ttl.rb
|
170
186
|
- lib/redcord/migration/version.rb
|
171
|
-
- lib/redcord/prepared_redis.rb
|
172
187
|
- lib/redcord/railtie.rb
|
173
188
|
- lib/redcord/range_interval.rb
|
189
|
+
- lib/redcord/redis.rb
|
174
190
|
- lib/redcord/redis_connection.rb
|
175
191
|
- lib/redcord/relation.rb
|
176
192
|
- lib/redcord/serializer.rb
|
177
|
-
- lib/redcord/server_scripts/
|
193
|
+
- lib/redcord/server_scripts/create_hash.erb.lua
|
178
194
|
- lib/redcord/server_scripts/delete_hash.erb.lua
|
179
195
|
- lib/redcord/server_scripts/find_by_attr.erb.lua
|
180
196
|
- lib/redcord/server_scripts/find_by_attr_count.erb.lua
|
@@ -184,6 +200,7 @@ files:
|
|
184
200
|
- lib/redcord/server_scripts/update_hash.erb.lua
|
185
201
|
- lib/redcord/tasks/redis.rake
|
186
202
|
- lib/redcord/tracer.rb
|
203
|
+
- lib/redcord/vacuum_helper.rb
|
187
204
|
homepage: https://github.com/chanzuckerberg/redis-record
|
188
205
|
licenses:
|
189
206
|
- MIT
|
@@ -203,8 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
220
|
- !ruby/object:Gem::Version
|
204
221
|
version: '0'
|
205
222
|
requirements: []
|
206
|
-
|
207
|
-
rubygems_version: 2.7.6.2
|
223
|
+
rubygems_version: 3.0.8
|
208
224
|
signing_key:
|
209
225
|
specification_version: 4
|
210
226
|
summary: A Ruby ORM like Active Record, but for Redis
|
@@ -1,147 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
require 'redis'
|
3
|
-
|
4
|
-
# TODO: Rename Redcord::PreparedRedis -> Redcord::Redis
|
5
|
-
class Redcord::PreparedRedis < Redis
|
6
|
-
extend T::Sig
|
7
|
-
|
8
|
-
sig do
|
9
|
-
params(
|
10
|
-
key: T.any(String, Symbol),
|
11
|
-
args: T::Hash[T.untyped, T.untyped],
|
12
|
-
).returns(Integer)
|
13
|
-
end
|
14
|
-
def create_hash_returning_id(key, args)
|
15
|
-
Redcord::Base.trace(
|
16
|
-
'redcord_redis_create_hash_returning_id',
|
17
|
-
model_name: key,
|
18
|
-
) do
|
19
|
-
evalsha(
|
20
|
-
self.class.server_script_shas[:create_hash_returning_id],
|
21
|
-
keys: [key],
|
22
|
-
argv: args.to_a.flatten,
|
23
|
-
).to_i
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
sig do
|
28
|
-
params(
|
29
|
-
model: String,
|
30
|
-
id: Integer,
|
31
|
-
args: T::Hash[T.untyped, T.untyped],
|
32
|
-
).void
|
33
|
-
end
|
34
|
-
def update_hash(model, id, args)
|
35
|
-
Redcord::Base.trace(
|
36
|
-
'redcord_redis_update_hash',
|
37
|
-
model_name: model,
|
38
|
-
) do
|
39
|
-
evalsha(
|
40
|
-
self.class.server_script_shas[:update_hash],
|
41
|
-
keys: [model, id],
|
42
|
-
argv: args.to_a.flatten,
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
sig do
|
48
|
-
params(
|
49
|
-
model: String,
|
50
|
-
id: Integer
|
51
|
-
).returns(Integer)
|
52
|
-
end
|
53
|
-
def delete_hash(model, id)
|
54
|
-
Redcord::Base.trace(
|
55
|
-
'redcord_redis_delete_hash',
|
56
|
-
model_name: model,
|
57
|
-
) do
|
58
|
-
evalsha(
|
59
|
-
self.class.server_script_shas[:delete_hash],
|
60
|
-
keys: [model, id]
|
61
|
-
)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
sig do
|
66
|
-
params(
|
67
|
-
model: String,
|
68
|
-
query_conditions: T::Hash[T.untyped, T.untyped],
|
69
|
-
select_attrs: T::Set[Symbol]
|
70
|
-
).returns(T::Hash[Integer, T::Hash[T.untyped, T.untyped]])
|
71
|
-
end
|
72
|
-
def find_by_attr(model, query_conditions, select_attrs=Set.new)
|
73
|
-
Redcord::Base.trace(
|
74
|
-
'redcord_redis_find_by_attr',
|
75
|
-
model_name: model,
|
76
|
-
) do
|
77
|
-
res = evalsha(
|
78
|
-
self.class.server_script_shas[:find_by_attr],
|
79
|
-
keys: [model] + query_conditions.to_a.flatten,
|
80
|
-
argv: select_attrs.to_a.flatten
|
81
|
-
)
|
82
|
-
# The Lua script will return this as a flattened array.
|
83
|
-
# Convert the result into a hash of {id -> model hash}
|
84
|
-
res_hash = res.each_slice(2)
|
85
|
-
res_hash.map { |key, val| [key.to_i, val.each_slice(2).to_h] }.to_h
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
sig do
|
90
|
-
params(
|
91
|
-
model: String,
|
92
|
-
query_conditions: T::Hash[T.untyped, T.untyped]
|
93
|
-
).returns(Integer)
|
94
|
-
end
|
95
|
-
def find_by_attr_count(model, query_conditions)
|
96
|
-
Redcord::Base.trace(
|
97
|
-
'redcord_redis_find_by_attr_count',
|
98
|
-
model_name: model,
|
99
|
-
) do
|
100
|
-
evalsha(
|
101
|
-
self.class.server_script_shas[:find_by_attr_count],
|
102
|
-
keys: [model] + query_conditions.to_a.flatten,
|
103
|
-
)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
sig { void }
|
108
|
-
def load_server_scripts!
|
109
|
-
script_names = Dir[File.join(
|
110
|
-
__dir__,
|
111
|
-
'server_scripts/*.lua',
|
112
|
-
)].map do |filename|
|
113
|
-
# lib/redcord/server_scripts/find_by_attr.erb.lua -> find_by_attr
|
114
|
-
T.must(filename.split('/').last).split('.').first&.to_sym
|
115
|
-
end
|
116
|
-
|
117
|
-
res = pipelined do
|
118
|
-
script_names.each do |script_name|
|
119
|
-
script(
|
120
|
-
:load,
|
121
|
-
Redcord::LuaScriptReader.read_lua_script(script_name.to_s),
|
122
|
-
)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
if self.class.class_variable_get(:@@server_script_shas).nil?
|
127
|
-
self.class.class_variable_set(
|
128
|
-
:@@server_script_shas,
|
129
|
-
script_names.zip(res).to_h
|
130
|
-
)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
@@server_script_shas = T.let(nil, T.nilable(T::Hash[Symbol, String]))
|
135
|
-
|
136
|
-
sig { returns(T::Hash[Symbol, String]) }
|
137
|
-
def self.server_script_shas
|
138
|
-
T.must(@@server_script_shas)
|
139
|
-
end
|
140
|
-
|
141
|
-
sig { void }
|
142
|
-
def self.load_server_scripts!
|
143
|
-
Redcord::Base.configurations[Rails.env].each do |_, config|
|
144
|
-
new(**(config.symbolize_keys)).load_server_scripts!
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
--[[
|
2
|
-
EVALSHA SHA1(__FILE__) [field value ...]
|
3
|
-
> Time complexity: O(N) where N is the number of fields being set.
|
4
|
-
|
5
|
-
Create a hash with the specified fields to their respective values stored at
|
6
|
-
key when key does not exist.
|
7
|
-
|
8
|
-
# Return value
|
9
|
-
The id of the created hash as a string.
|
10
|
-
--]]
|
11
|
-
|
12
|
-
-- The arguments can be accessed by Lua using the KEYS global variable in the
|
13
|
-
-- form of a one-based array (so KEYS[1], KEYS[2], ...).
|
14
|
-
-- All the additional arguments should not represent key names and can be
|
15
|
-
-- accessed by Lua using the ARGV global variable, very similarly to what
|
16
|
-
-- happens with keys (so ARGV[1], ARGV[2], ...).
|
17
|
-
|
18
|
-
-- KEYS[1] = Model.name
|
19
|
-
-- ARGV[1...2N] = attr_key attr_val [attr_key attr_val ..]
|
20
|
-
<%= include_lua 'shared/lua_helper_methods' %>
|
21
|
-
<%= include_lua 'shared/index_helper_methods' %>
|
22
|
-
|
23
|
-
-- Validate input to script before making Redis db calls
|
24
|
-
if #KEYS ~= 1 then
|
25
|
-
error('Expected keys to be of size 1')
|
26
|
-
end
|
27
|
-
if #ARGV % 2 ~= 0 then
|
28
|
-
error('Expected an even number of arguments')
|
29
|
-
end
|
30
|
-
|
31
|
-
local model = KEYS[1]
|
32
|
-
|
33
|
-
-- Call the Redis command: INCR "#{Model.name}:id_seq". If "#{Model.name}:id_seq" does
|
34
|
-
-- not exist, the command returns 0. It errors if the id_seq overflows a 64 bit
|
35
|
-
-- signed integer.
|
36
|
-
redis.call('incr', model .. ':id_seq')
|
37
|
-
|
38
|
-
-- The Lua version used by Redis does not support 64 bit integers:
|
39
|
-
-- https://github.com/antirez/redis/issues/5261
|
40
|
-
-- We ignore the integer response from INCR and use the string response from
|
41
|
-
-- the GET/MGET command.
|
42
|
-
local id, ttl = unpack(redis.call('mget', model .. ':id_seq', model .. ':ttl'))
|
43
|
-
local key = model .. ':id:' .. id
|
44
|
-
|
45
|
-
-- Forward the script arguments to the Redis command HSET.
|
46
|
-
-- Call the Redis command: HSET "#{Model.name}:id:#{id}" field value ...
|
47
|
-
redis.call('hset', key, unpack(ARGV))
|
48
|
-
|
49
|
-
-- Set TTL on key
|
50
|
-
if ttl and ttl ~= '-1' then
|
51
|
-
redis.call('expire', key, ttl)
|
52
|
-
end
|
53
|
-
|
54
|
-
-- Add id value for any index and range index attributes
|
55
|
-
local attrs_hash = to_hash(ARGV)
|
56
|
-
local index_attr_keys = redis.call('smembers', model .. ':index_attrs')
|
57
|
-
if #index_attr_keys > 0 then
|
58
|
-
for _, attr_key in ipairs(index_attr_keys) do
|
59
|
-
add_id_to_index_attr(model, attr_key, attrs_hash[attr_key], id)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
local range_index_attr_keys = redis.call('smembers', model .. ':range_index_attrs')
|
63
|
-
attrs_hash['id'] = id
|
64
|
-
if #range_index_attr_keys > 0 then
|
65
|
-
for _, attr_key in ipairs(range_index_attr_keys) do
|
66
|
-
add_id_to_range_index_attr(model, attr_key, attrs_hash[attr_key], id)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
return id
|