pgmq-ruby 0.4.0 → 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/ci.yml +42 -22
- data/.github/workflows/push.yml +1 -1
- data/.rspec +1 -0
- data/.rubocop.yml +66 -0
- data/.yard-lint.yml +1 -3
- data/CHANGELOG.md +35 -0
- data/CLAUDE.md +310 -0
- data/Gemfile +5 -5
- data/Gemfile.lint +16 -0
- data/Gemfile.lint.lock +120 -0
- data/Gemfile.lock +20 -6
- data/README.md +213 -10
- data/Rakefile +71 -2
- data/docker-compose.yml +2 -2
- data/lib/pgmq/client/consumer.rb +80 -7
- data/lib/pgmq/client/maintenance.rb +4 -21
- data/lib/pgmq/client/message_lifecycle.rb +69 -44
- data/lib/pgmq/client/metrics.rb +2 -2
- data/lib/pgmq/client/multi_queue.rb +9 -9
- data/lib/pgmq/client/producer.rb +7 -7
- data/lib/pgmq/client/queue_management.rb +9 -9
- data/lib/pgmq/client/topics.rb +268 -0
- data/lib/pgmq/client.rb +13 -12
- data/lib/pgmq/connection.rb +11 -11
- data/lib/pgmq/message.rb +11 -9
- data/lib/pgmq/metrics.rb +7 -7
- data/lib/pgmq/queue_metadata.rb +7 -7
- data/lib/pgmq/version.rb +1 -1
- data/lib/pgmq.rb +3 -3
- data/package-lock.json +331 -0
- data/package.json +9 -0
- data/pgmq-ruby.gemspec +20 -20
- data/renovate.json +20 -1
- metadata +8 -2
- data/.coditsu/ci.yml +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5648229b33f490f7228d7f9b106fb40939632c6effff094d028d9f7dd5fc045a
|
|
4
|
+
data.tar.gz: 7a7d0ae6dd7ee59cf329093f4772e8cd8706e25be0e627defca60f3983e96084
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5da9b67730f7c1b58af6be47c2364092988823b2c536e762ca56cc91bd5f27d2d678aef116373c7f0c62fec5037438f30ae0482979c0df3f9414aa3e5f41987a
|
|
7
|
+
data.tar.gz: 7c892fcc8d30d306670b7dd3fd3a3e3f70f573916b3e6526e0833a59acad85cdd44fcf830fd327ccba6608fe0e30eb072d2adc1bcb287ccc28c388e743cfd19e
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -64,7 +64,7 @@ jobs:
|
|
|
64
64
|
run: rm -f Gemfile.lock
|
|
65
65
|
|
|
66
66
|
- name: Set up Ruby
|
|
67
|
-
uses: ruby/setup-ruby@
|
|
67
|
+
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
|
|
68
68
|
with:
|
|
69
69
|
ruby-version: ${{ matrix.ruby }}
|
|
70
70
|
bundler-cache: true
|
|
@@ -102,57 +102,77 @@ jobs:
|
|
|
102
102
|
GITHUB_COVERAGE: ${{ matrix.coverage }}
|
|
103
103
|
run: bundle exec rspec
|
|
104
104
|
|
|
105
|
+
- name: Run examples
|
|
106
|
+
env:
|
|
107
|
+
PG_HOST: localhost
|
|
108
|
+
PG_PORT: 5433
|
|
109
|
+
PG_DATABASE: pgmq_test
|
|
110
|
+
PG_USER: postgres
|
|
111
|
+
PG_PASSWORD: postgres
|
|
112
|
+
run: bundle exec rake examples
|
|
113
|
+
|
|
105
114
|
yard-lint:
|
|
106
115
|
timeout-minutes: 5
|
|
107
116
|
runs-on: ubuntu-latest
|
|
108
117
|
strategy:
|
|
109
118
|
fail-fast: false
|
|
119
|
+
env:
|
|
120
|
+
BUNDLE_GEMFILE: Gemfile.lint
|
|
110
121
|
steps:
|
|
111
122
|
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
112
123
|
with:
|
|
113
124
|
fetch-depth: 0
|
|
114
|
-
- name: Remove Gemfile.lock for Ruby 4.0
|
|
115
|
-
run: rm -f Gemfile.lock
|
|
116
125
|
- name: Set up Ruby
|
|
117
|
-
uses: ruby/setup-ruby@
|
|
126
|
+
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
|
|
118
127
|
with:
|
|
119
128
|
ruby-version: '4.0.0'
|
|
120
129
|
bundler-cache: true
|
|
121
130
|
- name: Run yard-lint
|
|
122
131
|
run: bundle exec yard-lint lib/
|
|
123
132
|
|
|
124
|
-
|
|
133
|
+
rubocop:
|
|
125
134
|
timeout-minutes: 5
|
|
126
135
|
runs-on: ubuntu-latest
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
env:
|
|
137
|
+
BUNDLE_GEMFILE: Gemfile.lint
|
|
129
138
|
steps:
|
|
130
139
|
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
131
140
|
with:
|
|
132
141
|
fetch-depth: 0
|
|
133
|
-
- name:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
|
|
142
|
+
- name: Set up Ruby
|
|
143
|
+
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
|
|
144
|
+
with:
|
|
145
|
+
ruby-version: '4.0.0'
|
|
146
|
+
bundler-cache: true
|
|
147
|
+
- name: Run rubocop
|
|
148
|
+
run: bundle exec rubocop
|
|
149
|
+
|
|
150
|
+
lostconf:
|
|
151
|
+
timeout-minutes: 5
|
|
152
|
+
runs-on: ubuntu-latest
|
|
153
|
+
steps:
|
|
154
|
+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
155
|
+
with:
|
|
156
|
+
fetch-depth: 0
|
|
157
|
+
- name: Set up Node.js
|
|
158
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
159
|
+
with:
|
|
160
|
+
node-version: '20'
|
|
161
|
+
cache: 'npm'
|
|
162
|
+
- name: Install dependencies
|
|
163
|
+
run: npm ci
|
|
164
|
+
- name: Run lostconf
|
|
165
|
+
run: npx lostconf --fail-on-stale
|
|
147
166
|
|
|
148
167
|
ci-success:
|
|
149
168
|
name: CI Success
|
|
150
169
|
runs-on: ubuntu-latest
|
|
151
170
|
if: always()
|
|
152
171
|
needs:
|
|
153
|
-
-
|
|
172
|
+
- rubocop
|
|
154
173
|
- specs
|
|
155
174
|
- yard-lint
|
|
175
|
+
- lostconf
|
|
156
176
|
steps:
|
|
157
177
|
- name: Check all jobs passed
|
|
158
178
|
if: |
|
data/.github/workflows/push.yml
CHANGED
data/.rspec
CHANGED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
plugins:
|
|
4
|
+
- rubocop-capybara
|
|
5
|
+
- rubocop-factory_bot
|
|
6
|
+
- rubocop-performance
|
|
7
|
+
- rubocop-rspec
|
|
8
|
+
- rubocop-rspec_rails
|
|
9
|
+
|
|
10
|
+
inherit_gem:
|
|
11
|
+
standard: config/base.yml
|
|
12
|
+
standard-performance: config/base.yml
|
|
13
|
+
standard-rspec: config/base.yml
|
|
14
|
+
|
|
15
|
+
AllCops:
|
|
16
|
+
NewCops: enable
|
|
17
|
+
TargetRubyVersion: 3.2
|
|
18
|
+
Include:
|
|
19
|
+
- "**/*.rb"
|
|
20
|
+
- "**/*.gemspec"
|
|
21
|
+
- "**/Gemfile"
|
|
22
|
+
- "**/Rakefile"
|
|
23
|
+
- Gemfile.lint
|
|
24
|
+
|
|
25
|
+
# Disabled departments - not a Rails project, no Capybara/FactoryBot
|
|
26
|
+
Capybara:
|
|
27
|
+
Enabled: false
|
|
28
|
+
|
|
29
|
+
FactoryBot:
|
|
30
|
+
Enabled: false
|
|
31
|
+
|
|
32
|
+
RSpecRails:
|
|
33
|
+
Enabled: false
|
|
34
|
+
|
|
35
|
+
# Layout
|
|
36
|
+
Layout/LineLength:
|
|
37
|
+
Max: 120
|
|
38
|
+
|
|
39
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
40
|
+
EnforcedStyle: space
|
|
41
|
+
|
|
42
|
+
# RSpec - exclude integration examples (they use ExampleHelper, not RSpec)
|
|
43
|
+
RSpec:
|
|
44
|
+
Exclude:
|
|
45
|
+
- spec/integration/**/*
|
|
46
|
+
|
|
47
|
+
RSpec/ExampleLength:
|
|
48
|
+
Enabled: false
|
|
49
|
+
|
|
50
|
+
RSpec/IndexedLet:
|
|
51
|
+
Enabled: false
|
|
52
|
+
|
|
53
|
+
RSpec/MultipleExpectations:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
RSpec/MultipleMemoizedHelpers:
|
|
57
|
+
Max: 20
|
|
58
|
+
|
|
59
|
+
RSpec/NestedGroups:
|
|
60
|
+
Max: 4
|
|
61
|
+
|
|
62
|
+
RSpec/NoExpectationExample:
|
|
63
|
+
Enabled: false
|
|
64
|
+
|
|
65
|
+
RSpec/SpecFilePathFormat:
|
|
66
|
+
Enabled: false
|
data/.yard-lint.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 (2026-02-24)
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
- **[Breaking]** Remove `detach_archive(queue_name)` method. PGMQ 2.0 no longer requires archive table detachment as archive tables are no longer member objects. The server-side function was already a no-op in PGMQ 2.0+.
|
|
7
|
+
- **[Breaking]** Rename `vt_offset:` parameter to `vt:` in `set_vt`, `set_vt_batch`, and `set_vt_multi` methods. The `vt:` parameter now accepts either an integer offset (seconds from now) or an absolute `Time` object for PGMQ v1.11.0+.
|
|
8
|
+
|
|
9
|
+
### PGMQ v1.11.0 Features
|
|
10
|
+
- **[Feature]** Add `last_read_at` field to `PGMQ::Message`. Returns the timestamp of the last read operation for the message, or nil if the message has never been read. This enables tracking when messages were last accessed (PGMQ v1.8.1+).
|
|
11
|
+
- **[Feature]** Add Grouped Round-Robin reading for fair message processing:
|
|
12
|
+
- `read_grouped_rr(queue_name, vt:, qty:)` - Read messages in round-robin order across groups
|
|
13
|
+
- `read_grouped_rr_with_poll(queue_name, vt:, qty:, max_poll_seconds:, poll_interval_ms:)` - With long-polling
|
|
14
|
+
|
|
15
|
+
Messages are grouped by the first key in their JSON payload. This ensures fair processing
|
|
16
|
+
when multiple entities (users, orders, etc.) have messages in the queue, preventing any
|
|
17
|
+
single entity from monopolizing workers.
|
|
18
|
+
- **[Feature]** Add Topic Routing support (AMQP-like patterns). New methods in `PGMQ::Client`:
|
|
19
|
+
- `bind_topic(pattern, queue_name)` - Bind a topic pattern to a queue
|
|
20
|
+
- `unbind_topic(pattern, queue_name)` - Remove a topic binding
|
|
21
|
+
- `produce_topic(routing_key, message, headers:, delay:)` - Send message via routing key
|
|
22
|
+
- `produce_batch_topic(routing_key, messages, headers:, delay:)` - Batch send via routing key
|
|
23
|
+
- `list_topic_bindings(queue_name:)` - List all topic bindings
|
|
24
|
+
- `test_routing(routing_key)` - Test which queues a routing key matches
|
|
25
|
+
- `validate_routing_key(routing_key)` - Validate a routing key
|
|
26
|
+
- `validate_topic_pattern(pattern)` - Validate a topic pattern
|
|
27
|
+
|
|
28
|
+
Topic patterns support wildcards: `*` (single word) and `#` (zero or more words).
|
|
29
|
+
Requires PGMQ v1.11.0+.
|
|
30
|
+
|
|
31
|
+
### Testing
|
|
32
|
+
- **[Feature]** Add Fiber Scheduler integration tests demonstrating compatibility with Ruby's Fiber Scheduler API and the `async` gem for concurrent I/O operations.
|
|
33
|
+
|
|
34
|
+
### Infrastructure
|
|
35
|
+
- **[Fix]** Update docker-compose.yml volume mount for PostgreSQL 18+ compatibility.
|
|
36
|
+
- **[Change]** Replace Coditsu with StandardRB for code linting. This provides faster, more consistent linting using the community Ruby Style Guide.
|
|
37
|
+
|
|
3
38
|
## 0.4.0 (2025-12-26)
|
|
4
39
|
|
|
5
40
|
### Breaking Changes
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
PGMQ-Ruby is a **low-level Ruby client** for PGMQ (PostgreSQL Message Queue), analogous to how rdkafka-ruby relates to Kafka. It provides direct 1:1 wrappers for PGMQ SQL functions as a thin transport layer.
|
|
8
|
+
|
|
9
|
+
This is **NOT** a full job processing framework. Framework features (instrumentation, job processing, Rails ActiveJob, retry strategies, monitoring integrations) belong in the planned `pgmq-framework` gem.
|
|
10
|
+
|
|
11
|
+
**Key Design Principles:**
|
|
12
|
+
- Thin wrapper around PGMQ SQL functions (low-level primitives only)
|
|
13
|
+
- Thread-safe connection pooling (transport layer)
|
|
14
|
+
- PostgreSQL transaction support (database primitive, not framework abstraction)
|
|
15
|
+
- Framework-agnostic (works with Rails, Sinatra, plain Ruby)
|
|
16
|
+
- Minimal dependencies (only `pg` and `connection_pool`)
|
|
17
|
+
- No instrumentation/observability layer (framework concern)
|
|
18
|
+
|
|
19
|
+
## Development Commands
|
|
20
|
+
|
|
21
|
+
### Testing
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Start PostgreSQL with PGMQ extension (required for tests)
|
|
25
|
+
docker compose up -d
|
|
26
|
+
|
|
27
|
+
# Run all tests
|
|
28
|
+
bundle exec rspec
|
|
29
|
+
|
|
30
|
+
# Run specific test file
|
|
31
|
+
bundle exec rspec spec/unit/client_spec.rb
|
|
32
|
+
|
|
33
|
+
# Run integration tests only
|
|
34
|
+
bundle exec rspec spec/integration
|
|
35
|
+
|
|
36
|
+
# Run single test by line number
|
|
37
|
+
bundle exec rspec spec/unit/client_spec.rb:42
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Important:** PostgreSQL runs on port **5433** locally (see docker-compose.yml) to avoid conflicts with system PostgreSQL. Tests use this port automatically.
|
|
41
|
+
|
|
42
|
+
### Code Quality
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Run tests
|
|
46
|
+
bundle exec rspec
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Interactive Development
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Start IRB console with PGMQ loaded
|
|
54
|
+
bundle exec bin/console
|
|
55
|
+
|
|
56
|
+
# Console automatically connects to test database at localhost:5433
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
### Core Components
|
|
62
|
+
|
|
63
|
+
1. **PGMQ::Client** (`lib/pgmq/client.rb`)
|
|
64
|
+
- Main interface for all PGMQ operations (modular architecture, ~130 lines)
|
|
65
|
+
- Delegates connection management to PGMQ::Connection
|
|
66
|
+
- Validates queue names (48 char max, PostgreSQL identifier rules)
|
|
67
|
+
- Composed of 8 functional modules for separation of concerns:
|
|
68
|
+
- **Transaction** (`lib/pgmq/transaction.rb`) - PostgreSQL transaction support
|
|
69
|
+
- **QueueManagement** (`lib/pgmq/client/queue_management.rb`) - Queue lifecycle (create, drop, list)
|
|
70
|
+
- **Producer** (`lib/pgmq/client/producer.rb`) - Message sending operations
|
|
71
|
+
- **Consumer** (`lib/pgmq/client/consumer.rb`) - Single-queue reading operations
|
|
72
|
+
- **MultiQueue** (`lib/pgmq/client/multi_queue.rb`) - Multi-queue operations (UNION ALL)
|
|
73
|
+
- **MessageLifecycle** (`lib/pgmq/client/message_lifecycle.rb`) - Message state transitions (pop, delete, archive, set_vt)
|
|
74
|
+
- **Maintenance** (`lib/pgmq/client/maintenance.rb`) - Queue maintenance (purge, notifications)
|
|
75
|
+
- **Metrics** (`lib/pgmq/client/metrics.rb`) - Monitoring and metrics
|
|
76
|
+
- Benefits: Each module can be tested independently, easier maintenance, clear separation of concerns
|
|
77
|
+
- Pattern inspired by Waterdrop's modular client architecture
|
|
78
|
+
|
|
79
|
+
2. **PGMQ::Connection** (`lib/pgmq/connection.rb`)
|
|
80
|
+
- Thread-safe connection pooling using `connection_pool` gem
|
|
81
|
+
- Supports multiple connection strategies:
|
|
82
|
+
- Connection strings: `postgres://user:pass@host/db`
|
|
83
|
+
- Hash parameters: `{ host:, port:, dbname:, user:, password: }`
|
|
84
|
+
- Callables (for Rails): `-> { ActiveRecord::Base.connection.raw_connection }`
|
|
85
|
+
- Auto-reconnect on connection failures (configurable)
|
|
86
|
+
- Connection health checks before use
|
|
87
|
+
- Note: Connection parameters are required - users should manage ENV variables themselves using `ENV.fetch`
|
|
88
|
+
|
|
89
|
+
3. **PGMQ::Transaction** (`lib/pgmq/transaction.rb`)
|
|
90
|
+
- Low-level PostgreSQL transaction support (database primitive)
|
|
91
|
+
- Wraps PostgreSQL's native transactions (NOT a framework abstraction)
|
|
92
|
+
- Analogous to rdkafka-ruby providing Kafka transaction support
|
|
93
|
+
- Mixin providing `client.transaction do |txn|` support
|
|
94
|
+
- Enables atomic operations across multiple queues
|
|
95
|
+
- Rolls back automatically on errors
|
|
96
|
+
|
|
97
|
+
4. **Models**
|
|
98
|
+
- **PGMQ::Message** - Represents queue messages with `msg_id`, `message` (raw JSONB), `read_ct`, `enqueued_at`, `vt`, `queue_name` (for multi-queue ops)
|
|
99
|
+
- **PGMQ::Metrics** - Queue metrics (length, age, total messages)
|
|
100
|
+
- **PGMQ::QueueMetadata** - Queue information (name, creation time)
|
|
101
|
+
|
|
102
|
+
### Connection Pooling Details
|
|
103
|
+
|
|
104
|
+
- Default pool size: 5 connections
|
|
105
|
+
- Default timeout: 5 seconds
|
|
106
|
+
- Fiber-aware (works with Ruby 3.0+ Fiber Scheduler)
|
|
107
|
+
- Auto-reconnect enabled by default (can be disabled)
|
|
108
|
+
- Connection health verified before each use (if auto-reconnect enabled)
|
|
109
|
+
|
|
110
|
+
### Error Hierarchy
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
PGMQ::Errors::BaseError (StandardError)
|
|
114
|
+
├── PGMQ::Errors::ConnectionError
|
|
115
|
+
├── PGMQ::Errors::QueueNotFoundError
|
|
116
|
+
├── PGMQ::Errors::MessageNotFoundError
|
|
117
|
+
├── PGMQ::Errors::SerializationError
|
|
118
|
+
├── PGMQ::Errors::ConfigurationError
|
|
119
|
+
└── PGMQ::Errors::InvalidQueueNameError
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Queue Name Validation
|
|
123
|
+
|
|
124
|
+
- Maximum 48 characters (PGMQ limitation for table prefixes)
|
|
125
|
+
- Must start with letter or underscore
|
|
126
|
+
- Only letters, digits, underscores allowed
|
|
127
|
+
- Case-sensitive
|
|
128
|
+
- Validated in `Client#validate_queue_name!`
|
|
129
|
+
|
|
130
|
+
### Modular Architecture Pattern
|
|
131
|
+
|
|
132
|
+
The Client class follows Waterdrop's modular architecture pattern for better maintainability:
|
|
133
|
+
|
|
134
|
+
**Structure:**
|
|
135
|
+
- Small core `Client` class (~130 lines) that includes functional modules
|
|
136
|
+
- Each module focuses on a single domain area (queue management, producer, consumer, etc.)
|
|
137
|
+
- Modules access parent class helpers (`validate_queue_name!`, `with_connection`)
|
|
138
|
+
- Complete backward compatibility - no API changes
|
|
139
|
+
|
|
140
|
+
**Benefits:**
|
|
141
|
+
- **Testability**: Each module can be tested independently
|
|
142
|
+
- **Maintainability**: Smaller files, clearer boundaries (largest file ~210 lines)
|
|
143
|
+
- **Discoverability**: Logical grouping makes finding methods easier
|
|
144
|
+
- **Extensibility**: New modules can be added without bloating the core class
|
|
145
|
+
|
|
146
|
+
**Module Organization:**
|
|
147
|
+
1. Transaction support (existing mixin)
|
|
148
|
+
2. Queue lifecycle operations (create, drop, list)
|
|
149
|
+
3. Message production (sending)
|
|
150
|
+
4. Single-queue consumption (reading)
|
|
151
|
+
5. Multi-queue operations (efficient polling across queues)
|
|
152
|
+
6. Message lifecycle (pop, delete, archive, visibility timeout)
|
|
153
|
+
7. Maintenance operations (purge, detach archive)
|
|
154
|
+
8. Metrics and monitoring
|
|
155
|
+
|
|
156
|
+
All modules are automatically loaded via Zeitwerk.
|
|
157
|
+
|
|
158
|
+
## Common Patterns
|
|
159
|
+
|
|
160
|
+
### Client Initialization
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# Connection string (preferred)
|
|
164
|
+
client = PGMQ::Client.new('postgres://localhost:5433/pgmq_test')
|
|
165
|
+
|
|
166
|
+
# Connection hash
|
|
167
|
+
client = PGMQ::Client.new(
|
|
168
|
+
host: 'localhost',
|
|
169
|
+
port: 5433,
|
|
170
|
+
dbname: 'pgmq_test',
|
|
171
|
+
user: 'postgres',
|
|
172
|
+
password: 'postgres'
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Connection hash using ENV variables (user manages ENV themselves)
|
|
176
|
+
client = PGMQ::Client.new(
|
|
177
|
+
host: ENV.fetch('PG_HOST', 'localhost'),
|
|
178
|
+
port: ENV.fetch('PG_PORT', 5432).to_i,
|
|
179
|
+
dbname: ENV.fetch('PG_DATABASE', 'pgmq'),
|
|
180
|
+
user: ENV.fetch('PG_USER', 'postgres'),
|
|
181
|
+
password: ENV.fetch('PG_PASSWORD', 'postgres')
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Rails integration (reuses Rails connection pool)
|
|
185
|
+
client = PGMQ::Client.new(-> { ActiveRecord::Base.connection.raw_connection })
|
|
186
|
+
|
|
187
|
+
# Custom pool configuration
|
|
188
|
+
client = PGMQ::Client.new(
|
|
189
|
+
'postgres://localhost/db',
|
|
190
|
+
pool_size: 10,
|
|
191
|
+
pool_timeout: 10,
|
|
192
|
+
auto_reconnect: false
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Transaction Pattern
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# Atomic operations across queues
|
|
200
|
+
client.transaction do |txn|
|
|
201
|
+
msg = txn.read('pending', vt: 30)
|
|
202
|
+
if msg
|
|
203
|
+
txn.produce('processed', msg.payload)
|
|
204
|
+
txn.delete('pending', msg.msg_id)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Long Polling Pattern
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
# Efficient message consumption
|
|
213
|
+
loop do
|
|
214
|
+
msg = client.read_with_poll('orders',
|
|
215
|
+
vt: 30,
|
|
216
|
+
max_poll_seconds: 5,
|
|
217
|
+
poll_interval_ms: 100
|
|
218
|
+
)
|
|
219
|
+
break unless msg
|
|
220
|
+
|
|
221
|
+
process(msg)
|
|
222
|
+
client.delete('orders', msg.msg_id)
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Testing Guidelines
|
|
227
|
+
|
|
228
|
+
### Test Structure
|
|
229
|
+
|
|
230
|
+
- **Unit tests** (`spec/unit/`) - Test classes in isolation, mock database
|
|
231
|
+
- **Integration tests** (`spec/integration/`) - Test full workflow with real PostgreSQL
|
|
232
|
+
|
|
233
|
+
### Test Helpers
|
|
234
|
+
|
|
235
|
+
Located in `spec/support/database_helpers.rb`:
|
|
236
|
+
- Database cleanup between tests
|
|
237
|
+
- Connection helpers
|
|
238
|
+
- Common test utilities
|
|
239
|
+
|
|
240
|
+
### Coverage Requirements
|
|
241
|
+
|
|
242
|
+
- Minimum overall coverage: 80%
|
|
243
|
+
- Minimum per-file coverage: 70%
|
|
244
|
+
- SimpleCov configured in `spec/spec_helper.rb`
|
|
245
|
+
|
|
246
|
+
### Writing Tests
|
|
247
|
+
|
|
248
|
+
- Use descriptive test names
|
|
249
|
+
- Follow AAA pattern (Arrange, Act, Assert)
|
|
250
|
+
- Mock external dependencies in unit tests
|
|
251
|
+
- Use real database for integration tests
|
|
252
|
+
- Clean up queues in `after` blocks
|
|
253
|
+
|
|
254
|
+
## Code Style
|
|
255
|
+
|
|
256
|
+
- Ruby 3.2+ syntax
|
|
257
|
+
- Frozen string literals (`# frozen_string_literal: true`)
|
|
258
|
+
- Max line length: 120 characters (except specs/gemspec)
|
|
259
|
+
- YARD documentation for public methods
|
|
260
|
+
- Single quotes for strings (unless interpolation needed)
|
|
261
|
+
|
|
262
|
+
## CI/CD
|
|
263
|
+
|
|
264
|
+
GitHub Actions workflows in `.github/workflows/`:
|
|
265
|
+
- **ci.yml** - Runs RSpec on all Ruby versions (3.2, 3.3, 3.4, 3.5)
|
|
266
|
+
- **push.yml** - Additional checks on push
|
|
267
|
+
|
|
268
|
+
## Dependencies
|
|
269
|
+
|
|
270
|
+
**Runtime:**
|
|
271
|
+
- `pg` (~> 1.5) - PostgreSQL adapter
|
|
272
|
+
- `connection_pool` (~> 2.4) - Thread-safe connection pooling
|
|
273
|
+
|
|
274
|
+
**Development:**
|
|
275
|
+
- `rspec` - Testing framework
|
|
276
|
+
- `simplecov` - Code coverage
|
|
277
|
+
- `pry` / `pry-byebug` - Debugging tools
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
## Version Information
|
|
281
|
+
|
|
282
|
+
- Minimum Ruby: 3.2.0
|
|
283
|
+
- Supported PostgreSQL: 14-18 with PGMQ extension
|
|
284
|
+
- License: LGPL-3.0
|
|
285
|
+
|
|
286
|
+
## Important Files
|
|
287
|
+
|
|
288
|
+
### Core Library Structure
|
|
289
|
+
- `lib/pgmq/client.rb` - Main public API (~130 lines, includes all modules)
|
|
290
|
+
- `lib/pgmq/client/` - Client functional modules directory:
|
|
291
|
+
- `queue_management.rb` - Queue lifecycle operations (~100 lines)
|
|
292
|
+
- `producer.rb` - Message sending (~70 lines)
|
|
293
|
+
- `consumer.rb` - Single-queue reading (~140 lines)
|
|
294
|
+
- `multi_queue.rb` - Multi-queue operations (~200 lines)
|
|
295
|
+
- `message_lifecycle.rb` - Message state transitions (~210 lines)
|
|
296
|
+
- `maintenance.rb` - Queue maintenance (~30 lines)
|
|
297
|
+
- `metrics.rb` - Monitoring (~40 lines)
|
|
298
|
+
- `lib/pgmq/connection.rb` - Connection pooling and management
|
|
299
|
+
- `lib/pgmq/transaction.rb` - Transaction support
|
|
300
|
+
- `lib/pgmq/message.rb` - Message model (Data class)
|
|
301
|
+
- `lib/pgmq/metrics.rb` - Metrics model
|
|
302
|
+
- `lib/pgmq/queue_metadata.rb` - Queue metadata model
|
|
303
|
+
|
|
304
|
+
### Documentation & Testing
|
|
305
|
+
- `spec/spec_helper.rb` - RSpec configuration + SimpleCov setup
|
|
306
|
+
- `spec/integration/` - Integration tests with real PostgreSQL
|
|
307
|
+
- `spec/unit/` - Unit tests for individual components
|
|
308
|
+
- `DEVELOPMENT.md` - Comprehensive development documentation
|
|
309
|
+
- `README.md` - User-facing documentation with all API examples
|
|
310
|
+
- `CLAUDE.md` - AI assistant guidance (this file)
|
data/Gemfile
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
source
|
|
3
|
+
source "https://rubygems.org"
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
7
|
group :development, :test do
|
|
8
|
-
gem
|
|
9
|
-
gem
|
|
10
|
-
gem
|
|
8
|
+
gem "rake"
|
|
9
|
+
gem "rspec"
|
|
10
|
+
gem "async", "~> 2.6" # Fiber Scheduler for concurrent I/O testing
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
group :test do
|
|
14
|
-
gem
|
|
14
|
+
gem "simplecov", require: false
|
|
15
15
|
end
|
data/Gemfile.lint
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
# Documentation linting
|
|
6
|
+
gem "yard-lint"
|
|
7
|
+
|
|
8
|
+
# Code style (StandardRB via RuboCop)
|
|
9
|
+
gem "standard"
|
|
10
|
+
gem "standard-performance"
|
|
11
|
+
gem "rubocop-performance"
|
|
12
|
+
gem "rubocop-rspec"
|
|
13
|
+
gem "standard-rspec"
|
|
14
|
+
gem "rubocop-capybara"
|
|
15
|
+
gem "rubocop-factory_bot"
|
|
16
|
+
gem "rubocop-rspec_rails"
|