sqs_processor 0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +293 -0
- data/bin/sqs_processor +77 -0
- data/lib/sqs_processor/processor.rb +153 -0
- data/lib/sqs_processor/version.rb +3 -0
- data/lib/sqs_processor.rb +7 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6a7f784c9d006a263a073a856b1eccf16608848e03edf06ebc769c3f68d8748c
|
4
|
+
data.tar.gz: 6c42692cd419b8137f0b97109c1f215092952458ef7c1d17e283bb57f8aad6e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 281cf0dfbf315f4c9c5163e6017c5c0a0a549d777780a4f02b2f2d4fd49454d4b726fdfcfacc8d37d5e58e9cfe20a6583128284a8a5bf6d3cd206489e996f9ff
|
7
|
+
data.tar.gz: 3f46cf8f400ccd3619b5586a6ecfb54c5d7cde6d0b84f40abfdc664ba30e5fc95180a5090a277172b33333f39e49f7ee06ea916ac8aa70389a615c2d7cce150b
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 SQS Processor
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
# SQS Processor Gem
|
2
|
+
|
3
|
+
A Ruby gem for processing messages from Amazon SQS queues with configurable message handling and error recovery.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Long Polling**: Efficiently polls SQS with configurable wait times
|
8
|
+
- **Batch Processing**: Processes multiple messages per batch
|
9
|
+
- **Error Handling**: Robust error handling with message retention on failure
|
10
|
+
- **Customizable**: Extensible message processing logic
|
11
|
+
- **Logging**: Comprehensive logging with configurable levels
|
12
|
+
- **Command Line Options**: Flexible configuration via command line arguments
|
13
|
+
- **Environment Variables**: Support for AWS credentials and configuration via environment variables
|
14
|
+
|
15
|
+
## Prerequisites
|
16
|
+
|
17
|
+
- Ruby 2.6 or higher
|
18
|
+
- AWS credentials configured (via environment variables, IAM roles, or AWS CLI)
|
19
|
+
- SQS queue URL
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
### As a Gem
|
24
|
+
|
25
|
+
```bash
|
26
|
+
gem install sqs_processor
|
27
|
+
```
|
28
|
+
|
29
|
+
### From Source
|
30
|
+
|
31
|
+
1. Clone the repository
|
32
|
+
2. Install dependencies:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
bundle install
|
36
|
+
```
|
37
|
+
|
38
|
+
3. Build and install the gem:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
bundle exec rake install
|
42
|
+
```
|
43
|
+
|
44
|
+
4. Copy the environment example and configure your settings:
|
45
|
+
|
46
|
+
```bash
|
47
|
+
cp env.example .env
|
48
|
+
```
|
49
|
+
|
50
|
+
5. Edit `.env` with your AWS credentials and SQS queue URL:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
DATA_SYNC_AWS_ACCESS_KEY_ID=your_access_key_here
|
54
|
+
DATA_SYNC_AWS_SECRET_ACCESS_KEY=your_secret_key_here
|
55
|
+
DATA_SYNC_AWS_REGION=us-east-1
|
56
|
+
DATA_SYNC_SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/your-queue-name
|
57
|
+
```
|
58
|
+
|
59
|
+
## Usage
|
60
|
+
|
61
|
+
### Command Line Usage
|
62
|
+
|
63
|
+
```bash
|
64
|
+
sqs_processor [options]
|
65
|
+
```
|
66
|
+
|
67
|
+
### Command Line Options
|
68
|
+
|
69
|
+
```bash
|
70
|
+
sqs_processor [options]
|
71
|
+
|
72
|
+
Options:
|
73
|
+
-q, --queue-url URL SQS Queue URL
|
74
|
+
-r, --region REGION AWS Region
|
75
|
+
-m, --max-messages NUMBER Max messages per batch (default: 10)
|
76
|
+
-v, --visibility-timeout SECONDS Visibility timeout in seconds (default: 30)
|
77
|
+
-h, --help Show this help message
|
78
|
+
```
|
79
|
+
|
80
|
+
### Examples
|
81
|
+
|
82
|
+
```bash
|
83
|
+
# Basic usage with environment variables
|
84
|
+
sqs_processor
|
85
|
+
|
86
|
+
# Specify queue URL and region
|
87
|
+
sqs_processor -q https://sqs.us-west-2.amazonaws.com/123456789012/my-queue -r us-west-2
|
88
|
+
|
89
|
+
# Custom batch size and visibility timeout
|
90
|
+
sqs_processor -m 5 -v 60
|
91
|
+
```
|
92
|
+
|
93
|
+
### Programmatic Usage
|
94
|
+
|
95
|
+
#### Basic Usage
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
require 'sqs_processor'
|
99
|
+
|
100
|
+
# Create a processor instance
|
101
|
+
processor = SQSProcessor::Processor.new(
|
102
|
+
queue_url: 'https://sqs.us-east-1.amazonaws.com/123456789012/my-queue',
|
103
|
+
region: 'us-east-1',
|
104
|
+
max_messages: 10,
|
105
|
+
visibility_timeout: 30,
|
106
|
+
aws_access_key_id: 'your-access-key',
|
107
|
+
aws_secret_access_key: 'your-secret-key'
|
108
|
+
)
|
109
|
+
|
110
|
+
# Start processing messages
|
111
|
+
processor.process_messages
|
112
|
+
```
|
113
|
+
|
114
|
+
## Message Processing
|
115
|
+
|
116
|
+
The gem uses a hook-based approach for message processing. You must implement the `handle_message` method in your subclass to define how messages should be processed.
|
117
|
+
|
118
|
+
### Hook Method
|
119
|
+
|
120
|
+
The `handle_message(message, body)` method receives:
|
121
|
+
- `message`: The SQS message object (contains message_id, receipt_handle, etc.)
|
122
|
+
- `body`: The parsed JSON body of the message
|
123
|
+
|
124
|
+
Return `true` if processing was successful (message will be deleted from queue), or `false` if processing failed (message will remain in queue for retry).
|
125
|
+
|
126
|
+
### Example Message Format
|
127
|
+
|
128
|
+
```json
|
129
|
+
{
|
130
|
+
"event_type": "data_sync",
|
131
|
+
"dataset_id": "12345",
|
132
|
+
"timestamp": "2024-01-01T00:00:00Z"
|
133
|
+
}
|
134
|
+
```
|
135
|
+
|
136
|
+
### Custom Processing
|
137
|
+
|
138
|
+
To implement custom message processing logic, create a subclass of `SQSProcessor::Processor` and override the `handle_message` method:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
require 'sqs_processor'
|
142
|
+
|
143
|
+
class MyCustomProcessor < SQSProcessor::Processor
|
144
|
+
def handle_message(message, body)
|
145
|
+
# This method receives the SQS message object and parsed body
|
146
|
+
# Return true if processing was successful, false otherwise
|
147
|
+
|
148
|
+
case body['event_type']
|
149
|
+
when 'data_sync'
|
150
|
+
process_data_sync(body)
|
151
|
+
when 'report_generation'
|
152
|
+
process_report_generation(body)
|
153
|
+
else
|
154
|
+
logger.warn "Unknown event type: #{body['event_type']}"
|
155
|
+
false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def process_data_sync(body)
|
162
|
+
logger.info "Processing data sync for dataset: #{body['dataset_id']}"
|
163
|
+
# Your custom logic here
|
164
|
+
true
|
165
|
+
end
|
166
|
+
|
167
|
+
def process_report_generation(body)
|
168
|
+
logger.info "Processing report generation for report: #{body['report_id']}"
|
169
|
+
# Your custom logic here
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Usage
|
175
|
+
processor = MyCustomProcessor.new(
|
176
|
+
queue_url: 'your-queue-url',
|
177
|
+
region: 'us-east-1',
|
178
|
+
aws_access_key_id: 'your-access-key',
|
179
|
+
aws_secret_access_key: 'your-secret-key'
|
180
|
+
)
|
181
|
+
processor.process_messages
|
182
|
+
```
|
183
|
+
|
184
|
+
## Configuration
|
185
|
+
|
186
|
+
### Constructor Parameters
|
187
|
+
|
188
|
+
The `SQSProcessor::Processor.new` method accepts the following parameters:
|
189
|
+
|
190
|
+
- `queue_url:` (required) - The SQS queue URL
|
191
|
+
- `region:` (optional, default: 'us-east-1') - AWS region
|
192
|
+
- `max_messages:` (optional, default: 10) - Maximum messages per batch
|
193
|
+
- `visibility_timeout:` (optional, default: 30) - Message visibility timeout in seconds
|
194
|
+
- `aws_access_key_id:` (optional) - AWS access key ID
|
195
|
+
- `aws_secret_access_key:` (optional) - AWS secret access key
|
196
|
+
- `aws_session_token:` (optional) - AWS session token for temporary credentials
|
197
|
+
- `aws_credentials:` (optional) - Pre-configured AWS credentials object
|
198
|
+
- `logger:` (optional) - Custom logger instance
|
199
|
+
|
200
|
+
### Environment Variables
|
201
|
+
|
202
|
+
- `DATA_SYNC_AWS_ACCESS_KEY_ID`: Your AWS access key
|
203
|
+
- `DATA_SYNC_AWS_SECRET_ACCESS_KEY`: Your AWS secret key
|
204
|
+
- `DATA_SYNC_AWS_SESSION_TOKEN`: Your AWS session token (optional, for temporary credentials)
|
205
|
+
- `DATA_SYNC_AWS_REGION`: AWS region (default: us-east-1)
|
206
|
+
- `DATA_SYNC_SQS_QUEUE_URL`: Your SQS queue URL
|
207
|
+
|
208
|
+
### AWS Credentials
|
209
|
+
|
210
|
+
The gem supports multiple ways to provide AWS credentials:
|
211
|
+
|
212
|
+
1. **Initializer Configuration**: Set credentials in the initializer block
|
213
|
+
2. **Environment Variables**: Use the `DATA_SYNC_` prefixed environment variables
|
214
|
+
3. **AWS SDK Default Chain**: If no credentials are provided, the AWS SDK will use its default credential provider chain (IAM roles, AWS CLI, etc.)
|
215
|
+
4. **Direct Parameter**: Pass credentials directly to the processor constructor
|
216
|
+
|
217
|
+
The script supports multiple ways to provide AWS credentials:
|
218
|
+
|
219
|
+
1. **Environment Variables**: Set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
|
220
|
+
2. **IAM Roles**: If running on EC2 with IAM roles
|
221
|
+
3. **AWS CLI**: If you have AWS CLI configured
|
222
|
+
4. **AWS SDK Default Credential Provider Chain**: Automatic credential resolution
|
223
|
+
|
224
|
+
## Error Handling
|
225
|
+
|
226
|
+
- **JSON Parse Errors**: Messages with invalid JSON are logged but kept in queue
|
227
|
+
- **Processing Errors**: Failed messages remain in queue for retry
|
228
|
+
- **Network Errors**: Automatic retry with exponential backoff
|
229
|
+
- **Queue Errors**: Comprehensive error logging with stack traces
|
230
|
+
|
231
|
+
## Monitoring
|
232
|
+
|
233
|
+
The script provides detailed logging including:
|
234
|
+
|
235
|
+
- Queue attributes (message counts)
|
236
|
+
- Message processing status
|
237
|
+
- Error details with stack traces
|
238
|
+
- Processing performance metrics
|
239
|
+
|
240
|
+
## Best Practices
|
241
|
+
|
242
|
+
1. **Set appropriate visibility timeout**: Should be longer than your processing time
|
243
|
+
2. **Use long polling**: Reduces API calls and costs
|
244
|
+
3. **Handle errors gracefully**: Return `false` from processing methods to keep messages in queue
|
245
|
+
4. **Monitor queue depth**: Use the built-in queue attribute reporting
|
246
|
+
5. **Use appropriate batch sizes**: Balance between throughput and memory usage
|
247
|
+
|
248
|
+
## Troubleshooting
|
249
|
+
|
250
|
+
### Common Issues
|
251
|
+
|
252
|
+
1. **"Queue URL is required"**: Set `DATA_SYNC_SQS_QUEUE_URL` environment variable or use `-q` option
|
253
|
+
2. **"Access Denied"**: Check AWS credentials and SQS permissions
|
254
|
+
3. **"Queue does not exist"**: Verify queue URL and region
|
255
|
+
4. **Messages not being processed**: Check visibility timeout and processing logic
|
256
|
+
|
257
|
+
### Debug Mode
|
258
|
+
|
259
|
+
To enable debug logging, modify the logger level in the script:
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
@logger.level = Logger::DEBUG
|
263
|
+
```
|
264
|
+
|
265
|
+
## Development
|
266
|
+
|
267
|
+
### Running Tests
|
268
|
+
|
269
|
+
```bash
|
270
|
+
bundle exec rspec
|
271
|
+
```
|
272
|
+
|
273
|
+
### Code Style
|
274
|
+
|
275
|
+
```bash
|
276
|
+
bundle exec rubocop
|
277
|
+
```
|
278
|
+
|
279
|
+
### Building the Gem
|
280
|
+
|
281
|
+
```bash
|
282
|
+
bundle exec rake build
|
283
|
+
```
|
284
|
+
|
285
|
+
### Publishing the Gem
|
286
|
+
|
287
|
+
```bash
|
288
|
+
bundle exec rake release
|
289
|
+
```
|
290
|
+
|
291
|
+
## License
|
292
|
+
|
293
|
+
This gem is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
data/bin/sqs_processor
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sqs_processor'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def parse_options
|
7
|
+
options = {
|
8
|
+
queue_url: nil,
|
9
|
+
region: nil,
|
10
|
+
max_messages: 10,
|
11
|
+
visibility_timeout: 30
|
12
|
+
}
|
13
|
+
|
14
|
+
OptionParser.new do |opts|
|
15
|
+
opts.banner = 'Usage: sqs_processor [options]'
|
16
|
+
|
17
|
+
opts.on('-q', '--queue-url URL', 'SQS Queue URL') do |url|
|
18
|
+
options[:queue_url] = url
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on('-r', '--region REGION', 'AWS Region') do |region|
|
22
|
+
options[:region] = region
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-m', '--max-messages NUMBER', Integer, 'Max messages per batch (default: 10)') do |num|
|
26
|
+
options[:max_messages] = num
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-v', '--visibility-timeout SECONDS', Integer, 'Visibility timeout in seconds (default: 30)') do |timeout|
|
30
|
+
options[:visibility_timeout] = timeout
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-h', '--help', 'Show this help message') do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
end.parse!
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
41
|
+
|
42
|
+
if __FILE__ == $0
|
43
|
+
options = parse_options
|
44
|
+
|
45
|
+
begin
|
46
|
+
# Load environment variables
|
47
|
+
Dotenv.load
|
48
|
+
|
49
|
+
# Get queue URL from options or environment
|
50
|
+
queue_url = options[:queue_url] || ENV.fetch('DATA_SYNC_SQS_QUEUE_URL', nil)
|
51
|
+
|
52
|
+
unless queue_url
|
53
|
+
puts 'Error: Queue URL is required. Set DATA_SYNC_SQS_QUEUE_URL environment variable or use -q option.'
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
|
57
|
+
processor = SQSProcessor::Processor.new(
|
58
|
+
queue_url: queue_url,
|
59
|
+
region: options[:region] || ENV['DATA_SYNC_AWS_REGION'] || 'us-east-1',
|
60
|
+
max_messages: options[:max_messages],
|
61
|
+
visibility_timeout: options[:visibility_timeout],
|
62
|
+
aws_access_key_id: ENV.fetch('DATA_SYNC_AWS_ACCESS_KEY_ID', nil),
|
63
|
+
aws_secret_access_key: ENV.fetch('DATA_SYNC_AWS_SECRET_ACCESS_KEY', nil),
|
64
|
+
aws_session_token: ENV.fetch('DATA_SYNC_AWS_SESSION_TOKEN', nil)
|
65
|
+
)
|
66
|
+
|
67
|
+
# Show queue attributes before starting
|
68
|
+
processor.get_queue_attributes
|
69
|
+
|
70
|
+
# Start processing messages
|
71
|
+
processor.process_messages
|
72
|
+
rescue StandardError => e
|
73
|
+
puts "Error: #{e.message}"
|
74
|
+
puts e.backtrace.join("\n")
|
75
|
+
exit 1
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'aws-sdk-sqs'
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
require 'dotenv'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
# Load environment variables
|
8
|
+
Dotenv.load
|
9
|
+
|
10
|
+
module SQSProcessor
|
11
|
+
class Processor
|
12
|
+
attr_reader :sqs_client, :queue_url, :logger
|
13
|
+
|
14
|
+
def initialize(queue_url:, region: 'us-east-1', max_messages: 10, visibility_timeout: 30,
|
15
|
+
aws_access_key_id: nil, aws_secret_access_key: nil, aws_session_token: nil,
|
16
|
+
aws_credentials: nil, logger: nil)
|
17
|
+
@queue_url = queue_url
|
18
|
+
@region = region
|
19
|
+
@max_messages = max_messages
|
20
|
+
@visibility_timeout = visibility_timeout
|
21
|
+
@aws_credentials = aws_credentials
|
22
|
+
@aws_access_key_id = aws_access_key_id
|
23
|
+
@aws_secret_access_key = aws_secret_access_key
|
24
|
+
@aws_session_token = aws_session_token
|
25
|
+
|
26
|
+
setup_logger(logger)
|
27
|
+
setup_sqs_client
|
28
|
+
|
29
|
+
raise 'Queue URL is required.' unless @queue_url
|
30
|
+
end
|
31
|
+
|
32
|
+
def setup_logger(custom_logger = nil)
|
33
|
+
@logger = custom_logger || Logger.new(STDOUT)
|
34
|
+
@logger.level = Logger::INFO
|
35
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
36
|
+
"#{datetime.strftime('%Y-%m-%d %H:%M:%S')} [#{severity}] #{msg}\n"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_sqs_client
|
41
|
+
credentials = if @aws_credentials
|
42
|
+
@aws_credentials
|
43
|
+
elsif @aws_access_key_id && @aws_secret_access_key
|
44
|
+
Aws::Credentials.new(@aws_access_key_id, @aws_secret_access_key, @aws_session_token)
|
45
|
+
elsif ENV['DATA_SYNC_AWS_ACCESS_KEY_ID'] && ENV['DATA_SYNC_AWS_SECRET_ACCESS_KEY']
|
46
|
+
Aws::Credentials.new(ENV['DATA_SYNC_AWS_ACCESS_KEY_ID'], ENV['DATA_SYNC_AWS_SECRET_ACCESS_KEY'])
|
47
|
+
else
|
48
|
+
Aws::Credentials.new
|
49
|
+
end
|
50
|
+
|
51
|
+
@sqs_client = Aws::SQS::Client.new(
|
52
|
+
region: @region,
|
53
|
+
credentials: credentials
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_messages
|
58
|
+
logger.info 'Starting SQS message processing...'
|
59
|
+
logger.info "Queue URL: #{@queue_url}"
|
60
|
+
logger.info "Max messages per batch: #{@max_messages}"
|
61
|
+
logger.info "Visibility timeout: #{@visibility_timeout} seconds"
|
62
|
+
|
63
|
+
loop do
|
64
|
+
receive_messages
|
65
|
+
sleep 1 # Small delay between polling cycles
|
66
|
+
rescue StandardError => e
|
67
|
+
logger.error "Error in message processing loop: #{e.message}"
|
68
|
+
logger.error e.backtrace.join("\n")
|
69
|
+
sleep 5 # Longer delay on error
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def receive_messages
|
74
|
+
response = @sqs_client.receive_message(
|
75
|
+
queue_url: @queue_url,
|
76
|
+
max_number_of_messages: @max_messages,
|
77
|
+
visibility_timeout: @visibility_timeout,
|
78
|
+
wait_time_seconds: 20 # Long polling
|
79
|
+
)
|
80
|
+
|
81
|
+
if response.messages.empty?
|
82
|
+
logger.debug 'No messages received'
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
logger.info "Received #{response.messages.length} message(s)"
|
87
|
+
|
88
|
+
response.messages.each do |message|
|
89
|
+
process_single_message(message)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def process_single_message(message)
|
94
|
+
logger.info "Processing message: #{message.message_id}"
|
95
|
+
|
96
|
+
begin
|
97
|
+
# Parse message body
|
98
|
+
body = JSON.parse(message.body)
|
99
|
+
logger.info "Message body: #{body}"
|
100
|
+
|
101
|
+
# Call the hook method that should be implemented by the host application
|
102
|
+
result = handle_message(body['Message'])
|
103
|
+
|
104
|
+
if result
|
105
|
+
# Delete the message from queue after successful processing
|
106
|
+
delete_message(message)
|
107
|
+
logger.info "Successfully processed and deleted message: #{message.message_id}"
|
108
|
+
else
|
109
|
+
logger.warn "Message processing returned false, keeping message in queue: #{message.message_id}"
|
110
|
+
end
|
111
|
+
rescue JSON::ParserError => e
|
112
|
+
logger.error "Failed to parse message body as JSON: #{e.message}"
|
113
|
+
logger.error "Raw message body: #{message.body}"
|
114
|
+
# Keep message in queue for manual inspection
|
115
|
+
rescue StandardError => e
|
116
|
+
logger.error "Error processing message #{message.message_id}: #{e.message}"
|
117
|
+
logger.error e.backtrace.join("\n")
|
118
|
+
# Keep message in queue for retry
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Hook method that should be implemented by the host application
|
123
|
+
# Override this method in your subclass to implement custom message processing
|
124
|
+
def handle_message(message, body)
|
125
|
+
logger.warn 'handle_message method not implemented. Override this method in your subclass.'
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def delete_message(message)
|
130
|
+
@sqs_client.delete_message(
|
131
|
+
queue_url: @queue_url,
|
132
|
+
receipt_handle: message.receipt_handle
|
133
|
+
)
|
134
|
+
rescue StandardError => e
|
135
|
+
logger.error "Failed to delete message #{message.message_id}: #{e.message}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_queue_attributes
|
139
|
+
response = @sqs_client.get_queue_attributes(
|
140
|
+
queue_url: @queue_url,
|
141
|
+
attribute_names: ['All']
|
142
|
+
)
|
143
|
+
|
144
|
+
attributes = response.attributes
|
145
|
+
logger.info 'Queue attributes:'
|
146
|
+
logger.info " Approximate number of messages: #{attributes['ApproximateNumberOfMessages']}"
|
147
|
+
logger.info " Approximate number of messages not visible: #{attributes['ApproximateNumberOfMessagesNotVisible']}"
|
148
|
+
logger.info " Approximate number of messages delayed: #{attributes['ApproximateNumberOfMessagesDelayed']}"
|
149
|
+
|
150
|
+
attributes
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sqs_processor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Your Name
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: aws-sdk-sqs
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: logger
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: dotenv
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: bundler
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rake
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '13.0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '13.0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rspec
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '3.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rubocop
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '1.0'
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.0'
|
124
|
+
description: A comprehensive Ruby gem for processing messages from Amazon SQS queues
|
125
|
+
with configurable message handling, error recovery, and extensible processing logic.
|
126
|
+
email:
|
127
|
+
- your.email@example.com
|
128
|
+
executables:
|
129
|
+
- sqs_processor
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- LICENSE
|
134
|
+
- README.md
|
135
|
+
- bin/sqs_processor
|
136
|
+
- lib/sqs_processor.rb
|
137
|
+
- lib/sqs_processor/processor.rb
|
138
|
+
- lib/sqs_processor/version.rb
|
139
|
+
homepage: https://github.com/yourusername/sqs_processor
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: 2.6.0
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubygems_version: 3.6.9
|
158
|
+
specification_version: 4
|
159
|
+
summary: A Ruby gem for processing messages from Amazon SQS queues
|
160
|
+
test_files: []
|