redstream 0.4.3 → 0.5.0
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/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/README.md +1 -3
- data/lib/redstream/model.rb +5 -11
- data/lib/redstream/producer.rb +9 -15
- data/lib/redstream/version.rb +1 -1
- data/redstream.gemspec +0 -1
- data/spec/redstream/model_spec.rb +0 -49
- data/spec/redstream/producer_spec.rb +3 -37
- metadata +3 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08b45ec057fc3fa2404ff0c48c9f1befdd7dee5177b526690cb5edb907990ac7'
|
4
|
+
data.tar.gz: '081b1cbd5ed872b3d320b1ffe6e7f4342c5bfcb101b293a5cf9348241f8dfe44'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df13ccbbfe9b62e79d5dab0ac461f9c433aff8b6a2990079a4f5c007cb72e93cecf95fd65eb5aa9780da3542a95be01aa7ab83ac109b19b3af93faed69678e58
|
7
|
+
data.tar.gz: ab2e5e8cc1c2ec8b8031cd27621a53e302b1ca23b1d50accb17a348742a0cebdefbf9d92adc6f216534dfcd145262f098f128e3539fd64d57fae994e54745f5e
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -97,9 +97,7 @@ any errors occurring in between `after_save` and `after_commit` result in
|
|
97
97
|
inconsistencies between your primary and secondary datastore. By using these
|
98
98
|
kinds of "delay" messages triggered by `after_save` and fetched after e.g. 5
|
99
99
|
minutes, errors occurring in between `after_save` and `after_commit` can be
|
100
|
-
fixed when the delay message get processed.
|
101
|
-
delay messages after the messages for immediate retrieval have been
|
102
|
-
successfully sent, such that messages will not be processed twice, usually.
|
100
|
+
fixed when the delay message get processed.
|
103
101
|
|
104
102
|
Any messages are fetched in batches, such that e.g. elasticsearch can be
|
105
103
|
updated using its bulk API. For instance, depending on which elasticsearch ruby
|
data/lib/redstream/model.rb
CHANGED
@@ -12,8 +12,6 @@ module Redstream
|
|
12
12
|
# end
|
13
13
|
|
14
14
|
module Model
|
15
|
-
IVAR_DELAY_MESSAGE_ID = :@__redstream_delay_message_id__
|
16
|
-
|
17
15
|
def self.included(base)
|
18
16
|
base.extend(ClassMethods)
|
19
17
|
end
|
@@ -31,20 +29,16 @@ module Redstream
|
|
31
29
|
# responsible for writing to a redis stream
|
32
30
|
|
33
31
|
def redstream_callbacks(producer: Producer.new)
|
34
|
-
after_save { |object|
|
35
|
-
after_touch { |object|
|
36
|
-
after_destroy { |object|
|
32
|
+
after_save { |object| producer.delay(object) if object.saved_changes.present? }
|
33
|
+
after_touch { |object| producer.delay(object) }
|
34
|
+
after_destroy { |object| producer.delay(object) }
|
37
35
|
|
38
36
|
after_commit(on: [:create, :update]) do |object|
|
39
|
-
if object.saved_changes.present?
|
40
|
-
producer.queue(object, delay_message_id: instance_variable_get(IVAR_DELAY_MESSAGE_ID))
|
41
|
-
instance_variable_set(IVAR_DELAY_MESSAGE_ID, nil)
|
42
|
-
end
|
37
|
+
producer.queue(object) if object.saved_changes.present?
|
43
38
|
end
|
44
39
|
|
45
40
|
after_commit(on: :destroy) do |object|
|
46
|
-
producer.queue(object
|
47
|
-
instance_variable_set(IVAR_DELAY_MESSAGE_ID, nil)
|
41
|
+
producer.queue(object)
|
48
42
|
end
|
49
43
|
end
|
50
44
|
|
data/lib/redstream/producer.rb
CHANGED
@@ -52,11 +52,11 @@ module Redstream
|
|
52
52
|
def bulk(records)
|
53
53
|
records_array = Array(records)
|
54
54
|
|
55
|
-
|
55
|
+
bulk_delay(records_array)
|
56
56
|
|
57
57
|
yield
|
58
58
|
|
59
|
-
bulk_queue(records_array
|
59
|
+
bulk_queue(records_array)
|
60
60
|
end
|
61
61
|
|
62
62
|
# @api private
|
@@ -68,7 +68,7 @@ module Redstream
|
|
68
68
|
# @return The redis message ids
|
69
69
|
|
70
70
|
def bulk_delay(records)
|
71
|
-
|
71
|
+
records.each_slice(250) do |slice|
|
72
72
|
Redstream.connection_pool.with do |redis|
|
73
73
|
redis.pipelined do |pipeline|
|
74
74
|
slice.each do |object|
|
@@ -82,7 +82,7 @@ module Redstream
|
|
82
82
|
redis.wait(@wait, 0) if @wait
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
true
|
86
86
|
end
|
87
87
|
|
88
88
|
# @api private
|
@@ -90,15 +90,13 @@ module Redstream
|
|
90
90
|
# Writes messages to a stream in redis for immediate retrieval.
|
91
91
|
#
|
92
92
|
# @param records [#to_a] The object/objects that will be updated deleted
|
93
|
-
# @param delay_message_ids [#to_a] The delay message ids to delete
|
94
93
|
|
95
|
-
def bulk_queue(records
|
96
|
-
records.
|
94
|
+
def bulk_queue(records)
|
95
|
+
records.each_slice(250) do |slice|
|
97
96
|
Redstream.connection_pool.with do |redis|
|
98
97
|
redis.pipelined do |pipeline|
|
99
|
-
slice.each do |object
|
98
|
+
slice.each do |object|
|
100
99
|
pipeline.xadd(Redstream.stream_key_name(stream_name(object)), { payload: JSON.dump(object.redstream_payload) })
|
101
|
-
pipeline.xdel(Redstream.stream_key_name("#{stream_name(object)}.delay"), delay_message_ids[index]) if delay_message_ids
|
102
100
|
end
|
103
101
|
end
|
104
102
|
end
|
@@ -128,14 +126,10 @@ module Redstream
|
|
128
126
|
# Writes a single message to a stream in redis for immediate retrieval.
|
129
127
|
#
|
130
128
|
# @param object The object hat will be updated, deleted, etc.
|
131
|
-
# @param delay_message_id The delay message id to delete
|
132
129
|
|
133
|
-
def queue(object
|
130
|
+
def queue(object)
|
134
131
|
Redstream.connection_pool.with do |redis|
|
135
|
-
redis.
|
136
|
-
pipeline.xadd(Redstream.stream_key_name(stream_name(object)), { payload: JSON.dump(object.redstream_payload) })
|
137
|
-
pipeline.xdel(Redstream.stream_key_name("#{stream_name(object)}.delay"), delay_message_id) if delay_message_id
|
138
|
-
end
|
132
|
+
redis.xadd(Redstream.stream_key_name(stream_name(object)), { payload: JSON.dump(object.redstream_payload) })
|
139
133
|
end
|
140
134
|
|
141
135
|
true
|
data/lib/redstream/version.rb
CHANGED
data/redstream.gemspec
CHANGED
@@ -14,7 +14,6 @@ Gem::Specification.new do |spec|
|
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
17
|
spec.require_paths = ["lib"]
|
19
18
|
|
20
19
|
spec.add_development_dependency "activerecord"
|
@@ -8,12 +8,6 @@ RSpec.describe Redstream::Model do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
it "assigns the delay message id" do
|
12
|
-
Product.transaction do
|
13
|
-
expect(create(:product).instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_present
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
11
|
it "adds the correct payload for the delay message" do
|
18
12
|
Product.transaction do
|
19
13
|
product = create(:product)
|
@@ -26,13 +20,6 @@ RSpec.describe Redstream::Model do
|
|
26
20
|
expect { create(:product) }.to change { redis.xlen(Redstream.stream_key_name("products")) }
|
27
21
|
end
|
28
22
|
|
29
|
-
it "deletes the delay message on commit" do
|
30
|
-
product = create(:product)
|
31
|
-
|
32
|
-
expect(redis.xlen(Redstream.stream_key_name("products.delay"))).to eq(0)
|
33
|
-
expect(product.instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_nil
|
34
|
-
end
|
35
|
-
|
36
23
|
it "does not add a delay message after_save if there are no changes" do
|
37
24
|
product = create(:product)
|
38
25
|
|
@@ -55,16 +42,6 @@ RSpec.describe Redstream::Model do
|
|
55
42
|
end
|
56
43
|
end
|
57
44
|
|
58
|
-
it "assigns the delay message id" do
|
59
|
-
product = create(:product)
|
60
|
-
|
61
|
-
Product.transaction do
|
62
|
-
product.touch
|
63
|
-
|
64
|
-
expect(product.instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_present
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
45
|
it "sets the correct payload for the delay message" do
|
69
46
|
product = create(:product)
|
70
47
|
|
@@ -80,14 +57,6 @@ RSpec.describe Redstream::Model do
|
|
80
57
|
|
81
58
|
expect { product.touch }.to change { redis.xlen(Redstream.stream_key_name("products")) }
|
82
59
|
end
|
83
|
-
|
84
|
-
it "deletes the delay message after touch on commit" do
|
85
|
-
product = create(:product)
|
86
|
-
product.touch
|
87
|
-
|
88
|
-
expect(redis.xlen(Redstream.stream_key_name("products.delay"))).to eq(0)
|
89
|
-
expect(product.instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_nil
|
90
|
-
end
|
91
60
|
end
|
92
61
|
|
93
62
|
describe "after_destroy" do
|
@@ -99,16 +68,6 @@ RSpec.describe Redstream::Model do
|
|
99
68
|
end
|
100
69
|
end
|
101
70
|
|
102
|
-
it "assigns the delay message id" do
|
103
|
-
product = create(:product)
|
104
|
-
|
105
|
-
Product.transaction do
|
106
|
-
product.destroy
|
107
|
-
|
108
|
-
expect(product.instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_present
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
71
|
it "sets the correct payload for the delay message" do
|
113
72
|
product = create(:product)
|
114
73
|
|
@@ -124,13 +83,5 @@ RSpec.describe Redstream::Model do
|
|
124
83
|
|
125
84
|
expect { product.destroy }.to change { redis.xlen(Redstream.stream_key_name("products")) }.by(1)
|
126
85
|
end
|
127
|
-
|
128
|
-
it "deletes the delay message after destroy on commit" do
|
129
|
-
product = create(:product)
|
130
|
-
product.destroy
|
131
|
-
|
132
|
-
expect(redis.xlen(Redstream.stream_key_name("products.delay"))).to eq(0)
|
133
|
-
expect(product.instance_variable_get(Redstream::Model::IVAR_DELAY_MESSAGE_ID)).to be_nil
|
134
|
-
end
|
135
86
|
end
|
136
87
|
end
|
@@ -10,17 +10,6 @@ RSpec.describe Redstream::Producer do
|
|
10
10
|
expect { Redstream::Producer.new.queue(product) }.to change { redis.xlen(stream_key_name) }.by(1)
|
11
11
|
expect(redis.xrange(stream_key_name, "-", "+").last[1]).to eq("payload" => JSON.dump(product.redstream_payload))
|
12
12
|
end
|
13
|
-
|
14
|
-
it "deletes the delay message when given" do
|
15
|
-
product = create(:product)
|
16
|
-
|
17
|
-
producer = Redstream::Producer.new
|
18
|
-
|
19
|
-
id = producer.delay(product)
|
20
|
-
producer.queue(product, delay_message_id: id)
|
21
|
-
|
22
|
-
expect(redis.xlen(Redstream.stream_key_name("products.delay"))).to eq(0)
|
23
|
-
end
|
24
13
|
end
|
25
14
|
|
26
15
|
describe "#delay" do
|
@@ -48,9 +37,11 @@ RSpec.describe Redstream::Producer do
|
|
48
37
|
|
49
38
|
stream_key_name = Redstream.stream_key_name("products")
|
50
39
|
|
51
|
-
expect(redis.xlen("#{stream_key_name}.delay")).to eq(
|
40
|
+
expect(redis.xlen("#{stream_key_name}.delay")).to eq(2)
|
52
41
|
|
53
42
|
Redstream::Producer.new.bulk(Product.all) do
|
43
|
+
expect(redis.xlen("#{stream_key_name}.delay")).to eq(4)
|
44
|
+
|
54
45
|
messages = redis.xrange("#{stream_key_name}.delay", "-", "+").last(2).map { |message| message[1] }
|
55
46
|
|
56
47
|
expect(messages).to eq([
|
@@ -78,19 +69,6 @@ RSpec.describe Redstream::Producer do
|
|
78
69
|
{ "payload" => JSON.dump(products[1].redstream_payload) }
|
79
70
|
])
|
80
71
|
end
|
81
|
-
|
82
|
-
it "deletes the delay messages after the queue messages have been sent" do
|
83
|
-
products = create_list(:product, 2)
|
84
|
-
producer = Redstream::Producer.new
|
85
|
-
|
86
|
-
other_delay_message_id = producer.delay(create(:product))
|
87
|
-
|
88
|
-
producer.bulk(products) do
|
89
|
-
expect(redis.xlen(Redstream.stream_key_name("products.delay"))).to eq(3)
|
90
|
-
end
|
91
|
-
|
92
|
-
expect(redis.xrange(Redstream.stream_key_name("products.delay"), "-", "+").map(&:first)).to eq([other_delay_message_id])
|
93
|
-
end
|
94
72
|
end
|
95
73
|
|
96
74
|
describe "#bulk_queue" do
|
@@ -108,18 +86,6 @@ RSpec.describe Redstream::Producer do
|
|
108
86
|
{ "payload" => JSON.dump(products[1].redstream_payload) }
|
109
87
|
])
|
110
88
|
end
|
111
|
-
|
112
|
-
it "deletes the delay messages after the queue messages have been sent" do
|
113
|
-
products = create_list(:product, 2)
|
114
|
-
producer = Redstream::Producer.new
|
115
|
-
|
116
|
-
delay_message_ids = producer.bulk_delay(products)
|
117
|
-
other_delay_message_id = producer.delay(create(:product))
|
118
|
-
|
119
|
-
producer.bulk_queue(products, delay_message_ids: delay_message_ids)
|
120
|
-
|
121
|
-
expect(redis.xrange(Redstream.stream_key_name("products.delay"), "-", "+").map(&:first)).to eq([other_delay_message_id])
|
122
|
-
end
|
123
89
|
end
|
124
90
|
|
125
91
|
describe "#bulk_delay" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redstream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Vetter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -294,12 +294,4 @@ signing_key:
|
|
294
294
|
specification_version: 4
|
295
295
|
summary: Using redis streams to keep your primary database in sync with secondary
|
296
296
|
datastores
|
297
|
-
test_files:
|
298
|
-
- spec/redstream/consumer_spec.rb
|
299
|
-
- spec/redstream/delayer_spec.rb
|
300
|
-
- spec/redstream/lock_spec.rb
|
301
|
-
- spec/redstream/model_spec.rb
|
302
|
-
- spec/redstream/producer_spec.rb
|
303
|
-
- spec/redstream/trimmer_spec.rb
|
304
|
-
- spec/redstream_spec.rb
|
305
|
-
- spec/spec_helper.rb
|
297
|
+
test_files: []
|