sqewer 5.0.9 → 5.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/lib/sqewer/connection.rb +10 -3
- data/lib/sqewer/local_connection.rb +154 -0
- data/lib/sqewer/version.rb +1 -1
- data/lib/sqewer/worker.rb +1 -1
- data/sqewer.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3b121d612798813c7a9de3e795f82603cc57b9c
|
4
|
+
data.tar.gz: 4fb43eefa14ff0fe3c2838b34d8c7d787ebb8481
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce64c01eae4696d64c4215757c18afee1cefd3d99de906a5c8a12f7f28f265533a33a48aa4bbe434190f01fd76892334016bc493c0160146c90ab049f75e26a9
|
7
|
+
data.tar.gz: c1c8a1f5882fc38da3026cedd2fa3c1bd35ee1317061b49ff3c69dfd50ebe1ebf5880fad7d14d77ceb441d3b68a10c5c17d6a0c3fdf14ed3f100ccd8ceb92684
|
data/lib/sqewer/connection.rb
CHANGED
@@ -25,11 +25,18 @@ class Sqewer::Connection
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns the default adapter, connected to the queue set via the `SQS_QUEUE_URL`
|
28
|
-
# environment variable.
|
28
|
+
# environment variable. Switches to SQLite-backed local queue if the SQS_QUEUE_URL
|
29
|
+
# is prefixed with 'sqlite3://'
|
29
30
|
def self.default
|
30
|
-
|
31
|
+
url_str = ENV.fetch('SQS_QUEUE_URL')
|
32
|
+
uri = URI(url_str)
|
33
|
+
if uri.scheme == 'sqlite3'
|
34
|
+
Sqewer::LocalConnection.new(uri.to_s)
|
35
|
+
else
|
36
|
+
new(uri.to_s)
|
37
|
+
end
|
31
38
|
rescue KeyError => e
|
32
|
-
raise "SQS_QUEUE_URL not set in the environment. This is the queue URL
|
39
|
+
raise "SQS_QUEUE_URL not set in the environment. This is the queue URL Sqewer uses by default."
|
33
40
|
end
|
34
41
|
|
35
42
|
# Initializes a new adapter, with access to the SQS queue at the given URL.
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'rack'
|
2
|
+
class Sqewer::LocalConnection < Sqewer::Connection
|
3
|
+
ASSUME_DEADLETTER_AFTER_N_DELIVERIES = 10
|
4
|
+
|
5
|
+
def self.parse_queue_url(queue_url_starting_with_sqlite3_proto)
|
6
|
+
uri = URI.parse(queue_url_starting_with_sqlite3_proto)
|
7
|
+
|
8
|
+
unless uri.scheme == 'sqlite3'
|
9
|
+
raise "The scheme of the SQS queue URL should be with `sqlite3' but was %s" % uri.scheme
|
10
|
+
end
|
11
|
+
|
12
|
+
path_components = ['/', uri.hostname, uri.path].reject(&:nil?).reject(&:empty?).join('/').squeeze('/')
|
13
|
+
dbfile_path = File.expand_path(path_components)
|
14
|
+
|
15
|
+
queue_name = Rack::Utils.parse_nested_query(uri.query).fetch('queue', 'sqewer_local')
|
16
|
+
|
17
|
+
[dbfile_path, queue_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(queue_url_with_sqlite3_scheme)
|
21
|
+
require 'sqlite3'
|
22
|
+
@db_path, @queue_name = self.class.parse_queue_url(queue_url_with_sqlite3_scheme)
|
23
|
+
with_db do |db|
|
24
|
+
db.execute("CREATE TABLE IF NOT EXISTS sqewer_messages_v2 (
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT ,
|
26
|
+
queue_name VARCHAR NOT NULL,
|
27
|
+
receipt_handle VARCHAR NOT NULL,
|
28
|
+
deliver_after_epoch INTEGER,
|
29
|
+
times_delivered_so_far INTEGER DEFAULT 0,
|
30
|
+
last_delivery_at_epoch INTEGER,
|
31
|
+
visible BOOLEAN DEFAULT 't',
|
32
|
+
message_body TEXT)"
|
33
|
+
)
|
34
|
+
db.execute("CREATE INDEX IF NOT EXISTS on_receipt_handle ON sqewer_messages_v2 (receipt_handle)")
|
35
|
+
db.execute("CREATE INDEX IF NOT EXISTS on_queue_name ON sqewer_messages_v2 (queue_name)")
|
36
|
+
end
|
37
|
+
rescue LoadError => e
|
38
|
+
raise e, "You need the sqlite3 gem in your Gemfile to use LocalConnection. Add it to your Gemfile (`gem 'sqlite3'')"
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<Message>] an array of Message objects
|
42
|
+
def receive_messages
|
43
|
+
messages = load_receipt_handles_and_bodies
|
44
|
+
messages.map {|message| Message.new(message[0], message[1]) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# @yield [#send_message] the object you can send messages through (will be flushed at method return)
|
48
|
+
# @return [void]
|
49
|
+
def send_multiple_messages
|
50
|
+
buffer = SendBuffer.new
|
51
|
+
yield(buffer)
|
52
|
+
messages = buffer.messages
|
53
|
+
persist_messages(messages)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Deletes multiple messages after they all have been succesfully decoded and processed.
|
57
|
+
#
|
58
|
+
# @yield [#delete_message] an object you can delete an individual message through
|
59
|
+
# @return [void]
|
60
|
+
def delete_multiple_messages
|
61
|
+
buffer = DeleteBuffer.new
|
62
|
+
yield(buffer)
|
63
|
+
delete_persisted_messages(buffer.messages)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Only gets used in tests
|
67
|
+
def truncate!
|
68
|
+
with_db do |db|
|
69
|
+
db.execute("DELETE FROM sqewer_messages_v2 WHERE queue_name = ?", @queue_name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def with_db(**k)
|
76
|
+
SQLite3::Database.open(@db_path, **k) do |db|
|
77
|
+
db.busy_timeout = 5
|
78
|
+
return yield db
|
79
|
+
end
|
80
|
+
rescue SQLite3::CantOpenException => e
|
81
|
+
message_with_path = [e.message, 'at', @db_path].join(' ')
|
82
|
+
raise SQLite3::CantOpenException.new(message_with_path)
|
83
|
+
end
|
84
|
+
|
85
|
+
def with_readonly_db(&blk)
|
86
|
+
with_db(readonly: true, &blk)
|
87
|
+
end
|
88
|
+
|
89
|
+
def delete_persisted_messages(messages)
|
90
|
+
ids_to_delete = messages.map{|m| m.fetch(:receipt_handle) }
|
91
|
+
with_db do |db|
|
92
|
+
db.execute("BEGIN")
|
93
|
+
ids_to_delete.each do |id|
|
94
|
+
db.execute("DELETE FROM sqewer_messages_v2 WHERE receipt_handle = ?", id)
|
95
|
+
end
|
96
|
+
db.execute("COMMIT")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def load_receipt_handles_and_bodies
|
101
|
+
t = Time.now.to_i
|
102
|
+
|
103
|
+
# First make messages that were previously marked invisible but not deleted visible again
|
104
|
+
with_db do |db|
|
105
|
+
db.execute("BEGIN")
|
106
|
+
# Make messages visible that have to be redelivered
|
107
|
+
db.execute("UPDATE sqewer_messages_v2
|
108
|
+
SET visible = 't'
|
109
|
+
WHERE queue_name = ? AND visible = 'f' AND last_delivery_at_epoch < ?", @queue_name.to_s, t - 60)
|
110
|
+
# Remove hopeless messages
|
111
|
+
db.execute("DELETE FROM sqewer_messages_v2
|
112
|
+
WHERE queue_name = ? AND times_delivered_so_far >= ?", @queue_name.to_s, ASSUME_DEADLETTER_AFTER_N_DELIVERIES)
|
113
|
+
db.execute("COMMIT")
|
114
|
+
end
|
115
|
+
|
116
|
+
rows = with_readonly_db do |db|
|
117
|
+
db.execute("SELECT id, receipt_handle, message_body FROM sqewer_messages_v2
|
118
|
+
WHERE queue_name = ? AND visible = 't' AND deliver_after_epoch <= ? AND last_delivery_at_epoch <= ?",
|
119
|
+
@queue_name.to_s, t, t)
|
120
|
+
end
|
121
|
+
|
122
|
+
with_db do |db|
|
123
|
+
db.execute("BEGIN")
|
124
|
+
rows.map do |(id, *_)|
|
125
|
+
db.execute("UPDATE sqewer_messages_v2
|
126
|
+
SET visible = 'f', times_delivered_so_far = times_delivered_so_far + 1, last_delivery_at_epoch = ?
|
127
|
+
WHERE id = ?", t, id)
|
128
|
+
end
|
129
|
+
db.execute("COMMIT")
|
130
|
+
end
|
131
|
+
|
132
|
+
rows.map do |(_, *receipt_handle_and_body)|
|
133
|
+
receipt_handle_and_body
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def persist_messages(messages)
|
138
|
+
epoch = Time.now.to_i
|
139
|
+
bodies_and_deliver_afters = messages.map do |msg|
|
140
|
+
[msg.fetch(:message_body), epoch + msg.fetch(:delay_seconds, 0)]
|
141
|
+
end
|
142
|
+
|
143
|
+
with_db do |db|
|
144
|
+
db.execute("BEGIN")
|
145
|
+
bodies_and_deliver_afters.map do |body, deliver_after_epoch|
|
146
|
+
db.execute("INSERT INTO sqewer_messages_v2
|
147
|
+
(queue_name, receipt_handle, message_body, deliver_after_epoch, last_delivery_at_epoch)
|
148
|
+
VALUES(?, ?, ?, ?, ?)",
|
149
|
+
@queue_name.to_s, SecureRandom.uuid, body, deliver_after_epoch, epoch)
|
150
|
+
end
|
151
|
+
db.execute("COMMIT")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/sqewer/version.rb
CHANGED
data/lib/sqewer/worker.rb
CHANGED
@@ -110,7 +110,7 @@ class Sqewer::Worker
|
|
110
110
|
@logger.debug { "[worker] Received and buffered %d messages" % messages.length } if messages.any?
|
111
111
|
else
|
112
112
|
@logger.debug { "[worker] No messages received" }
|
113
|
-
|
113
|
+
sleep SLEEP_SECONDS_ON_EMPTY_QUEUE
|
114
114
|
end
|
115
115
|
else
|
116
116
|
@logger.debug { "[worker] Cache is full (%d items), postponing receive" % @execution_queue.length }
|
data/sqewer.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.require_paths = ["lib"]
|
31
31
|
|
32
32
|
spec.add_runtime_dependency 'aws-sdk', '~> 2'
|
33
|
+
spec.add_runtime_dependency 'rack'
|
33
34
|
spec.add_runtime_dependency 'very_tiny_state_machine'
|
34
35
|
spec.add_runtime_dependency 'ks'
|
35
36
|
spec.add_runtime_dependency 'retriable'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqewer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0
|
4
|
+
version: 5.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: very_tiny_state_machine
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,6 +238,7 @@ files:
|
|
224
238
|
- lib/sqewer/extensions/active_job_adapter.rb
|
225
239
|
- lib/sqewer/extensions/appsignal_wrapper.rb
|
226
240
|
- lib/sqewer/extensions/railtie.rb
|
241
|
+
- lib/sqewer/local_connection.rb
|
227
242
|
- lib/sqewer/middleware_stack.rb
|
228
243
|
- lib/sqewer/null_logger.rb
|
229
244
|
- lib/sqewer/resubmit.rb
|