Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-oslo.messaging for
openSUSE:Factory checked in at 2026-10-26 10:01:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oslo.messaging (Old)
and /work/SRC/openSUSE:Factory/.python-oslo.messaging.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-oslo.messaging"
Mon Oct 26 10:01:53 2026 rev:40 rq:1392500 version:18.3.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-oslo.messaging/python-oslo.messaging.changes
2026-09-08 16:58:01.511759085 +0200
+++
/work/SRC/openSUSE:Factory/.python-oslo.messaging.new.2252680/python-oslo.messaging.changes
2026-10-26 10:03:15.355900890 +0100
@@ -1,0 +2,9 @@
+Sun Oct 25 17:41:36 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 18.3.1:
+ * Fix QManager reply queue collision under cotyledon/threading
+ * Fix rabbit consumer reconnection loop
+ * Update TOX_CONSTRAINTS_FILE for stable/2026.2
+ * Update .gitreview for stable/2026.2
+
+-------------------------------------------------------------------
Old:
----
oslo_messaging-18.3.0.tar.gz
New:
----
oslo_messaging-18.3.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-oslo.messaging.spec ++++++
--- /var/tmp/diff_new_pack.UccplY/_old 2026-10-26 10:03:16.036929450 +0100
+++ /var/tmp/diff_new_pack.UccplY/_new 2026-10-26 10:03:16.038929534 +0100
@@ -17,7 +17,7 @@
Name: python-oslo.messaging
-Version: 18.3.0
+Version: 18.3.1
Release: 0
Summary: OpenStack oslo.messaging library
License: Apache-2.0
++++++ oslo_messaging-18.3.0.tar.gz -> oslo_messaging-18.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo_messaging-18.3.0/AUTHORS
new/oslo_messaging-18.3.1/AUTHORS
--- old/oslo_messaging-18.3.0/AUTHORS 2026-08-24 16:00:30.821021800 +0200
+++ new/oslo_messaging-18.3.1/AUTHORS 2026-10-19 13:24:06.831040400 +0200
@@ -122,12 +122,14 @@
Juan Antonio Osorio Robles <[email protected]>
Juan Larriba <[email protected]>
Julien Danjou <[email protected]>
+Julien Le Jeune <[email protected]>
Kenneth Giusti <[email protected]>
Kevin Benton <[email protected]>
Kirill Bespalov <[email protected]>
Komei Shimamura <[email protected]>
Konstantin Kalin <[email protected]>
Kui Shi <[email protected]>
+Kushnarenko Nikita <[email protected]>
LIU Yulong <[email protected]>
Lance Bragstad <[email protected]>
Li Ma <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo_messaging-18.3.0/ChangeLog
new/oslo_messaging-18.3.1/ChangeLog
--- old/oslo_messaging-18.3.0/ChangeLog 2026-08-24 16:00:30.655008000 +0200
+++ new/oslo_messaging-18.3.1/ChangeLog 2026-10-19 13:24:06.662030700 +0200
@@ -1,6 +1,14 @@
CHANGES
=======
+18.3.1
+------
+
+* Fix QManager reply queue collision under cotyledon/threading
+* Fix rabbit consumer reconnection loop
+* Update TOX\_CONSTRAINTS\_FILE for stable/2026.2
+* Update .gitreview for stable/2026.2
+
18.3.0
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo_messaging-18.3.0/PKG-INFO
new/oslo_messaging-18.3.1/PKG-INFO
--- old/oslo_messaging-18.3.0/PKG-INFO 2026-08-24 16:00:30.983034800 +0200
+++ new/oslo_messaging-18.3.1/PKG-INFO 2026-10-19 13:24:07.000049800 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: oslo.messaging
-Version: 18.3.0
+Version: 18.3.1
Summary: Oslo Messaging API
Author-email: OpenStack <[email protected]>
Project-URL: Homepage, https://docs.openstack.org/oslo.messaging
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo.messaging.egg-info/PKG-INFO
new/oslo_messaging-18.3.1/oslo.messaging.egg-info/PKG-INFO
--- old/oslo_messaging-18.3.0/oslo.messaging.egg-info/PKG-INFO 2026-08-24
16:00:30.827022000 +0200
+++ new/oslo_messaging-18.3.1/oslo.messaging.egg-info/PKG-INFO 2026-10-19
13:24:06.836040500 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: oslo.messaging
-Version: 18.3.0
+Version: 18.3.1
Summary: Oslo Messaging API
Author-email: OpenStack <[email protected]>
Project-URL: Homepage, https://docs.openstack.org/oslo.messaging
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo.messaging.egg-info/SOURCES.txt
new/oslo_messaging-18.3.1/oslo.messaging.egg-info/SOURCES.txt
--- old/oslo_messaging-18.3.0/oslo.messaging.egg-info/SOURCES.txt
2026-08-24 16:00:30.905028600 +0200
+++ new/oslo_messaging-18.3.1/oslo.messaging.egg-info/SOURCES.txt
2026-10-19 13:24:06.921045500 +0200
@@ -111,6 +111,7 @@
oslo_messaging/tests/test_utils.py
oslo_messaging/tests/utils.py
oslo_messaging/tests/drivers/__init__.py
+oslo_messaging/tests/drivers/test_amqpdriver.py
oslo_messaging/tests/drivers/test_impl_kafka.py
oslo_messaging/tests/drivers/test_impl_rabbit.py
oslo_messaging/tests/drivers/test_pool.py
@@ -164,6 +165,7 @@
releasenotes/notes/drop-python27-support-5ef2f365d8930483.yaml
releasenotes/notes/enforce_fips_mode-07dd259eb8a73c2b.yaml
releasenotes/notes/fix-access_policy-deafult-a6954a147cb002b0.yaml
+releasenotes/notes/fix-qmanager-pgid-setsid-race-0a1b2c3d4e5f6a7b.yaml
releasenotes/notes/fix-queue-manager-with-podman-36078437037fa198.yaml
releasenotes/notes/get-rpc-client-0b4aa62160864b29.yaml
releasenotes/notes/get-rpc-helpers-cls-8911826ac08aef2a.yaml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo.messaging.egg-info/pbr.json
new/oslo_messaging-18.3.1/oslo.messaging.egg-info/pbr.json
--- old/oslo_messaging-18.3.0/oslo.messaging.egg-info/pbr.json 2026-08-24
16:00:30.844023500 +0200
+++ new/oslo_messaging-18.3.1/oslo.messaging.egg-info/pbr.json 2026-10-19
13:24:06.853041600 +0200
@@ -1 +1 @@
-{"git_version": "3b02f8ab", "is_release": true}
\ No newline at end of file
+{"git_version": "9df3fda5", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo_messaging/_drivers/amqpdriver.py
new/oslo_messaging-18.3.1/oslo_messaging/_drivers/amqpdriver.py
--- old/oslo_messaging-18.3.0/oslo_messaging/_drivers/amqpdriver.py
2026-08-24 15:59:42.959014700 +0200
+++ new/oslo_messaging-18.3.1/oslo_messaging/_drivers/amqpdriver.py
2026-10-19 13:23:14.348776600 +0200
@@ -63,21 +63,46 @@
self.file_name = '/dev/shm/{}_{}_qmanager'.format( # nosec
self.hostname,
self.processname)
- # We use the process group to restart the counter on service restart
- self.pg = os.getpgrp()
- # We need to also handle containerized deployments, so let's
- # parse start time (in jiffies) since system boot
+ @staticmethod
+ def _service_identity():
+ # The identity of the current *generation* of the service (the master
+ # and all its workers), used to decide whether the shared counter must
+ # be reset (the service restarted) or incremented.
#
+ # It MUST be evaluated when the queue name is built, NOT captured at
+ # object construction time, because the process group can change
+ # *after* the oslo.messaging objects already exist: any supervisor
+ # that calls os.setsid() once its objects are instantiated moves the
+ # process into a new group. This is what happens with the cotyledon
+ # supervisor, whose master calls os.setsid() after the default
+ # transport is created (before the service starts serving) and then
+ # forks the workers that actually call get(). A QManager built in the
+ # master before setsid
+ # captures the *pre-setsid* process group (the container init group),
+ # while the workers that actually call get() run *after* setsid in the
+ # master's own group. As every QManager of the process (reply queue,
+ # per-cell transports, per-connection fanout queues) shares the same
+ # /dev/shm counter, mixing a master-born identity with a worker-born
+ # one makes them reset the counter for one another: several processes
+ # then build the same reply queue name (reply_<host>:<proc>:1) and
+ # steal each other's RPC replies.
+ # See https://bugs.launchpad.net/oslo.messaging/+bug/2110957
+ pg = os.getpgrp()
+
+ # Disambiguate a recycled pid across a restart with the start time (in
+ # jiffies since system boot) of that process.
# https://www.man7.org/linux/man-pages//man5/proc_pid_stat.5.html
#
- # We fallback to process id here when process group id is 0, as /proc/0
- # does not exist. This is only hit in an edge case with the crun
- # container runtime (default for podman), see issue for more details:
+ # We fall back to our own pid when the process group id is 0, as
+ # /proc/0 does not exist. This is only hit in an edge case with the
+ # crun container runtime (default for podman) where a "podman exec"
+ # process gets no process group of its own, see
# https://github.com/containers/crun/issues/1642
- proc_id = self.pg or os.getpid()
+ proc_id = pg or os.getpid()
with open(f'/proc/{proc_id}/stat') as f:
- self.start_time = int(f.read().split()[21])
+ start_time = int(f.read().split()[21])
+ return pg, start_time
def get(self):
lock_name = 'oslo_read_shm_{}_{}'.format(
@@ -85,6 +110,11 @@
@lockutils.synchronized(lock_name, external=True)
def read_from_shm():
+ # Identity of the current service generation (the process group
+ # shared by the master and all its workers), re-evaluated on every
+ # call -- see _service_identity for why it must not be cached.
+ cur_pg, cur_start_time = self._service_identity()
+
# Grab the counter from shm
# This function is thread and process safe thanks to lockutils
try:
@@ -94,12 +124,12 @@
counter = int(counter)
start_time = int(start_time)
except (FileNotFoundError, ValueError):
- pg = self.pg
+ pg = cur_pg
counter = 0
- start_time = self.start_time
+ start_time = cur_start_time
# Increment the counter
- if pg == self.pg and start_time == self.start_time:
+ if pg == cur_pg and start_time == cur_start_time:
counter += 1
else:
# The process group is changed, or start time since system boot
@@ -109,8 +139,8 @@
# Write the new counter
with open(self.file_name, 'w') as f:
- f.write(str(self.pg) + ':' + str(counter) + ':' +
- str(self.start_time))
+ f.write(str(cur_pg) + ':' + str(counter) + ':' +
+ str(cur_start_time))
return counter
counter = read_from_shm()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo_messaging/_drivers/impl_rabbit.py
new/oslo_messaging-18.3.1/oslo_messaging/_drivers/impl_rabbit.py
--- old/oslo_messaging-18.3.0/oslo_messaging/_drivers/impl_rabbit.py
2026-08-24 15:59:42.971978700 +0200
+++ new/oslo_messaging-18.3.1/oslo_messaging/_drivers/impl_rabbit.py
2026-10-19 13:23:14.353781200 +0200
@@ -1117,6 +1117,10 @@
# NOTE(gsantomaggio): we must reraise this without
# trigger error_callback
raise
+ except rpc_common.Timeout:
+ # NOTE must reraise this without
+ # trigger error_callback
+ raise
except Exception as exc:
error_callback and error_callback(exc)
self._set_current_channel(None)
@@ -1372,11 +1376,17 @@
LOG.error('Failed to consume message from queue: %s', exc)
def _consume():
- # NOTE(sileht): in case the acknowledgment or requeue of a
- # message fail, the kombu transport can be disconnected
- # In this case, we must redeclare our consumers, so raise
- # a recoverable error to trigger the reconnection code.
+ # NOTE: Kombu transport can be interrupted if the acknowledgement
+ # or requeue of a message fails. We need to ensure the connection
+ # and re-declare consumers.
+ # Kombu's ensure_connection utilises a failover strategy, instead
+ # of retring to connect to the same host.
if not self.connection.connected:
+ # NOTE(bug/2126768): When a rabbit host is dead, reconnecting
+ # to the same host would loop forever until the node is fixed.
+ # Instead switch to the next host given by the failover
+ # strategy and raise a recoverable connection error.
+ self.connection.maybe_switch_next()
raise self.connection.recoverable_connection_errors[0]
while self._new_tags:
@@ -1391,13 +1401,18 @@
if self._consume_loop_stopped:
return
- if self._heartbeat_supported_and_enabled():
- self._heartbeat_check()
-
try:
self.connection.drain_events(timeout=poll_timeout)
return
except TimeoutError:
+ # Note(lajoskatona): Heartbeat check is only needed
+ # on timeout (idle connection). When drain_events
+ # returns successfully, the connection is clearly
+ # alive, as kombu's own ConsumerMixin behavior works
+ #
https://github.com/celery/kombu/blob/3633f916e455ad4d8835ee23abc0b526f5920ed7/kombu/mixins.py#L197-L198
+ if self._heartbeat_supported_and_enabled():
+ self._heartbeat_check()
+
poll_timeout = timer.check_return(
_raise_timeout, maximum=self._poll_timeout)
except self.connection.channel_errors as exc:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo_messaging/tests/drivers/test_amqpdriver.py
new/oslo_messaging-18.3.1/oslo_messaging/tests/drivers/test_amqpdriver.py
--- old/oslo_messaging-18.3.0/oslo_messaging/tests/drivers/test_amqpdriver.py
1970-01-01 01:00:00.000000000 +0100
+++ new/oslo_messaging-18.3.1/oslo_messaging/tests/drivers/test_amqpdriver.py
2026-10-19 13:23:14.365792500 +0200
@@ -0,0 +1,145 @@
+# Copyright 2026 OVHcloud
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+from unittest import mock
+
+import fixtures
+from oslo_concurrency import lockutils
+
+from oslo_messaging._drivers import amqpdriver
+from oslo_messaging.tests import utils as test_utils
+
+
+class QManagerTestCase(test_utils.BaseTestCase):
+ """Tests for the reply/fanout queue name manager (use_queue_manager).
+
+ Regression coverage for
+ https://bugs.launchpad.net/oslo.messaging/+bug/2110957 : the per-process
+ identity used to key the shared /dev/shm counter must be evaluated when the
+ queue name is built (get()), not cached at __init__ time, otherwise a
+ QManager created in the cotyledon master before os.setsid() and a QManager
+ created later in a worker disagree on the identity, keep resetting the
+ shared counter, and hand out duplicate reply queue names.
+ """
+
+ def setUp(self):
+ super().setUp()
+ # get() takes an external (file) lock via lockutils; give it a path.
+ lock_path = self.useFixture(fixtures.TempDir()).path
+ lockutils.set_defaults(lock_path=lock_path)
+
+ def _make_qmanager(self):
+ tmp = self.useFixture(fixtures.TempDir()).path
+ qm = amqpdriver.QManager(hostname='host', processname='conductor')
+ # Do not touch the real /dev/shm.
+ qm.file_name = os.path.join(tmp, 'host_conductor_qmanager')
+ return qm
+
+ def test_get_increments_for_a_stable_identity(self):
+ qm = self._make_qmanager()
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(7, 111)):
+ self.assertEqual('host:conductor:1', qm.get())
+ self.assertEqual('host:conductor:2', qm.get())
+ self.assertEqual('host:conductor:3', qm.get())
+
+ def test_get_resets_when_identity_changes(self):
+ # A different (pg, start_time) means the service was restarted: the
+ # counter must start over so queue names get reused.
+ qm = self._make_qmanager()
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(7, 111)):
+ self.assertEqual('host:conductor:1', qm.get())
+ self.assertEqual('host:conductor:2', qm.get())
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(9, 222)):
+ self.assertEqual('host:conductor:1', qm.get())
+
+ def test_get_resets_on_restart_with_constant_pid(self):
+ # Containerised service running as a constant pid/pgid across restarts
+ # (bug 2078935): the process group is unchanged but the start_time (in
+ # jiffies since boot) differs, so the restart must still be detected
+ # and the counter reset. This is why start_time is part of the key.
+ qm = self._make_qmanager()
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(1, 111)):
+ self.assertEqual('host:conductor:1', qm.get())
+ self.assertEqual('host:conductor:2', qm.get())
+ # Same pgid (1), different start_time -> restarted.
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(1, 999)):
+ self.assertEqual('host:conductor:1', qm.get())
+
+ def test_identity_evaluated_at_get_time_not_at_init(self):
+ # The bug: object built in the master (pre-setsid pgid) but used by a
+ # worker (post-setsid pgid). With the identity resolved at get() time,
+ # what matters is the *calling* process, not the constructing one.
+ qm = self._make_qmanager() # "constructed in the master"
+
+ # Now the worker (pgid 7) uses it: the stored identity must be the
+ # worker's, so the reply queue is stable and no longer keyed on the
+ # pre-setsid process group of the master.
+ with mock.patch.object(qm, '_service_identity',
+ return_value=(7, 111)):
+ self.assertEqual('host:conductor:1', qm.get())
+ self.assertEqual('host:conductor:2', qm.get())
+
+ def test_no_collision_between_master_born_and_worker_born_managers(self):
+ # Two QManagers sharing the same shm file (e.g. the reply-queue
+ # manager inherited from the master and a fanout manager created in
+ # the worker).
+ # Before the fix their cached self.pg differed (1 vs 7) and they reset
+ # the counter for one another, both landing on ":1". Now both resolve
+ # the identity at get() time -> same worker identity -> the counter
+ # keeps growing and names stay unique.
+ reply_qm = self._make_qmanager() # master-born
+ fanout_qm = self._make_qmanager() # worker-born
+ fanout_qm.file_name = reply_qm.file_name # same shared counter
+
+ worker_identity = (7, 111)
+ with mock.patch.object(reply_qm, '_service_identity',
+ return_value=worker_identity), \
+ mock.patch.object(fanout_qm, '_service_identity',
+ return_value=worker_identity):
+ first = reply_qm.get()
+ second = fanout_qm.get()
+
+ self.assertNotEqual(first, second)
+ self.assertEqual(['host:conductor:1', 'host:conductor:2'],
+ [first, second])
+
+ def test_service_identity_uses_process_group(self):
+ stat = ('7 (nova-conductor) S 1 7 7 0 -1 0 0 0 0 0 0 0 0 0 20 0 2 0 '
+ '424242 0 0')
+ with mock.patch.object(amqpdriver.os, 'getpgrp', return_value=7), \
+ mock.patch('builtins.open', mock.mock_open(read_data=stat)):
+ pg, start_time = amqpdriver.QManager._service_identity()
+ self.assertEqual(7, pg)
+ self.assertEqual(424242, start_time)
+
+ def test_service_identity_falls_back_to_pid_when_pgrp_zero(self):
+ # crun edge case (https://github.com/containers/crun/issues/1642):
+ # os.getpgrp() returns 0 and /proc/0 does not exist, so the start_time
+ # is read from our own pid instead. This is evaluated here at call
+ # time, no longer relying on the identity cached at construction. The
+ # returned pgid stays 0, which is shared across the exec'd processes.
+ stat = ('123 (nova-conductor) S 1 123 123 0 -1 0 0 0 0 0 0 0 0 0 20 0 '
+ '2 0 424242 0 0')
+ with mock.patch.object(amqpdriver.os, 'getpgrp', return_value=0), \
+ mock.patch.object(amqpdriver.os, 'getpid', return_value=123), \
+ mock.patch('builtins.open', mock.mock_open(read_data=stat)):
+ pg, start_time = amqpdriver.QManager._service_identity()
+ self.assertEqual(0, pg)
+ self.assertEqual(424242, start_time)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/oslo_messaging/tests/drivers/test_impl_rabbit.py
new/oslo_messaging-18.3.1/oslo_messaging/tests/drivers/test_impl_rabbit.py
--- old/oslo_messaging-18.3.0/oslo_messaging/tests/drivers/test_impl_rabbit.py
2026-08-24 15:59:42.991923300 +0200
+++ new/oslo_messaging-18.3.1/oslo_messaging/tests/drivers/test_impl_rabbit.py
2026-10-19 13:23:14.366793600 +0200
@@ -486,6 +486,64 @@
# Ensure a new channel have been setuped
self.assertNotEqual(channel, conn.connection.channel)
+ def test_consume_switches_host_when_disconnected(self):
+ transport = oslo_messaging.get_transport(self.conf,
+ 'kombu+memory:////')
+ self.addCleanup(transport.cleanup)
+ with transport._driver._get_connection(
+ driver_common.PURPOSE_LISTEN) as conn:
+ with mock.patch('kombu.connection.Connection.connected',
+ new_callable=mock.PropertyMock,
+ return_value=False), \
+ mock.patch('kombu.connection.Connection.'
+ 'recoverable_connection_errors',
+ new_callable=mock.PropertyMock,
+ return_value=(driver_common.Timeout,)), \
+ mock.patch.object(
+ conn.connection.connection,
+ 'maybe_switch_next') as mock_switch:
+ # The recoverable error is raised repeatedly until the
+ # consume timeout expires and Timeout is re-raised.
+ self.assertRaises(driver_common.Timeout,
+ conn.consume, timeout=0.01)
+ # We failed over to the next host at least once ...
+ mock_switch.assert_called()
+
+ def test_heartbeat_check_only_on_timeout(self):
+ transport = oslo_messaging.get_transport(self.conf,
+ 'kombu+memory:////')
+ self.addCleanup(transport.cleanup)
+ with transport._driver._get_connection(
+ driver_common.PURPOSE_LISTEN) as conn:
+ with mock.patch.object(
+ conn.connection, '_heartbeat_check') as mock_hb:
+ conn.connection.connection.drain_events = mock.Mock(
+ return_value=None)
+ conn.consume(timeout=0.01)
+ # heartbeat_check should NOT be called when
+ # drain_events succeeds
+ mock_hb.assert_not_called()
+
+ def test_heartbeat_check_called_on_drain_events_timeout(self):
+ transport = oslo_messaging.get_transport(self.conf,
+ 'kombu+memory:////')
+ self.addCleanup(transport.cleanup)
+ with transport._driver._get_connection(
+ driver_common.PURPOSE_LISTEN) as conn:
+ with mock.patch.object(
+ conn.connection, '_heartbeat_check') as mock_hb, \
+ mock.patch.object(
+ conn.connection,
+ '_heartbeat_supported_and_enabled',
+ return_value=True):
+ conn.connection.connection.drain_events = mock.Mock(
+ side_effect=TimeoutError)
+ self.assertRaises(driver_common.Timeout,
+ conn.consume, timeout=0.01)
+ # _heartbeat_check is only called when timeout raised,
+ # otherwise it is unnecessary as the connection is alive.
+ mock_hb.assert_called()
+
class TestRabbitTransportURL(test_utils.BaseTestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/oslo_messaging-18.3.0/releasenotes/notes/fix-qmanager-pgid-setsid-race-0a1b2c3d4e5f6a7b.yaml
new/oslo_messaging-18.3.1/releasenotes/notes/fix-qmanager-pgid-setsid-race-0a1b2c3d4e5f6a7b.yaml
---
old/oslo_messaging-18.3.0/releasenotes/notes/fix-qmanager-pgid-setsid-race-0a1b2c3d4e5f6a7b.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/oslo_messaging-18.3.1/releasenotes/notes/fix-qmanager-pgid-setsid-race-0a1b2c3d4e5f6a7b.yaml
2026-10-19 13:23:14.303733800 +0200
@@ -0,0 +1,17 @@
+---
+fixes:
+ - |
+ Fixed a reply queue name collision with ``use_queue_manager = True``. The
+ ``QManager`` used to only rely on the process-group identity captured at
+ object construction time to key the shared ``/dev/shm`` counter. When the
+ process group changes after some oslo.messaging objects already exist --
+ as happens under the cotyledon supervisor, whose master calls
+ ``os.setsid()`` after creating them -- a manager born in the master and a
+ manager born in a worker disagreed on that identity and kept resetting the
+ shared counter, so several processes built the same reply
+ queue name (``reply_<host>:<proc>:1``) and stole each other's RPC replies
+ (leading to ``No calling threads waiting for msg_id`` and
+ ``MessagingTimeout``). The identity is now re-evaluated when the queue
+ name is built. When the process group id is 0 (crun edge case) it falls
+ back to the process id, as before. See
+ `bug 2110957 <https://bugs.launchpad.net/oslo.messaging/+bug/2110957>`_.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/oslo_messaging-18.3.0/tox.ini
new/oslo_messaging-18.3.1/tox.ini
--- old/oslo_messaging-18.3.0/tox.ini 2026-08-24 15:59:43.053770500 +0200
+++ new/oslo_messaging-18.3.1/tox.ini 2026-10-19 13:23:14.378805000 +0200
@@ -8,7 +8,7 @@
ZUUL_CACHE_DIR
REQUIREMENTS_PIP_LOCATION
constraints =
-
{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+
{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.2}
deps =
-r{toxinidir}/test-requirements.txt
commands =