pgmq-ruby 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5648229b33f490f7228d7f9b106fb40939632c6effff094d028d9f7dd5fc045a
4
- data.tar.gz: 7a7d0ae6dd7ee59cf329093f4772e8cd8706e25be0e627defca60f3983e96084
3
+ metadata.gz: 812f7254909aa9125fa79b5304cb3fad8461cce5372dd02206c7cfdf9b99afa9
4
+ data.tar.gz: 754f398b518800546fa2c2fe2bbaf3a3d3eb742ca5c162d9c89cd1415ac40a57
5
5
  SHA512:
6
- metadata.gz: 5da9b67730f7c1b58af6be47c2364092988823b2c536e762ca56cc91bd5f27d2d678aef116373c7f0c62fec5037438f30ae0482979c0df3f9414aa3e5f41987a
7
- data.tar.gz: 7c892fcc8d30d306670b7dd3fd3a3e3f70f573916b3e6526e0833a59acad85cdd44fcf830fd327ccba6608fe0e30eb072d2adc1bcb287ccc28c388e743cfd19e
6
+ metadata.gz: 40b65d9001f469d2e88c04cc5f4bcf85b234b850a19ee4089e0b25f52b266bf45c8680398de43a61a042998a08760b867d3be1878aad518567caa6a0801cec80
7
+ data.tar.gz: 175b62d75d9fa46391570026e345b18c099ab75dabfe4518acd89e750de4fce97eebd01fcc8733ce771492343a74f50dbca9534c2305e47599eebe966dd04d9d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 (2026-04-02)
4
+
5
+ ### Breaking Changes
6
+ - **[Breaking]** Drop Ruby 3.2 support. Minimum required Ruby version is now 3.3.0.
7
+
8
+ ### Connection Management
9
+ - **[Breaking]** Detect shared `PG::Connection` across pool slots at creation time. When a callable connection factory returns the same `PG::Connection` object to multiple pool slots, concurrent threads corrupt libpq's internal state, causing nil `PG::Result` (`NoMethodError: undefined method 'ntuples' for nil`), segfaults, or wrong data. The pool now tracks connection identity via `ObjectSpace::WeakKeyMap` and raises `PGMQ::Errors::ConfigurationError` immediately with a descriptive error message. WeakKeyMap entries are automatically cleaned up when connections are GC'd. **This change is breaking for configurations that intentionally share a single `PG::Connection` across multiple pool slots. Users must ensure their callable returns a distinct `PG::Connection` per pool slot or configure `pool_size: 1` when reusing a single shared connection.**
10
+
11
+ ### Infrastructure
12
+ - **[Change]** Migrate test framework from RSpec to Minitest/Spec with Mocha for mocking, aligning with the broader Karafka ecosystem conventions.
13
+ - **[Change]** Replace `rubocop-rspec` with `rubocop-minitest` for test linting.
14
+ - **[Change]** Add `bin/integrations` runner script that centralizes integration spec execution. Specs no longer need `require_relative "support/example_helper"` — the runner injects it via `-r` flag. Run all specs with `bin/integrations` or specific ones with `bin/integrations spec/integration/foo_spec.rb`.
15
+
3
16
  ## 0.5.0 (2026-02-24)
4
17
 
5
18
  ### Breaking Changes
@@ -153,7 +166,7 @@ Initial release of pgmq-ruby - a low-level Ruby client for PGMQ (PostgreSQL Mess
153
166
  - [Enhancement] Example scripts demonstrating all features.
154
167
 
155
168
  ### Dependencies
156
- - Ruby >= 3.2.0
169
+ - Ruby >= 3.3.0
157
170
  - PostgreSQL >= 14 with PGMQ extension
158
171
  - `pg` gem (~> 1.5)
159
172
  - `connection_pool` gem (~> 2.4)
data/README.md CHANGED
@@ -877,7 +877,13 @@ bundle install
877
877
  docker compose up -d
878
878
 
879
879
  # Run tests
880
- bundle exec rspec
880
+ bundle exec rake test
881
+
882
+ # Run all integration specs
883
+ bin/integrations
884
+
885
+ # Run a specific integration spec
886
+ bin/integrations spec/integration/basic_produce_consume_spec.rb
881
887
 
882
888
  # Run console
883
889
  bundle exec bin/console
@@ -169,9 +169,32 @@ module PGMQ
169
169
  # @return [ConnectionPool]
170
170
  def create_pool
171
171
  params = @conn_params
172
+ seen_connections = ObjectSpace::WeakKeyMap.new
173
+ seen_mutex = Mutex.new
172
174
 
173
175
  ConnectionPool.new(size: @pool_size, timeout: @pool_timeout) do
174
- create_connection(params)
176
+ conn = create_connection(params)
177
+
178
+ # Detect shared connections: if a callable returns the same PG::Connection
179
+ # object to multiple pool slots, concurrent use will corrupt libpq state
180
+ # (nil results, segfaults, wrong data). Fail fast with a clear message.
181
+ if conn.is_a?(PG::Connection)
182
+ seen_mutex.synchronize do
183
+ if seen_connections.key?(conn)
184
+ raise PGMQ::Errors::ConfigurationError,
185
+ "Connection callable returned the same PG::Connection object " \
186
+ "(object_id: #{conn.object_id}) to multiple pool slots. " \
187
+ "PG::Connection is NOT thread-safe — concurrent use causes nil results, " \
188
+ "segfaults, and data corruption. Ensure your callable returns a unique " \
189
+ "PG::Connection instance on each invocation (for example, by calling " \
190
+ "PG.connect inside the callable)."
191
+ end
192
+
193
+ seen_connections[conn] = true
194
+ end
195
+ end
196
+
197
+ conn
175
198
  end
176
199
  rescue => e
177
200
  raise PGMQ::Errors::ConnectionError, "Failed to create connection pool: #{e.message}"
data/lib/pgmq/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PGMQ
4
4
  # Current version of the pgmq-ruby gem
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
data/pgmq-ruby.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  "Like AWS SQS and RSMQ, but on Postgres."
14
14
  spec.homepage = "https://github.com/mensfeld/pgmq-ruby"
15
15
  spec.license = "LGPL-3.0"
16
- spec.required_ruby_version = ">= 3.2.0"
16
+ spec.required_ruby_version = ">= 3.3.0"
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = "https://github.com/mensfeld/pgmq-ruby"
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.metadata["documentation_uri"] = "https://github.com/mensfeld/pgmq-ruby#readme"
23
23
  spec.metadata["rubygems_mfa_required"] = "true"
24
24
 
25
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|examples)/}) }
25
+ spec.files = Dir["lib/**/*", "CHANGELOG.md", "LICENSE", "README.md", "pgmq-ruby.gemspec"]
26
26
  spec.require_paths = ["lib"]
27
27
 
28
28
  # Runtime dependencies
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgmq-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -59,23 +59,9 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - ".github/workflows/ci.yml"
63
- - ".github/workflows/push.yml"
64
- - ".gitignore"
65
- - ".rspec"
66
- - ".rubocop.yml"
67
- - ".ruby-version"
68
- - ".yard-lint.yml"
69
62
  - CHANGELOG.md
70
- - CLAUDE.md
71
- - Gemfile
72
- - Gemfile.lint
73
- - Gemfile.lint.lock
74
- - Gemfile.lock
75
63
  - LICENSE
76
64
  - README.md
77
- - Rakefile
78
- - docker-compose.yml
79
65
  - lib/pgmq.rb
80
66
  - lib/pgmq/client.rb
81
67
  - lib/pgmq/client/consumer.rb
@@ -93,10 +79,7 @@ files:
93
79
  - lib/pgmq/queue_metadata.rb
94
80
  - lib/pgmq/transaction.rb
95
81
  - lib/pgmq/version.rb
96
- - package-lock.json
97
- - package.json
98
82
  - pgmq-ruby.gemspec
99
- - renovate.json
100
83
  homepage: https://github.com/mensfeld/pgmq-ruby
101
84
  licenses:
102
85
  - LGPL-3.0
@@ -114,14 +97,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
97
  requirements:
115
98
  - - ">="
116
99
  - !ruby/object:Gem::Version
117
- version: 3.2.0
100
+ version: 3.3.0
118
101
  required_rubygems_version: !ruby/object:Gem::Requirement
119
102
  requirements:
120
103
  - - ">="
121
104
  - !ruby/object:Gem::Version
122
105
  version: '0'
123
106
  requirements: []
124
- rubygems_version: 4.0.3
107
+ rubygems_version: 4.0.6
125
108
  specification_version: 4
126
109
  summary: Ruby client for PGMQ (Postgres Message Queue)
127
110
  test_files: []
@@ -1,183 +0,0 @@
1
- name: CI
2
-
3
- concurrency:
4
- group: ${{ github.workflow }}-${{ github.ref }}
5
- cancel-in-progress: true
6
-
7
- on:
8
- pull_request:
9
- branches: [ master ]
10
- schedule:
11
- - cron: '0 1 * * *'
12
-
13
- permissions:
14
- contents: read
15
-
16
- jobs:
17
- specs:
18
- timeout-minutes: 15
19
- runs-on: ubuntu-latest
20
- strategy:
21
- fail-fast: false
22
- matrix:
23
- ruby:
24
- - '4.0.0'
25
- - '3.4'
26
- - '3.3'
27
- - '3.2'
28
- postgres:
29
- - '14'
30
- - '15'
31
- - '16'
32
- - '17'
33
- - '18'
34
- include:
35
- - ruby: '3.4'
36
- postgres: '18'
37
- coverage: 'true'
38
-
39
- services:
40
- postgres:
41
- image: ghcr.io/pgmq/pg${{ matrix.postgres }}-pgmq:latest
42
- env:
43
- POSTGRES_USER: postgres
44
- POSTGRES_PASSWORD: postgres
45
- POSTGRES_DB: pgmq_test
46
- options: >-
47
- --health-cmd pg_isready
48
- --health-interval 10s
49
- --health-timeout 5s
50
- --health-retries 5
51
- ports:
52
- - 5433:5432
53
-
54
- steps:
55
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
56
- with:
57
- fetch-depth: 0
58
-
59
- - name: Install package dependencies
60
- run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends postgresql-client"
61
-
62
- - name: Remove Gemfile.lock for Ruby previews
63
- if: contains(matrix.ruby, '4.0')
64
- run: rm -f Gemfile.lock
65
-
66
- - name: Set up Ruby
67
- uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
68
- with:
69
- ruby-version: ${{ matrix.ruby }}
70
- bundler-cache: true
71
- bundler: 'latest'
72
- self-hosted: false
73
-
74
- - name: Wait for PostgreSQL
75
- run: sleep 5
76
-
77
- - name: Install latest bundler
78
- run: |
79
- gem install bundler --no-document
80
- gem update --system --no-document
81
- bundle config set without 'tools benchmarks docs'
82
-
83
- - name: Bundle install
84
- run: |
85
- bundle config set without development
86
- bundle install --jobs 4 --retry 3
87
-
88
- - name: Create PGMQ extension
89
- env:
90
- PGPASSWORD: postgres
91
- run: |
92
- psql -h localhost -p 5433 -U postgres -d pgmq_test -c "CREATE EXTENSION IF NOT EXISTS pgmq CASCADE;"
93
-
94
- - name: Run all tests
95
- env:
96
- PG_HOST: localhost
97
- PG_PORT: 5433
98
- PG_DATABASE: pgmq_test
99
- PG_USER: postgres
100
- PG_PASSWORD: postgres
101
- CI: true
102
- GITHUB_COVERAGE: ${{ matrix.coverage }}
103
- run: bundle exec rspec
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
-
114
- yard-lint:
115
- timeout-minutes: 5
116
- runs-on: ubuntu-latest
117
- strategy:
118
- fail-fast: false
119
- env:
120
- BUNDLE_GEMFILE: Gemfile.lint
121
- steps:
122
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
123
- with:
124
- fetch-depth: 0
125
- - name: Set up Ruby
126
- uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
127
- with:
128
- ruby-version: '4.0.0'
129
- bundler-cache: true
130
- - name: Run yard-lint
131
- run: bundle exec yard-lint lib/
132
-
133
- rubocop:
134
- timeout-minutes: 5
135
- runs-on: ubuntu-latest
136
- env:
137
- BUNDLE_GEMFILE: Gemfile.lint
138
- steps:
139
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
140
- with:
141
- fetch-depth: 0
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
166
-
167
- ci-success:
168
- name: CI Success
169
- runs-on: ubuntu-latest
170
- if: always()
171
- needs:
172
- - rubocop
173
- - specs
174
- - yard-lint
175
- - lostconf
176
- steps:
177
- - name: Check all jobs passed
178
- if: |
179
- contains(needs.*.result, 'failure') ||
180
- contains(needs.*.result, 'cancelled') ||
181
- contains(needs.*.result, 'skipped')
182
- run: exit 1
183
- - run: echo "All CI checks passed!"
@@ -1,35 +0,0 @@
1
- name: Push Gem
2
-
3
- on:
4
- push:
5
- tags:
6
- - v*
7
-
8
- permissions:
9
- contents: read
10
-
11
- jobs:
12
- push:
13
- if: github.repository_owner == 'mensfeld'
14
- runs-on: ubuntu-latest
15
- environment: deployment
16
-
17
- permissions:
18
- contents: write
19
- id-token: write
20
-
21
- steps:
22
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
23
- with:
24
- fetch-depth: 0
25
-
26
- - name: Set up Ruby
27
- uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
28
- with:
29
- bundler-cache: false
30
-
31
- - name: Bundle install
32
- run: |
33
- bundle install --jobs 4 --retry 3
34
-
35
- - uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2
data/.gitignore DELETED
@@ -1,67 +0,0 @@
1
- *.gem
2
- *.rbc
3
- /.config
4
- /coverage/
5
- /InstalledFiles
6
- /pkg/
7
- /spec/reports/
8
- /spec/examples.txt
9
- /test/tmp/
10
- /test/version_tmp/
11
- /tmp/
12
-
13
- # Used by dotenv library to load environment variables.
14
- # .env
15
-
16
- # Ignore Byebug command history file.
17
- .byebug_history
18
-
19
- ## Specific to RubyMotion:
20
- .dat*
21
- .repl_history
22
- build/
23
- *.bridgesupport
24
- build-iPhoneOS/
25
- build-iPhoneSimulator/
26
-
27
- ## Specific to RubyMotion (use of CocoaPods):
28
- #
29
- # We recommend against adding the Pods directory to your .gitignore. However
30
- # you should judge for yourself, the pros and cons are mentioned at:
31
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
- #
33
- # vendor/Pods/
34
-
35
- ## Documentation cache and generated files:
36
- /.yardoc/
37
- /_yardoc/
38
- /doc/
39
- /rdoc/
40
-
41
- ## RSpec test status file
42
- .rspec_status
43
-
44
- ## Environment normalization:
45
- /.bundle/
46
- /vendor/bundle
47
- /lib/bundler/man/
48
-
49
- # for a library or gem, you might want to ignore these files since the code is
50
- # intended to run in multiple environments; otherwise, check them in:
51
- # Gemfile.lock
52
- # .ruby-version
53
- # .ruby-gemset
54
-
55
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
56
- .rvmrc
57
-
58
- # Used by RuboCop. Remote config files pulled in from inherit_from directive.
59
- # .rubocop-https?--*
60
-
61
- .DS_Store
62
-
63
- ## Claude Code settings (user-specific)
64
- .claude/
65
-
66
- ## Coditsu configuration (user-specific)
67
- .coditsu/local.yml
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --require spec_helper
2
- --exclude-pattern spec/integration/**/*
data/.rubocop.yml DELETED
@@ -1,66 +0,0 @@
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/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 4.0.0