falqon 0.1.0 → 1.1.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/CHANGELOG.md +26 -10
- data/Gemfile +12 -12
- data/README.md +4 -3
- data/lib/falqon/concerns/hooks.rb +1 -1
- data/lib/falqon/message.rb +21 -20
- data/lib/falqon/queue.rb +46 -31
- data/lib/falqon/strategies/linear.rb +7 -7
- data/lib/falqon/strategies/none.rb +7 -7
- data/lib/falqon/sub_queue.rb +1 -0
- data/lib/falqon/sub_set.rb +1 -0
- data/lib/falqon/version.rb +1 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ac7a29b42908bf546c1ef179712b24d4c29cdc41f268172ae94efc01f2e28a5
|
4
|
+
data.tar.gz: 89c27514ea3ecd84fd191e20696a251c8a304d5149f4276007dc815181cb7bd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94b591a05d679e6df6b1308adcebb2619a30923ce4a5c2168f5dd07d381737b59ee6ed8ff4883d8913ab8d07c1a516ec32021cedf068c023f127d81394685c9e
|
7
|
+
data.tar.gz: a83d8fc3f97b8bbbc7c399fd3e7674986470d3fe07fe6384d2605a5510378a1bdddf93f0ac2ad7622ecb1db4018916bd05a91e3e96e362ebc1a93fa1395c67e1
|
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## Falqon v0.1.0 (2024-11-16)
|
4
|
-
|
5
|
-
Initial release
|
6
|
-
|
7
|
-
## Falqon v0.0.1 (2023-06-22)
|
8
|
-
|
9
|
-
# Changelog
|
10
|
-
|
11
3
|
All notable changes to this project will be documented in this file.
|
12
4
|
|
13
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
@@ -21,5 +13,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
21
13
|
|
22
14
|
### Removed
|
23
15
|
|
24
|
-
|
25
|
-
|
16
|
+
## Falqon v1.1.0 (2025-05-18)
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- Rewrite `Falqon::Queue.all` to return queues alphabetically by name
|
21
|
+
|
22
|
+
### Removed
|
23
|
+
|
24
|
+
- Remove support for Ruby 3.1
|
25
|
+
|
26
|
+
## Falqon v1.0.0 (2024-12-07)
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
|
30
|
+
- Use atomic execution (MULTI/EXEC) where possible.
|
31
|
+
|
32
|
+
## Falqon v0.1.0 (2024-11-16)
|
33
|
+
|
34
|
+
Initial release
|
35
|
+
|
36
|
+
## Falqon v0.0.1 (2023-06-22)
|
37
|
+
|
38
|
+
[unreleased]: https://github.com/floriandejonckheere/falqon/compare/v1.1.0...HEAD
|
39
|
+
[1.1.0]: https://github.com/floriandejonckheere/falqon-pro/releases/tag/v1.1.0
|
40
|
+
[1.0.0]: https://github.com/floriandejonckheere/falqon-pro/releases/tag/v1.0.0
|
41
|
+
[0.1.0]: https://github.com/floriandejonckheere/falqon-pro/releases/tag/v0.1.0
|
data/Gemfile
CHANGED
@@ -14,35 +14,35 @@ group :development do
|
|
14
14
|
gem "yard-sorbet", "0.9.0", require: false
|
15
15
|
|
16
16
|
# Simple server
|
17
|
-
gem "webrick", "1.
|
17
|
+
gem "webrick", "1.9.1", require: false
|
18
18
|
end
|
19
19
|
|
20
20
|
group :development, :test do
|
21
21
|
# Debugger
|
22
|
-
gem "debug", "1.
|
22
|
+
gem "debug", "1.10.0", require: false
|
23
23
|
|
24
24
|
# Build objects for tests
|
25
|
-
gem "factory_bot", "6.5.
|
25
|
+
gem "factory_bot", "6.5.1", require: false
|
26
26
|
|
27
27
|
# Generate fake data
|
28
|
-
gem "ffaker", "2.
|
28
|
+
gem "ffaker", "2.24.0", require: false
|
29
29
|
|
30
30
|
# Mock Redis server
|
31
|
-
gem "mock_redis", "0.
|
31
|
+
gem "mock_redis", "0.49.0", require: false
|
32
32
|
|
33
33
|
# Behavior-driven test framework
|
34
34
|
gem "rspec", "3.13.0", require: false
|
35
35
|
|
36
36
|
# Linter
|
37
|
-
gem "rubocop", "1.
|
38
|
-
gem "rubocop-factory_bot", "2.
|
39
|
-
gem "rubocop-performance", "1.
|
40
|
-
gem "rubocop-rake", "0.
|
41
|
-
gem "rubocop-rspec", "3.
|
37
|
+
gem "rubocop", "1.75.6", require: false
|
38
|
+
gem "rubocop-factory_bot", "2.27.1", require: false
|
39
|
+
gem "rubocop-performance", "1.25.0", require: false
|
40
|
+
gem "rubocop-rake", "0.7.1", require: false
|
41
|
+
gem "rubocop-rspec", "3.6.0", require: false
|
42
42
|
|
43
43
|
# Type checker
|
44
|
-
gem "sorbet", "0.5.
|
45
|
-
gem "tapioca", "0.16.
|
44
|
+
gem "sorbet", "0.5.12115", require: false
|
45
|
+
gem "tapioca", "0.16.11", require: false
|
46
46
|
|
47
47
|
# Time control
|
48
48
|
gem "timecop", "0.9.10", require: false
|
data/README.md
CHANGED
@@ -23,8 +23,9 @@ Falqon offers an elegant solution for messaging queues in Ruby.
|
|
23
23
|
|
24
24
|
- Install Falqon and get working with messaging queues in a heartbeat using the [quickstart guide](#quickstart)
|
25
25
|
- Check out the [API documentation](https://docs.falqon.dev/) for more information on how to use Falqon in your application
|
26
|
-
- Check out the CLI documentation](https://docs.falqon.dev/) for more information on how to manage queues and messages from the command line
|
26
|
+
- Check out the [CLI documentation](https://docs.falqon.dev/Falqon/CLI.html) for more information on how to manage queues and messages from the command line
|
27
27
|
- Read the [architecture documentation](#architecture) to learn more about how Falqon works under the hood
|
28
|
+
- Read about [Falqon Pro](https://docs.falqon.dev/pro), the commercial addon for Falqon that offers additional features
|
28
29
|
|
29
30
|
## Quickstart
|
30
31
|
|
@@ -52,7 +53,7 @@ Or install it yourself as:
|
|
52
53
|
### Configuration
|
53
54
|
|
54
55
|
The default configuration works out of the box with the provided `docker-compose.yml` file.
|
55
|
-
See [configuration](
|
56
|
+
See [configuration](https://docs.falqon.dev/Falqon/Configuration.html) if you want to adjust the configuration.
|
56
57
|
|
57
58
|
### Usage
|
58
59
|
|
@@ -134,7 +135,7 @@ The documentation in `docs/` is automatically built by [YARD](https://yardoc.org
|
|
134
135
|
Locally, you can build the documentation using the following commands:
|
135
136
|
|
136
137
|
```sh
|
137
|
-
rake
|
138
|
+
rake yard
|
138
139
|
```
|
139
140
|
|
140
141
|
In development, you can start a local server to preview the documentation:
|
data/lib/falqon/message.rb
CHANGED
@@ -101,13 +101,17 @@ module Falqon
|
|
101
101
|
sig { returns(Message) }
|
102
102
|
def create
|
103
103
|
redis.with do |r|
|
104
|
-
|
105
|
-
r.set("#{queue.id}:data:#{id}", data)
|
104
|
+
message_id = id
|
106
105
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
106
|
+
r.multi do |t|
|
107
|
+
# Store data
|
108
|
+
t.set("#{queue.id}:data:#{message_id}", data)
|
109
|
+
|
110
|
+
# Set metadata
|
111
|
+
t.hset("#{queue.id}:metadata:#{message_id}",
|
112
|
+
:created_at, Time.now.to_i,
|
113
|
+
:updated_at, Time.now.to_i,)
|
114
|
+
end
|
111
115
|
end
|
112
116
|
|
113
117
|
self
|
@@ -141,12 +145,14 @@ module Falqon
|
|
141
145
|
sig { void }
|
142
146
|
def delete
|
143
147
|
redis.with do |r|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
148
|
+
r.multi do |t|
|
149
|
+
# Delete message from queue
|
150
|
+
queue.pending.remove(id)
|
151
|
+
queue.dead.remove(id)
|
152
|
+
|
153
|
+
# Delete data and metadata
|
154
|
+
t.del("#{queue.id}:data:#{id}", "#{queue.id}:metadata:#{id}")
|
155
|
+
end
|
150
156
|
end
|
151
157
|
end
|
152
158
|
|
@@ -156,9 +162,7 @@ module Falqon
|
|
156
162
|
#
|
157
163
|
sig { returns Integer }
|
158
164
|
def size
|
159
|
-
redis.with
|
160
|
-
r.strlen("#{queue.id}:data:#{id}")
|
161
|
-
end
|
165
|
+
redis.with { |r| r.strlen("#{queue.id}:data:#{id}") }
|
162
166
|
end
|
163
167
|
|
164
168
|
# Metadata of the message
|
@@ -183,7 +187,7 @@ module Falqon
|
|
183
187
|
def_delegator :queue, :logger
|
184
188
|
|
185
189
|
##
|
186
|
-
# Metadata for
|
190
|
+
# Metadata for a message
|
187
191
|
#
|
188
192
|
class Metadata < T::Struct
|
189
193
|
# Status (unknown, pending, processing, scheduled, dead)
|
@@ -209,11 +213,8 @@ module Falqon
|
|
209
213
|
# @!visibility private
|
210
214
|
#
|
211
215
|
def self.parse(data)
|
212
|
-
# Keys that are not numeric
|
213
|
-
keys = ["status", "retry_error"]
|
214
|
-
|
215
216
|
# Transform keys to symbols, and values to integers
|
216
|
-
new(data.to_h { |k, v| [k.to_sym,
|
217
|
+
new(data.to_h { |k, v| [k.to_sym, (send(props.dig(k.to_sym, :type).name.to_sym, v) if v)] })
|
217
218
|
end
|
218
219
|
end
|
219
220
|
end
|
data/lib/falqon/queue.rb
CHANGED
@@ -91,15 +91,17 @@ module Falqon
|
|
91
91
|
|
92
92
|
raise Falqon::VersionMismatchError, "Queue #{name} is using protocol version #{queue_version}, but this client is using protocol version #{version}" if queue_version && queue_version.to_i != @version
|
93
93
|
|
94
|
-
|
95
|
-
|
94
|
+
r.multi do |t|
|
95
|
+
# Register the queue
|
96
|
+
t.sadd([Falqon.configuration.prefix, "queues"].compact.join(":"), name)
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
# Set creation and update timestamp (if not set)
|
99
|
+
t.hsetnx("#{id}:metadata", :created_at, Time.now.to_i)
|
100
|
+
t.hsetnx("#{id}:metadata", :updated_at, Time.now.to_i)
|
100
101
|
|
101
|
-
|
102
|
-
|
102
|
+
# Set protocol version
|
103
|
+
t.hsetnx("#{id}:metadata", :version, @version)
|
104
|
+
end
|
103
105
|
end
|
104
106
|
|
105
107
|
run_hook :initialize, :after
|
@@ -185,18 +187,23 @@ module Falqon
|
|
185
187
|
# Move identifier from pending queue to processing queue
|
186
188
|
message_id = r.blmove(pending.id, processing.id, :left, :right).to_i
|
187
189
|
|
188
|
-
#
|
189
|
-
r.
|
190
|
+
# Get retry count
|
191
|
+
retries = r.hget("#{id}:metadata:#{message_id}", :retries).to_i
|
190
192
|
|
191
|
-
|
192
|
-
|
193
|
-
|
193
|
+
r.multi do |t|
|
194
|
+
# Set message status
|
195
|
+
t.hset("#{id}:metadata:#{message_id}", :status, "processing")
|
194
196
|
|
195
|
-
|
196
|
-
|
197
|
+
# Set update timestamp
|
198
|
+
t.hset("#{id}:metadata", :updated_at, Time.now.to_i)
|
199
|
+
t.hset("#{id}:metadata:#{message_id}", :updated_at, Time.now.to_i)
|
197
200
|
|
198
|
-
|
199
|
-
|
201
|
+
# Increment processing counter
|
202
|
+
t.hincrby("#{id}:metadata", :processed, 1)
|
203
|
+
|
204
|
+
# Increment retry counter if message is retried
|
205
|
+
t.hincrby("#{id}:metadata", :retried, 1) if retries.positive?
|
206
|
+
end
|
200
207
|
|
201
208
|
Message.new(self, id: message_id)
|
202
209
|
end
|
@@ -318,11 +325,13 @@ module Falqon
|
|
318
325
|
message_ids = pending.clear + processing.clear + scheduled.clear + dead.clear
|
319
326
|
|
320
327
|
redis.with do |r|
|
321
|
-
|
322
|
-
|
328
|
+
r.multi do |t|
|
329
|
+
# Clear metadata
|
330
|
+
t.hdel("#{id}:metadata", :processed, :failed, :retried)
|
323
331
|
|
324
|
-
|
325
|
-
|
332
|
+
# Set update timestamp
|
333
|
+
t.hset("#{id}:metadata", :updated_at, Time.now.to_i)
|
334
|
+
end
|
326
335
|
end
|
327
336
|
|
328
337
|
run_hook :clear, :after
|
@@ -351,11 +360,13 @@ module Falqon
|
|
351
360
|
.each(&:clear)
|
352
361
|
|
353
362
|
redis.with do |r|
|
354
|
-
|
355
|
-
|
363
|
+
r.multi do |t|
|
364
|
+
# Delete metadata
|
365
|
+
t.del("#{id}:metadata")
|
356
366
|
|
357
|
-
|
358
|
-
|
367
|
+
# Deregister the queue
|
368
|
+
t.srem([Falqon.configuration.prefix, "queues"].compact.join(":"), name)
|
369
|
+
end
|
359
370
|
end
|
360
371
|
|
361
372
|
run_hook :delete, :after
|
@@ -461,19 +472,22 @@ module Falqon
|
|
461
472
|
redis.with do |r|
|
462
473
|
# Select all identifiers that are due (score <= current timestamp)
|
463
474
|
# FIXME: replace with zrange(by_score: true) when https://github.com/sds/mock_redis/issues/307 is resolved
|
475
|
+
# TODO: work in batches
|
464
476
|
message_ids = r.zrangebyscore(scheduled.id, 0, Time.now.to_i).map(&:to_i)
|
465
477
|
|
466
478
|
logger.debug "Scheduling messages #{message_ids.join(', ')} on queue #{name}"
|
467
479
|
|
468
|
-
|
469
|
-
|
470
|
-
|
480
|
+
r.multi do |t|
|
481
|
+
message_ids.each do |message_id|
|
482
|
+
# Set message status
|
483
|
+
t.hset("#{id}:metadata:#{message_id}", :status, "pending")
|
471
484
|
|
472
|
-
|
473
|
-
|
485
|
+
# Add identifier to pending queue
|
486
|
+
pending.add(message_id)
|
474
487
|
|
475
|
-
|
476
|
-
|
488
|
+
# Remove identifier from scheduled queue
|
489
|
+
scheduled.remove(message_id)
|
490
|
+
end
|
477
491
|
end
|
478
492
|
end
|
479
493
|
|
@@ -572,6 +586,7 @@ module Falqon
|
|
572
586
|
Falqon.configuration.redis.with do |r|
|
573
587
|
r
|
574
588
|
.smembers([Falqon.configuration.prefix, "queues"].compact.join(":"))
|
589
|
+
.sort
|
575
590
|
.map { |id| new(id) }
|
576
591
|
end
|
577
592
|
end
|
@@ -38,14 +38,14 @@ module Falqon
|
|
38
38
|
# Increment retry count
|
39
39
|
retries = r.hincrby("#{queue.id}:metadata:#{message.id}", :retries, 1)
|
40
40
|
|
41
|
-
# Set error metadata
|
42
|
-
r.hset(
|
43
|
-
"#{queue.id}:metadata:#{message.id}",
|
44
|
-
:retried_at, Time.now.to_i,
|
45
|
-
:retry_error, error.message,
|
46
|
-
)
|
47
|
-
|
48
41
|
r.multi do |t|
|
42
|
+
# Set error metadata
|
43
|
+
t.hset(
|
44
|
+
"#{queue.id}:metadata:#{message.id}",
|
45
|
+
:retried_at, Time.now.to_i,
|
46
|
+
:retry_error, error.message,
|
47
|
+
)
|
48
|
+
|
49
49
|
if retries < queue.max_retries || queue.max_retries == -1
|
50
50
|
if queue.retry_delay.positive?
|
51
51
|
queue.logger.debug "Scheduling message #{message.id} on queue #{queue.name} in #{queue.retry_delay} seconds (attempt #{retries})"
|
@@ -20,14 +20,14 @@ module Falqon
|
|
20
20
|
sig { params(message: Message, error: Error).void }
|
21
21
|
def retry(message, error)
|
22
22
|
queue.redis.with do |r|
|
23
|
-
# Set error metadata
|
24
|
-
r.hset(
|
25
|
-
"#{queue.id}:metadata:#{message.id}",
|
26
|
-
:retried_at, Time.now.to_i,
|
27
|
-
:retry_error, error.message,
|
28
|
-
)
|
29
|
-
|
30
23
|
r.multi do |t|
|
24
|
+
# Set error metadata
|
25
|
+
t.hset(
|
26
|
+
"#{queue.id}:metadata:#{message.id}",
|
27
|
+
:retried_at, Time.now.to_i,
|
28
|
+
:retry_error, error.message,
|
29
|
+
)
|
30
|
+
|
31
31
|
# Kill message immediately
|
32
32
|
message.kill
|
33
33
|
|
data/lib/falqon/sub_queue.rb
CHANGED
@@ -65,6 +65,7 @@ module Falqon
|
|
65
65
|
message_ids = r.lrange(id, 0, -1)
|
66
66
|
|
67
67
|
# Delete all data and clear queue
|
68
|
+
# TODO: clear data in batches
|
68
69
|
r.del(*message_ids.flat_map { |message_id| ["#{queue.id}:data:#{message_id}", "#{queue.id}:metadata:#{message_id}"] }, id, "#{queue.id}:id")
|
69
70
|
|
70
71
|
# Return identifiers
|
data/lib/falqon/sub_set.rb
CHANGED
data/lib/falqon/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falqon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Florian Dejonckheere
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.5'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: redis
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '5.
|
33
|
+
version: '5.4'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '5.
|
40
|
+
version: '5.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: sorbet-runtime
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '2.
|
75
|
+
version: '2.7'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '2.
|
82
|
+
version: '2.7'
|
83
83
|
description: Simple, efficient, and reliable messaging queue for Ruby
|
84
84
|
email:
|
85
85
|
- florian@floriandejonckheere.be
|
@@ -130,7 +130,7 @@ licenses:
|
|
130
130
|
metadata:
|
131
131
|
source_code_uri: https://github.com/floriandejonckheere/falqon.git
|
132
132
|
rubygems_mfa_required: 'true'
|
133
|
-
post_install_message:
|
133
|
+
post_install_message:
|
134
134
|
rdoc_options: []
|
135
135
|
require_paths:
|
136
136
|
- lib
|
@@ -138,15 +138,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
138
|
requirements:
|
139
139
|
- - ">="
|
140
140
|
- !ruby/object:Gem::Version
|
141
|
-
version: '3.
|
141
|
+
version: '3.2'
|
142
142
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: '0'
|
147
147
|
requirements: []
|
148
|
-
rubygems_version: 3.
|
149
|
-
signing_key:
|
148
|
+
rubygems_version: 3.4.20
|
149
|
+
signing_key:
|
150
150
|
specification_version: 4
|
151
151
|
summary: Simple messaging queue
|
152
152
|
test_files: []
|