sqewer 5.0.9 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|